oh-my-codex 0.19.1 → 0.20.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/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +3 -1
- package/crates/omx-api/src/lib.rs +3 -3
- package/crates/omx-api/tests/cli.rs +4 -4
- package/crates/omx-explore/src/main.rs +4 -4
- package/crates/omx-sparkshell/src/codex_bridge.rs +2 -2
- package/dist/agents/__tests__/definitions.test.js +3 -3
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +71 -42
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +3 -3
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -2
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +7 -5
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.js +19 -12
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +151 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts.map +1 -1
- package/dist/autopilot/planner-routing.js +4 -2
- package/dist/autopilot/planner-routing.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +38 -2
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts +2 -0
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +8 -0
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/capabilities/__tests__/lockfile.test.d.ts +2 -0
- package/dist/capabilities/__tests__/lockfile.test.d.ts.map +1 -0
- package/dist/capabilities/__tests__/lockfile.test.js +115 -0
- package/dist/capabilities/__tests__/lockfile.test.js.map +1 -0
- package/dist/capabilities/lockfile.d.ts +102 -0
- package/dist/capabilities/lockfile.d.ts.map +1 -0
- package/dist/capabilities/lockfile.js +334 -0
- package/dist/capabilities/lockfile.js.map +1 -0
- package/dist/cli/__tests__/agents.test.js +5 -5
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/capabilities.test.d.ts +2 -0
- package/dist/cli/__tests__/capabilities.test.d.ts.map +1 -0
- package/dist/cli/__tests__/capabilities.test.js +135 -0
- package/dist/cli/__tests__/capabilities.test.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +237 -6
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +9 -9
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +1 -1
- package/dist/cli/__tests__/doctor-spark-routing.test.js +22 -6
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +53 -18
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +3 -3
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +95 -6
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +46 -18
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +3 -3
- package/dist/cli/__tests__/setup-refresh.test.js +29 -7
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +4 -4
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/agents.js +1 -1
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/capabilities.d.ts +3 -0
- package/dist/cli/capabilities.d.ts.map +1 -0
- package/dist/cli/capabilities.js +118 -0
- package/dist/cli/capabilities.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +7 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +77 -19
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +31 -0
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +7 -4
- package/dist/cli/setup.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +23 -23
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +9 -9
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +51 -26
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/models.d.ts +16 -6
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +19 -8
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +12 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +2 -2
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +2 -2
- package/dist/leader/__tests__/contract.test.js +108 -1
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +26 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +170 -0
- package/dist/leader/contract.js.map +1 -1
- package/dist/modes/base.d.ts +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +27 -1
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +2 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +1 -0
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.d.ts +5 -1
- package/dist/pipeline/stages/ultragoal.d.ts.map +1 -1
- package/dist/pipeline/stages/ultragoal.js +14 -4
- package/dist/pipeline/stages/ultragoal.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +132 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +17 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +97 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +412 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +238 -12
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +450 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +14 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +72 -11
- package/dist/state/operations.js.map +1 -1
- package/dist/state/terminal-normalization.d.ts +8 -0
- package/dist/state/terminal-normalization.d.ts.map +1 -0
- package/dist/state/terminal-normalization.js +127 -0
- package/dist/state/terminal-normalization.js.map +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +3 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/delegation-policy.test.js +2 -2
- package/dist/team/__tests__/delegation-policy.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +25 -25
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +28 -28
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +7 -7
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js +5 -5
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +12 -12
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +57 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +3 -3
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +11 -0
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +2 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +6 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +68 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +5 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +41 -9
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +7 -7
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts +2 -0
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts.map +1 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js +118 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js.map +1 -0
- package/dist/utils/worktree-tool-context.d.ts +25 -0
- package/dist/utils/worktree-tool-context.d.ts.map +1 -0
- package/dist/utils/worktree-tool-context.js +116 -0
- package/dist/utils/worktree-tool-context.js.map +1 -0
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +96 -10
- package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +2 -2
- package/prompts/prometheus-strict-metis.md +1 -1
- package/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/skills/code-review/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +1 -1
- package/skills/omx-setup/SKILL.md +5 -5
- package/skills/prometheus-strict/SKILL.md +3 -3
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/team/SKILL.md +1 -1
- package/skills/ultrawork/references/agent-tiers.md +2 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +477 -0
- package/src/scripts/codex-native-hook.ts +272 -11
|
@@ -1479,6 +1479,166 @@ PY`,
|
|
|
1479
1479
|
await rm(cwd, { recursive: true, force: true });
|
|
1480
1480
|
}
|
|
1481
1481
|
});
|
|
1482
|
+
it("adds resume-by-id instructions for persisted subagents on SessionStart resume", async () => {
|
|
1483
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-reopen-"));
|
|
1484
|
+
try {
|
|
1485
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1486
|
+
const sessionId = "omx-reopen-session";
|
|
1487
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reopen", pid: process.pid });
|
|
1488
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1489
|
+
schemaVersion: 1,
|
|
1490
|
+
sessions: {
|
|
1491
|
+
[sessionId]: {
|
|
1492
|
+
session_id: sessionId,
|
|
1493
|
+
leader_thread_id: "codex-leader-reopen",
|
|
1494
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1495
|
+
threads: {
|
|
1496
|
+
"codex-leader-reopen": {
|
|
1497
|
+
thread_id: "codex-leader-reopen",
|
|
1498
|
+
kind: "leader",
|
|
1499
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1500
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1501
|
+
turn_count: 1,
|
|
1502
|
+
},
|
|
1503
|
+
"thread-architect-reopen": {
|
|
1504
|
+
thread_id: "thread-architect-reopen",
|
|
1505
|
+
kind: "subagent",
|
|
1506
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1507
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1508
|
+
turn_count: 2,
|
|
1509
|
+
role: "architect",
|
|
1510
|
+
lane_id: "plan-review",
|
|
1511
|
+
scope: "SessionStart reopen",
|
|
1512
|
+
status: "available",
|
|
1513
|
+
last_handoff_summary: "reviewed the restart plan",
|
|
1514
|
+
},
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
},
|
|
1518
|
+
});
|
|
1519
|
+
const result = await dispatchCodexNativeHook({
|
|
1520
|
+
hook_event_name: "SessionStart",
|
|
1521
|
+
cwd,
|
|
1522
|
+
session_id: "codex-leader-reopen",
|
|
1523
|
+
source: "resume",
|
|
1524
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
1525
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
1526
|
+
assert.match(additionalContext, /\[Persisted subagent reopen\]/);
|
|
1527
|
+
assert.match(additionalContext, /resume_agent\("thread-architect-reopen"\)/);
|
|
1528
|
+
assert.match(additionalContext, /role: architect; lane: plan-review; scope: SessionStart reopen; status: available/);
|
|
1529
|
+
assert.match(additionalContext, /saved subagent ids found: 1/);
|
|
1530
|
+
const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8"));
|
|
1531
|
+
assert.match(tracking.sessions?.[sessionId]?.threads?.["thread-architect-reopen"]?.resume_requested_at ?? "", /^\d{4}-\d{2}-\d{2}T/);
|
|
1532
|
+
}
|
|
1533
|
+
finally {
|
|
1534
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1535
|
+
}
|
|
1536
|
+
});
|
|
1537
|
+
it("does not suggest duplicate same-role subagent spawns when reopen ids exist", async () => {
|
|
1538
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-duplicates-"));
|
|
1539
|
+
try {
|
|
1540
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1541
|
+
const sessionId = "omx-reuse-session";
|
|
1542
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reuse", pid: process.pid });
|
|
1543
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1544
|
+
schemaVersion: 1,
|
|
1545
|
+
sessions: {
|
|
1546
|
+
[sessionId]: {
|
|
1547
|
+
session_id: sessionId,
|
|
1548
|
+
leader_thread_id: "codex-leader-reuse",
|
|
1549
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1550
|
+
threads: {
|
|
1551
|
+
"codex-leader-reuse": {
|
|
1552
|
+
thread_id: "codex-leader-reuse",
|
|
1553
|
+
kind: "leader",
|
|
1554
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1555
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1556
|
+
turn_count: 1,
|
|
1557
|
+
},
|
|
1558
|
+
"thread-critic-reuse": {
|
|
1559
|
+
thread_id: "thread-critic-reuse",
|
|
1560
|
+
kind: "subagent",
|
|
1561
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1562
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1563
|
+
turn_count: 1,
|
|
1564
|
+
role: "critic",
|
|
1565
|
+
lane_id: "risk-review",
|
|
1566
|
+
status: "closed",
|
|
1567
|
+
},
|
|
1568
|
+
},
|
|
1569
|
+
},
|
|
1570
|
+
},
|
|
1571
|
+
});
|
|
1572
|
+
const result = await dispatchCodexNativeHook({
|
|
1573
|
+
hook_event_name: "SessionStart",
|
|
1574
|
+
cwd,
|
|
1575
|
+
session_id: "codex-leader-reuse",
|
|
1576
|
+
source: "startup",
|
|
1577
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
1578
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
1579
|
+
assert.match(additionalContext, /resume_agent\("thread-critic-reuse"\)/);
|
|
1580
|
+
assert.match(additionalContext, /avoid duplicate same-type subagent spawns/);
|
|
1581
|
+
assert.match(additionalContext, /do not spawn a new agent solely because reopen failed/);
|
|
1582
|
+
assert.doesNotMatch(additionalContext, /spawn.*critic.*replacement/i);
|
|
1583
|
+
}
|
|
1584
|
+
finally {
|
|
1585
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
it("surfaces clear warnings for unavailable persisted subagents without spawning replacements", async () => {
|
|
1589
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-warning-"));
|
|
1590
|
+
try {
|
|
1591
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1592
|
+
const sessionId = "omx-warning-session";
|
|
1593
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-warning", pid: process.pid });
|
|
1594
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1595
|
+
schemaVersion: 1,
|
|
1596
|
+
sessions: {
|
|
1597
|
+
[sessionId]: {
|
|
1598
|
+
session_id: sessionId,
|
|
1599
|
+
leader_thread_id: "codex-leader-warning",
|
|
1600
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1601
|
+
threads: {
|
|
1602
|
+
"codex-leader-warning": {
|
|
1603
|
+
thread_id: "codex-leader-warning",
|
|
1604
|
+
kind: "leader",
|
|
1605
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1606
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1607
|
+
turn_count: 1,
|
|
1608
|
+
},
|
|
1609
|
+
"thread-executor-unavailable": {
|
|
1610
|
+
thread_id: "thread-executor-unavailable",
|
|
1611
|
+
kind: "subagent",
|
|
1612
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1613
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1614
|
+
turn_count: 1,
|
|
1615
|
+
role: "executor",
|
|
1616
|
+
lane_id: "implementation",
|
|
1617
|
+
status: "unavailable",
|
|
1618
|
+
resume_failed_at: "2026-07-09T00:02:00.000Z",
|
|
1619
|
+
resume_failure_reason: "Codex reported missing thread id",
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
},
|
|
1623
|
+
},
|
|
1624
|
+
});
|
|
1625
|
+
const result = await dispatchCodexNativeHook({
|
|
1626
|
+
hook_event_name: "SessionStart",
|
|
1627
|
+
cwd,
|
|
1628
|
+
session_id: "codex-leader-warning",
|
|
1629
|
+
source: "resume",
|
|
1630
|
+
}, { cwd, sessionOwnerPid: process.pid });
|
|
1631
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
1632
|
+
assert.match(additionalContext, /No compatible saved subagent id is currently marked reopenable/);
|
|
1633
|
+
assert.match(additionalContext, /Reopen warnings:/);
|
|
1634
|
+
assert.match(additionalContext, /thread-executor-unavailable/);
|
|
1635
|
+
assert.match(additionalContext, /last failure: Codex reported missing thread id/);
|
|
1636
|
+
assert.doesNotMatch(additionalContext, /resume_agent\("thread-executor-unavailable"\)/);
|
|
1637
|
+
}
|
|
1638
|
+
finally {
|
|
1639
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1482
1642
|
it("preserves canonical OMX session scope when native SessionStart arrives with a different id", async () => {
|
|
1483
1643
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-reconcile-"));
|
|
1484
1644
|
try {
|
|
@@ -4150,6 +4310,66 @@ standardMaxRounds = 15
|
|
|
4150
4310
|
await rm(cwd, { recursive: true, force: true });
|
|
4151
4311
|
}
|
|
4152
4312
|
});
|
|
4313
|
+
it("keeps conductor guidance on autopilot activation after capacity-only native subagent evidence", async () => {
|
|
4314
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-capacity-native-"));
|
|
4315
|
+
try {
|
|
4316
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
4317
|
+
await dispatchCodexNativeHook({
|
|
4318
|
+
hook_event_name: "PostToolUse",
|
|
4319
|
+
cwd,
|
|
4320
|
+
session_id: "sess-autopilot-capacity-native",
|
|
4321
|
+
thread_id: "thread-autopilot-capacity-native",
|
|
4322
|
+
turn_id: "turn-autopilot-capacity-native-spawn",
|
|
4323
|
+
tool_name: "multi_agent_v1.spawn_agent",
|
|
4324
|
+
tool_response: { error: "collab spawn failed: agent thread limit reached" },
|
|
4325
|
+
}, { cwd });
|
|
4326
|
+
const result = await dispatchCodexNativeHook({
|
|
4327
|
+
hook_event_name: "UserPromptSubmit",
|
|
4328
|
+
cwd,
|
|
4329
|
+
session_id: "sess-autopilot-capacity-native",
|
|
4330
|
+
thread_id: "thread-autopilot-capacity-native",
|
|
4331
|
+
turn_id: "turn-autopilot-capacity-native-prompt",
|
|
4332
|
+
prompt: "$autopilot implement issue #3078",
|
|
4333
|
+
}, { cwd });
|
|
4334
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
4335
|
+
assert.equal(result.skillState?.skill, "autopilot");
|
|
4336
|
+
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
4337
|
+
assert.match(message, /Autopilot protocol:/);
|
|
4338
|
+
assert.match(message, /Conductor mode contract:/);
|
|
4339
|
+
assert.match(message, /Golden Rule: When the Main agent is acting in Conductor mode/);
|
|
4340
|
+
assert.match(message, /Conductor reuse and ledger guidance:/);
|
|
4341
|
+
assert.doesNotMatch(message, /Native subagent support is unavailable in this environment/);
|
|
4342
|
+
assert.doesNotMatch(message, /Reason: agent_thread_limit_reached/);
|
|
4343
|
+
}
|
|
4344
|
+
finally {
|
|
4345
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4346
|
+
}
|
|
4347
|
+
});
|
|
4348
|
+
it("omits conductor block and emits unsupported native guidance for unsupported autopilot first-run payload", async () => {
|
|
4349
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-unsupported-native-"));
|
|
4350
|
+
try {
|
|
4351
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
4352
|
+
const result = await dispatchCodexNativeHook({
|
|
4353
|
+
hook_event_name: "UserPromptSubmit",
|
|
4354
|
+
cwd,
|
|
4355
|
+
session_id: "sess-autopilot-unsupported-native",
|
|
4356
|
+
thread_id: "thread-autopilot-unsupported-native",
|
|
4357
|
+
turn_id: "turn-autopilot-unsupported-native",
|
|
4358
|
+
prompt: "$autopilot implement issue #3078",
|
|
4359
|
+
capabilities: { native_subagents: false, multi_agent_v1: false },
|
|
4360
|
+
}, { cwd });
|
|
4361
|
+
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || "");
|
|
4362
|
+
assert.match(message, /Autopilot protocol:/);
|
|
4363
|
+
assert.doesNotMatch(message, /Conductor mode contract:/);
|
|
4364
|
+
assert.doesNotMatch(message, /Conductor reuse and ledger guidance:/);
|
|
4365
|
+
assert.match(message, /Native subagent support is unavailable in this environment/);
|
|
4366
|
+
assert.match(message, /Reason: native_subagents_unsupported/);
|
|
4367
|
+
assert.match(message, /Do not call multi_agent_v1\.close_agent/);
|
|
4368
|
+
}
|
|
4369
|
+
finally {
|
|
4370
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4371
|
+
}
|
|
4372
|
+
});
|
|
4153
4373
|
it("records ultragoal prompt skill activation with goal-tool handoff guidance", async () => {
|
|
4154
4374
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-"));
|
|
4155
4375
|
try {
|
|
@@ -6522,6 +6742,69 @@ exit 0
|
|
|
6522
6742
|
}, { cwd });
|
|
6523
6743
|
assert.equal(protectedWrite.outputJson?.decision, "block", `${filePath} should not be model-writable during deep-interview`);
|
|
6524
6744
|
}
|
|
6745
|
+
const runtimeRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-runtime-root-"));
|
|
6746
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
6747
|
+
const runtimeSessionId = "sess-di-runtime-artifact";
|
|
6748
|
+
const runtimeStateDir = join(runtimeRoot, ".omx", "state");
|
|
6749
|
+
const runtimeSessionDir = join(runtimeStateDir, "sessions", runtimeSessionId);
|
|
6750
|
+
try {
|
|
6751
|
+
process.env.OMX_ROOT = runtimeRoot;
|
|
6752
|
+
await mkdir(runtimeSessionDir, { recursive: true });
|
|
6753
|
+
await writeJson(join(runtimeStateDir, "session.json"), { session_id: runtimeSessionId, cwd });
|
|
6754
|
+
await writeJson(join(runtimeSessionDir, "skill-active-state.json"), {
|
|
6755
|
+
version: 1,
|
|
6756
|
+
active: true,
|
|
6757
|
+
skill: "deep-interview",
|
|
6758
|
+
phase: "planning",
|
|
6759
|
+
session_id: runtimeSessionId,
|
|
6760
|
+
thread_id: threadId,
|
|
6761
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: runtimeSessionId, thread_id: threadId }],
|
|
6762
|
+
});
|
|
6763
|
+
await writeJson(join(runtimeSessionDir, "deep-interview-state.json"), {
|
|
6764
|
+
active: true,
|
|
6765
|
+
mode: "deep-interview",
|
|
6766
|
+
current_phase: "intent-first",
|
|
6767
|
+
session_id: runtimeSessionId,
|
|
6768
|
+
thread_id: threadId,
|
|
6769
|
+
});
|
|
6770
|
+
const allowedRuntimeStateWrite = await dispatchCodexNativeHook({
|
|
6771
|
+
hook_event_name: "PreToolUse",
|
|
6772
|
+
cwd,
|
|
6773
|
+
session_id: runtimeSessionId,
|
|
6774
|
+
thread_id: threadId,
|
|
6775
|
+
tool_name: "Write",
|
|
6776
|
+
tool_use_id: "tool-di-runtime-state-write",
|
|
6777
|
+
tool_input: { file_path: join(runtimeSessionDir, "deep-interview-state.json"), content: "{}\n" },
|
|
6778
|
+
}, { cwd });
|
|
6779
|
+
assert.equal(allowedRuntimeStateWrite.outputJson, null);
|
|
6780
|
+
const blockedRuntimeStatePeerWrite = await dispatchCodexNativeHook({
|
|
6781
|
+
hook_event_name: "PreToolUse",
|
|
6782
|
+
cwd,
|
|
6783
|
+
session_id: runtimeSessionId,
|
|
6784
|
+
thread_id: threadId,
|
|
6785
|
+
tool_name: "Write",
|
|
6786
|
+
tool_use_id: "tool-di-runtime-peer-state-write",
|
|
6787
|
+
tool_input: { file_path: join(runtimeRoot, ".omx", "state", "sessions", "../outside", "deep-interview-state.json"), content: "{}\n" },
|
|
6788
|
+
}, { cwd });
|
|
6789
|
+
assert.equal(blockedRuntimeStatePeerWrite.outputJson?.decision, "block");
|
|
6790
|
+
const blockedRuntimeUnrelatedWrite = await dispatchCodexNativeHook({
|
|
6791
|
+
hook_event_name: "PreToolUse",
|
|
6792
|
+
cwd,
|
|
6793
|
+
session_id: runtimeSessionId,
|
|
6794
|
+
thread_id: threadId,
|
|
6795
|
+
tool_name: "Write",
|
|
6796
|
+
tool_use_id: "tool-di-runtime-unrelated-write",
|
|
6797
|
+
tool_input: { file_path: join(runtimeRoot, "unrelated", "deep-interview-state.json"), content: "{}\n" },
|
|
6798
|
+
}, { cwd });
|
|
6799
|
+
assert.equal(blockedRuntimeUnrelatedWrite.outputJson?.decision, "block");
|
|
6800
|
+
}
|
|
6801
|
+
finally {
|
|
6802
|
+
if (typeof previousOmxRoot === "string")
|
|
6803
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
6804
|
+
else
|
|
6805
|
+
delete process.env.OMX_ROOT;
|
|
6806
|
+
await rm(runtimeRoot, { recursive: true, force: true });
|
|
6807
|
+
}
|
|
6525
6808
|
// Cross-mode non-terminal `omx state write` payloads are activations,
|
|
6526
6809
|
// because state_write normalizes them to active=true after the hook.
|
|
6527
6810
|
const blockedStateCliMutation = await preToolUse({
|
|
@@ -9371,6 +9654,32 @@ PY`,
|
|
|
9371
9654
|
assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent");
|
|
9372
9655
|
assert.match(String(blocker.error_summary), /agent thread limit reached/);
|
|
9373
9656
|
assert.ok(Date.parse(String(blocker.expires_at)) > Date.parse(String(blocker.observed_at)));
|
|
9657
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false);
|
|
9658
|
+
}
|
|
9659
|
+
finally {
|
|
9660
|
+
await rm(cwd, { recursive: true, force: true });
|
|
9661
|
+
}
|
|
9662
|
+
});
|
|
9663
|
+
it("records unsupported native subagent support blocker from spawn_agent PostToolUse output", async () => {
|
|
9664
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-support-record-"));
|
|
9665
|
+
try {
|
|
9666
|
+
const result = await dispatchCodexNativeHook({
|
|
9667
|
+
hook_event_name: "PostToolUse",
|
|
9668
|
+
cwd,
|
|
9669
|
+
session_id: "sess-subagent-support-record",
|
|
9670
|
+
thread_id: "thread-subagent-support-record",
|
|
9671
|
+
turn_id: "turn-subagent-support-record",
|
|
9672
|
+
tool_name: "multi_agent_v1.spawn_agent",
|
|
9673
|
+
tool_response: { error: "unknown tool: multi_agent_v1.spawn_agent is unavailable" },
|
|
9674
|
+
}, { cwd });
|
|
9675
|
+
assert.equal(result.omxEventName, "post-tool-use");
|
|
9676
|
+
const blocker = JSON.parse(await readFile(join(cwd, ".omx", "state", "native-subagent-support.json"), "utf-8"));
|
|
9677
|
+
assert.equal(blocker.status, "unsupported");
|
|
9678
|
+
assert.equal(blocker.reason, "multi_agent_v1_unavailable");
|
|
9679
|
+
assert.equal(blocker.session_id, "sess-subagent-support-record");
|
|
9680
|
+
assert.equal(blocker.thread_id, "thread-subagent-support-record");
|
|
9681
|
+
assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent");
|
|
9682
|
+
assert.match(String(blocker.evidence), /unknown tool/);
|
|
9374
9683
|
}
|
|
9375
9684
|
finally {
|
|
9376
9685
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -17768,6 +18077,50 @@ PY`,
|
|
|
17768
18077
|
await rm(cwd, { recursive: true, force: true });
|
|
17769
18078
|
}
|
|
17770
18079
|
});
|
|
18080
|
+
it("blocks unsupported active conductor source edits with native delegation recovery guidance", async () => {
|
|
18081
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unsupported-conductor-source-"));
|
|
18082
|
+
try {
|
|
18083
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
18084
|
+
const sessionId = "sess-unsupported-conductor-source";
|
|
18085
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
18086
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
18087
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
18088
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
18089
|
+
active: true,
|
|
18090
|
+
mode: "ultragoal",
|
|
18091
|
+
current_phase: "planning",
|
|
18092
|
+
session_id: sessionId,
|
|
18093
|
+
});
|
|
18094
|
+
await writeJson(join(stateDir, "native-subagent-support.json"), {
|
|
18095
|
+
schema_version: 1,
|
|
18096
|
+
status: "unsupported",
|
|
18097
|
+
reason: "multi_agent_v1_unavailable",
|
|
18098
|
+
session_id: sessionId,
|
|
18099
|
+
evidence: "unknown tool: multi_agent_v1.spawn_agent",
|
|
18100
|
+
observed_at: new Date().toISOString(),
|
|
18101
|
+
cwd,
|
|
18102
|
+
});
|
|
18103
|
+
const result = await dispatchCodexNativeHook({
|
|
18104
|
+
hook_event_name: "PreToolUse",
|
|
18105
|
+
cwd,
|
|
18106
|
+
session_id: sessionId,
|
|
18107
|
+
thread_id: "thread-unsupported-conductor-source",
|
|
18108
|
+
tool_name: "Write",
|
|
18109
|
+
tool_input: { file_path: "src/runtime.ts", content: "export const value = 1;\n" },
|
|
18110
|
+
}, { cwd });
|
|
18111
|
+
const output = result.outputJson;
|
|
18112
|
+
const context = String(output?.hookSpecificOutput?.additionalContext ?? "");
|
|
18113
|
+
assert.equal(output?.decision, "block");
|
|
18114
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/);
|
|
18115
|
+
assert.match(context, /Native subagent support is unavailable in this environment/);
|
|
18116
|
+
assert.match(context, /Reason: multi_agent_v1_unavailable/);
|
|
18117
|
+
assert.match(context, /blocked\/cancelled/);
|
|
18118
|
+
assert.match(context, /do not call multi_agent_v1\.close_agent/i);
|
|
18119
|
+
}
|
|
18120
|
+
finally {
|
|
18121
|
+
await rm(cwd, { recursive: true, force: true });
|
|
18122
|
+
}
|
|
18123
|
+
});
|
|
17771
18124
|
it("blocks Main-root ralplan writes even when payload has only a typed agent_role", async () => {
|
|
17772
18125
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-agent-role-main-"));
|
|
17773
18126
|
try {
|
|
@@ -18167,6 +18520,65 @@ PY`,
|
|
|
18167
18520
|
await rm(cwd, { recursive: true, force: true });
|
|
18168
18521
|
}
|
|
18169
18522
|
});
|
|
18523
|
+
it("allows structured ultragoal steering cleanup through dynamic nested shell loops", async () => {
|
|
18524
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-steer-cleanup-"));
|
|
18525
|
+
try {
|
|
18526
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
18527
|
+
const sessionId = "sess-ultragoal-steer-cleanup";
|
|
18528
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
18529
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
18530
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
18531
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
18532
|
+
active: true,
|
|
18533
|
+
mode: "ultragoal",
|
|
18534
|
+
current_phase: "planning",
|
|
18535
|
+
session_id: sessionId,
|
|
18536
|
+
});
|
|
18537
|
+
const result = await dispatchCodexNativeHook({
|
|
18538
|
+
hook_event_name: "PreToolUse",
|
|
18539
|
+
cwd,
|
|
18540
|
+
session_id: sessionId,
|
|
18541
|
+
thread_id: "thread-ultragoal-steer-cleanup",
|
|
18542
|
+
tool_name: "Bash",
|
|
18543
|
+
tool_input: {
|
|
18544
|
+
command: `bash -lc 'for goal_id in G001-atomized G002-atomized; do omx ultragoal steer --kind mark_blocked_superseded --target-goal-id "$goal_id" --evidence ".omx/ultragoal cleanup supersedes atomized pseudo-goals." --rationale "Structured steering cleanup keeps durable Ultragoal metadata auditable." --json; done'`,
|
|
18545
|
+
},
|
|
18546
|
+
}, { cwd });
|
|
18547
|
+
assert.equal(result.outputJson, null);
|
|
18548
|
+
}
|
|
18549
|
+
finally {
|
|
18550
|
+
await rm(cwd, { recursive: true, force: true });
|
|
18551
|
+
}
|
|
18552
|
+
});
|
|
18553
|
+
it("blocks unsafe dynamic nested shell writes in conductor mode", async () => {
|
|
18554
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-dynamic-shell-write-block-"));
|
|
18555
|
+
try {
|
|
18556
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
18557
|
+
const sessionId = "sess-dynamic-shell-write-block";
|
|
18558
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
18559
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
18560
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
18561
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
18562
|
+
active: true,
|
|
18563
|
+
mode: "ultragoal",
|
|
18564
|
+
current_phase: "planning",
|
|
18565
|
+
session_id: sessionId,
|
|
18566
|
+
});
|
|
18567
|
+
const result = await dispatchCodexNativeHook({
|
|
18568
|
+
hook_event_name: "PreToolUse",
|
|
18569
|
+
cwd,
|
|
18570
|
+
session_id: sessionId,
|
|
18571
|
+
thread_id: "thread-dynamic-shell-write-block",
|
|
18572
|
+
tool_name: "Bash",
|
|
18573
|
+
tool_input: { command: `bash -lc 'cp "$SOURCE_FILE" src/runtime.ts'` },
|
|
18574
|
+
}, { cwd });
|
|
18575
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
18576
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Bash nested shell execution is dynamic and cannot be validated/);
|
|
18577
|
+
}
|
|
18578
|
+
finally {
|
|
18579
|
+
await rm(cwd, { recursive: true, force: true });
|
|
18580
|
+
}
|
|
18581
|
+
});
|
|
18170
18582
|
it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata", async () => {
|
|
18171
18583
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-"));
|
|
18172
18584
|
try {
|