From 2aad749e2474f79a532764c9f6f7ce4abc3a68f0 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 27 Feb 2026 20:58:21 -0800 Subject: [PATCH] Reuse persisted cwd when recovering stale provider sessions - Read and validate `cwd` from persisted runtime payload when restarting sessions - Apply persisted `cwd` during stale-session recovery paths, including `sendTurn` - Add tests to verify resumed sessions preserve the original working directory --- .../provider/Layers/ProviderService.test.ts | 34 +++++++++++++++++++ .../src/provider/Layers/ProviderService.ts | 15 ++++++++ 2 files changed, 49 insertions(+) diff --git a/apps/server/src/provider/Layers/ProviderService.test.ts b/apps/server/src/provider/Layers/ProviderService.test.ts index d6976479af046e4ec32095239011c59b0bc58b53..b18a838939d31e2a6258f0c70798b2d64a80ba18 100644 --- a/apps/server/src/provider/Layers/ProviderService.test.ts +++ b/apps/server/src/provider/Layers/ProviderService.test.ts @@ -378,6 +378,7 @@ it.effect( [ { provider: "codex", + cwd: "/tmp/project", resumeThreadId: startedSession.threadId, }, ], @@ -465,6 +466,7 @@ routing.layer("ProviderServiceLive routing", (it) => { [ { provider: "codex", + cwd: "/tmp/project", resumeThreadId: initial.threadId, }, ], @@ -475,6 +477,38 @@ routing.layer("ProviderServiceLive routing", (it) => { }), ); + it.effect("recovers stale sessions for sendTurn using persisted cwd", () => + Effect.gen(function* () { + const provider = yield* ProviderService; + + const initial = yield* provider.startSession(asThreadId("thread-1"), { + provider: "codex", + cwd: "/tmp/project-send-turn", + }); + + yield* provider.stopAll(); + routing.codex.startSession.mockClear(); + routing.codex.sendTurn.mockClear(); + + yield* provider.sendTurn({ + sessionId: initial.sessionId, + input: "resume", + attachments: [], + }); + + assert.deepEqual(routing.codex.startSession.mock.calls, [ + [ + { + provider: "codex", + cwd: "/tmp/project-send-turn", + resumeThreadId: initial.threadId, + }, + ], + ]); + assert.equal(routing.codex.sendTurn.mock.calls.length, 1); + }), + ); + it.effect("stops all sessions and clears adapter runtime", () => Effect.gen(function* () { const provider = yield* ProviderService; diff --git a/apps/server/src/provider/Layers/ProviderService.ts b/apps/server/src/provider/Layers/ProviderService.ts index 1a50a95d22cc7d2164b2d91b21d611eab719c0b1..d49dc5579ffe887c479f45c3e0ab0f63a7f5eb42 100644 --- a/apps/server/src/provider/Layers/ProviderService.ts +++ b/apps/server/src/provider/Layers/ProviderService.ts @@ -93,6 +93,18 @@ function toRuntimePayloadFromSession(session: ProviderSession): Record 0 ? trimmed : undefined; +} + const makeProviderService = (options?: ProviderServiceLiveOptions) => Effect.gen(function* () { const canonicalEventLogger = @@ -266,8 +278,11 @@ const makeProviderService = (options?: ProviderServiceLiveOptions) => ); } + const persistedCwd = readPersistedCwd(input.binding.runtimePayload); + const resumed = yield* adapter.startSession({ provider: input.binding.provider, + ...(persistedCwd ? { cwd: persistedCwd } : {}), ...(resumeThreadId ? { resumeThreadId } : {}), ...(hasResumeCursor ? { resumeCursor: input.binding.resumeCursor } : {}), }); -- 2.54.0