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.
Files changed (101) hide show
  1. package/dist/cli/index.js +34 -31
  2. package/dist/cli/run/on-complete-hook.d.ts +2 -0
  3. package/dist/cli-node/index.js +34 -31
  4. package/package.json +1 -1
  5. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  6. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  7. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  8. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +2 -2
  9. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +2 -2
  10. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  14. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  16. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +3 -17
  18. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  19. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  20. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  21. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  22. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/rules/test/bundled-rules-priority.test.ts +11 -16
  24. package/packages/omo-codex/plugin/components/rules/test/bundled-rules.test.ts +16 -23
  25. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-budget.test.ts +9 -7
  26. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-context.test.ts +0 -6
  27. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-dedup.test.ts +6 -4
  28. package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-directive.test.ts +12 -9
  29. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +28 -37
  30. package/packages/omo-codex/plugin/components/rules/test/formatter.test.ts +37 -69
  31. package/packages/omo-codex/plugin/components/rules/test/hook-output.test.ts +2 -3
  32. package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +1 -15
  33. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  34. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  35. package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +0 -3
  36. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +2 -16
  37. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  38. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +3 -9
  40. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  43. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +0 -136
  45. package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +0 -2
  46. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  47. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +0 -1
  49. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +2 -2
  50. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-hook.test.ts +0 -3
  51. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +2 -35
  52. package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +4 -5
  53. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  65. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  66. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  67. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  68. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  69. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  70. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  71. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  72. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  73. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  76. package/packages/omo-codex/plugin/package-lock.json +20 -20
  77. package/packages/omo-codex/plugin/package.json +1 -1
  78. package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +35 -8
  79. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +110 -0
  80. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +19 -173
  81. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -24
  82. package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +175 -13
  83. package/packages/omo-codex/plugin/test/aggregate.test.mjs +78 -2
  84. package/packages/omo-codex/plugin/test/auto-update-release-notes.test.mjs +19 -33
  85. package/packages/omo-codex/plugin/test/lcx-contribute-bug-fix-template.test.mjs +21 -27
  86. package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +0 -36
  87. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +101 -0
  88. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +1 -119
  89. package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +0 -40
  90. package/packages/omo-codex/plugin/test/teammode-communication.test.mjs +6 -62
  91. package/packages/omo-codex/plugin/test/teammode-thread-links.test.mjs +3 -36
  92. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +0 -44
  93. package/packages/omo-codex/plugin/test/teammode-worktree.test.mjs +2 -6
  94. package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +0 -3
  95. package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +0 -3
  96. package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
  97. package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +35 -8
  98. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +110 -0
  99. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +0 -92
  100. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +0 -314
  101. 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. The real run of leader session
10
- // 019eed1e- proved members default to silence: 10 members, ~6 min, 4 end-of-work reports, zero
11
- // peer messages, zero mid-task reports, zero send_message_to_thread calls - because the prose said
12
- // "over-communicate relentlessly" (aspirational, no trigger) and never named the tool or targets.
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 tells it to push via the tool as it works
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, /Your Codex thread link is codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
79
- assert.match(prompt, /Work inside `\/tmp\/review-worktree`/);
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 flips the member's guide to ISOLATION IS ON with the derived branch", () => {
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:3ebe53198af1a035e21991b814b41ddb4819c09cfde23b8864be33a92c636428
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.8",
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 plus two third-party tools that it installs at runtime
5
- (it does NOT vendor their source). Each runtime tool's license and required
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. Project-original content (no third-party source vendored)
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
- ## 2. CloakBrowser (CloakHQ) — Tier-2 stealth Chromium (runtime dependency)
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
- ## 3. agent-browser (vercel-labs) — Tier-2 CDP automation CLI (runtime dependency)
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
- });