feat: merge per-group codex config overlay
This commit is contained in:
@@ -299,6 +299,76 @@ describe('agent-runner timeout behavior', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('merges a per-group codex config overlay before injecting managed MCP servers', async () => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
fakeProc = createFakeProcess();
|
||||||
|
const overlayPath =
|
||||||
|
'/tmp/nanoclaw-test-groups/test-group/.codex/config.toml';
|
||||||
|
const sessionConfigPath =
|
||||||
|
'/tmp/nanoclaw-test-data/sessions/test-group/.codex/config.toml';
|
||||||
|
let sessionToml = `model = "gpt-5.4"\n`;
|
||||||
|
|
||||||
|
vi.mocked(fs.existsSync).mockImplementation((p: fs.PathLike) => {
|
||||||
|
const str = String(p);
|
||||||
|
return (
|
||||||
|
str.includes('dist/index.js') ||
|
||||||
|
str.includes('dist/ipc-mcp-stdio.js') ||
|
||||||
|
str.endsWith('/.codex/config.toml') ||
|
||||||
|
str === overlayPath
|
||||||
|
);
|
||||||
|
});
|
||||||
|
vi.mocked(fs.readFileSync).mockImplementation((p: fs.PathLike | number) => {
|
||||||
|
const str = String(p);
|
||||||
|
if (str === overlayPath) {
|
||||||
|
return `# room-specific overlay
|
||||||
|
[mcp_servers.ouroboros]
|
||||||
|
command = "/tmp/ouroboros/bin/ouroboros"
|
||||||
|
args = ["mcp", "serve"]
|
||||||
|
|
||||||
|
[mcp_servers.ouroboros.env]
|
||||||
|
OUROBOROS_AGENT_RUNTIME = "codex"
|
||||||
|
OUROBOROS_LLM_BACKEND = "codex"
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
if (str === sessionConfigPath) {
|
||||||
|
return sessionToml;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
vi.mocked(fs.writeFileSync).mockImplementation(
|
||||||
|
(p: fs.PathLike | number, data: string | NodeJS.ArrayBufferView) => {
|
||||||
|
if (String(p) === sessionConfigPath) {
|
||||||
|
sessionToml = String(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const codexGroup: RegisteredGroup = {
|
||||||
|
...testGroup,
|
||||||
|
agentType: 'codex',
|
||||||
|
};
|
||||||
|
|
||||||
|
const resultPromise = runAgentProcess(
|
||||||
|
codexGroup,
|
||||||
|
testInput,
|
||||||
|
() => {},
|
||||||
|
async () => {},
|
||||||
|
);
|
||||||
|
|
||||||
|
fakeProc.emit('close', 0);
|
||||||
|
const result = await resultPromise;
|
||||||
|
expect(result.status).toBe('success');
|
||||||
|
|
||||||
|
const tomlWrite = vi
|
||||||
|
.mocked(fs.writeFileSync)
|
||||||
|
.mock.calls.filter((call) => String(call[0]) === sessionConfigPath)
|
||||||
|
.at(-1);
|
||||||
|
const toml = String(tomlWrite?.[1]);
|
||||||
|
expect(toml).toContain('[mcp_servers.ouroboros]');
|
||||||
|
expect(toml).toContain('OUROBOROS_AGENT_RUNTIME = "codex"');
|
||||||
|
expect(toml).toContain('[mcp_servers.nanoclaw]');
|
||||||
|
});
|
||||||
|
|
||||||
it('waits for queued streamed output before resolving an error exit', async () => {
|
it('waits for queued streamed output before resolving an error exit', async () => {
|
||||||
let releaseOutputs: (() => void) | undefined;
|
let releaseOutputs: (() => void) | undefined;
|
||||||
const outputsFlushed = new Promise<void>((resolve) => {
|
const outputsFlushed = new Promise<void>((resolve) => {
|
||||||
|
|||||||
@@ -253,6 +253,26 @@ function prepareGroupEnvironment(
|
|||||||
fs.copyFileSync(src, dst);
|
fs.copyFileSync(src, dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const groupCodexConfigOverlayPath = path.join(
|
||||||
|
groupDir,
|
||||||
|
'.codex',
|
||||||
|
'config.toml',
|
||||||
|
);
|
||||||
|
const sessionCodexConfigPath = path.join(sessionCodexDir, 'config.toml');
|
||||||
|
if (fs.existsSync(groupCodexConfigOverlayPath)) {
|
||||||
|
const overlayToml = fs
|
||||||
|
.readFileSync(groupCodexConfigOverlayPath, 'utf-8')
|
||||||
|
.trim();
|
||||||
|
if (overlayToml) {
|
||||||
|
const baseToml = fs.existsSync(sessionCodexConfigPath)
|
||||||
|
? fs.readFileSync(sessionCodexConfigPath, 'utf-8').trimEnd()
|
||||||
|
: '';
|
||||||
|
const mergedToml = [baseToml, overlayToml]
|
||||||
|
.filter((value) => value.length > 0)
|
||||||
|
.join('\n\n');
|
||||||
|
fs.writeFileSync(sessionCodexConfigPath, mergedToml + '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
const sessionAgentsPath = path.join(sessionCodexDir, 'AGENTS.md');
|
const sessionAgentsPath = path.join(sessionCodexDir, 'AGENTS.md');
|
||||||
const codexPlatformPrompt = readPlatformPrompt('codex', projectRoot);
|
const codexPlatformPrompt = readPlatformPrompt('codex', projectRoot);
|
||||||
const codexPairedRoomPrompt = isPairedRoom
|
const codexPairedRoomPrompt = isPairedRoom
|
||||||
|
|||||||
Reference in New Issue
Block a user