mirror of
https://github.com/modelstudioai/cli.git
synced 2026-09-14 19:49:23 +08:00
fix(agent): timeout error
This commit is contained in:
@@ -33,23 +33,47 @@ function parseSdkResponseBody(raw: string): ApiErrorBody {
|
||||
return { message: raw.trim() || undefined };
|
||||
}
|
||||
|
||||
/**
|
||||
* The SDK's session polling deadline surfaces as a plain `UserError` (no
|
||||
* dedicated timeout class as of SDK 0.3.x), so it is recognized by its stable
|
||||
* message shape: "Session did not complete within the timeout (N seconds)."
|
||||
* (session-runtime's assertNotTimedOut — the SDK's only timeout UserError).
|
||||
* It is a client-side wait limit, not a usage mistake → per bl's error
|
||||
* boundary it must exit TIMEOUT, not USAGE.
|
||||
*/
|
||||
function isSdkPollingTimeout(error: UserError): boolean {
|
||||
return /did not complete within the timeout/i.test(error.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run an SDK-backed operation, translating SDK error types into BailianError so
|
||||
* bl's error handler produces the right exit code and hint formatting.
|
||||
* SDK `UserError` → USAGE; SDK `ApiError` (server HTTP error) → GENERAL via
|
||||
* `mapApiError` (server message passed through verbatim, with
|
||||
* httpStatus/apiCode/requestId metadata for --output json); fetch transport
|
||||
* failures (`TypeError: fetch failed`) are rethrown untouched so the runtime
|
||||
* error handler maps them to NETWORK with an errno-specific hint, matching the
|
||||
* native client path; any other Error → GENERAL (message passed through, per
|
||||
* bl's "don't translate server errors" boundary).
|
||||
* SDK `UserError` → USAGE — except the polling-deadline UserError, which is a
|
||||
* client-side timeout → TIMEOUT with a wait-longer hint; SDK `ApiError`
|
||||
* (server HTTP error) → GENERAL via `mapApiError` (server message passed
|
||||
* through verbatim, with httpStatus/apiCode/requestId metadata for
|
||||
* --output json); fetch transport failures (`TypeError: fetch failed`) are
|
||||
* rethrown untouched so the runtime error handler maps them to NETWORK with an
|
||||
* errno-specific hint, matching the native client path; any other Error →
|
||||
* GENERAL (message passed through, per bl's "don't translate server errors"
|
||||
* boundary).
|
||||
*/
|
||||
export async function withAgentErrors<T>(fn: () => Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
if (error instanceof BailianError) throw error;
|
||||
if (error instanceof UserError) throw new BailianError(error.message, ExitCode.USAGE);
|
||||
if (error instanceof UserError) {
|
||||
if (isSdkPollingTimeout(error)) {
|
||||
throw new BailianError(
|
||||
error.message,
|
||||
ExitCode.TIMEOUT,
|
||||
// `bl` prefix is safe: agent commands ship on `bl` only.
|
||||
"The session may still be running — check `bl managed-agent session get --session-id <id>` or `session events`.",
|
||||
);
|
||||
}
|
||||
throw new BailianError(error.message, ExitCode.USAGE);
|
||||
}
|
||||
if (error instanceof Error && isSdkApiError(error)) {
|
||||
throw mapApiError(error.statusCode, parseSdkResponseBody(error.responseBody));
|
||||
}
|
||||
|
||||
@@ -39,6 +39,22 @@ test("SDK UserError maps to USAGE", async () => {
|
||||
expect(mapped.message).toBe("bad agents.yaml");
|
||||
});
|
||||
|
||||
test("SDK polling-timeout UserError maps to TIMEOUT (5), not USAGE", async () => {
|
||||
// 消息形状来自 SDK session-runtime 的 assertNotTimedOut —— 客户端等待超时,
|
||||
// 按 bl 错误边界必须归 TIMEOUT,不能告诉自动化调用方“参数错误”。
|
||||
const mapped = await catchMapped(
|
||||
new UserError("Session did not complete within the timeout (600 seconds)."),
|
||||
);
|
||||
expect(mapped.exitCode).toBe(ExitCode.TIMEOUT);
|
||||
expect(mapped.message).toBe("Session did not complete within the timeout (600 seconds).");
|
||||
expect(mapped.hint).toMatch(/session get/);
|
||||
});
|
||||
|
||||
test("提及 timeout 但非轮询超时句式的 UserError 仍归 USAGE", async () => {
|
||||
const mapped = await catchMapped(new UserError("Invalid timeout value in agents.yaml"));
|
||||
expect(mapped.exitCode).toBe(ExitCode.USAGE);
|
||||
});
|
||||
|
||||
test("SDK ApiError with DashScope-style JSON body surfaces clean message and api metadata", async () => {
|
||||
const body = JSON.stringify({
|
||||
code: "InvalidParameter",
|
||||
|
||||
Reference in New Issue
Block a user