refactor: centralize agent run pending status

This commit is contained in:
Peter Steinberger
2026-05-05 18:21:58 +01:00
parent b32d4c5255
commit 7188e4f4ad
5 changed files with 25 additions and 2 deletions

View File

@@ -70,6 +70,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Gateway/OpenAI-compatible: send the assistant role SSE chunk as soon as streaming chat-completion headers are accepted, so cold agent setup cannot leave `/v1/chat/completions` clients with a bodyless 200 response until their idle timeout fires.
- Agents/media: avoid direct generated-media completion fallback while the announce-agent run is still pending, so async video and music completions do not duplicate raw media messages. (#77754)
- TUI/sessions: bound the session picker to recent rows and use exact lookup-style refreshes for the active session, so dusty stores no longer make TUI hydrate weeks-old transcripts before becoming responsive. Thanks @vincentkoc.
- Doctor/gateway: report recent supervisor restart handoffs in `openclaw doctor --deep`, using the installed service environment when available so service-managed clean exits are visible in guided diagnostics. Thanks @shakkernerd.
- Gateway/status: show recent supervisor restart handoffs in `openclaw gateway status --deep`, including JSON details, so clean service-managed restarts are reported as restart handoffs instead of opaque stopped-service diagnostics. Thanks @shakkernerd.

View File

@@ -6,6 +6,7 @@ import { normalizeAccountId } from "../routing/session-key.js";
import { defaultRuntime } from "../runtime.js";
import { deriveSessionChatTypeFromKey } from "../sessions/session-chat-type-shared.js";
import { isCronSessionKey } from "../sessions/session-key-utils.js";
import { isNonTerminalAgentRunStatus } from "../shared/agent-run-status.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import {
mergeDeliveryContext,
@@ -694,7 +695,7 @@ function isGatewayAgentRunPending(response: unknown): boolean {
return false;
}
const status = (response as { status?: unknown }).status;
return status === "accepted" || status === "in_flight" || status === "started";
return isNonTerminalAgentRunStatus(status);
}
function inferCompletionChatType(params: {

View File

@@ -1,3 +1,4 @@
import { isNonTerminalAgentRunStatus } from "../../shared/agent-run-status.js";
import { setSafeTimeout } from "../../utils/timer-delay.js";
import type { DedupeEntry } from "../server-shared.js";
@@ -91,7 +92,7 @@ function readTerminalSnapshotFromDedupeEntry(entry: DedupeEntry): AgentWaitTermi
}
| undefined;
const status = typeof payload?.status === "string" ? payload.status : undefined;
if (status === "accepted" || status === "started" || status === "in_flight") {
if (isNonTerminalAgentRunStatus(status)) {
return null;
}

View File

@@ -0,0 +1,15 @@
import { describe, expect, it } from "vitest";
import { isNonTerminalAgentRunStatus } from "./agent-run-status.js";
describe("isNonTerminalAgentRunStatus", () => {
it.each(["accepted", "started", "in_flight"])("recognizes %s as non-terminal", (status) => {
expect(isNonTerminalAgentRunStatus(status)).toBe(true);
});
it.each(["ok", "error", "timeout", "queued", "", null, undefined, 1, {}, []])(
"does not recognize %s as non-terminal",
(status) => {
expect(isNonTerminalAgentRunStatus(status)).toBe(false);
},
);
});

View File

@@ -0,0 +1,5 @@
const NON_TERMINAL_AGENT_RUN_STATUSES = new Set(["accepted", "started", "in_flight"]);
export function isNonTerminalAgentRunStatus(status: unknown): boolean {
return typeof status === "string" && NON_TERMINAL_AGENT_RUN_STATUSES.has(status);
}