fix(sdk): treat terminal wait timeouts as timed out (#74697)

* fix: wait-status mapping sdk regression

* fix(sdk): treat terminal wait timeouts as timed out

---------

Co-authored-by: openclaw-clawsweeper[bot] <280122609+openclaw-clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper-repair <clawsweeper-repair@users.noreply.github.com>
This commit is contained in:
clawsweeper[bot]
2026-04-30 00:27:32 -07:00
committed by GitHub
parent 25f0b5dda3
commit 8d4928b505
3 changed files with 36 additions and 2 deletions

View File

@@ -75,6 +75,7 @@ Docs: https://docs.openclaw.ai
- Gateway/models: serve the last successful model catalog while stale reloads refresh in the background, so Gateway control-plane and OpenAI-compatible requests no longer block behind model-provider rediscovery after model config changes. Refs #74135, #74630, and #74633. Thanks @DerFlash, @moltar-bot, and @Saboor711.
- CLI/status: resolve read-only channel setup runtime fallback from the packaged OpenClaw dist root, so `status --all`, `status --deep`, channel, and doctor paths do not crash when an external channel plugin needs setup metadata. Fixes #74693. Thanks @giangthb.
- SDK/events: keep per-run SDK event streams from surfacing duplicate raw chat projection frames, while normalizing chat-only projection frames and preserving raw access through `rawEvents`. Refs #74704. Thanks @BunsDev.
- SDK: report Gateway terminal `agent.wait` timeout snapshots with lifecycle metadata as `timed_out` while keeping bare wait deadlines non-terminal. Thanks @clawsweeper.
- Google Meet: block managed Chrome intro/test speech until browser health proves the participant is in-call, and expose `speechReady` diagnostics so login, admission, permission, and audio-bridge blockers no longer look like successful speech. Refs #72478. Thanks @DougButdorf.
- Slack/commands: keep native command argument menus on select controls for encoded choice values up to Slack's option limit and truncate fallback button labels to Slack's button-text limit, so long valid choices no longer render invalid Slack blocks. Thanks @slackapi.
- Agents/Codex: flush accepted debounced steering messages before normal app-server turn cleanup, so inbound follow-ups acknowledged as queued are not dropped when the turn completes before the debounce fires. Thanks @vincentkoc.

View File

@@ -42,10 +42,16 @@ function resolveGatewayUrl(options: OpenClawOptions): string | undefined {
function runStatusFromWaitPayload(payload: unknown): RunResult["status"] {
const record =
typeof payload === "object" && payload !== null
? (payload as { aborted?: unknown; status?: unknown; stopReason?: unknown })
? (payload as Record<string, unknown> & { aborted?: unknown; status?: unknown })
: {};
const status = typeof record.status === "string" ? record.status.toLowerCase() : undefined;
const stopReason = typeof record.stopReason === "string" ? record.stopReason.toLowerCase() : "";
const hasTerminalTimeoutMetadata =
readOptionalTimestamp(record.endedAt) !== undefined ||
readOptionalString(record.error) !== undefined ||
stopReason.length > 0 ||
typeof record.livenessState === "string" ||
record.yielded === true;
if (
status === "aborted" ||
status === "cancelled" ||
@@ -65,7 +71,12 @@ function runStatusFromWaitPayload(payload: unknown): RunResult["status"] {
return "completed";
}
if (status === "timeout") {
if (stopReason === "timeout" || stopReason === "timed_out" || record.aborted === true) {
if (
stopReason === "timeout" ||
stopReason === "timed_out" ||
record.aborted === true ||
hasTerminalTimeoutMetadata
) {
return "timed_out";
}
return "accepted";

View File

@@ -176,6 +176,28 @@ describe("OpenClaw SDK", () => {
});
});
it("maps terminal timeout snapshots without stop reasons to timed_out", async () => {
const transport = new FakeTransport({
"agent.wait": {
status: "timeout",
runId: "run_timed_out",
startedAt: 123,
endedAt: 456,
},
});
const oc = new OpenClaw({ transport });
const result = await oc.runs.wait("run_timed_out");
expect(result).toMatchObject({
runId: "run_timed_out",
status: "timed_out",
startedAt: 123,
endedAt: 456,
});
expect(result.error).toBeUndefined();
});
it("splits provider-qualified model refs and rejects unsupported run options", async () => {
const transport = new FakeTransport({
agent: { status: "accepted", runId: "run_openrouter" },