From a2a58357a438dc9316095701ad08fb7b4e492e4b Mon Sep 17 00:00:00 2001 From: bft-codebot Date: Mon, 11 May 2026 02:46:23 +0000 Subject: [PATCH] sync(bfmono): fix(gambit): run nested codex decks from bot root (+19 more) (bfmono@426e8dbbf) This PR is an automated gambitmono sync of bfmono Gambit packages. - Source: `packages/gambit/` - Core: `packages/gambit/packages/gambit-core/` - bfmono rev: 426e8dbbf Changes: - 426e8dbbf fix(gambit): run nested codex decks from bot root - 5e9f37c1d fix(gambit): pass Codex app-server thread sandbox authority - 8952a1f9b docs(gambit): point policy references at memos vault - 412e36c9f feat(gambit): expose workloop task host service methods - bd65fd7bc fix(gambit): remove task delegation host service methods - 5776f3e98 feat(workloop): route runtime work through host services - af2da504c chore(gambit): cut 1.0.0-rc.2 - 84f610a1d docs(workloop): align Gambit brand hierarchy - ca4aff086 chore(gambit): remove legacy desktop fallbacks - e281e27f1 fix(gambit): hydrate chat transcript from persisted state - 35438f961 chore(gambit): record full precommit verification - e88e4957e docs(gambit): reposition around scenarios and graders - 2d2691076 fix(gambit): reject sandboxed chat runs - 98ab911eb docs(gambit): use canonical graders frontmatter - bb582eeb6 feat(gambit): improve chat event observability - 43e0588ba feat(gambit): stream and control chat turns - 538d0ad1b feat(gambit): add local deck chat repro server - 60078d9f6 fix(workloop): disable Codex websockets in chief runtime - 93d44fb06 fix(gambit): preserve whitespace in streamed assistant deltas - 76e21a05f fix(workloop): preserve Codex auth refresh failures Do not edit this repo directly; make changes in bfmono and re-run the sync. --- src/cli.codex_smoke.test.ts | 44 +++++++++++++++++++++++++++++++++++++ src/providers/codex.ts | 21 ++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/cli.codex_smoke.test.ts b/src/cli.codex_smoke.test.ts index cf5aa7c0..096de176 100644 --- a/src/cli.codex_smoke.test.ts +++ b/src/cli.codex_smoke.test.ts @@ -222,6 +222,7 @@ async function runDeck(input: { cwd?: string; command?: "run" | "repl"; extraArgs?: Array; + env?: Record; }): Promise<{ code: number; stdout: string; @@ -244,6 +245,7 @@ async function runDeck(input: { GAMBIT_CODEX_DISABLE_MCP: "1", CODEX_ARGS_LOG: input.argsLogPath, CODEX_REQUESTS_LOG: input.requestLogPath, + ...(input.env ?? {}), }, stdout: "piped", stderr: "piped", @@ -503,6 +505,48 @@ Deno.test({ ), true, ); + + await Deno.remove(mock.requestLogPath).catch((err) => { + if (err instanceof Deno.errors.NotFound) return; + throw err; + }); + const nestedDeckDir = path.join( + dir, + "coworkers", + "agents", + "chief-of-staff", + ); + await Deno.mkdir(nestedDeckDir, { recursive: true }); + const nestedDeck = await writeDeck( + nestedDeckDir, + "codex-cli/default", + undefined, + "Nested Chief deck.", + ); + const nestedRun = await runDeck({ + deckPath: nestedDeck, + codexBinPath: mock.binPath, + argsLogPath: mock.argsLogPath, + requestLogPath: mock.requestLogPath, + cwd: dir, + env: { GAMBIT_BOT_ROOT: dir }, + }); + assertEquals( + nestedRun.code, + 0, + formatCommandDiagnostics( + "run nested codex-cli/default under GAMBIT_BOT_ROOT", + nestedRun, + ), + ); + assertEquals( + nestedRun.requestLog.includes(`"cwd":"${dir}"`), + true, + ); + assertEquals( + nestedRun.requestLog.includes(`"writableRoots":["${dir}"]`), + true, + ); } finally { await Deno.remove(dir, { recursive: true }).catch((err) => { if (err instanceof Deno.errors.NotFound) return; diff --git a/src/providers/codex.ts b/src/providers/codex.ts index e5d4c0bb..394c3690 100644 --- a/src/providers/codex.ts +++ b/src/providers/codex.ts @@ -170,9 +170,30 @@ function codexDeckDir(deckPath?: string): string | undefined { ); } +function codexBotRootCwd(deckPath?: string): string | undefined { + const botRootRaw = Deno.env.get(BOT_ROOT_ENV)?.trim(); + if (!botRootRaw) return undefined; + const botRoot = path.resolve(botRootRaw); + const trimmedDeckPath = deckPath?.trim(); + if (!trimmedDeckPath) return botRoot; + const deckAbsolutePath = path.isAbsolute(trimmedDeckPath) + ? path.resolve(trimmedDeckPath) + : path.resolve(botRoot, trimmedDeckPath); + const relative = path.relative(botRoot, deckAbsolutePath); + if ( + relative === "" || + (!relative.startsWith("..") && !path.isAbsolute(relative)) + ) { + return botRoot; + } + return undefined; +} + function codexRunCwd(input: { cwd?: string; deckPath?: string }): string { const explicitCwd = input.cwd?.trim(); if (explicitCwd) return explicitCwd; + const botRoot = codexBotRootCwd(input.deckPath); + if (botRoot) return botRoot; const deckDir = codexDeckDir(input.deckPath); if (deckDir) return deckDir; return runCwd();