oh-my-opencode 4.18.2 → 4.19.0
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/README.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +4 -4
- package/README.ru.md +2 -2
- package/README.zh-cn.md +2 -2
- package/dist/agents/atlas/agent.d.ts +6 -5
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +648 -136
- package/dist/cli-node/index.js +648 -136
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +6 -10
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/index.js +3515 -4365
- package/dist/oh-my-opencode.schema.json +23 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/ulw-plan/SKILL.md +11 -7
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +73 -25
- package/package.json +14 -14
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +352 -17
- 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/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +97 -30
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +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/AGENTS.md +1 -0
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- 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/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
- 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/src/boulder-reader.ts +3 -71
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -0
- 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/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.md +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- 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/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +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-guarding-ulw-loop-spawns.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/stop-checking-ulw-loop-resume.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/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +11 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +353 -18
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +11 -7
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
3
4
|
import { tmpdir } from "node:os";
|
|
4
5
|
import { join } from "node:path";
|
|
@@ -6,7 +7,26 @@ import test from "node:test";
|
|
|
6
7
|
|
|
7
8
|
import { migrateConfigFile } from "../scripts/migrate-codex-config.mjs";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
function parseTomlWithPython(config) {
|
|
11
|
+
const python = resolvePython();
|
|
12
|
+
const result = spawnSync(
|
|
13
|
+
python,
|
|
14
|
+
["-c", "import json, sys, tomllib; print(json.dumps(tomllib.loads(sys.stdin.read())))"],
|
|
15
|
+
{ encoding: "utf8", input: config },
|
|
16
|
+
);
|
|
17
|
+
assert.equal(result.status, 0, result.stderr);
|
|
18
|
+
return JSON.parse(result.stdout);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resolvePython() {
|
|
22
|
+
for (const command of ["python3", "python"]) {
|
|
23
|
+
const result = spawnSync(command, ["-c", "import tomllib"], { encoding: "utf8" });
|
|
24
|
+
if (result.status === 0) return command;
|
|
25
|
+
}
|
|
26
|
+
assert.fail("Python with tomllib is required for TOML parse assertions");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test("#given SessionStart migration sees an inline-commented V2 cap #when migrating twice #then preserves the line and ordering byte-for-byte", async () => {
|
|
10
30
|
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-migration-"));
|
|
11
31
|
const configPath = join(root, "config.toml");
|
|
12
32
|
await writeFile(
|
|
@@ -26,24 +46,240 @@ test("#given SessionStart config migration sees a low subagent cap #when migrati
|
|
|
26
46
|
"",
|
|
27
47
|
"[features.multi_agent_v2]",
|
|
28
48
|
"enabled = false",
|
|
29
|
-
"
|
|
49
|
+
"usage_hint_enabled = false",
|
|
50
|
+
"max_concurrent_threads_per_session = 7 # user cap",
|
|
51
|
+
"show_tool_use = false",
|
|
30
52
|
"",
|
|
31
53
|
].join("\n"),
|
|
32
54
|
);
|
|
33
55
|
|
|
34
|
-
const
|
|
56
|
+
const firstResult = await migrateConfigFile(configPath);
|
|
57
|
+
const firstPass = await readFile(configPath, "utf8");
|
|
58
|
+
const secondResult = await migrateConfigFile(configPath);
|
|
59
|
+
|
|
60
|
+
const secondPass = await readFile(configPath, "utf8");
|
|
61
|
+
assert.equal(firstResult.changed, true);
|
|
62
|
+
assert.equal(secondResult.changed, false);
|
|
63
|
+
assert.equal(secondPass, firstPass);
|
|
64
|
+
assert.match(
|
|
65
|
+
secondPass,
|
|
66
|
+
/usage_hint_enabled = false\nmax_concurrent_threads_per_session = 7 # user cap\nshow_tool_use = false/,
|
|
67
|
+
);
|
|
68
|
+
assert.match(secondPass, /\[agents\][\s\S]*?max_threads = 1000/);
|
|
69
|
+
assert.match(secondPass, /max_depth = 4/);
|
|
70
|
+
assert.match(secondPass, /\[agents\.explorer\]\nconfig_file = "\.\/agents\/explorer\.toml"/);
|
|
71
|
+
assert.match(secondPass, /\[features\.multi_agent_v2\][\s\S]*?enabled = false/);
|
|
72
|
+
assert.doesNotMatch(secondPass, /^max_threads\s*=\s*6$/m);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
for (const header of [
|
|
76
|
+
'["features"."multi_agent_v2"]',
|
|
77
|
+
"[ features . multi_agent_v2 ]",
|
|
78
|
+
'["features"."multi_agent_v\\u0032"]',
|
|
79
|
+
]) {
|
|
80
|
+
test(`#given SessionStart migration sees equivalent V2 header ${header} #when migrating twice #then preserves one valid table and the explicit cap`, async () => {
|
|
81
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-equivalent-header-"));
|
|
82
|
+
const configPath = join(root, "config.toml");
|
|
83
|
+
await writeFile(
|
|
84
|
+
configPath,
|
|
85
|
+
[
|
|
86
|
+
'model = "gpt-5.4"',
|
|
87
|
+
"",
|
|
88
|
+
header,
|
|
89
|
+
"usage_hint_enabled = false",
|
|
90
|
+
"max_concurrent_threads_per_session = 7 # user cap",
|
|
91
|
+
"show_tool_use = false",
|
|
92
|
+
"",
|
|
93
|
+
].join("\n"),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await migrateConfigFile(configPath);
|
|
97
|
+
const firstPass = await readFile(configPath, "utf8");
|
|
98
|
+
const parsed = parseTomlWithPython(firstPass);
|
|
99
|
+
const secondResult = await migrateConfigFile(configPath);
|
|
100
|
+
const secondPass = await readFile(configPath, "utf8");
|
|
101
|
+
|
|
102
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
|
|
103
|
+
assert.equal(secondResult.changed, false);
|
|
104
|
+
assert.equal(secondPass, firstPass);
|
|
105
|
+
assert.equal((secondPass.match(/max_concurrent_threads_per_session\s*=/g) ?? []).length, 1);
|
|
106
|
+
assert.match(
|
|
107
|
+
secondPass,
|
|
108
|
+
/usage_hint_enabled = false\nmax_concurrent_threads_per_session = 7 # user cap\nshow_tool_use = false/,
|
|
109
|
+
);
|
|
110
|
+
assert.match(secondPass, new RegExp(header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
for (const fixture of [
|
|
115
|
+
{
|
|
116
|
+
name: "quoted cap key",
|
|
117
|
+
lines: [
|
|
118
|
+
"[features.multi_agent_v2]",
|
|
119
|
+
'"max_concurrent_threads_per_session" = 7 # user cap',
|
|
120
|
+
],
|
|
121
|
+
preservedLine: '"max_concurrent_threads_per_session" = 7 # user cap',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "escaped quoted cap key",
|
|
125
|
+
lines: [
|
|
126
|
+
"[features.multi_agent_v2]",
|
|
127
|
+
'"max_concurrent_threads_per_sessio\\u006e" = 7 # user cap',
|
|
128
|
+
],
|
|
129
|
+
preservedLine: '"max_concurrent_threads_per_sessio\\u006e" = 7 # user cap',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "dotted cap key",
|
|
133
|
+
lines: [
|
|
134
|
+
"[features]",
|
|
135
|
+
"multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
|
|
136
|
+
],
|
|
137
|
+
preservedLine: "multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "root-qualified dotted cap key",
|
|
141
|
+
lines: ["features.multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap"],
|
|
142
|
+
preservedLine: "features.multi_agent_v2.max_concurrent_threads_per_session = 7 # user cap",
|
|
143
|
+
},
|
|
144
|
+
]) {
|
|
145
|
+
test(`#given SessionStart migration sees a ${fixture.name} #when migrating twice #then preserves the semantic cap without duplication`, async () => {
|
|
146
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-semantic-key-"));
|
|
147
|
+
const configPath = join(root, "config.toml");
|
|
148
|
+
await writeFile(configPath, ['model = "gpt-5.4"', "", ...fixture.lines, ""].join("\n"));
|
|
149
|
+
|
|
150
|
+
await migrateConfigFile(configPath);
|
|
151
|
+
const firstPass = await readFile(configPath, "utf8");
|
|
152
|
+
const parsed = parseTomlWithPython(firstPass);
|
|
153
|
+
const secondResult = await migrateConfigFile(configPath);
|
|
154
|
+
const secondPass = await readFile(configPath, "utf8");
|
|
155
|
+
|
|
156
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
|
|
157
|
+
assert.equal(secondResult.changed, false);
|
|
158
|
+
assert.equal(secondPass, firstPass);
|
|
159
|
+
assert.match(secondPass, new RegExp(fixture.preservedLine.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
|
|
160
|
+
assert.doesNotMatch(secondPass, /^max_concurrent_threads_per_session = 16$/m);
|
|
161
|
+
if (fixture.name === "root-qualified dotted cap key") {
|
|
162
|
+
assert.equal(parsed.features.multi_agent_v2.enabled, false);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
test("#given multiline string contains V2 cap lookalikes #when SessionStart migrates #then writes the absent semantic default", async () => {
|
|
168
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-multiline-lookalike-"));
|
|
169
|
+
const configPath = join(root, "config.toml");
|
|
170
|
+
await writeFile(
|
|
171
|
+
configPath,
|
|
172
|
+
[
|
|
173
|
+
'model = "gpt-5.4"',
|
|
174
|
+
'notes = """',
|
|
175
|
+
"[features.multi_agent_v2]",
|
|
176
|
+
"max_concurrent_threads_per_session = 7",
|
|
177
|
+
'"""',
|
|
178
|
+
"",
|
|
179
|
+
].join("\n"),
|
|
180
|
+
);
|
|
35
181
|
|
|
182
|
+
await migrateConfigFile(configPath);
|
|
36
183
|
const content = await readFile(configPath, "utf8");
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
assert.match(
|
|
40
|
-
assert.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
184
|
+
const parsed = parseTomlWithPython(content);
|
|
185
|
+
|
|
186
|
+
assert.match(parsed.notes, /max_concurrent_threads_per_session = 7/);
|
|
187
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 16);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("#given V2 section multiline value contains a cap lookalike #when SessionStart migrates #then writes the absent default", async () => {
|
|
191
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-section-multiline-lookalike-"));
|
|
192
|
+
const configPath = join(root, "config.toml");
|
|
193
|
+
await writeFile(
|
|
194
|
+
configPath,
|
|
195
|
+
[
|
|
196
|
+
'model = "gpt-5.4"',
|
|
197
|
+
"[features.multi_agent_v2]",
|
|
198
|
+
'notes = """',
|
|
199
|
+
"max_concurrent_threads_per_session = 7",
|
|
200
|
+
'"""',
|
|
201
|
+
"",
|
|
202
|
+
].join("\n"),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
await migrateConfigFile(configPath);
|
|
206
|
+
const parsed = parseTomlWithPython(await readFile(configPath, "utf8"));
|
|
207
|
+
|
|
208
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 16);
|
|
44
209
|
});
|
|
45
210
|
|
|
46
|
-
test("#given
|
|
211
|
+
test("#given V2 root-dotted disable and cap #when gpt-5.6 SessionStart migrates #then removes disable and V1 agents cap", async () => {
|
|
212
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-root-dotted-v2-cleanup-"));
|
|
213
|
+
const configPath = join(root, "config.toml");
|
|
214
|
+
await writeFile(
|
|
215
|
+
configPath,
|
|
216
|
+
[
|
|
217
|
+
'model = "gpt-5.6-terra"',
|
|
218
|
+
"features.multi_agent_v2.max_concurrent_threads_per_session = 7",
|
|
219
|
+
"features.multi_agent_v2.enabled = false",
|
|
220
|
+
"",
|
|
221
|
+
"[agents]",
|
|
222
|
+
"max_threads = 1000",
|
|
223
|
+
"",
|
|
224
|
+
].join("\n"),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
await migrateConfigFile(configPath, { sessionModel: "gpt-5.6-terra" });
|
|
228
|
+
const content = await readFile(configPath, "utf8");
|
|
229
|
+
const parsed = parseTomlWithPython(content);
|
|
230
|
+
|
|
231
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
|
|
232
|
+
assert.equal(parsed.features.multi_agent_v2.enabled, undefined);
|
|
233
|
+
assert.equal(parsed.agents, undefined);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("#given quoted dotted V1 keys under features #when SessionStart migrates #then replaces enabled without a duplicate", async () => {
|
|
237
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-quoted-dotted-v1-"));
|
|
238
|
+
const configPath = join(root, "config.toml");
|
|
239
|
+
await writeFile(
|
|
240
|
+
configPath,
|
|
241
|
+
[
|
|
242
|
+
'model = "gpt-5.4"',
|
|
243
|
+
"",
|
|
244
|
+
"[features]",
|
|
245
|
+
'"multi_agent_v2".enabled = true',
|
|
246
|
+
'"multi_agent_v2".max_concurrent_threads_per_session = 7',
|
|
247
|
+
"",
|
|
248
|
+
].join("\n"),
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
await migrateConfigFile(configPath);
|
|
252
|
+
const content = await readFile(configPath, "utf8");
|
|
253
|
+
const parsed = parseTomlWithPython(content);
|
|
254
|
+
|
|
255
|
+
assert.equal(parsed.features.multi_agent_v2.enabled, false);
|
|
256
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
|
|
257
|
+
assert.equal((content.match(/enabled\s*=/g) ?? []).length, 1);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("#given multiline string closes after an escaped quote #when SessionStart migrates #then preserves the following explicit cap", async () => {
|
|
261
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-overlapping-closer-"));
|
|
262
|
+
const configPath = join(root, "config.toml");
|
|
263
|
+
await writeFile(
|
|
264
|
+
configPath,
|
|
265
|
+
[
|
|
266
|
+
'model = "gpt-5.4"',
|
|
267
|
+
'notes = """abc\\\""""',
|
|
268
|
+
"[features.multi_agent_v2]",
|
|
269
|
+
"max_concurrent_threads_per_session = 7",
|
|
270
|
+
"",
|
|
271
|
+
].join("\n"),
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
await migrateConfigFile(configPath);
|
|
275
|
+
const content = await readFile(configPath, "utf8");
|
|
276
|
+
const parsed = parseTomlWithPython(content);
|
|
277
|
+
|
|
278
|
+
assert.equal(parsed.features.multi_agent_v2.max_concurrent_threads_per_session, 7);
|
|
279
|
+
assert.equal((content.match(/^\[features\.multi_agent_v2\]$/gm) ?? []).length, 1);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("#given gpt-5.6 session model with no models_cache and an explicit V2 cap #when migrating #then removes agents.max_threads but preserves the cap", async () => {
|
|
47
283
|
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-gpt56-nocache-"));
|
|
48
284
|
const configPath = join(root, "config.toml");
|
|
49
285
|
await writeFile(
|
|
@@ -58,7 +294,7 @@ test("#given gpt-5.6 session model with no models_cache #when migrating #then do
|
|
|
58
294
|
"",
|
|
59
295
|
"[features.multi_agent_v2]",
|
|
60
296
|
"enabled = false",
|
|
61
|
-
"max_concurrent_threads_per_session =
|
|
297
|
+
"max_concurrent_threads_per_session = 6",
|
|
62
298
|
"",
|
|
63
299
|
].join("\n"),
|
|
64
300
|
);
|
|
@@ -73,7 +309,28 @@ test("#given gpt-5.6 session model with no models_cache #when migrating #then do
|
|
|
73
309
|
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
74
310
|
assert.doesNotMatch(content, /^\s*enabled\s*=\s*false/m);
|
|
75
311
|
assert.match(content, /max_depth = 4/);
|
|
76
|
-
assert.match(content, /max_concurrent_threads_per_session =
|
|
312
|
+
assert.match(content, /max_concurrent_threads_per_session = 6/);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
test("#given SessionStart config migration has no V2 cap #when migrating #then writes the conservative managed default", async () => {
|
|
316
|
+
const root = await mkdtemp(join(tmpdir(), "lazycodex-subagent-limit-missing-cap-"));
|
|
317
|
+
const configPath = join(root, "config.toml");
|
|
318
|
+
await writeFile(
|
|
319
|
+
configPath,
|
|
320
|
+
[
|
|
321
|
+
'model = "gpt-5.4"',
|
|
322
|
+
"",
|
|
323
|
+
"[features.multi_agent_v2]",
|
|
324
|
+
"enabled = false",
|
|
325
|
+
"",
|
|
326
|
+
].join("\n"),
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
await migrateConfigFile(configPath);
|
|
330
|
+
|
|
331
|
+
const content = await readFile(configPath, "utf8");
|
|
332
|
+
assert.match(content, /max_concurrent_threads_per_session = 16/);
|
|
333
|
+
assert.doesNotMatch(content, /max_concurrent_threads_per_session = 1000/);
|
|
77
334
|
});
|
|
78
335
|
|
|
79
336
|
test("#given config without any model #when migrating #then does not introduce agents.max_threads", async () => {
|
|
@@ -89,7 +346,7 @@ test("#given config without any model #when migrating #then does not introduce a
|
|
|
89
346
|
const content = await readFile(configPath, "utf8");
|
|
90
347
|
assert.doesNotMatch(content, /^\s*max_threads\s*=/m);
|
|
91
348
|
assert.match(content, /max_depth = 4/);
|
|
92
|
-
assert.match(content, /max_concurrent_threads_per_session =
|
|
349
|
+
assert.match(content, /max_concurrent_threads_per_session = 16/);
|
|
93
350
|
});
|
|
94
351
|
|
|
95
352
|
test("#given config without any model but an existing low cap #when migrating #then still raises the existing cap", async () => {
|
|
@@ -32,10 +32,11 @@ export const expectedSkills = [
|
|
|
32
32
|
"visual-qa",
|
|
33
33
|
];
|
|
34
34
|
|
|
35
|
-
export const hiddenSharedSkills = ["ultraresearch"];
|
|
36
|
-
|
|
37
35
|
export const componentSkillSources = [
|
|
38
36
|
["comment-checker", "components/comment-checker/skills/comment-checker"],
|
|
37
|
+
["lcx-contribute-bug-fix", "components/lcx/skills/lcx-contribute-bug-fix"],
|
|
38
|
+
["lcx-doctor", "components/lcx/skills/lcx-doctor"],
|
|
39
|
+
["lcx-report-bug", "components/lcx/skills/lcx-report-bug"],
|
|
39
40
|
["lsp", "components/lsp/skills/lsp"],
|
|
40
41
|
["rules", "components/rules/skills/rules"],
|
|
41
42
|
["teammode", "components/teammode/skills/teammode"],
|
|
@@ -126,22 +127,6 @@ prefixes when identity is needed.
|
|
|
126
127
|
const reviewWorkCodexGatePattern = new RegExp(reviewWorkCodexGate.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
127
128
|
|
|
128
129
|
export function removeCodexSkillOverlays(skillName, content) {
|
|
129
|
-
if (skillName === "ulw-research") {
|
|
130
|
-
return content
|
|
131
|
-
.replace(
|
|
132
|
-
", any 'ulw' research wording,",
|
|
133
|
-
", the legacy alias 'ultraresearch', any 'ulw' research wording,",
|
|
134
|
-
)
|
|
135
|
-
.replace(
|
|
136
|
-
"the word \"ulw-research\" (also `/ulw-research`, `$ulw-research`), ",
|
|
137
|
-
"the word \"ulw-research\" (also `/ulw-research`, `$ulw-research`), the legacy alias \"ultraresearch\" (also `/ultraresearch`, `$ultraresearch`), ",
|
|
138
|
-
)
|
|
139
|
-
.replace(
|
|
140
|
-
"answer those normally, and mention that `ulw-research` is available when a question would clearly benefit from it.",
|
|
141
|
-
"answer those normally, and mention that `ulw-research` is available (legacy alias: `ultraresearch`) when a question would clearly benefit from it.",
|
|
142
|
-
)
|
|
143
|
-
.replace("# ULW-Research Synthesis: <query>", "# Ultraresearch Synthesis: <query>");
|
|
144
|
-
}
|
|
145
130
|
if (skillName === "start-work") {
|
|
146
131
|
return content
|
|
147
132
|
.replace(startWorkCodexCompletion, startWorkOriginalCompletion)
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
assertPackagedContentMatches,
|
|
10
10
|
componentSkillSources,
|
|
11
11
|
expectedSkills,
|
|
12
|
-
hiddenSharedSkills,
|
|
13
12
|
listSkillFiles,
|
|
14
13
|
removeCodexCompatibilityGuidance,
|
|
15
14
|
removeCodexSkillOverlays,
|
|
@@ -113,7 +112,6 @@ test("#given shared skill package source #when aggregate Codex shared skills are
|
|
|
113
112
|
// when / then
|
|
114
113
|
for (const skillName of sharedSkillNames) {
|
|
115
114
|
if (componentSkillNames.has(skillName)) continue;
|
|
116
|
-
if (hiddenSharedSkills.includes(skillName)) continue;
|
|
117
115
|
const sharedContent = await readFile(join(sharedSkillsRoot, skillName, "SKILL.md"), "utf8");
|
|
118
116
|
const aggregateContent = await readFile(join(aggregateSkillsRoot, skillName, "SKILL.md"), "utf8");
|
|
119
117
|
assert.equal(
|
|
@@ -213,11 +211,8 @@ test("#given synced ulw-loop skill #when Codex hint metadata is inspected #then
|
|
|
213
211
|
test("#given shipped Codex skill payloads #when legacy ultraresearch alias is inspected #then it is not packaged", async () => {
|
|
214
212
|
// given
|
|
215
213
|
const skillsRoot = join(root, "skills");
|
|
216
|
-
const skillRoot = join(skillsRoot, "ultraresearch");
|
|
217
214
|
|
|
218
215
|
// then
|
|
219
|
-
await assert.rejects(readFile(join(skillRoot, "SKILL.md"), "utf8"), { code: "ENOENT" });
|
|
220
|
-
await assert.rejects(readFile(join(skillRoot, "agents", "openai.yaml"), "utf8"), { code: "ENOENT" });
|
|
221
216
|
await assertNoLegacyResearchAliasInTree(skillsRoot, "skills");
|
|
222
217
|
for (const [skillName, sourcePath] of componentSkillSources) {
|
|
223
218
|
await assertNoLegacyResearchAliasInTree(join(root, sourcePath), `components/${skillName}`);
|