Skip to content

Commit 4ed89e4

Browse files
Treat retryable Codex errors as runtime warnings
- Map Codex `error` notifications with `willRetry: true` to `runtime.warning` - Keep active turns/sessions running during reconnect warnings - Add adapter and ingestion tests for retry-warning behavior
1 parent 99dbe4f commit 4ed89e4

3 files changed

Lines changed: 83 additions & 2 deletions

File tree

apps/server/src/orchestration/Layers/ProviderRuntimeIngestion.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,6 +1091,50 @@ describe("ProviderRuntimeIngestion", () => {
10911091
expect(thread.session?.lastError).toBe("runtime exploded");
10921092
});
10931093

1094+
it("keeps the session running when a runtime.warning arrives during an active turn", async () => {
1095+
const harness = await createHarness();
1096+
const now = new Date().toISOString();
1097+
1098+
harness.emit({
1099+
type: "turn.started",
1100+
eventId: asEventId("evt-warning-turn-started"),
1101+
provider: "codex",
1102+
createdAt: now,
1103+
threadId: asThreadId("thread-1"),
1104+
turnId: asTurnId("turn-warning"),
1105+
payload: {},
1106+
});
1107+
1108+
harness.emit({
1109+
type: "runtime.warning",
1110+
eventId: asEventId("evt-warning-runtime"),
1111+
provider: "codex",
1112+
createdAt: now,
1113+
threadId: asThreadId("thread-1"),
1114+
turnId: asTurnId("turn-warning"),
1115+
payload: {
1116+
message: "Reconnecting... 2/5",
1117+
detail: {
1118+
willRetry: true,
1119+
},
1120+
},
1121+
});
1122+
1123+
const thread = await waitForThread(
1124+
harness.engine,
1125+
(entry) =>
1126+
entry.session?.status === "running" &&
1127+
entry.session?.activeTurnId === "turn-warning" &&
1128+
entry.activities.some(
1129+
(activity: ProviderRuntimeTestActivity) =>
1130+
activity.id === "evt-warning-runtime" && activity.kind === "runtime.warning",
1131+
),
1132+
);
1133+
expect(thread.session?.status).toBe("running");
1134+
expect(thread.session?.activeTurnId).toBe("turn-warning");
1135+
expect(thread.session?.lastError).toBeNull();
1136+
});
1137+
10941138
it("maps session/thread lifecycle and item.started into session/activity projections", async () => {
10951139
const harness = await createHarness();
10961140
const now = new Date().toISOString();

apps/server/src/provider/Layers/CodexAdapter.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,42 @@ lifecycleLayer("CodexAdapterLive lifecycle", (it) => {
407407
}),
408408
);
409409

410+
it.effect("maps retryable Codex error notifications to runtime.warning", () =>
411+
Effect.gen(function* () {
412+
const adapter = yield* CodexAdapter;
413+
const firstEventFiber = yield* Stream.runHead(adapter.streamEvents).pipe(Effect.forkChild);
414+
415+
lifecycleManager.emit("event", {
416+
id: asEventId("evt-retryable-error"),
417+
kind: "notification",
418+
provider: "codex",
419+
threadId: asThreadId("thread-1"),
420+
createdAt: new Date().toISOString(),
421+
method: "error",
422+
turnId: asTurnId("turn-1"),
423+
payload: {
424+
error: {
425+
message: "Reconnecting... 2/5",
426+
},
427+
willRetry: true,
428+
},
429+
} satisfies ProviderEvent);
430+
431+
const firstEvent = yield* Fiber.join(firstEventFiber);
432+
433+
assert.equal(firstEvent._tag, "Some");
434+
if (firstEvent._tag !== "Some") {
435+
return;
436+
}
437+
assert.equal(firstEvent.value.type, "runtime.warning");
438+
if (firstEvent.value.type !== "runtime.warning") {
439+
return;
440+
}
441+
assert.equal(firstEvent.value.turnId, "turn-1");
442+
assert.equal(firstEvent.value.payload.message, "Reconnecting... 2/5");
443+
}),
444+
);
445+
410446
it.effect("preserves request type when mapping serverRequest/resolved", () =>
411447
Effect.gen(function* () {
412448
const adapter = yield* CodexAdapter;

apps/server/src/provider/Layers/CodexAdapter.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,13 +1192,14 @@ function mapToRuntimeEvents(
11921192
if (event.method === "error") {
11931193
const message =
11941194
asString(asObject(payload?.error)?.message) ?? event.message ?? "Provider runtime error";
1195+
const willRetry = payload?.willRetry === true;
11951196
return [
11961197
{
1197-
type: "runtime.error",
1198+
type: willRetry ? "runtime.warning" : "runtime.error",
11981199
...runtimeEventBase(event, canonicalThreadId),
11991200
payload: {
12001201
message,
1201-
class: "provider_error",
1202+
...(!willRetry ? { class: "provider_error" as const } : {}),
12021203
...(event.payload !== undefined ? { detail: event.payload } : {}),
12031204
},
12041205
},

0 commit comments

Comments
 (0)