authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-10-05 00:13:16-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-05 00:13:16-07:00
logcf3e714b0f58e29c8fa8660db50d2187e3263b65
tree25b080b898a0b4d84624744098c1e4903d1d40d8
parent250e052f44dd313b658abebc707242a7b25be340
signature Signed by PGP key B5690EEEBB952194

fix(server): Stop ends a Codex command after its thread was settled (#15546)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

2 files changed, 18 insertions(+), 3 deletions(-)

apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts+11-3
......@@ -3917,7 +3917,9 @@ describe("CodexAdapterV2 post-settle continuation", () => {
39173917 ),
39183918 );
39193919
3920 const backgroundStopCases = [true, false, "still_running"] as const;
3920 // "thread_unloaded": the thread was settled, so T3 unsubscribed and Codex
3921 // unloaded it (killing its terminals) before Stop arrived.
3922 const backgroundStopCases = [true, false, "still_running", "thread_unloaded"] as const;
39213923 const makeBackgroundStopTranscript = (terminated: (typeof backgroundStopCases)[number]) => {
39223924 const stillRunning = terminated === "still_running";
39233925 return makeCodexReplayTranscript({
......@@ -3936,9 +3938,15 @@ describe("CodexAdapterV2 post-settle continuation", () => {
39363938 {
39373939 type: "emit_inbound",
39383940 label: "terminate-background-command",
3939 frame: { id: 4, result: { terminated: terminated === true } },
3941 frame:
3942 terminated === "thread_unloaded"
3943 ? {
3944 id: 4,
3945 error: { code: -32600, message: `thread not found: ${BG_NATIVE_THREAD}` },
3946 }
3947 : { id: 4, result: { terminated: terminated === true } },
39403948 },
3941 ...(terminated !== true
3949 ...(terminated === false || stillRunning
39423950 ? [
39433951 {
39443952 type: "expect_outbound" as const,
apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts+7
......@@ -2054,6 +2054,13 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi
20542054 Effect.catchTags({
20552055 CodexAppServerProcessExitedError: () => Effect.succeed({ terminated: true }),
20562056 CodexAppServerInputStreamEndedError: () => Effect.succeed({ terminated: true }),
2057 // The thread is unloaded, as Codex does a minute after a
2058 // settle or archive unsubscribes it. Unloading kills the
2059 // thread's terminals, so nothing is left to stop.
2060 CodexAppServerRequestError: (error) =>
2061 error.code === -32600 && error.errorMessage.startsWith("thread not found:")
2062 ? Effect.succeed({ terminated: true })
2063 : Effect.fail(error),
20572064 }),
20582065 );
20592066 const result = yield* decodeCodexBackgroundTerminalTerminateResponse(response);