oh-my-opencode 4.16.1 → 4.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/pre-publish-review/SKILL.md +3 -0
- package/README.md +2 -2
- package/dist/agents/hephaestus/agent.d.ts +1 -1
- package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
- package/dist/agents/momus-gpt-5-6.d.ts +1 -0
- package/dist/agents/types.d.ts +2 -0
- package/dist/cli/index.js +800 -536
- package/dist/cli-node/index.js +800 -536
- package/dist/features/background-agent/error-classifier.d.ts +5 -0
- package/dist/features/background-agent/parent-wake-dedupe.d.ts +1 -0
- package/dist/features/background-agent/parent-wake-flush-runner.d.ts +2 -0
- package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -0
- package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
- package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
- package/dist/index.js +464 -54
- package/dist/shared/live-server-route.d.ts +1 -1
- package/dist/skills/review-work/SKILL.md +4 -2
- package/dist/skills/start-work/SKILL.md +2 -0
- package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
- package/dist/skills/ulw-research/SKILL.md +5 -3
- package/dist/skills/visual-qa/SKILL.md +1 -1
- package/dist/tui.js +125 -13
- package/package.json +13 -13
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +398 -246
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
- package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
- package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
- package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
- package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
- package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -1
- package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
- package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +24 -41
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-executor.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +21 -49
- package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +18 -39
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +1 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +1 -0
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -0
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +73 -19
- package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
- package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +221 -20
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +71 -3
- package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +35 -7
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +7 -5
- package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +1 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +3 -1
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +3 -3
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +47 -1
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +1 -1
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
- package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +35 -1
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +412 -19
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +65 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +13 -4
- package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +436 -284
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
- package/packages/shared-skills/skills/review-work/SKILL.md +4 -2
- package/packages/shared-skills/skills/start-work/SKILL.md +2 -0
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/shared-skills/skills/ulw-research/SKILL.md +5 -3
- package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
|
@@ -493,7 +493,7 @@ test("#given multi_agent_v2 enabled #when forcing disable #then flips the flag t
|
|
|
493
493
|
"",
|
|
494
494
|
].join("\n");
|
|
495
495
|
|
|
496
|
-
const result = forceDisableMultiAgentV2(config);
|
|
496
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
497
497
|
|
|
498
498
|
assert.match(result, /enabled = false/);
|
|
499
499
|
assert.doesNotMatch(result, /enabled = true/);
|
|
@@ -510,7 +510,7 @@ test("#given no multi_agent_v2 section #when forcing disable #then appends a dis
|
|
|
510
510
|
"",
|
|
511
511
|
].join("\n");
|
|
512
512
|
|
|
513
|
-
const result = forceDisableMultiAgentV2(config);
|
|
513
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
514
514
|
|
|
515
515
|
assert.match(result, /\[features\.multi_agent_v2\]\nenabled = false\n/);
|
|
516
516
|
assert.match(result, /plugins = true/);
|
|
@@ -525,7 +525,7 @@ test("#given multi_agent_v2 section without enabled key #when forcing disable #t
|
|
|
525
525
|
"",
|
|
526
526
|
].join("\n");
|
|
527
527
|
|
|
528
|
-
const result = forceDisableMultiAgentV2(config);
|
|
528
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
529
529
|
|
|
530
530
|
assert.match(result, /\[features\.multi_agent_v2\]\nenabled = false\n/);
|
|
531
531
|
assert.match(result, /max_concurrent_threads_per_session = 10000/);
|
|
@@ -541,7 +541,7 @@ test("#given [features] boolean shorthand multi_agent_v2 = true #when forcing di
|
|
|
541
541
|
"",
|
|
542
542
|
].join("\n");
|
|
543
543
|
|
|
544
|
-
const result = forceDisableMultiAgentV2(config);
|
|
544
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
545
545
|
|
|
546
546
|
assert.doesNotMatch(result, /^multi_agent_v2\s*=/m);
|
|
547
547
|
assert.match(result, /\[features\.multi_agent_v2\]\nenabled = false\n/);
|
|
@@ -557,7 +557,7 @@ test("#given multi_agent_v2 enabled #when forcing disable #then annotates the ma
|
|
|
557
557
|
"",
|
|
558
558
|
].join("\n");
|
|
559
559
|
|
|
560
|
-
const result = forceDisableMultiAgentV2(config);
|
|
560
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
561
561
|
|
|
562
562
|
assert.match(result, /openai\/codex#26753/);
|
|
563
563
|
assert.match(result, /LAZYCODEX_CONFIG_MIGRATION_DISABLED=1/);
|
|
@@ -567,7 +567,7 @@ test("#given multi_agent_v2 enabled #when forcing disable #then annotates the ma
|
|
|
567
567
|
test("#given no multi_agent_v2 section #when forcing disable #then annotates the appended section", () => {
|
|
568
568
|
const config = ['model = "gpt-5.5"', ""].join("\n");
|
|
569
569
|
|
|
570
|
-
const result = forceDisableMultiAgentV2(config);
|
|
570
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
571
571
|
|
|
572
572
|
assert.match(result, /openai\/codex#26753/);
|
|
573
573
|
assert.match(result, /^#[^\n]*\n(?:#[^\n]*\n)*\[features\.multi_agent_v2\]\nenabled = false\n/m);
|
|
@@ -576,8 +576,8 @@ test("#given no multi_agent_v2 section #when forcing disable #then annotates the
|
|
|
576
576
|
test("#given an annotated managed section #when forcing disable runs again #then does not duplicate the comment", () => {
|
|
577
577
|
const config = ['model = "gpt-5.5"', "", "[features.multi_agent_v2]", "enabled = true", ""].join("\n");
|
|
578
578
|
|
|
579
|
-
const annotated = forceDisableMultiAgentV2(config);
|
|
580
|
-
const rerun = forceDisableMultiAgentV2(`${annotated.replace("enabled = false", "enabled = true")}
|
|
579
|
+
const annotated = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
580
|
+
const rerun = forceDisableMultiAgentV2(`${annotated.replace("enabled = false", "enabled = true")}`, { multiAgentVersion: null });
|
|
581
581
|
|
|
582
582
|
const markers = rerun.match(/openai\/codex#26753/g) ?? [];
|
|
583
583
|
assert.equal(markers.length, 1);
|
|
@@ -594,7 +594,7 @@ test("#given [features] boolean shorthand multi_agent_v2 = false #when forcing d
|
|
|
594
594
|
"",
|
|
595
595
|
].join("\n");
|
|
596
596
|
|
|
597
|
-
const result = forceDisableMultiAgentV2(config);
|
|
597
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
598
598
|
const parsed = parseTomlWithPython(result);
|
|
599
599
|
|
|
600
600
|
assert.doesNotMatch(result, /^\s*multi_agent_v2\s*=/m);
|
|
@@ -614,7 +614,7 @@ test("#given multi_agent_v2 already disabled #when forcing disable #then returns
|
|
|
614
614
|
"",
|
|
615
615
|
].join("\n");
|
|
616
616
|
|
|
617
|
-
const result = forceDisableMultiAgentV2(config);
|
|
617
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null });
|
|
618
618
|
|
|
619
619
|
assert.equal(result, config);
|
|
620
620
|
});
|
|
@@ -756,7 +756,7 @@ test("#given global config with forced multi_agent_v2 #when full migration runs
|
|
|
756
756
|
test("#given enabled = true with an inline comment #when forcing disable #then flips to false and preserves the comment", () => {
|
|
757
757
|
const config = ["[features.multi_agent_v2]", "enabled = true # tuned by me", ""].join("\n");
|
|
758
758
|
|
|
759
|
-
const result = forceDisableMultiAgentV2(config);
|
|
759
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
760
760
|
|
|
761
761
|
assert.match(result, /^enabled = false # tuned by me$/m);
|
|
762
762
|
assert.doesNotMatch(result, /enabled = true/);
|
|
@@ -767,7 +767,7 @@ test("#given enabled = true with an inline comment #when forcing disable #then f
|
|
|
767
767
|
test("#given a section header with an inline comment #when forcing disable #then patches in place without duplicating the table", () => {
|
|
768
768
|
const config = ["[features.multi_agent_v2] # pinned by me", "enabled = true", ""].join("\n");
|
|
769
769
|
|
|
770
|
-
const result = forceDisableMultiAgentV2(config);
|
|
770
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
771
771
|
|
|
772
772
|
assert.equal((result.match(/\[features\.multi_agent_v2\]/g) ?? []).length, 1);
|
|
773
773
|
assert.match(result, /enabled = false/);
|
|
@@ -779,19 +779,19 @@ test("#given an already-guarded commented config #when re-running #then output i
|
|
|
779
779
|
const configA = ["[features.multi_agent_v2]", "enabled = true # tuned by me", ""].join("\n");
|
|
780
780
|
const configB = ["[features.multi_agent_v2] # pinned by me", "enabled = true", ""].join("\n");
|
|
781
781
|
|
|
782
|
-
const firstA = forceDisableMultiAgentV2(configA);
|
|
783
|
-
const rerunA = forceDisableMultiAgentV2(firstA);
|
|
782
|
+
const firstA = forceDisableMultiAgentV2(configA, { multiAgentVersion: "v1" });
|
|
783
|
+
const rerunA = forceDisableMultiAgentV2(firstA, { multiAgentVersion: "v1" });
|
|
784
784
|
assert.equal(rerunA, firstA);
|
|
785
785
|
|
|
786
|
-
const firstB = forceDisableMultiAgentV2(configB);
|
|
787
|
-
const rerunB = forceDisableMultiAgentV2(firstB);
|
|
786
|
+
const firstB = forceDisableMultiAgentV2(configB, { multiAgentVersion: "v1" });
|
|
787
|
+
const rerunB = forceDisableMultiAgentV2(firstB, { multiAgentVersion: "v1" });
|
|
788
788
|
assert.equal(rerunB, firstB);
|
|
789
789
|
});
|
|
790
790
|
|
|
791
791
|
test("#given user-disabled with an inline comment #when forcing disable #then returns config unchanged", () => {
|
|
792
792
|
const config = ["[features.multi_agent_v2]", "enabled = false # I turned this off myself", ""].join("\n");
|
|
793
793
|
|
|
794
|
-
const result = forceDisableMultiAgentV2(config);
|
|
794
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
795
795
|
|
|
796
796
|
assert.equal(result, config);
|
|
797
797
|
});
|
|
@@ -799,7 +799,7 @@ test("#given user-disabled with an inline comment #when forcing disable #then re
|
|
|
799
799
|
test("#given [features] shorthand true with an inline comment #when forcing disable #then removes the shorthand and appends one disabled table", () => {
|
|
800
800
|
const config = ["[features]", "plugins = true", "multi_agent_v2 = true # legacy", ""].join("\n");
|
|
801
801
|
|
|
802
|
-
const result = forceDisableMultiAgentV2(config);
|
|
802
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
803
803
|
|
|
804
804
|
assert.doesNotMatch(result, /^\s*multi_agent_v2\s*=/m);
|
|
805
805
|
assert.equal((result.match(/\[features\.multi_agent_v2\]/g) ?? []).length, 1);
|
|
@@ -816,12 +816,344 @@ test("#given a following section header with an inline comment #when inserting e
|
|
|
816
816
|
"",
|
|
817
817
|
].join("\n");
|
|
818
818
|
|
|
819
|
-
const result = forceDisableMultiAgentV2(config);
|
|
819
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
820
820
|
|
|
821
821
|
assert.match(result, /\[features\.multi_agent_v2\]\nenabled = false\n/);
|
|
822
822
|
assert.match(result, /\[mcp_servers\.x\][^\n]*\nenabled = true/);
|
|
823
823
|
});
|
|
824
824
|
|
|
825
|
+
test("#given gpt-5.6-terra with managed disable #when model catalog prefers v2 #then clears enabled=false and managed comments", () => {
|
|
826
|
+
const config = [
|
|
827
|
+
'model = "gpt-5.6-terra"',
|
|
828
|
+
"",
|
|
829
|
+
"# Managed by LazyCodex: multi_agent_v2 is re-disabled on every Codex session start",
|
|
830
|
+
"# because enabling it fails every turn with HTTP 400 (openai/codex#26753).",
|
|
831
|
+
"# Opt out: LAZYCODEX_CONFIG_MIGRATION_DISABLED=1 (or OMO_CODEX_CONFIG_MIGRATION_DISABLED=1).",
|
|
832
|
+
"",
|
|
833
|
+
"[features.multi_agent_v2]",
|
|
834
|
+
"enabled = false",
|
|
835
|
+
"max_concurrent_threads_per_session = 1000",
|
|
836
|
+
"",
|
|
837
|
+
].join("\n");
|
|
838
|
+
|
|
839
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v2" });
|
|
840
|
+
|
|
841
|
+
assert.doesNotMatch(result, /^\s*enabled\s*=\s*false/m);
|
|
842
|
+
assert.doesNotMatch(result, /openai\/codex#26753/);
|
|
843
|
+
assert.doesNotMatch(result, /Managed by LazyCodex: multi_agent_v2/);
|
|
844
|
+
assert.match(result, /\[features\.multi_agent_v2\]/);
|
|
845
|
+
assert.match(result, /max_concurrent_threads_per_session = 1000/);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
test("#given gpt-5.6 v2 model with hide_spawn_agent_metadata=false #when clearing #then removes the stale metadata override", () => {
|
|
849
|
+
const config = [
|
|
850
|
+
'model = "gpt-5.6-sol"',
|
|
851
|
+
"",
|
|
852
|
+
"[features.multi_agent_v2]",
|
|
853
|
+
"enabled = false",
|
|
854
|
+
"hide_spawn_agent_metadata = false",
|
|
855
|
+
"max_concurrent_threads_per_session = 1000",
|
|
856
|
+
"",
|
|
857
|
+
].join("\n");
|
|
858
|
+
|
|
859
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v2" });
|
|
860
|
+
|
|
861
|
+
assert.doesNotMatch(result, /^\s*enabled\s*=\s*false/m);
|
|
862
|
+
assert.doesNotMatch(result, /^\s*hide_spawn_agent_metadata\s*=/m);
|
|
863
|
+
assert.match(result, /max_concurrent_threads_per_session = 1000/);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("#given gpt-5.6 v2 model with hide_spawn_agent_metadata=true #when clearing #then leaves the default-matching value alone", () => {
|
|
867
|
+
const config = [
|
|
868
|
+
'model = "gpt-5.6-sol"',
|
|
869
|
+
"",
|
|
870
|
+
"[features.multi_agent_v2]",
|
|
871
|
+
"enabled = false",
|
|
872
|
+
"hide_spawn_agent_metadata = true",
|
|
873
|
+
"max_concurrent_threads_per_session = 1000",
|
|
874
|
+
"",
|
|
875
|
+
].join("\n");
|
|
876
|
+
|
|
877
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v2" });
|
|
878
|
+
|
|
879
|
+
assert.doesNotMatch(result, /^\s*enabled\s*=\s*false/m);
|
|
880
|
+
assert.match(result, /hide_spawn_agent_metadata = true/);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
test("#given v1 model with hide_spawn_agent_metadata=false #when forcing disable #then keeps the metadata override", () => {
|
|
884
|
+
const config = [
|
|
885
|
+
'model = "gpt-5.5"',
|
|
886
|
+
"",
|
|
887
|
+
"[features.multi_agent_v2]",
|
|
888
|
+
"hide_spawn_agent_metadata = false",
|
|
889
|
+
"max_concurrent_threads_per_session = 1000",
|
|
890
|
+
"",
|
|
891
|
+
].join("\n");
|
|
892
|
+
|
|
893
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v1" });
|
|
894
|
+
|
|
895
|
+
assert.match(result, /hide_spawn_agent_metadata = false/);
|
|
896
|
+
assert.match(result, /enabled = false/);
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
test("#given gpt-5.6-sol with no multi_agent_v2 section #when model catalog prefers v2 #then does not append a disable", () => {
|
|
900
|
+
const config = ['model = "gpt-5.6-sol"', "", "[features]", "plugins = true", ""].join("\n");
|
|
901
|
+
|
|
902
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: "v2" });
|
|
903
|
+
|
|
904
|
+
assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
|
|
905
|
+
assert.doesNotMatch(result, /enabled = false/);
|
|
906
|
+
assert.match(result, /plugins = true/);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
test("#given gpt-5.6-terra managed disable #when full migration sees models_cache v2 #then clears disable and drops agents.max_threads", async () => {
|
|
910
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-gpt56-"));
|
|
911
|
+
const codexHome = join(root, "codex-home");
|
|
912
|
+
await mkdir(codexHome, { recursive: true });
|
|
913
|
+
const configPath = join(codexHome, "config.toml");
|
|
914
|
+
await writeFile(
|
|
915
|
+
configPath,
|
|
916
|
+
[
|
|
917
|
+
'model = "gpt-5.6-terra"',
|
|
918
|
+
'model_reasoning_effort = "xhigh"',
|
|
919
|
+
"",
|
|
920
|
+
"# Managed by LazyCodex: multi_agent_v2 is re-disabled on every Codex session start",
|
|
921
|
+
"# because enabling it fails every turn with HTTP 400 (openai/codex#26753).",
|
|
922
|
+
"",
|
|
923
|
+
"[agents]",
|
|
924
|
+
"max_threads = 1000",
|
|
925
|
+
"max_depth = 2",
|
|
926
|
+
"",
|
|
927
|
+
"[features.multi_agent_v2]",
|
|
928
|
+
"enabled = false",
|
|
929
|
+
"max_concurrent_threads_per_session = 1000",
|
|
930
|
+
"",
|
|
931
|
+
].join("\n"),
|
|
932
|
+
);
|
|
933
|
+
await writeFile(
|
|
934
|
+
join(codexHome, "models_cache.json"),
|
|
935
|
+
JSON.stringify({
|
|
936
|
+
models: [{ slug: "gpt-5.6-terra", multi_agent_version: "v2" }],
|
|
937
|
+
}),
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
const result = await migrateCodexConfig({
|
|
941
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
942
|
+
cwd: root,
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
assert.deepEqual(result.changed, [configPath]);
|
|
946
|
+
const content = await readFile(configPath, "utf8");
|
|
947
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
948
|
+
assert.doesNotMatch(content, /openai\/codex#26753/);
|
|
949
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
950
|
+
assert.match(content, /max_depth = 2/);
|
|
951
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
test("#given config default gpt-5.5 #when SessionStart model is gpt-5.6-terra #then prefers session model and clears disable", () => {
|
|
955
|
+
const config = [
|
|
956
|
+
'model = "gpt-5.5"',
|
|
957
|
+
"",
|
|
958
|
+
"[features.multi_agent_v2]",
|
|
959
|
+
"enabled = false",
|
|
960
|
+
"max_concurrent_threads_per_session = 1000",
|
|
961
|
+
"",
|
|
962
|
+
].join("\n");
|
|
963
|
+
|
|
964
|
+
const result = forceDisableMultiAgentV2(config, {
|
|
965
|
+
sessionModel: "gpt-5.6-terra",
|
|
966
|
+
multiAgentVersion: "v2",
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
assert.doesNotMatch(result, /^\s*enabled\s*=\s*false/m);
|
|
970
|
+
assert.match(result, /max_concurrent_threads_per_session = 1000/);
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
test("#given SessionStart without model #when requireSessionModel is set #then skips legacy force-disable", () => {
|
|
974
|
+
const config = ['model = "gpt-5.5"', "", "[features]", "plugins = true", ""].join("\n");
|
|
975
|
+
|
|
976
|
+
const result = forceDisableMultiAgentV2(config, {
|
|
977
|
+
multiAgentVersion: null,
|
|
978
|
+
requireSessionModel: true,
|
|
979
|
+
sessionModel: null,
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
assert.equal(result, config);
|
|
983
|
+
assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test("#given legacy [features] shorthand #when requireSessionModel skips force-disable #then still removes the shorthand", () => {
|
|
987
|
+
const config = ['model = "gpt-5.5"', "", "[features]", "plugins = true", "multi_agent_v2 = true", ""].join("\n");
|
|
988
|
+
|
|
989
|
+
const result = forceDisableMultiAgentV2(config, {
|
|
990
|
+
multiAgentVersion: null,
|
|
991
|
+
requireSessionModel: true,
|
|
992
|
+
sessionModel: null,
|
|
993
|
+
});
|
|
994
|
+
|
|
995
|
+
assert.doesNotMatch(result, /^\s*multi_agent_v2\s*=/m);
|
|
996
|
+
assert.match(result, /plugins = true/);
|
|
997
|
+
assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
test("#given legacy [features] shorthand #when session model has no catalog entry #then still removes the shorthand", () => {
|
|
1001
|
+
const config = ['model = "gpt-5.5"', "", "[features]", "multi_agent_v2 = false", ""].join("\n");
|
|
1002
|
+
|
|
1003
|
+
const result = forceDisableMultiAgentV2(config, {
|
|
1004
|
+
multiAgentVersion: null,
|
|
1005
|
+
sessionModel: "gpt-5.7-nova",
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
assert.doesNotMatch(result, /^\s*multi_agent_v2\s*=/m);
|
|
1009
|
+
assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
|
|
1010
|
+
});
|
|
1011
|
+
|
|
1012
|
+
test("#given legacy shorthand and no session model on hook path #when full migration runs #then output stays valid TOML", async () => {
|
|
1013
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-shorthand-hook-"));
|
|
1014
|
+
const codexHome = join(root, "codex-home");
|
|
1015
|
+
await mkdir(codexHome, { recursive: true });
|
|
1016
|
+
const configPath = join(codexHome, "config.toml");
|
|
1017
|
+
await writeFile(
|
|
1018
|
+
configPath,
|
|
1019
|
+
['model = "gpt-5.5"', 'model_reasoning_effort = "high"', "", "[features]", "plugins = true", "multi_agent_v2 = true", ""].join("\n"),
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
await migrateCodexConfig({
|
|
1023
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
1024
|
+
cwd: root,
|
|
1025
|
+
sessionModel: null,
|
|
1026
|
+
requireSessionModel: true,
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
const content = await readFile(configPath, "utf8");
|
|
1030
|
+
const parsed = parseTomlWithPython(content);
|
|
1031
|
+
assert.doesNotMatch(content, /^\s*multi_agent_v2\s*=\s*(?:true|false)/m);
|
|
1032
|
+
assert.equal(parsed.features.plugins, true);
|
|
1033
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 1000);
|
|
1034
|
+
assert.equal("enabled" in parsed.features.multi_agent_v2, false);
|
|
1035
|
+
});
|
|
1036
|
+
|
|
1037
|
+
test("#given SoT migration failure #when hook migration runs #then config.toml repair still happens", async () => {
|
|
1038
|
+
const { runAutoUpdateCheck } = await import("../scripts/auto-update.mjs");
|
|
1039
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-sot-isolation-"));
|
|
1040
|
+
const codexHome = join(root, "codex-home");
|
|
1041
|
+
await mkdir(codexHome, { recursive: true });
|
|
1042
|
+
const configPath = join(codexHome, "config.toml");
|
|
1043
|
+
await writeFile(
|
|
1044
|
+
configPath,
|
|
1045
|
+
['model = "gpt-5.6-sol"', "", "[features.multi_agent_v2]", "enabled = false", ""].join("\n"),
|
|
1046
|
+
);
|
|
1047
|
+
await writeFile(
|
|
1048
|
+
join(codexHome, "models_cache.json"),
|
|
1049
|
+
JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }),
|
|
1050
|
+
);
|
|
1051
|
+
// HOME pointing at a FILE makes migrateOmoSotConfig's mkdir(~/.omo) throw.
|
|
1052
|
+
const brokenHome = join(root, "not-a-dir");
|
|
1053
|
+
await writeFile(brokenHome, "");
|
|
1054
|
+
|
|
1055
|
+
await runAutoUpdateCheck({
|
|
1056
|
+
env: {
|
|
1057
|
+
CODEX_HOME: codexHome,
|
|
1058
|
+
HOME: brokenHome,
|
|
1059
|
+
USERPROFILE: brokenHome,
|
|
1060
|
+
LAZYCODEX_AUTO_UPDATE_DISABLED: "1",
|
|
1061
|
+
LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json"),
|
|
1062
|
+
LAZYCODEX_AUTO_UPDATE_STATE_PATH: join(root, "state.json"),
|
|
1063
|
+
},
|
|
1064
|
+
sessionModel: "gpt-5.6-sol",
|
|
1065
|
+
requireSessionModel: true,
|
|
1066
|
+
});
|
|
1067
|
+
|
|
1068
|
+
const content = await readFile(configPath, "utf8");
|
|
1069
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
test("#given config default gpt-5.5 #when full migration gets SessionStart gpt-5.6-terra #then clears disable using session model", async () => {
|
|
1073
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-session-model-"));
|
|
1074
|
+
const codexHome = join(root, "codex-home");
|
|
1075
|
+
await mkdir(codexHome, { recursive: true });
|
|
1076
|
+
const configPath = join(codexHome, "config.toml");
|
|
1077
|
+
await writeFile(
|
|
1078
|
+
configPath,
|
|
1079
|
+
[
|
|
1080
|
+
'model = "gpt-5.5"',
|
|
1081
|
+
'model_reasoning_effort = "high"',
|
|
1082
|
+
"",
|
|
1083
|
+
"[agents]",
|
|
1084
|
+
"max_threads = 1000",
|
|
1085
|
+
"",
|
|
1086
|
+
"[features.multi_agent_v2]",
|
|
1087
|
+
"enabled = false",
|
|
1088
|
+
"max_concurrent_threads_per_session = 1000",
|
|
1089
|
+
"",
|
|
1090
|
+
].join("\n"),
|
|
1091
|
+
);
|
|
1092
|
+
await writeFile(
|
|
1093
|
+
join(codexHome, "models_cache.json"),
|
|
1094
|
+
JSON.stringify({
|
|
1095
|
+
models: [
|
|
1096
|
+
{ slug: "gpt-5.5", multi_agent_version: "v1" },
|
|
1097
|
+
{ slug: "gpt-5.6-terra", multi_agent_version: "v2" },
|
|
1098
|
+
],
|
|
1099
|
+
}),
|
|
1100
|
+
);
|
|
1101
|
+
|
|
1102
|
+
const result = await migrateCodexConfig({
|
|
1103
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
1104
|
+
cwd: root,
|
|
1105
|
+
sessionModel: "gpt-5.6-terra",
|
|
1106
|
+
requireSessionModel: true,
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
assert.deepEqual(result.changed, [configPath]);
|
|
1110
|
+
const content = await readFile(configPath, "utf8");
|
|
1111
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
1112
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
1113
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
test("#given no session model and no root model #when forcing disable #then leaves the enable state untouched", () => {
|
|
1117
|
+
const config = ["[features]", "plugins = true", "", "[features.multi_agent_v2]", "enabled = true", ""].join("\n");
|
|
1118
|
+
|
|
1119
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null, sessionModel: null });
|
|
1120
|
+
|
|
1121
|
+
assert.match(result, /enabled = true/);
|
|
1122
|
+
assert.doesNotMatch(result, /enabled = false/);
|
|
1123
|
+
assert.doesNotMatch(result, /openai\/codex#26753/);
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
test("#given no session model and no root model #when config has no multi_agent_v2 section #then does not append a disable", () => {
|
|
1127
|
+
const config = ["[features]", "plugins = true", ""].join("\n");
|
|
1128
|
+
|
|
1129
|
+
const result = forceDisableMultiAgentV2(config, { multiAgentVersion: null, sessionModel: null });
|
|
1130
|
+
|
|
1131
|
+
assert.equal(result, config);
|
|
1132
|
+
assert.doesNotMatch(result, /\[features\.multi_agent_v2\]/);
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
test("#given user-modified config without root model #when full non-hook migration runs #then writes no disable and no new agents.max_threads", async () => {
|
|
1136
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-desktop-no-model-"));
|
|
1137
|
+
const codexHome = join(root, "codex-home");
|
|
1138
|
+
await mkdir(codexHome, { recursive: true });
|
|
1139
|
+
const configPath = join(codexHome, "config.toml");
|
|
1140
|
+
// Codex Desktop sessions select the model in the UI; config.toml has no root
|
|
1141
|
+
// `model`, so migration cannot prove the session is not a GPT-5.6
|
|
1142
|
+
// reserved-schema model (#6002).
|
|
1143
|
+
await writeFile(configPath, ['model_reasoning_effort = "high"', "", "[features]", "plugins = true", ""].join("\n"));
|
|
1144
|
+
|
|
1145
|
+
await migrateCodexConfig({
|
|
1146
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
1147
|
+
cwd: root,
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
const content = await readFile(configPath, "utf8");
|
|
1151
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
1152
|
+
assert.doesNotMatch(content, /openai\/codex#26753/);
|
|
1153
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
1154
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
1155
|
+
});
|
|
1156
|
+
|
|
825
1157
|
async function canCreateSymlink(type) {
|
|
826
1158
|
const root = await mkdtemp(join(tmpdir(), "lazycodex-symlink-capability-"));
|
|
827
1159
|
const target = join(root, "target");
|
|
@@ -847,3 +1179,64 @@ async function canCreateSymlink(type) {
|
|
|
847
1179
|
return false;
|
|
848
1180
|
}
|
|
849
1181
|
}
|
|
1182
|
+
|
|
1183
|
+
test("#given model_catalog_json declares a v2 model as v1 #when full migration runs #then keeps the managed disable and max_threads", async () => {
|
|
1184
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-catalog-override-"));
|
|
1185
|
+
const codexHome = join(root, "codex-home");
|
|
1186
|
+
await mkdir(codexHome, { recursive: true });
|
|
1187
|
+
const configPath = join(codexHome, "config.toml");
|
|
1188
|
+
const catalogPath = join(root, "custom-catalog.json");
|
|
1189
|
+
// Codex Desktop user forces gpt-5.6-sol to v1 via an explicit replacement catalog.
|
|
1190
|
+
await writeFile(configPath, [
|
|
1191
|
+
'model = "gpt-5.6-sol"',
|
|
1192
|
+
`model_catalog_json = "${catalogPath}"`,
|
|
1193
|
+
"",
|
|
1194
|
+
"[agents]",
|
|
1195
|
+
"max_threads = 1000",
|
|
1196
|
+
"max_depth = 2",
|
|
1197
|
+
"",
|
|
1198
|
+
"[features.multi_agent_v2]",
|
|
1199
|
+
"enabled = false",
|
|
1200
|
+
"max_concurrent_threads_per_session = 1000",
|
|
1201
|
+
"",
|
|
1202
|
+
].join("\n"));
|
|
1203
|
+
// models_cache.json STILL says v2 (stale), but the explicit catalog wins and says v1.
|
|
1204
|
+
await writeFile(join(codexHome, "models_cache.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }));
|
|
1205
|
+
await writeFile(catalogPath, JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v1" }] }));
|
|
1206
|
+
|
|
1207
|
+
await migrateCodexConfig({
|
|
1208
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
1209
|
+
cwd: root,
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
const content = await readFile(configPath, "utf8");
|
|
1213
|
+
assert.match(content, /enabled = false/, "explicit v1 catalog must keep the managed disable");
|
|
1214
|
+
assert.match(content, /max_threads = 1000/, "explicit v1 catalog must keep agents.max_threads");
|
|
1215
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
1216
|
+
assert.match(content, /max_depth = 2/);
|
|
1217
|
+
});
|
|
1218
|
+
|
|
1219
|
+
test("#given model_catalog_json declares a model as v2 #when full migration runs #then clears the managed disable", async () => {
|
|
1220
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-multi-agent-v2-catalog-v2-"));
|
|
1221
|
+
const codexHome = join(root, "codex-home");
|
|
1222
|
+
await mkdir(codexHome, { recursive: true });
|
|
1223
|
+
const configPath = join(codexHome, "config.toml");
|
|
1224
|
+
const catalogPath = join(root, "custom-catalog.json");
|
|
1225
|
+
await writeFile(configPath, [
|
|
1226
|
+
'model = "custom-model"',
|
|
1227
|
+
`model_catalog_json = "${catalogPath}"`,
|
|
1228
|
+
"",
|
|
1229
|
+
"[features.multi_agent_v2]",
|
|
1230
|
+
"enabled = false",
|
|
1231
|
+
"",
|
|
1232
|
+
].join("\n"));
|
|
1233
|
+
await writeFile(catalogPath, JSON.stringify({ models: [{ slug: "custom-model", multi_agent_version: "v2" }] }));
|
|
1234
|
+
|
|
1235
|
+
await migrateCodexConfig({
|
|
1236
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
1237
|
+
cwd: root,
|
|
1238
|
+
});
|
|
1239
|
+
|
|
1240
|
+
const content = await readFile(configPath, "utf8");
|
|
1241
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m, "explicit v2 catalog must clear the disable");
|
|
1242
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
|
|
7
|
+
import { migrateCodexConfig } from "../scripts/migrate-codex-config.mjs";
|
|
8
|
+
|
|
9
|
+
test("#given relative model_catalog_json declares gpt-5.6 model as v1 #when migrating #then resolves catalog from config directory", async () => {
|
|
10
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-v2-relative-catalog-"));
|
|
11
|
+
const codexHome = join(root, "codex-home");
|
|
12
|
+
await mkdir(codexHome, { recursive: true });
|
|
13
|
+
const configPath = join(codexHome, "config.toml");
|
|
14
|
+
await writeFile(
|
|
15
|
+
configPath,
|
|
16
|
+
[
|
|
17
|
+
'model = "gpt-5.6-sol"',
|
|
18
|
+
'model_catalog_json = "custom-catalog.json"',
|
|
19
|
+
"",
|
|
20
|
+
"[agents]",
|
|
21
|
+
"max_threads = 1000",
|
|
22
|
+
"",
|
|
23
|
+
"[features.multi_agent_v2]",
|
|
24
|
+
"enabled = false",
|
|
25
|
+
"max_concurrent_threads_per_session = 1000",
|
|
26
|
+
"",
|
|
27
|
+
].join("\n"),
|
|
28
|
+
);
|
|
29
|
+
await writeFile(join(codexHome, "models_cache.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v2" }] }));
|
|
30
|
+
await writeFile(join(codexHome, "custom-catalog.json"), JSON.stringify({ models: [{ slug: "gpt-5.6-sol", multi_agent_version: "v1" }] }));
|
|
31
|
+
|
|
32
|
+
await migrateCodexConfig({
|
|
33
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
34
|
+
cwd: root,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const content = await readFile(configPath, "utf8");
|
|
38
|
+
assert.match(content, /enabled = false/);
|
|
39
|
+
assert.match(content, /max_threads = 1000/);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test("#given no SessionStart model and root gpt-5.6 model without catalog #when migrating #then clears stale V2 disable", async () => {
|
|
43
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-v2-root-gpt56-nocache-"));
|
|
44
|
+
const codexHome = join(root, "codex-home");
|
|
45
|
+
await mkdir(codexHome, { recursive: true });
|
|
46
|
+
const configPath = join(codexHome, "config.toml");
|
|
47
|
+
await writeFile(
|
|
48
|
+
configPath,
|
|
49
|
+
[
|
|
50
|
+
'model = "gpt-5.6-terra"',
|
|
51
|
+
"",
|
|
52
|
+
"[agents]",
|
|
53
|
+
"max_threads = 1000",
|
|
54
|
+
"",
|
|
55
|
+
"[features.multi_agent_v2]",
|
|
56
|
+
"enabled = false",
|
|
57
|
+
"max_concurrent_threads_per_session = 1000",
|
|
58
|
+
"",
|
|
59
|
+
].join("\n"),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
await migrateCodexConfig({
|
|
63
|
+
env: { CODEX_HOME: codexHome, LAZYCODEX_MODEL_CATALOG_STATE_PATH: join(root, "model-state.json") },
|
|
64
|
+
cwd: root,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const content = await readFile(configPath, "utf8");
|
|
68
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
69
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
70
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
71
|
+
});
|
|
@@ -42,3 +42,68 @@ test("#given SessionStart config migration sees a low subagent cap #when migrati
|
|
|
42
42
|
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
43
43
|
assert.doesNotMatch(content, /^max_threads\s*=\s*6$/m);
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
test("#given gpt-5.6 session model with no models_cache #when migrating #then does not write agents.max_threads", async () => {
|
|
47
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-gpt56-nocache-"));
|
|
48
|
+
const configPath = join(root, "config.toml");
|
|
49
|
+
await writeFile(
|
|
50
|
+
configPath,
|
|
51
|
+
[
|
|
52
|
+
'model = "gpt-5.6-sol"',
|
|
53
|
+
'model_reasoning_effort = "xhigh"',
|
|
54
|
+
"",
|
|
55
|
+
"[agents]",
|
|
56
|
+
"max_threads = 1000",
|
|
57
|
+
"max_depth = 4",
|
|
58
|
+
"",
|
|
59
|
+
"[features.multi_agent_v2]",
|
|
60
|
+
"enabled = false",
|
|
61
|
+
"max_concurrent_threads_per_session = 1000",
|
|
62
|
+
"",
|
|
63
|
+
].join("\n"),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const result = await migrateConfigFile(configPath, {
|
|
67
|
+
env: { CODEX_HOME: root },
|
|
68
|
+
sessionModel: "gpt-5.6-sol",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const content = await readFile(configPath, "utf8");
|
|
72
|
+
assert.equal(result.changed, true);
|
|
73
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
74
|
+
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
75
|
+
assert.match(content, /max_depth = 4/);
|
|
76
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("#given config without any model #when migrating #then does not introduce agents.max_threads", async () => {
|
|
80
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-no-model-"));
|
|
81
|
+
const configPath = join(root, "config.toml");
|
|
82
|
+
await writeFile(
|
|
83
|
+
configPath,
|
|
84
|
+
['model_reasoning_effort = "high"', "", "[agents]", "max_depth = 4", ""].join("\n"),
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
await migrateConfigFile(configPath, { env: { CODEX_HOME: root } });
|
|
88
|
+
|
|
89
|
+
const content = await readFile(configPath, "utf8");
|
|
90
|
+
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
91
|
+
assert.match(content, /max_depth = 4/);
|
|
92
|
+
assert.match(content, /max_concurrent_threads_per_session = 1000/);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("#given config without any model but an existing low cap #when migrating #then still raises the existing cap", async () => {
|
|
96
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-no-model-raise-"));
|
|
97
|
+
const configPath = join(root, "config.toml");
|
|
98
|
+
await writeFile(
|
|
99
|
+
configPath,
|
|
100
|
+
['model_reasoning_effort = "high"', "", "[agents]", "max_threads = 6", "max_depth = 4", ""].join("\n"),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
await migrateConfigFile(configPath, { env: { CODEX_HOME: root } });
|
|
104
|
+
|
|
105
|
+
const content = await readFile(configPath, "utf8");
|
|
106
|
+
assert.match(content, /max_threads = 1000/);
|
|
107
|
+
assert.doesNotMatch(content, /max_threads = 6/);
|
|
108
|
+
assert.match(content, /max_depth = 4/);
|
|
109
|
+
});
|