From ee494b70421ec068d061b589e37efa93ac7075e3 Mon Sep 17 00:00:00 2001 From: Victor Vogel Date: Sat, 24 Oct 2026 08:26:38 -0500 Subject: [PATCH 1/3] fix(server): Codex switches respect each instance's CODEX_HOME variable Co-Authored-By: Claude Opus 5.5 --- .../src/provider/Drivers/CodexDriver.test.ts | 41 +++++++++++++++++++ .../src/provider/Drivers/CodexDriver.ts | 9 +++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/apps/server/src/provider/Drivers/CodexDriver.test.ts b/apps/server/src/provider/Drivers/CodexDriver.test.ts index db8f42faea7f0..c198efea5d77e 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.test.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.test.ts @@ -383,6 +383,47 @@ it.layer(layerTest)("CodexDriver", (it) => { ), ); + it.effect.skipIf(windowsHost)( + "uses an environment CODEX_HOME as the instance's shared home", + () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const tempDir = yield* fs + .makeTempDirectoryScoped({ prefix: "t3-codex-driver-" }) + .pipe(Effect.flatMap((directory) => fs.realPath(directory))); + + const instances = yield* Effect.forEach(["work", "personal"], (account) => + Effect.gen(function* () { + const codexHome = NodePath.join(tempDir, `codex-${account}`); + const binaryPath = NodePath.join(codexHome, "packages", "standalone", "bin", "codex"); + yield* fs.makeDirectory(NodePath.dirname(binaryPath), { recursive: true }); + yield* fs.writeFileString(binaryPath, "#!/bin/sh\n"); + yield* fs.chmod(binaryPath, 0o755); + const instance = yield* CodexDriver.create({ + instanceId: ProviderInstanceId.make(`codex-${account}`), + displayName: account, + enabled: false, + environment: [{ name: "CODEX_HOME", value: codexHome, sensitive: false }], + config: { ...CodexDriver.defaultConfig(), binaryPath }, + }); + return { + codexHome, + continuationKey: instance.continuationIdentity.continuationKey, + update: (yield* instance.snapshot.resolveMaintenance()).update, + }; + }), + ); + + for (const { codexHome, continuationKey, update } of instances) { + expect(continuationKey).toBe(`codex:home:${codexHome}`); + expect(update?.env).toEqual({ CODEX_HOME: codexHome }); + } + }).pipe( + Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), + Effect.scoped, + ), + ); + for (const fixture of [ { name: "leaves mise npm-backend installations manual-only", diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index 5462b13867b71..21337c0cbfe32 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -140,7 +140,14 @@ export const CodexDriver: ProviderDriver< const modelCatalog = yield* ModelCatalog.ModelCatalog; const currentCatalog = modelCatalog.current(DRIVER_KIND); const processEnv = yield* mergeProviderInstanceEnvironment(environment); - const homeLayout = yield* resolveCodexHomeLayout(config); + // Codex reads CODEX_HOME from the environment when homePath is empty, so + // that home owns this instance's sessions and must key continuation. + const environmentHome = processEnv.CODEX_HOME?.trim(); + const homeLayout = yield* resolveCodexHomeLayout( + !config.homePath.trim() && !config.shadowHomePath.trim() && environmentHome + ? { ...config, homePath: environmentHome } + : config, + ); const continuationIdentity = codexContinuationIdentity(homeLayout); const stampIdentity = withInstanceIdentity({ instanceId, From bbf5c9c066c3344bf95ed638181e4a50510d31a3 Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 31 Oct 2026 08:04:20 +0000 Subject: [PATCH 2/3] test(server): simplify Codex environment home regression --- .../src/provider/Drivers/CodexDriver.test.ts | 55 ++++++------------- .../src/provider/Drivers/CodexDriver.ts | 2 - 2 files changed, 16 insertions(+), 41 deletions(-) diff --git a/apps/server/src/provider/Drivers/CodexDriver.test.ts b/apps/server/src/provider/Drivers/CodexDriver.test.ts index cda1d1e83adbe..2c4662159b21c 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.test.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.test.ts @@ -478,45 +478,22 @@ it.layer(layerTest)("CodexDriver", (it) => { }).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), Effect.scoped), ); - it.effect.skipIf(windowsHost)( - "uses an environment CODEX_HOME as the instance's shared home", - () => - Effect.gen(function* () { - const fs = yield* FileSystem.FileSystem; - const tempDir = yield* fs - .makeTempDirectoryScoped({ prefix: "t3-codex-driver-" }) - .pipe(Effect.flatMap((directory) => fs.realPath(directory))); - - const instances = yield* Effect.forEach(["work", "personal"], (account) => - Effect.gen(function* () { - const codexHome = NodePath.join(tempDir, `codex-${account}`); - const binaryPath = NodePath.join(codexHome, "packages", "standalone", "bin", "codex"); - yield* fs.makeDirectory(NodePath.dirname(binaryPath), { recursive: true }); - yield* fs.writeFileString(binaryPath, "#!/bin/sh\n"); - yield* fs.chmod(binaryPath, 0o755); - const instance = yield* CodexDriver.create({ - instanceId: ProviderInstanceId.make(`codex-${account}`), - displayName: account, - enabled: false, - environment: [{ name: "CODEX_HOME", value: codexHome, sensitive: false }], - config: { ...CodexDriver.defaultConfig(), binaryPath }, - }); - return { - codexHome, - continuationKey: instance.continuationIdentity.continuationKey, - update: (yield* instance.snapshot.resolveMaintenance()).update, - }; - }), - ); - - for (const { codexHome, continuationKey, update } of instances) { - expect(continuationKey).toBe(`codex:home:${codexHome}`); - expect(update?.env).toEqual({ CODEX_HOME: codexHome }); - } - }).pipe( - Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), - Effect.scoped, - ), + it.effect("uses each instance's environment CODEX_HOME for continuation", () => + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-codex-driver-" }); + for (const account of ["work", "personal"]) { + const codexHome = NodePath.join(tempDir, account); + const instance = yield* CodexDriver.create({ + instanceId: ProviderInstanceId.make(account), + displayName: account, + enabled: false, + environment: [{ name: "CODEX_HOME", value: codexHome, sensitive: false }], + config: CodexDriver.defaultConfig(), + }); + expect(instance.continuationIdentity.continuationKey).toBe(`codex:home:${codexHome}`); + } + }).pipe(Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, noSpawn), Effect.scoped), ); for (const fixture of [ diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index 5701d38c0ce3b..f0ae70213c7b0 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -151,8 +151,6 @@ export const CodexDriver: ProviderDriver< const modelCatalog = yield* ModelCatalog.ModelCatalog; const currentCatalog = modelCatalog.current(DRIVER_KIND); const processEnv = yield* mergeProviderInstanceEnvironment(environment); - // Codex reads CODEX_HOME from the environment when homePath is empty, so - // that home owns this instance's sessions and must key continuation. const environmentHome = processEnv.CODEX_HOME?.trim(); const homeLayout = yield* resolveCodexHomeLayout( !config.homePath.trim() && !config.shadowHomePath.trim() && environmentHome From 75408277981a28f431834841ab369efde0b6fefa Mon Sep 17 00:00:00 2001 From: maria-rcks Date: Sat, 31 Oct 2026 08:13:40 +0000 Subject: [PATCH 3/3] fix(server): preserve whitespace in Codex environment homes --- apps/server/src/provider/Drivers/CodexDriver.test.ts | 4 ++-- apps/server/src/provider/Drivers/CodexDriver.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/server/src/provider/Drivers/CodexDriver.test.ts b/apps/server/src/provider/Drivers/CodexDriver.test.ts index 2c4662159b21c..c047336ef5dfc 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.test.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.test.ts @@ -482,10 +482,10 @@ it.layer(layerTest)("CodexDriver", (it) => { Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; const tempDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-codex-driver-" }); - for (const account of ["work", "personal"]) { + for (const [index, account] of ["work", "personal", "work "].entries()) { const codexHome = NodePath.join(tempDir, account); const instance = yield* CodexDriver.create({ - instanceId: ProviderInstanceId.make(account), + instanceId: ProviderInstanceId.make(`account-${index}`), displayName: account, enabled: false, environment: [{ name: "CODEX_HOME", value: codexHome, sensitive: false }], diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index f0ae70213c7b0..c108fdba3aaab 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -151,9 +151,9 @@ export const CodexDriver: ProviderDriver< const modelCatalog = yield* ModelCatalog.ModelCatalog; const currentCatalog = modelCatalog.current(DRIVER_KIND); const processEnv = yield* mergeProviderInstanceEnvironment(environment); - const environmentHome = processEnv.CODEX_HOME?.trim(); + const environmentHome = processEnv.CODEX_HOME; const homeLayout = yield* resolveCodexHomeLayout( - !config.homePath.trim() && !config.shadowHomePath.trim() && environmentHome + !config.homePath.trim() && !config.shadowHomePath.trim() && environmentHome?.trim() ? { ...config, homePath: environmentHome } : config, );