lazycodex-ai 5.0.0-beta.8 → 5.0.0-beta.9
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/dist/cli/index.js +34 -31
- package/dist/cli/run/on-complete-hook.d.ts +2 -0
- package/dist/cli-node/index.js +34 -31
- package/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- 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/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +2 -2
- 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/lazycodex-executor-verify/test/codex-hook.test.ts +3 -17
- 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/rules/test/bundled-rules-priority.test.ts +11 -16
- package/packages/omo-codex/plugin/components/rules/test/bundled-rules.test.ts +16 -23
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-budget.test.ts +9 -7
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-context.test.ts +0 -6
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-dedup.test.ts +6 -4
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-directive.test.ts +12 -9
- package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +28 -37
- package/packages/omo-codex/plugin/components/rules/test/formatter.test.ts +37 -69
- package/packages/omo-codex/plugin/components/rules/test/hook-output.test.ts +2 -3
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +1 -15
- 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/cli.test.ts +0 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +2 -16
- 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/test/thread-title-hook.test.ts +3 -9
- 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/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +0 -136
- package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +0 -2
- 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/components/ulw-loop/test/checkpoint-continuation.test.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-hook.test.ts +0 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +2 -35
- package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +4 -5
- 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 +20 -20
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +35 -8
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +110 -0
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +19 -173
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -24
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +175 -13
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +78 -2
- package/packages/omo-codex/plugin/test/auto-update-release-notes.test.mjs +19 -33
- package/packages/omo-codex/plugin/test/lcx-contribute-bug-fix-template.test.mjs +21 -27
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +0 -36
- package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +101 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +1 -119
- package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +0 -40
- package/packages/omo-codex/plugin/test/teammode-communication.test.mjs +6 -62
- package/packages/omo-codex/plugin/test/teammode-thread-links.test.mjs +3 -36
- package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +0 -44
- package/packages/omo-codex/plugin/test/teammode-worktree.test.mjs +2 -6
- package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +0 -3
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +0 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +35 -8
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +110 -0
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +0 -92
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +0 -314
- package/packages/omo-codex/plugin/test/ulw-plan-scope-contract.test.mjs +0 -24
|
@@ -3,14 +3,12 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import { root } from "./aggregate-plugin-fixture.mjs";
|
|
7
6
|
import { cleanupTeamRoot, createTeamRoot, runTeam, teamDir } from "./teammode-safety-fixture.mjs";
|
|
8
7
|
|
|
9
|
-
// The teammode guide.md is what every member actually reads.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// These tests pin the fix: a concrete push-communication protocol is the default the manual renders.
|
|
8
|
+
// The teammode guide.md is what every member actually reads. A real leader-session run proved members
|
|
9
|
+
// default to silence when the guide never names the tool or the address book, so the tests below pin
|
|
10
|
+
// only the machine tokens the generator must surface: the cross-thread tool name and the team.json
|
|
11
|
+
// field paths members address messages by. Guide/prompt wording itself is prose and stays unpinned.
|
|
14
12
|
|
|
15
13
|
function buildTwoMemberTeam(tempRoot, sessionId) {
|
|
16
14
|
runTeam(tempRoot, "init", "--name", "Comms", "--session-name", "frequent", "--session", sessionId);
|
|
@@ -39,69 +37,15 @@ test("#given a bound team #when the member field manual renders #then it names t
|
|
|
39
37
|
cleanupTeamRoot(tempRoot);
|
|
40
38
|
}
|
|
41
39
|
});
|
|
42
|
-
|
|
43
|
-
test("#given a bound team #when the member field manual renders #then it gives concrete per-moment cadence triggers, not aspirational 'constantly'", () => {
|
|
44
|
-
const tempRoot = createTeamRoot("omo-codex-teammode-comms-");
|
|
45
|
-
try {
|
|
46
|
-
buildTwoMemberTeam(tempRoot, "comms-cadence");
|
|
47
|
-
const guide = readGuide(tempRoot, "comms-cadence");
|
|
48
|
-
|
|
49
|
-
// then - a concrete heartbeat cadence the model can bind to
|
|
50
|
-
assert.match(guide, /every few tool calls/, "guide must give a concrete heartbeat trigger");
|
|
51
|
-
assert.match(guide, /WORKING:/, "guide must keep the WORKING heartbeat marker");
|
|
52
|
-
assert.match(guide, /BLOCKED:/, "guide must keep the BLOCKED marker");
|
|
53
|
-
// then - an explicit peer-notify rule so cross-cutting findings are pushed, not siloed
|
|
54
|
-
assert.match(guide, /to a \*\*peer\*\*/, "guide must have an explicit peer-notify rule");
|
|
55
|
-
assert.match(guide, /touches their slice/, "guide must trigger peer messages on slice-touching findings");
|
|
56
|
-
// then - the reassurance WHY that ties the two halves together: frequent reporting is what earns
|
|
57
|
-
// uninterrupted focus, so the leader has no reason to interrupt a member who keeps it informed
|
|
58
|
-
assert.match(guide, /uninterrupted focus/i, "guide must tell members frequent updates buy uninterrupted focus from the leader");
|
|
59
|
-
// then - the end-only directive that previously won is gone (it licensed batch-at-the-end reporting)
|
|
60
|
-
assert.doesNotMatch(guide, /the moment your work is complete/, "the standalone end-only report directive must be removed");
|
|
61
|
-
} finally {
|
|
62
|
-
cleanupTeamRoot(tempRoot);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("#given a member bootstrap trigger #when it is generated #then it points at the push tool and a continuous cadence, not an end-of-work report", () => {
|
|
40
|
+
test("#given a member bootstrap trigger #when it is generated #then it names the cross-thread push tool", () => {
|
|
67
41
|
const tempRoot = createTeamRoot("omo-codex-teammode-comms-");
|
|
68
42
|
try {
|
|
69
43
|
buildTwoMemberTeam(tempRoot, "comms-bootstrap");
|
|
70
44
|
const prompt = runTeam(tempRoot, "member-prompt", "--team", "comms-bootstrap", "--id", "A").stdout;
|
|
71
45
|
|
|
72
|
-
// then - the first thing a member reads
|
|
46
|
+
// then - the first thing a member reads names the tool it can push through
|
|
73
47
|
assert.match(prompt, /codex_app\.send_message_to_thread/, "bootstrap must name the push tool");
|
|
74
|
-
assert.match(prompt, /never just one report at the end/, "bootstrap must reject end-only reporting");
|
|
75
|
-
// then - it no longer frames reporting as a one-shot completion event
|
|
76
|
-
assert.doesNotMatch(prompt, /report to the leader the moment you are done/, "the end-only bootstrap line must be removed");
|
|
77
48
|
} finally {
|
|
78
49
|
cleanupTeamRoot(tempRoot);
|
|
79
50
|
}
|
|
80
51
|
});
|
|
81
|
-
|
|
82
|
-
test("#given the leader-facing SKILL.md #when it describes communication #then it requires --session so members can reach the leader and frames members as pushing", () => {
|
|
83
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
84
|
-
|
|
85
|
-
// then - the leader is told members PUSH to it (not only that the leader sends)
|
|
86
|
-
assert.match(skill, /Members push to you/i, "SKILL.md must frame members as pushing to the leader");
|
|
87
|
-
// then - the --session requirement is stated WITH its reason (member->leader push needs a real leader thread)
|
|
88
|
-
assert.match(skill, /--session <your own thread id>/, "SKILL.md must require init --session <your own thread id>");
|
|
89
|
-
assert.match(skill, /stuck polling|cannot report to you/i, "SKILL.md must explain why --session matters");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// The leader (a GPT-5.5 main session) over-polls when nothing reframes silence as work: real jobdori
|
|
93
|
-
// run 019f07a6- fired 6 leader->child pings, 4 pure "WORKING CHECK"/"NUDGE" nags that interrupted the
|
|
94
|
-
// child mid-slice. The member guide already mandates frequent reporting; the missing half is telling the
|
|
95
|
-
// leader to WAIT. These tests pin that the leader-facing skill reframes quiet-as-working and gates any
|
|
96
|
-
// outbound ping behind concrete decision rules instead of letting anxiety drive a barrage.
|
|
97
|
-
test("#given the leader-facing SKILL.md #when it covers waiting on members #then it reframes a quiet member as working and gates leader pings behind decision rules", () => {
|
|
98
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
99
|
-
|
|
100
|
-
// then - a quiet member between heartbeats is reframed as working, not a stall to chase
|
|
101
|
-
// (\s+ tolerates markdown line-wrapping between the two words)
|
|
102
|
-
assert.match(skill, /working,\s+not stalled/i, "SKILL.md must reframe a quiet member as working, not stalled");
|
|
103
|
-
// then - outbound leader messages are gated behind explicit decision rules, not anxiety
|
|
104
|
-
assert.match(skill, /Message a member only when/i, "SKILL.md must gate leader pings behind a concrete decision rule");
|
|
105
|
-
// then - the anti-pattern nag pings the real run produced are named as what NOT to send
|
|
106
|
-
assert.match(skill, /are you\s+done\?/i, "SKILL.md must name the 'are you done?' style nag as forbidden");
|
|
107
|
-
});
|
|
@@ -71,46 +71,13 @@ test("#given a worktree-backed team has bound member threads #when guide and sta
|
|
|
71
71
|
const status = runTeam(tempRoot, "status", "--team", sessionId).stdout;
|
|
72
72
|
const prompt = runTeam(tempRoot, "member-prompt", "--team", sessionId, "--id", "A").stdout;
|
|
73
73
|
|
|
74
|
+
// then - guide, status, and bootstrap prompt all surface the member's bound thread link and cwd
|
|
74
75
|
assert.match(guide, /codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
75
76
|
assert.match(guide, /codex:\/\/threads\/019ef2dd-5b99-7ae2-8461-ffa6ca309d23/);
|
|
76
77
|
assert.match(guide, /worktree `\/tmp\/review-worktree`/);
|
|
77
78
|
assert.match(status, /link=codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
78
|
-
assert.match(prompt, /
|
|
79
|
-
assert.match(prompt,
|
|
80
|
-
assert.match(prompt, /Before editing, verify that your assigned worktree exists and contains repo files/);
|
|
81
|
-
assert.match(prompt, /BLOCKED: worktree not ready/);
|
|
82
|
-
} finally {
|
|
83
|
-
cleanupTeamRoot(tempRoot);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("#given a worktree member has no cwd bound yet #when guide and prompt render #then they tell Codex to wait for readiness", () => {
|
|
88
|
-
const tempRoot = createTeamRoot("omo-codex-teammode-worktree-wait-");
|
|
89
|
-
try {
|
|
90
|
-
const sessionId = "worktree-wait";
|
|
91
|
-
runTeam(tempRoot, "init", "--name", "Wait", "--session-name", "worktrees", "--session", sessionId, "--worktree", "--base-branch", "main");
|
|
92
|
-
addMember(tempRoot, sessionId, {
|
|
93
|
-
id: "A",
|
|
94
|
-
name: "worktree-driver",
|
|
95
|
-
focus: "drive a pending Codex worktree thread",
|
|
96
|
-
lens: "ownership",
|
|
97
|
-
deliverable: "ready worktree report",
|
|
98
|
-
});
|
|
99
|
-
addMember(tempRoot, sessionId, {
|
|
100
|
-
id: "B",
|
|
101
|
-
name: "integration-watch",
|
|
102
|
-
focus: "watch integration boundaries",
|
|
103
|
-
lens: "perspective",
|
|
104
|
-
deliverable: "integration notes",
|
|
105
|
-
});
|
|
106
|
-
runTeam(tempRoot, "bind-thread", "--team", sessionId, "--id", "A", "--thread", "019ef350-ee78-72a3-bd5e-e40cebc3d814");
|
|
107
|
-
|
|
108
|
-
const guide = readFileSync(`${teamDir(tempRoot, sessionId)}/guide.md`, "utf8");
|
|
109
|
-
const prompt = runTeam(tempRoot, "member-prompt", "--team", sessionId, "--id", "A").stdout;
|
|
110
|
-
|
|
111
|
-
assert.match(guide, /If Codex returns only `pendingWorktreeId`/);
|
|
112
|
-
assert.match(guide, /wait until Codex surfaces a real thread id/);
|
|
113
|
-
assert.match(prompt, /If this prompt arrived before your Codex worktree checkout is ready/);
|
|
79
|
+
assert.match(prompt, /codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
80
|
+
assert.match(prompt, /\/tmp\/review-worktree/);
|
|
114
81
|
} finally {
|
|
115
82
|
cleanupTeamRoot(tempRoot);
|
|
116
83
|
}
|
|
@@ -3,7 +3,6 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import { root } from "./aggregate-plugin-fixture.mjs";
|
|
7
6
|
import {
|
|
8
7
|
cleanupTeamRoot,
|
|
9
8
|
createTeamRoot,
|
|
@@ -53,45 +52,6 @@ function addV2Members(tempRoot, sessionId) {
|
|
|
53
52
|
);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
test("#given flat V2 tools are available #when the leader reads teammode #then transport selection and user announcement precede init", () => {
|
|
57
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
58
|
-
const inspectIndex = skill.indexOf("Inspect your active tool list");
|
|
59
|
-
const initIndex = skill.indexOf('team.mjs" init');
|
|
60
|
-
|
|
61
|
-
assert.notEqual(inspectIndex, -1, "skill must inspect the active tool list");
|
|
62
|
-
assert.notEqual(initIndex, -1, "skill must still document init");
|
|
63
|
-
assert.ok(inspectIndex < initIndex, "transport inspection must happen before init");
|
|
64
|
-
assert.match(skill, /spawn_agent.*task_name[\s\S]*send_message[\s\S]*followup_task[\s\S]*wait_agent[\s\S]*list_agents[\s\S]*interrupt_agent/);
|
|
65
|
-
assert.match(skill, /tell the user|announce.*transport/i);
|
|
66
|
-
assert.match(skill, /MultiAgentV2/i);
|
|
67
|
-
assert.match(skill, /Codex App.*fallback/i);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("#given neither transport's tools are visible #when the leader reads teammode #then search hits are revalidated and fallback is capability-aware without team state", () => {
|
|
71
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
72
|
-
|
|
73
|
-
const searchIndex = skill.indexOf("tool_search");
|
|
74
|
-
const unavailableIndex = skill.indexOf("Teammode unavailable");
|
|
75
|
-
assert.notEqual(searchIndex, -1, "skill must route hidden tools through the tool_search check");
|
|
76
|
-
assert.notEqual(unavailableIndex, -1, "skill must give the leader unavailable announcement templates");
|
|
77
|
-
assert.ok(searchIndex < unavailableIndex, "tool_search must precede the unavailable conclusion");
|
|
78
|
-
assert.match(skill, /revalidate.*COMPLETE.*compatible transport set/is, "a deferred-tool hit must be revalidated as a complete transport");
|
|
79
|
-
assert.match(skill, /another visible plain-subagent mechanism.*spawn.*communicate.*observe/is, "plain-subagent fallback requires a complete visible lifecycle");
|
|
80
|
-
assert.match(skill, /Otherwise continue serially.*capability limitation/is, "missing all transports must continue serially rather than promise workers");
|
|
81
|
-
assert.match(skill, /Do NOT run `init`/, "the fallback must not create team state");
|
|
82
|
-
assert.doesNotMatch(skill, /splitting the work across plain fire-and-forget subagents/i, "the skill must not promise unavailable plain subagents");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("#given a MultiAgentV2 team #when the leader spawns members #then guidance limits calls to exposed V2 fields", () => {
|
|
86
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
87
|
-
|
|
88
|
-
assert.match(skill, /using only the V2 schema fields/i);
|
|
89
|
-
assert.match(skill, /`task_name`[\s\S]*`message`[\s\S]*`fork_turns`/);
|
|
90
|
-
assert.match(skill, /role, priority, or task-specific routing instruction in `message`/i);
|
|
91
|
-
assert.match(skill, /V2 does not accept[\s\S]*`agent_type`, `model`, `reasoning_effort`, or `service_tier`/);
|
|
92
|
-
assert.match(skill, /inherit the[\s\S]*session model/i);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
55
|
test("#given MultiAgentV2 transport #when members are added and bound #then task names and canonical agent paths form the address book", () => {
|
|
96
56
|
const tempRoot = createTeamRoot("omo-codex-teammode-v2-transport-");
|
|
97
57
|
try {
|
|
@@ -123,7 +83,6 @@ test("#given MultiAgentV2 transport #when members are added and bound #then task
|
|
|
123
83
|
{ id: "B", taskName: "mailbox_delivery", agentPath: "/root/mailbox_delivery", threadId: null, threadTitle: null, status: "active" },
|
|
124
84
|
],
|
|
125
85
|
);
|
|
126
|
-
assert.match(guide, /Transport:.*MultiAgentV2/i);
|
|
127
86
|
assert.match(guide, /members\[\]\.agentPath/);
|
|
128
87
|
assert.match(guide, /send_message/);
|
|
129
88
|
assert.match(guide, /followup_task/);
|
|
@@ -223,9 +182,7 @@ test("#given a V2 team #when the whole team is archived without a note #then no
|
|
|
223
182
|
const archiveEntry = team.log.find((entry) => entry.event === "archive");
|
|
224
183
|
|
|
225
184
|
assert.equal(team.status, "archived");
|
|
226
|
-
assert.doesNotMatch(result.stdout, /closed all members/i, "V2 has no runtime close/archive operation to claim");
|
|
227
185
|
assert.match(result.stdout, /no runtime archive operation/i);
|
|
228
|
-
assert.doesNotMatch(archiveEntry.detail, /all members closed/i);
|
|
229
186
|
assert.match(archiveEntry.detail, /no runtime archive operation/i);
|
|
230
187
|
} finally {
|
|
231
188
|
cleanupTeamRoot(tempRoot);
|
|
@@ -261,7 +218,6 @@ test("#given a codex_app team #when one member is archived #then the existing wo
|
|
|
261
218
|
const result = runTeam(tempRoot, "archive", "--team", "app-archive-member", "--id", "A");
|
|
262
219
|
|
|
263
220
|
assert.match(result.stdout, /archived member A/);
|
|
264
|
-
assert.doesNotMatch(result.stdout, /no runtime archive operation/i);
|
|
265
221
|
} finally {
|
|
266
222
|
cleanupTeamRoot(tempRoot);
|
|
267
223
|
}
|
|
@@ -83,24 +83,21 @@ test("#given a worktree team #when worktree-add A #then a real git worktree on a
|
|
|
83
83
|
assert.match(result.stdout, /A/);
|
|
84
84
|
assert.match(result.stdout, /wait for the real Codex thread id/);
|
|
85
85
|
assert.match(result.stdout, /bind-thread before sending bootstrap/);
|
|
86
|
-
assert.doesNotMatch(result.stdout, /Tell that member to:/);
|
|
87
86
|
} finally {
|
|
88
87
|
cleanupTeamRoot(tempRoot);
|
|
89
88
|
}
|
|
90
89
|
});
|
|
91
90
|
|
|
92
|
-
test("#given worktree-add ran #when the field manual is regenerated #then it
|
|
91
|
+
test("#given worktree-add ran #when the field manual is regenerated #then it shows the member's derived worktree branch", () => {
|
|
93
92
|
const tempRoot = createTeamRoot("omo-codex-teammode-wt-guide-");
|
|
94
93
|
try {
|
|
95
94
|
initGitRepo(tempRoot);
|
|
96
95
|
bootstrapTeam(tempRoot, "wt-guide", { worktree: false });
|
|
97
|
-
// before: no isolation, so the manual tells members to signal collisions instead
|
|
98
|
-
assert.match(readFileSync(join(teamDir(tempRoot, "wt-guide"), "guide.md"), "utf8"), /does not use isolated git worktrees/);
|
|
99
96
|
|
|
100
97
|
runTeam(tempRoot, "worktree-add", "--team", "wt-guide", "--id", "A");
|
|
101
98
|
|
|
99
|
+
// then - the regenerated guide carries the derived branch recorded in team state
|
|
102
100
|
const guide = readFileSync(join(teamDir(tempRoot, "wt-guide"), "guide.md"), "utf8");
|
|
103
|
-
assert.match(guide, /ISOLATION IS ON/);
|
|
104
101
|
assert.match(guide, /team\/wt-guide\/A/, "the member's row must show its derived worktree branch");
|
|
105
102
|
} finally {
|
|
106
103
|
cleanupTeamRoot(tempRoot);
|
|
@@ -225,7 +222,6 @@ test("#given a member branch that cannot be merged #when integrate runs #then it
|
|
|
225
222
|
const out = `${result.stdout}${result.stderr}`;
|
|
226
223
|
assert.notEqual(result.status, 0);
|
|
227
224
|
assert.match(out, /could not integrate|not something we can merge|ghost-branch-404/i, "must surface git's actual reason");
|
|
228
|
-
assert.doesNotMatch(out, /Conflicting files/, "a non-conflict failure must NOT be mislabeled as a merge conflict");
|
|
229
225
|
} finally {
|
|
230
226
|
cleanupTeamRoot(tempRoot);
|
|
231
227
|
}
|
|
@@ -33,7 +33,6 @@ test("#given synced skills #when the ultrawork skill is inspected #then it carri
|
|
|
33
33
|
const directive = readFileSync(join(root, "components", "ultrawork", "directive.md"), "utf8");
|
|
34
34
|
assert.match(skill, /^---\nname: ultrawork\n/);
|
|
35
35
|
assert.equal(skill.endsWith(directive), true);
|
|
36
|
-
assert.match(skill, /## 1\. Create the goal with binding success criteria/);
|
|
37
36
|
});
|
|
38
37
|
|
|
39
38
|
test("#given the built ultrawork CLI #when a ulw prompt is submitted #then a compact skill pointer is emitted", () => {
|
|
@@ -42,10 +41,8 @@ test("#given the built ultrawork CLI #when a ulw prompt is submitted #then a com
|
|
|
42
41
|
const output = JSON.parse(result.stdout);
|
|
43
42
|
const context = output.hookSpecificOutput.additionalContext;
|
|
44
43
|
assert.match(context, /^<ultrawork-mode>/);
|
|
45
|
-
assert.match(context, /First user-visible line this turn MUST be exactly:/);
|
|
46
44
|
assert.match(context, /create_goal/);
|
|
47
45
|
assert.equal(context.includes(skillPath), true);
|
|
48
|
-
assert.equal(context.includes("Tier triage"), false);
|
|
49
46
|
assert.equal(Buffer.byteLength(context, "utf8") < POINTER_MAX_BYTES, true);
|
|
50
47
|
});
|
|
51
48
|
|
|
@@ -54,15 +54,12 @@ function matchesCas(state, expected, clauses) {
|
|
|
54
54
|
|
|
55
55
|
for (const surface of surfaces) {
|
|
56
56
|
test(`#given ${surface.name} #when deeper review becomes required before plan completion #then durable request state covers explicit and automatic review without inventing a digest`, async () => {
|
|
57
|
-
const skill = await readFile(surface.skillPath, "utf8");
|
|
58
57
|
const workflow = await readFile(surface.workflowPath, "utf8");
|
|
59
58
|
const contract = readJsonContract(workflow, "ulw-plan-review-request-state-contract");
|
|
60
59
|
|
|
61
60
|
assert.equal(contract.transition, "replace");
|
|
62
61
|
assert.equal(contract.phase, "review_requested");
|
|
63
62
|
assert.deepEqual(contract.applies_when, ["explicit_review_modifier_before_complete_plan", "intent=unclear_and_nontrivial"]);
|
|
64
|
-
assert.match(skill, /Include `--review-required`[\s\S]{0,180}non-Trivial UNCLEAR/);
|
|
65
|
-
assert.match(workflow, /--draft-only/);
|
|
66
63
|
assert.equal(contract.atomic, true);
|
|
67
64
|
assert.equal(contract.review_required, true);
|
|
68
65
|
assert.equal(contract.plan_path, ".omo/plans/<slug>.md");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// omo-codex-install:1d8359e77ebe41655f091cd2e455f320cde2f9c681f0651797c47d8fdfa2dd76:
|
|
2
|
+
// omo-codex-install:1d8359e77ebe41655f091cd2e455f320cde2f9c681f0651797c47d8fdfa2dd76:c76513d859dcf200fa0178cedc7bbd268cbb9a16c2f2b30d3edd344daec24755
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __returnValue = (v) => v;
|
|
5
5
|
function __exportSetter(name, newValue) {
|
|
@@ -5927,7 +5927,7 @@ var package_default;
|
|
|
5927
5927
|
var init_package = __esm(() => {
|
|
5928
5928
|
package_default = {
|
|
5929
5929
|
name: "@oh-my-opencode/omo-codex",
|
|
5930
|
-
version: "5.0.0-beta.
|
|
5930
|
+
version: "5.0.0-beta.9",
|
|
5931
5931
|
type: "module",
|
|
5932
5932
|
private: true,
|
|
5933
5933
|
description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
|
|
@@ -1,19 +1,46 @@
|
|
|
1
1
|
# ATTRIBUTION / NOTICE
|
|
2
2
|
|
|
3
3
|
This skill (`ultimate-browsing`, part of `@oh-my-opencode/shared-skills`) ships
|
|
4
|
-
project-original content
|
|
5
|
-
(it does NOT vendor their source).
|
|
6
|
-
notices are reproduced below.
|
|
4
|
+
project-original content, one vendored-and-modified upstream engine, plus two
|
|
5
|
+
third-party tools that it installs at runtime (it does NOT vendor their source).
|
|
6
|
+
Each component's provenance, license, and required notices are reproduced below.
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## 1.
|
|
10
|
+
## 1. insane-search engine — vendored upstream snapshot, modified
|
|
11
|
+
|
|
12
|
+
`engine/**` originates from the **insane-search** project and is NOT
|
|
13
|
+
project-original code, despite being heavily modified since import.
|
|
14
|
+
|
|
15
|
+
- Upstream source: https://github.com/fivetaku/insane-search
|
|
16
|
+
- Vendored into this repository on 2026-06-21 by commit
|
|
17
|
+
**`a4e4ed797`** (`feat(ultimate-browsing): vendor insane-search engine (junk-excluded)`),
|
|
18
|
+
via an explicit file whitelist that excluded caches and smoke-test junk.
|
|
19
|
+
- Baseline: the upstream state as of that date, a **pre-0.7.0 snapshot**.
|
|
20
|
+
Upstream's CHANGELOG dates 0.7.0 to 2026-06-22; imported files carry no
|
|
21
|
+
version marker. We have never re-vendored since; the tree has diverged in
|
|
22
|
+
both directions.
|
|
23
|
+
- Modifications by this project (non-exhaustive): de-personalization
|
|
24
|
+
(`4743199a5`), the Phase 2.5 surrogate retrieval stage and surrogate registry,
|
|
25
|
+
the provenance/trust result contract, the `bias_check.py` no-site-name CI gate,
|
|
26
|
+
module split of the fetch chain, and the Python test suite under
|
|
27
|
+
`engine/tests/`.
|
|
28
|
+
- No upstream `LICENSE` file was included in the vendored snapshot, so this
|
|
29
|
+
repository has no upstream license text to reproduce here. Do not infer
|
|
30
|
+
project-original licensing from that absence; treat `engine/**` as
|
|
31
|
+
upstream-derived when reasoning about provenance.
|
|
32
|
+
|
|
33
|
+
The binding version policy — which upstream baseline we sit on, why we stay
|
|
34
|
+
pinned, what a future re-vendor must preserve, and what it must not import — is
|
|
35
|
+
[`engine/AGENTS.md` §UPSTREAM BASELINE AND VERSION POLICY](engine/AGENTS.md).
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. Project-original content (no third-party source vendored)
|
|
11
40
|
|
|
12
41
|
The following are authored by the oh-my-openagent project and carry no third-party
|
|
13
42
|
license obligation:
|
|
14
43
|
|
|
15
|
-
- `engine/**` — the insane-search Tier-1 fetch engine (curl_cffi grid, WAF
|
|
16
|
-
detection, Playwright fallback templates, `bias_check.py` no-site-name gate).
|
|
17
44
|
- `references/insane-search/**` and `references/agent-reach/**` — the Tier-1 and
|
|
18
45
|
Tier-1.5 reference docs.
|
|
19
46
|
- `scripts/extract_cookies.py`, `scripts/cookie_paths.py`, `scripts/cookie_crypto.py`
|
|
@@ -26,7 +53,7 @@ user installs separately; this skill includes none of their source.
|
|
|
26
53
|
|
|
27
54
|
---
|
|
28
55
|
|
|
29
|
-
##
|
|
56
|
+
## 3. CloakBrowser (CloakHQ) — Tier-2 stealth Chromium (runtime dependency)
|
|
30
57
|
|
|
31
58
|
The Tier-2 stealth browser is **CloakBrowser**, installed at runtime via `pip`
|
|
32
59
|
(`pip install cloakbrowser`). No CloakBrowser source is vendored in this repository.
|
|
@@ -73,7 +100,7 @@ SOFTWARE.
|
|
|
73
100
|
|
|
74
101
|
---
|
|
75
102
|
|
|
76
|
-
##
|
|
103
|
+
## 4. agent-browser (vercel-labs) — Tier-2 CDP automation CLI (runtime dependency)
|
|
77
104
|
|
|
78
105
|
The Tier-2 automation CLI is **agent-browser**, installed at runtime via `npm`
|
|
79
106
|
(`npm i -g agent-browser`). No agent-browser source is vendored in this repository.
|
|
@@ -2,6 +2,116 @@
|
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-08-10 / 38d268995
|
|
4
4
|
|
|
5
|
+
## UPSTREAM BASELINE AND VERSION POLICY
|
|
6
|
+
|
|
7
|
+
**READ THIS BEFORE TOUCHING `engine/**` OR PROPOSING AN UPSTREAM SYNC.**
|
|
8
|
+
|
|
9
|
+
This engine is NOT project-original code. It is a vendored-and-modified snapshot of
|
|
10
|
+
[fivetaku/insane-search](https://github.com/fivetaku/insane-search), and the version
|
|
11
|
+
we run on is a deliberate choice, not an accident of neglect.
|
|
12
|
+
|
|
13
|
+
### The pin
|
|
14
|
+
|
|
15
|
+
| Fact | Value |
|
|
16
|
+
|---|---|
|
|
17
|
+
| Upstream project | `https://github.com/fivetaku/insane-search` |
|
|
18
|
+
| Vendoring commit | `a4e4ed797` (2026-06-21) `feat(ultimate-browsing): vendor insane-search engine (junk-excluded)` |
|
|
19
|
+
| De-personalization | `4743199a5` (2026-06-21) |
|
|
20
|
+
| Pinned upstream baseline | upstream state as of 2026-06-21, **pre-0.7.0** (0.7.0 is dated 2026-06-22) |
|
|
21
|
+
| Re-vendors since | none — every later change here is ours |
|
|
22
|
+
|
|
23
|
+
We intentionally track a **pinned baseline plus local divergence**, not upstream HEAD.
|
|
24
|
+
There is no submodule and no automated drift check for this engine (unlike the
|
|
25
|
+
`frontend` skill's upstream submodules): the vendored files ARE the source of truth,
|
|
26
|
+
and upstream is a reference we port FROM, deliberately, file by file.
|
|
27
|
+
|
|
28
|
+
### Why we do not blind-rebase onto upstream HEAD
|
|
29
|
+
|
|
30
|
+
1. **Upstream reset its public history.** On 2026-08-06 upstream published a single
|
|
31
|
+
squashed commit, `019ee16 refactor: reset public history at 0.14.0`, discarding the
|
|
32
|
+
prior public history through 0.13.x. There is no upstream commit graph to rebase onto and no
|
|
33
|
+
way to cherry-pick an individual upstream change by sha — only whole-file diffing
|
|
34
|
+
against a moving HEAD.
|
|
35
|
+
2. **Upstream 0.14.0 REMOVED capability.** The endpoint-mining / internal-API
|
|
36
|
+
auto-derivation / site-recipe subsystems that upstream carried publicly between
|
|
37
|
+
0.12.0 and 0.14.0 are gone from upstream HEAD. Syncing to HEAD is therefore not
|
|
38
|
+
strictly an upgrade: parts of it are a downgrade relative to the intermediate
|
|
39
|
+
versions, and none of it is recoverable from the reset history.
|
|
40
|
+
3. **Our tree diverged on purpose.** The KEEP list below is functionality upstream
|
|
41
|
+
never had. A wholesale overwrite with upstream HEAD would silently delete it.
|
|
42
|
+
4. **Different threat model.** Our engine ships inside a published npm package and a
|
|
43
|
+
public marketplace mirror, under a CI no-site-name gate and a de-personalization
|
|
44
|
+
deny-list. Upstream carries neither constraint, so upstream code is not
|
|
45
|
+
drop-in-shippable here.
|
|
46
|
+
|
|
47
|
+
### KEEP — our divergences a re-vendor MUST NOT regress
|
|
48
|
+
|
|
49
|
+
These exist only in our tree. Any upstream sync that removes or bypasses one of them
|
|
50
|
+
is a regression, not an upgrade:
|
|
51
|
+
|
|
52
|
+
- **Phase 2.5 surrogate retrieval** (`surrogate.py`, `surrogates.yaml`) — archive /
|
|
53
|
+
reader / proxy routes tried before paying for a browser spin-up, with per-entry
|
|
54
|
+
`last_verified` staleness handling and `--allow-proxy` gating.
|
|
55
|
+
- **Provenance / trust contract** (`result_schema.py`) — `Provenance` and `Trust`
|
|
56
|
+
literals on every result, so a snapshot can never be reported as the live page.
|
|
57
|
+
- **Surrogate dead-end validation** (L1.5 in `validators.py`) — interstitial titles and
|
|
58
|
+
AMP-style redirect stubs rejected instead of returned as content.
|
|
59
|
+
- **The no-site-name rule and its CI gate** (`bias_check.py`) — zero hard-coded site
|
|
60
|
+
names, brands, or target domains in `engine/**`.
|
|
61
|
+
- **Module split of the fetch chain** — `curl_probe` / `referers` / `url_transforms` /
|
|
62
|
+
`waf_detector` / `validators` / `executor` / `summary` as separate modules rather than
|
|
63
|
+
one monolith.
|
|
64
|
+
- **The Python test suite** under `engine/tests/` with its HTML/JSON fixtures.
|
|
65
|
+
- **De-personalization** — no personal absolute paths, no personal auth token literals,
|
|
66
|
+
no personal browser choice; enforced by `depersonalization-gate.test.ts`.
|
|
67
|
+
- **Skill-level layering** — the engine is Tier 1 under a router that also owns Tier 1.5
|
|
68
|
+
(agent-reach) and Tier 2 (CloakBrowser + agent-browser). Upstream has no such tiering.
|
|
69
|
+
|
|
70
|
+
### WANT — upstream improvements worth porting forward
|
|
71
|
+
|
|
72
|
+
Our snapshot predates these; they are wanted, and each must be ported as a reviewed,
|
|
73
|
+
site-agnostic change that preserves every KEEP item above. Port individually; never as
|
|
74
|
+
a tree overwrite:
|
|
75
|
+
|
|
76
|
+
- **Content quality**: dedicated markdown conversion of fetched HTML, main-content
|
|
77
|
+
extraction, PDF text extraction, and JSON-LD rescue when the HTML body is thin.
|
|
78
|
+
- **Transient-failure retry** and **render-merge** of statically fetched HTML with the
|
|
79
|
+
browser-rendered DOM.
|
|
80
|
+
- **Differential block classification** — distinguishing a bot-detection block from an
|
|
81
|
+
infrastructure or authentication failure, instead of collapsing both into `challenge`.
|
|
82
|
+
- **Additional stealth fetch backends** beyond the current Playwright templates, and
|
|
83
|
+
additional WAF vendor profiles.
|
|
84
|
+
- **Per-host route learning** — remembering which route succeeded for a host, with a TTL
|
|
85
|
+
and a bounded store. Must stay runtime state, never committed site knowledge (R4).
|
|
86
|
+
- **Engine-level Phase 0 routing** — the official-public-API preference is currently only
|
|
87
|
+
a documented rule (R5) the agent can skip; upstream moved it into code so it cannot be
|
|
88
|
+
skipped. Worth adopting.
|
|
89
|
+
|
|
90
|
+
### OUT OF SCOPE
|
|
91
|
+
|
|
92
|
+
- **The removed upstream endpoint-mining / internal-API auto-derivation / site-recipe
|
|
93
|
+
subsystems.** They are absent from upstream HEAD and are not reconstructed here. They
|
|
94
|
+
also sit against R3/R4 and R7's anti-bias rule: discovered internal endpoints are
|
|
95
|
+
runtime findings, never committed engine knowledge.
|
|
96
|
+
- **Any upstream code carrying site-specific selectors, domains, or brand names.** It
|
|
97
|
+
fails `bias_check.py` at the door; re-derive it site-agnostically or leave it out.
|
|
98
|
+
- **Automated upstream tracking.** No submodule, no drift check, no auto-bump. Syncing is
|
|
99
|
+
a deliberate, reviewed, human-initiated act.
|
|
100
|
+
|
|
101
|
+
### THE SYNC RULE
|
|
102
|
+
|
|
103
|
+
Any future upstream sync preserves BOTH sides. Concretely:
|
|
104
|
+
|
|
105
|
+
1. Diff the specific upstream capability you want against our tree — do not overwrite
|
|
106
|
+
files wholesale, and never `git checkout` upstream over `engine/`.
|
|
107
|
+
2. Port it as its own reviewed change, keeping every KEEP item intact.
|
|
108
|
+
3. Re-run `python3 engine/bias_check.py` and the `engine/tests/` suite; a port that
|
|
109
|
+
introduces a site name or breaks a fixture does not ship.
|
|
110
|
+
4. Update the pin table above (baseline, date, what was ported) in the same change, plus
|
|
111
|
+
the provenance section of [`../ATTRIBUTION.md`](../ATTRIBUTION.md).
|
|
112
|
+
5. If a port must drop a KEEP item, say so explicitly in the PR and get it agreed first —
|
|
113
|
+
silent regressions of the KEEP list are the failure mode this policy exists to prevent.
|
|
114
|
+
|
|
5
115
|
## OVERVIEW
|
|
6
116
|
|
|
7
117
|
A 17-module Python package embedded in the `ultimate-browsing` skill: a site-agnostic fetch chain that escalates from a cheap curl probe to a real browser, with declarative WAF and surrogate registries. Not "optional scripts" — it has its own CLI entry (`python3 -m engine URL`), two YAML config schemas, a 4-file test suite, and a standalone CI guard. Package exports (`__init__.py`): `fetch`, `FetchResult`, `Attempt`, `Verdict`, `ValidationResult`, `validate`, `CHALLENGE_MARKERS`, `detect`, `TRANSFORMS`, `apply_transform`.
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
-
import { basename, dirname, join } from "node:path";
|
|
4
|
-
import test from "node:test";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
findInvalidSpawnAgentRoleParameters,
|
|
8
|
-
findSpawnAgentCallsWithoutForkContextFalse,
|
|
9
|
-
root,
|
|
10
|
-
} from "./aggregate-plugin-fixture.mjs";
|
|
11
|
-
import { listSkillFiles } from "./sync-skills-test-support.mjs";
|
|
12
|
-
|
|
13
|
-
test("#given synced skills with Codex compatibility guidance #when spawn_agent is documented #then invalid role parameters are absent", async () => {
|
|
14
|
-
const skillsDir = join(root, "skills");
|
|
15
|
-
const skillEntries = await readdir(skillsDir, { withFileTypes: true });
|
|
16
|
-
const skillFiles = skillEntries
|
|
17
|
-
.filter((entry) => entry.isDirectory())
|
|
18
|
-
.map((entry) => join(skillsDir, entry.name, "SKILL.md"));
|
|
19
|
-
|
|
20
|
-
const invalidCalls = [];
|
|
21
|
-
for (const skillPath of skillFiles) {
|
|
22
|
-
const content = await readFile(skillPath, "utf8");
|
|
23
|
-
for (const call of findInvalidSpawnAgentRoleParameters(content)) {
|
|
24
|
-
invalidCalls.push(`${basename(dirname(skillPath))}/${basename(skillPath)}: ${call}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
assert.deepEqual(invalidCalls, []);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test('#given synced skills and bundled rules #when role-specific agents are spawned #then they set fork_context=false', async () => {
|
|
32
|
-
const skillsDir = join(root, "skills");
|
|
33
|
-
const skillEntries = await readdir(skillsDir, { withFileTypes: true });
|
|
34
|
-
const promptFiles = skillEntries
|
|
35
|
-
.filter((entry) => entry.isDirectory())
|
|
36
|
-
.map((entry) => join(skillsDir, entry.name, "SKILL.md"));
|
|
37
|
-
promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"));
|
|
38
|
-
promptFiles.push(join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"));
|
|
39
|
-
|
|
40
|
-
const missingForkContext = [];
|
|
41
|
-
for (const promptPath of promptFiles) {
|
|
42
|
-
const content = await readFile(promptPath, "utf8");
|
|
43
|
-
for (const call of findSpawnAgentCallsWithoutForkContextFalse(content)) {
|
|
44
|
-
missingForkContext.push(`${basename(dirname(promptPath))}/${basename(promptPath)}: ${call}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
assert.deepEqual(missingForkContext, []);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("#given long-running orchestration prompts #when waiting on child agents #then parent liveness is surfaced", async () => {
|
|
52
|
-
const promptFiles = [
|
|
53
|
-
join(root, "skills", "ulw-loop", "SKILL.md"),
|
|
54
|
-
join(root, "skills", "ulw-loop", "references", "full-workflow.md"),
|
|
55
|
-
join(root, "skills", "review-work", "SKILL.md"),
|
|
56
|
-
join(root, "skills", "start-work", "SKILL.md"),
|
|
57
|
-
join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.5.md"),
|
|
58
|
-
join(root, "components", "rules", "bundled-rules", "hephaestus", "gpt-5.6.md"),
|
|
59
|
-
];
|
|
60
|
-
|
|
61
|
-
const missingLivenessGuidance = [];
|
|
62
|
-
for (const promptPath of promptFiles) {
|
|
63
|
-
const content = await readFile(promptPath, "utf8");
|
|
64
|
-
if (!/active\s+subagent count/.test(content) || !/latest `WORKING:` phase/.test(content)) {
|
|
65
|
-
missingLivenessGuidance.push(`${basename(dirname(promptPath))}/${basename(promptPath)}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
assert.deepEqual(missingLivenessGuidance, []);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("#given packaged Codex prompt surfaces #when inspected #then removed sparkshell guidance is absent", async () => {
|
|
73
|
-
const promptRoots = [
|
|
74
|
-
join(root, "skills"),
|
|
75
|
-
join(root, "components", "rules", "bundled-rules"),
|
|
76
|
-
join(root, "components", "ultrawork", "agents"),
|
|
77
|
-
join(root, "components", "ulw-loop", "skills"),
|
|
78
|
-
];
|
|
79
|
-
const promptFilePattern = /\.(?:json|md|toml|ya?ml)$/i;
|
|
80
|
-
const removedSparkshellPattern = /\b(?:sparkshell|spark[-_\s]+shell)\b/i;
|
|
81
|
-
const matches = [];
|
|
82
|
-
|
|
83
|
-
for (const promptRoot of promptRoots) {
|
|
84
|
-
for (const relativePath of await listSkillFiles(promptRoot)) {
|
|
85
|
-
if (!promptFilePattern.test(relativePath)) continue;
|
|
86
|
-
const content = await readFile(join(promptRoot, relativePath), "utf8");
|
|
87
|
-
if (removedSparkshellPattern.test(content)) matches.push(`${promptRoot}/${relativePath}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
assert.deepEqual(matches, []);
|
|
92
|
-
});
|