diff --git a/src/mgr/server.ts b/src/mgr/server.ts index 1e9f9b0..5ff70dc 100644 --- a/src/mgr/server.ts +++ b/src/mgr/server.ts @@ -1484,12 +1484,12 @@ async function waitForEventsPage(input: { store: AgentRunStore; runId: string; a input.store.countEvents(input.runId, input.afterSeq), input.store.listCommands(input.runId, 0, 500), ]); - const latestCommand = commands.at(-1) ?? null; + const allCommandsTerminal = commands.length > 0 && commands.every((command) => isTerminalCommandState(command.state)); let completionReason: "expect-reached" | "run-terminal" | "command-terminal" | "timeout" | null = null; let timedOut = false; if (observed >= input.expect) completionReason = "expect-reached"; else if (isTerminalRunStatus(run.status)) completionReason = "run-terminal"; - else if (latestCommand !== null && isTerminalCommandState(latestCommand.state)) completionReason = "command-terminal"; + else if (allCommandsTerminal) completionReason = "command-terminal"; else if (Date.now() >= deadline) { completionReason = "timeout"; timedOut = true; diff --git a/src/selftest/cases/10-manager-memory.ts b/src/selftest/cases/10-manager-memory.ts index da8c2ff..0a8e086 100644 --- a/src/selftest/cases/10-manager-memory.ts +++ b/src/selftest/cases/10-manager-memory.ts @@ -90,6 +90,18 @@ async function assertEventsLongPolling(client: ManagerClient, store: MemoryAgent assert.equal(commandPage.timedOut, false); assert.equal(commandPage.completionReason, "command-terminal"); + const mixedCommandRun = createLongPollRun(store, "memory-mixed-command-state"); + store.createCommand(mixedCommandRun.id, { type: "turn", payload: { prompt: "still active" }, idempotencyKey: "memory-active-command" }); + const completedCommand = store.createCommand(mixedCommandRun.id, { type: "steer", payload: { prompt: "completed steer" }, idempotencyKey: "memory-completed-steer" }); + store.finishCommand(completedCommand.id, { terminalStatus: "completed", failureKind: null, failureMessage: null }); + const mixedAfterSeq = store.listEvents(mixedCommandRun.id, 0, 500).at(-1)?.seq ?? 0; + const mixedWait = client.get(`/api/v1/runs/${encodeURIComponent(mixedCommandRun.id)}/events?afterSeq=${mixedAfterSeq}&expect=1&timeoutMs=1000&limit=20`) as Promise; + setTimeout(() => store.appendEvent(mixedCommandRun.id, "backend_status", { phase: "active-command-still-running" }), 10); + const mixedPage = await mixedWait; + assert.equal(mixedPage.timedOut, false); + assert.equal(mixedPage.observed, 1); + assert.equal(mixedPage.completionReason, "expect-reached"); + const runTerminal = createLongPollRun(store, "memory-run-terminal"); const runAfterSeq = store.listEvents(runTerminal.id, 0, 500).at(-1)?.seq ?? 0; const runWait = client.get(`/api/v1/runs/${encodeURIComponent(runTerminal.id)}/events?afterSeq=${runAfterSeq}&expect=10&timeoutMs=1000&limit=20`) as Promise;