From d5fbd4fa5f0074058565cfbf5008b4d2d8a085f0 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 24 Sep 2026 11:28:37 -0700 Subject: [PATCH 1/2] fix(server): Codex V2 sessions expand ~ in the binary path The Codex driver expands a leading ~ in binaryPath for the readiness probe, but the V2 adapter spread the raw config into its settings, so a path like ~/bin/codex passed the probe and then failed to spawn for every real session. Expand it where the adapter builds its settings, next to the home path resolution. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../Adapters/CodexAdapterV2.test.ts | 49 +++++++++++++++++++ .../Adapters/CodexAdapterV2.ts | 2 + 2 files changed, 51 insertions(+) diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts index fe9ec4c4fc81..e4174c69b638 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts @@ -1,3 +1,6 @@ +// @effect-diagnostics-next-line nodeBuiltinImport:off +import * as NodeOS from "node:os"; + import { historyResponseItems } from "../ContextHandoffBudget.ts"; import type { ProviderAdapterV2HistoricalContext } from "../ProviderAdapter.ts"; import { @@ -38,6 +41,7 @@ import * as Predicate from "effect/Predicate"; import * as FileSystem from "effect/FileSystem"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; +import * as Path from "effect/Path"; import * as PlatformError from "effect/PlatformError"; import * as Ref from "effect/Ref"; import * as Schema from "effect/Schema"; @@ -45,6 +49,7 @@ import * as Stream from "effect/Stream"; import { TestClock } from "effect/testing"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; +import { ServerConfig } from "../../config.ts"; import * as McpProviderSession from "../../mcp/McpProviderSession.ts"; import type { EventNdjsonLogger } from "../../provider/Layers/EventNdjsonLogger.ts"; import { @@ -77,6 +82,7 @@ import { CodexAppServerClientFactory, codexAppServerClientFactoryFromSettingsLayer, type CodexAppServerClientFactoryShape, + createCodexAdapterV2, makeCodexAdapterV2, makeCodexAppServerProtocolLogger, makeCodexAppServerSpawnCommand, @@ -746,6 +752,49 @@ describe("CodexAdapterV2 process spawning", () => { ]); }).pipe(Effect.provideService(HostProcessPlatform, "linux")), ); + + it.effect("expands ~ in the configured binary path before spawning", () => + Effect.gen(function* () { + const spawnedCommands: Array = []; + const spawner = ChildProcessSpawner.make((command) => { + if (ChildProcess.isStandardCommand(command)) spawnedCommands.push(command.command); + return Effect.fail( + PlatformError.systemError({ _tag: "NotFound", module: "ChildProcess", method: "spawn" }), + ); + }); + const path = yield* Path.Path; + const adapter = yield* createCodexAdapterV2({ + instanceId: CODEX_DEFAULT_INSTANCE_ID, + displayName: undefined, + environment: [], + enabled: true, + config: { ...DEFAULT_CODEX_SETTINGS, binaryPath: "~/bin/codex" }, + }).pipe( + Effect.provide( + Layer.mergeAll( + codexAppServerClientFactoryFromSettingsLayer, + ServerConfig.layerTest(process.cwd(), { prefix: "t3-codex-binary-home-" }), + ), + ), + Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), + Effect.provideService(ProviderEventLoggers, NoOpProviderEventLoggers), + ); + + yield* adapter + .openSession({ + threadId: ThreadId.make("thread-binary-home"), + providerSessionId: ProviderSessionId.make("provider-session-binary-home"), + modelSelection: CODEX_TEST_MODEL_SELECTION, + runtimePolicy: CODEX_TEST_RUNTIME_POLICY, + }) + .pipe(Effect.scoped, Effect.exit); + + assert.deepEqual(spawnedCommands, [path.join(NodeOS.homedir(), "bin", "codex")]); + }).pipe( + Effect.provide(Layer.merge(idAllocatorLayer, NodeServices.layer)), + Effect.provideService(HostProcessPlatform, "linux"), + ), + ); }); describe("CodexAdapterV2 dynamic tool projection", () => { diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts index 5af8a14fbf65..46cb637eb932 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts @@ -79,6 +79,7 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; import { getCodexServiceTierOptionValue } from "../../codexModelOptions.ts"; import { ServerConfig } from "../../config.ts"; +import { expandHomePath } from "../../pathExpansion.ts"; import { buildCodexDeveloperInstructions } from "../../provider/CodexDeveloperInstructions.ts"; import { describeMcpElicitation, @@ -1411,6 +1412,7 @@ export const createCodexAdapterV2 = ( const settings = { ...config, enabled, + binaryPath: expandHomePath(config.binaryPath), homePath: homeLayout.effectiveHomePath ?? "", } satisfies CodexSettings; From b05560849cbb9bba97601fea8c5910485647eabc Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 24 Sep 2026 12:46:44 -0700 Subject: [PATCH 2/2] test(server): drop an unused diagnostics directive The nodeBuiltinImport rule does not apply to this test file, so the directive tripped TS377000 in the typecheck step. Co-Authored-By: Claude Opus 5.5 (1M context) --- apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts index e4174c69b638..2ee436f3dcbf 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts @@ -1,4 +1,3 @@ -// @effect-diagnostics-next-line nodeBuiltinImport:off import * as NodeOS from "node:os"; import { historyResponseItems } from "../ContextHandoffBudget.ts";