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
3 changes: 3 additions & 0 deletions packages/cli/src/templates/codex/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ project_doc_fallback_filenames = ["AGENTS.md"]
# - max_concurrent_threads_per_session: keeps at most three bounded workers.
# - min_wait_timeout_ms: 120000 ms = 2 min. A worker that needs longer must
# report progress through its task record instead of holding the coordinator.
# - default_wait_timeout_ms must be at least the configured minimum; Codex
# rejects the project config at startup when its built-in default is lower.
[features.multi_agent_v2]
enabled = true
max_concurrent_threads_per_session = 3
min_wait_timeout_ms = 120000
default_wait_timeout_ms = 120000

# Native custom-agent dispatch uses the validated Hermes dispatch and Result
# Envelope files. It works directly in the current project workspace.
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/utils/uninstall-scrubbers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ const LEGACY_CODEX_COMMENTS = new Set([
"[agents].max_threads (codex will reject the combination).",
"NOT auto-enable the feature without it.",
"report progress through its task record instead of holding the coordinator.",
"default_wait_timeout_ms must be at least the configured minimum; Codex",
"rejects the project config at startup when its built-in default is lower.",
"short for Trellis subagents that routinely take 2-10 min. Hard",
"ceiling is 3,600,000 (1 h).",
"Native custom-agent dispatch cannot mechanically replace or validate the",
Expand All @@ -66,6 +68,7 @@ const LEGACY_CODEX_TABLE_VALUES = new Map<string, Set<string>>([
"min_wait_timeout_ms = 480000",
"max_concurrent_threads_per_session = 3",
"min_wait_timeout_ms = 120000",
"default_wait_timeout_ms = 120000",
]),
],
[
Expand Down Expand Up @@ -93,7 +96,8 @@ function isLegacyCodexComment(comment: string | null): boolean {
comment.startsWith("`wait` tool") ||
comment.startsWith("`enabled = true`") ||
comment.startsWith("- max_concurrent_threads_per_session:") ||
comment.startsWith("- min_wait_timeout_ms:")
comment.startsWith("- min_wait_timeout_ms:") ||
comment.startsWith("- default_wait_timeout_ms")
);
}

Expand Down
4 changes: 3 additions & 1 deletion packages/cli/test/commands/update.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,16 @@ describe("update() integration", () => {
.replace(
"max_concurrent_threads_per_session = 3",
"max_concurrent_threads_per_session = 1",
);
)
.replace("default_wait_timeout_ms = 120000\n", "");
fs.writeFileSync(configPath, customized);

await update({});

const updated = fs.readFileSync(configPath, "utf-8");
expect(updated).toContain('model = "gpt-5.6-custom"');
expect(updated).toContain("max_concurrent_threads_per_session = 3");
expect(updated).toContain("default_wait_timeout_ms = 120000");
expect(readHashesV2(hashFilePath())[configRelative]).toBe(
computeHash(updated),
);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/test/configurators/platforms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ describe("configurePlatform", () => {
const written = fs.readFileSync(configPath, "utf-8");
expect(written).toContain('model = "gpt-5.6-custom"');
expect(written).toContain("max_concurrent_threads_per_session = 3");
expect(written).toContain("default_wait_timeout_ms = 120000");
});

it("configurePlatform('codex') adds managed settings to an unmarked user config", async () => {
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/test/scripts/closure.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1306,11 +1306,20 @@ describe.skipIf(!hasPython())("Lean Research Closure CLI", () => {
it("shows a compact timeline without raw dispatch audit payloads", () => {
expect(plan(root, ["Result A"]).status).toBe(0);
expect(run(root, "validate").status).toBe(0);
const latestTaskEvent = JSON.parse(
fs.readFileSync(path.join(taskDir, "hermes", "task-events.jsonl"), "utf-8")
.trim()
.split("\n")
.at(-1) ?? "{}",
) as { timestamp?: string };
const dispatchTimestamp = new Date(
Date.parse(latestTaskEvent.timestamp ?? "1970-01-01T00:00:00Z") + 1_000,
).toISOString();
fs.writeFileSync(
path.join(taskDir, "hermes", "dispatch_audit.jsonl"),
`${JSON.stringify({
type: "dispatch_audit",
timestamp: "2026-07-25T12:00:00+00:00",
timestamp: dispatchTimestamp,
job_id: "job-7",
event: "result_confirmed",
decision: "RAW_DECISION_MUST_NOT_APPEAR\n+++ a/dispatch.log",
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/test/templates/codex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@ describe("codex getConfigTemplate", () => {
expect(config.content).toContain("# TRELLIS:CODEX_MODEL_DEFAULTS:END");
expect(config.content).toContain('native_authority = "protocol"');
expect(config.content).not.toContain("strict_authority");

const minimum = Number(
config.content.match(/min_wait_timeout_ms\s*=\s*(\d+)/)?.[1],
);
const defaultWait = Number(
config.content.match(/default_wait_timeout_ms\s*=\s*(\d+)/)?.[1],
);
expect(minimum).toBeGreaterThan(0);
expect(defaultWait).toBeGreaterThanOrEqual(minimum);
});
});

Expand Down