Skip to content

Commit 83aefea

Browse files
fix(gateway): preserve startup-failed agent waits
1 parent 2ba9e47 commit 83aefea

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

src/gateway/server-methods/agent-job.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ let agentRunListenerStarted = false;
2626
type AgentRunSnapshot = {
2727
runId: string;
2828
status: "ok" | "error" | "timeout";
29+
phase?: "end" | "error" | "startup-failed";
2930
startedAt?: number;
3031
endedAt?: number;
3132
error?: string;
@@ -149,6 +150,7 @@ function createSnapshotFromLifecycleEvent(params: {
149150
runId,
150151
status:
151152
phase === "error" || phase === "startup-failed" ? "error" : data?.aborted ? "timeout" : "ok",
153+
phase,
152154
startedAt,
153155
endedAt,
154156
error,
@@ -194,6 +196,10 @@ function ensureAgentRunListener() {
194196
phase,
195197
data: evt.data,
196198
});
199+
const existing = getCachedAgentRun(evt.runId);
200+
if (phase === "end" && existing?.phase === "startup-failed") {
201+
return;
202+
}
197203
agentRunStarts.delete(evt.runId);
198204
if (phase === "error") {
199205
schedulePendingAgentRunError(snapshot);
@@ -348,7 +354,7 @@ export async function waitForAgentJob(params: {
348354
clearPendingTimeoutTimer();
349355
return;
350356
}
351-
if (phase !== "end" && phase !== "error") {
357+
if (phase !== "end" && phase !== "error" && phase !== "startup-failed") {
352358
return;
353359
}
354360
const latest = ignoreCachedSnapshot ? undefined : getCachedAgentRun(runId);
@@ -365,6 +371,11 @@ export async function waitForAgentJob(params: {
365371
scheduleErrorFinish(snapshot);
366372
return;
367373
}
374+
if (phase === "startup-failed") {
375+
recordAgentRunSnapshot(snapshot);
376+
finish(snapshot);
377+
return;
378+
}
368379
if (snapshot.status === "timeout") {
369380
scheduleTimeoutFinish(snapshot);
370381
return;

src/gateway/server-methods/server-methods.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,35 @@ describe("waitForAgentJob", () => {
9595
expect(snapshot?.endedAt).toBe(400);
9696
});
9797

98+
it("preserves startup-failed when a trailing lifecycle end follows", async () => {
99+
const runId = `run-startup-failed-${Date.now()}-${Math.random().toString(36).slice(2)}`;
100+
const waitPromise = waitForAgentJob({ runId, timeoutMs: 1_000 });
101+
102+
emitAgentEvent({
103+
runId,
104+
stream: "lifecycle",
105+
data: { phase: "start", startedAt: 425 },
106+
});
107+
emitAgentEvent({
108+
runId,
109+
stream: "lifecycle",
110+
data: { phase: "startup-failed", startedAt: 425, endedAt: 450, reason: "boot failed" },
111+
});
112+
emitAgentEvent({
113+
runId,
114+
stream: "lifecycle",
115+
data: { phase: "end", startedAt: 425, endedAt: 500 },
116+
});
117+
118+
const snapshot = await waitPromise;
119+
expect(snapshot).not.toBeNull();
120+
expect(snapshot?.status).toBe("error");
121+
expect(snapshot?.phase).toBe("startup-failed");
122+
expect(snapshot?.startedAt).toBe(425);
123+
expect(snapshot?.endedAt).toBe(450);
124+
expect(snapshot?.error).toBe("boot failed");
125+
});
126+
98127
it("ignores transient aborted end events when the same run later succeeds", async () => {
99128
const runId = `run-timeout-retry-${Date.now()}-${Math.random().toString(36).slice(2)}`;
100129
const waitPromise = waitForAgentJob({ runId, timeoutMs: 1_000 });

0 commit comments

Comments
 (0)