Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/cli.codex_smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ async function runDeck(input: {
cwd?: string;
command?: "run" | "repl";
extraArgs?: Array<string>;
env?: Record<string, string>;
}): Promise<{
code: number;
stdout: string;
Expand All @@ -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",
Expand Down Expand Up @@ -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;
Expand Down
21 changes: 21 additions & 0 deletions src/providers/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Loading