oh-my-codex 0.18.7 → 0.18.8
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 +5 -5
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/native-config.test.js +42 -1
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +8 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +1 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +17 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +61 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +65 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/update.test.js +110 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +11 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +108 -15
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +47 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +423 -3
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +258 -12
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +1 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +435 -32
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +42 -0
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +521 -15
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +132 -4
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +180 -21
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +324 -28
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +3 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +42 -19
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +128 -19
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +61 -62
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +24 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +136 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/scripts/__tests__/codex-native-hook.test.js +593 -11
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +88 -31
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +75 -11
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +3 -16
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +25 -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 +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +7 -39
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -14
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +195 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +142 -12
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +8 -8
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/skills/autopilot/SKILL.md +3 -1
- package/skills/code-review/SKILL.md +7 -7
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ultraqa/SKILL.md +9 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +686 -13
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +105 -28
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +91 -4
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
|
@@ -69,6 +69,35 @@ async function writeJson(path: string, value: unknown): Promise<void> {
|
|
|
69
69
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
async function writeNativeMappedSessionState(
|
|
73
|
+
cwd: string,
|
|
74
|
+
stateDir: string,
|
|
75
|
+
sessionId: string,
|
|
76
|
+
nativeSessionId: string,
|
|
77
|
+
): Promise<void> {
|
|
78
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
79
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
80
|
+
session_id: sessionId,
|
|
81
|
+
native_session_id: nativeSessionId,
|
|
82
|
+
cwd,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function writeSessionSkillActiveState(
|
|
87
|
+
stateDir: string,
|
|
88
|
+
sessionId: string,
|
|
89
|
+
skill: string,
|
|
90
|
+
phase: string,
|
|
91
|
+
): Promise<void> {
|
|
92
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
93
|
+
active: true,
|
|
94
|
+
skill,
|
|
95
|
+
phase,
|
|
96
|
+
session_id: sessionId,
|
|
97
|
+
active_skills: [{ skill, phase, active: true, session_id: sessionId }],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
72
101
|
async function setTeamPaneIds(
|
|
73
102
|
cwd: string,
|
|
74
103
|
teamName: string,
|
|
@@ -1604,7 +1633,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1604
1633
|
assert.equal(sessionState.previous_native_session_id, oldNativeSessionId);
|
|
1605
1634
|
assert.equal(sessionState.owner_omx_session_id, ownerSessionId);
|
|
1606
1635
|
|
|
1607
|
-
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
1636
|
+
let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null;
|
|
1608
1637
|
const promptResult = await dispatchCodexNativeHook(
|
|
1609
1638
|
{
|
|
1610
1639
|
hook_event_name: "UserPromptSubmit",
|
|
@@ -1617,14 +1646,18 @@ describe("codex native hook dispatch", () => {
|
|
|
1617
1646
|
{
|
|
1618
1647
|
cwd,
|
|
1619
1648
|
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
1620
|
-
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
1649
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
|
|
1621
1650
|
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
1622
1651
|
},
|
|
1623
1652
|
},
|
|
1624
1653
|
);
|
|
1625
1654
|
|
|
1626
1655
|
assert.equal(promptResult.omxEventName, "keyword-detector");
|
|
1627
|
-
assert.deepEqual(reconcileCall, {
|
|
1656
|
+
assert.deepEqual(reconcileCall, {
|
|
1657
|
+
cwd,
|
|
1658
|
+
sessionId: ownerSessionId,
|
|
1659
|
+
sessionIds: [ownerSessionId, nativeSessionId],
|
|
1660
|
+
});
|
|
1628
1661
|
} finally {
|
|
1629
1662
|
await rm(cwd, { recursive: true, force: true });
|
|
1630
1663
|
}
|
|
@@ -1645,7 +1678,7 @@ describe("codex native hook dispatch", () => {
|
|
|
1645
1678
|
sessionState.owner_omx_session_id = invalidOwnerSessionId;
|
|
1646
1679
|
await writeJson(sessionStatePath, sessionState);
|
|
1647
1680
|
|
|
1648
|
-
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
1681
|
+
let reconcileCall: { cwd: string; sessionId?: string; sessionIds?: string[] } | null = null;
|
|
1649
1682
|
const promptResult = await dispatchCodexNativeHook(
|
|
1650
1683
|
{
|
|
1651
1684
|
hook_event_name: "UserPromptSubmit",
|
|
@@ -1658,14 +1691,18 @@ describe("codex native hook dispatch", () => {
|
|
|
1658
1691
|
{
|
|
1659
1692
|
cwd,
|
|
1660
1693
|
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
1661
|
-
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
1694
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
|
|
1662
1695
|
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
1663
1696
|
},
|
|
1664
1697
|
},
|
|
1665
1698
|
);
|
|
1666
1699
|
|
|
1667
1700
|
assert.equal(promptResult.omxEventName, "keyword-detector");
|
|
1668
|
-
assert.deepEqual(reconcileCall, {
|
|
1701
|
+
assert.deepEqual(reconcileCall, {
|
|
1702
|
+
cwd,
|
|
1703
|
+
sessionId: canonicalSessionId,
|
|
1704
|
+
sessionIds: [canonicalSessionId, nativeSessionId],
|
|
1705
|
+
});
|
|
1669
1706
|
} finally {
|
|
1670
1707
|
await rm(cwd, { recursive: true, force: true });
|
|
1671
1708
|
}
|
|
@@ -3460,6 +3497,17 @@ standardMaxRounds = 15
|
|
|
3460
3497
|
assert.match(message, /Do not advance from deep-interview to ralplan merely because the first question was answered/);
|
|
3461
3498
|
assert.match(message, /Planner output has been reviewed sequentially by Architect and then Critic/);
|
|
3462
3499
|
assert.match(message, /do not hand off to Ultragoal or implementation until .*ralplan_architect_review.*ralplan_critic_review/);
|
|
3500
|
+
|
|
3501
|
+
const autopilotState = JSON.parse(await readFile(
|
|
3502
|
+
join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate", "autopilot-state.json"),
|
|
3503
|
+
"utf-8",
|
|
3504
|
+
)) as { state?: { handoff_artifacts?: { context_snapshot_path?: string } } };
|
|
3505
|
+
const snapshotPath = autopilotState.state?.handoff_artifacts?.context_snapshot_path ?? "";
|
|
3506
|
+
assert.match(snapshotPath, /^\.omx\/context\/implement-issue-2430-\d{8}T\d{6}Z\.md$/);
|
|
3507
|
+
const snapshot = await readFile(join(cwd, snapshotPath), "utf-8");
|
|
3508
|
+
assert.match(snapshot, /activation prompt \/ task seed: \$autopilot implement issue #2430/);
|
|
3509
|
+
assert.match(snapshot, /scope note: this seed captures the Autopilot activation prompt/);
|
|
3510
|
+
assert.match(snapshot, /constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa/);
|
|
3463
3511
|
} finally {
|
|
3464
3512
|
await rm(cwd, { recursive: true, force: true });
|
|
3465
3513
|
}
|
|
@@ -5035,7 +5083,51 @@ esac
|
|
|
5035
5083
|
}
|
|
5036
5084
|
});
|
|
5037
5085
|
|
|
5038
|
-
it("
|
|
5086
|
+
it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => {
|
|
5087
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"));
|
|
5088
|
+
const originalTmux = process.env.TMUX;
|
|
5089
|
+
const originalTmuxPane = process.env.TMUX_PANE;
|
|
5090
|
+
const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
5091
|
+
const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
5092
|
+
try {
|
|
5093
|
+
process.env.TMUX = "1";
|
|
5094
|
+
process.env.TMUX_PANE = "%1";
|
|
5095
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
5096
|
+
process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1";
|
|
5097
|
+
|
|
5098
|
+
let reconcileCalled = false;
|
|
5099
|
+
const result = await dispatchCodexNativeHook(
|
|
5100
|
+
{
|
|
5101
|
+
hook_event_name: "UserPromptSubmit",
|
|
5102
|
+
cwd,
|
|
5103
|
+
session_id: "omx-doctor-plugin-hook-smoke",
|
|
5104
|
+
prompt: "$ralplan doctor plugin hook smoke test",
|
|
5105
|
+
},
|
|
5106
|
+
{
|
|
5107
|
+
cwd,
|
|
5108
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
5109
|
+
reconcileCalled = true;
|
|
5110
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
5111
|
+
},
|
|
5112
|
+
},
|
|
5113
|
+
);
|
|
5114
|
+
|
|
5115
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
5116
|
+
assert.equal(reconcileCalled, false);
|
|
5117
|
+
} finally {
|
|
5118
|
+
if (originalTmux === undefined) delete process.env.TMUX;
|
|
5119
|
+
else process.env.TMUX = originalTmux;
|
|
5120
|
+
if (originalTmuxPane === undefined) delete process.env.TMUX_PANE;
|
|
5121
|
+
else process.env.TMUX_PANE = originalTmuxPane;
|
|
5122
|
+
if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
5123
|
+
else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner;
|
|
5124
|
+
if (originalDoctorSmoke === undefined) delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
5125
|
+
else process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke;
|
|
5126
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5127
|
+
}
|
|
5128
|
+
});
|
|
5129
|
+
|
|
5130
|
+
it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
|
|
5039
5131
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-"));
|
|
5040
5132
|
const originalTmux = process.env.TMUX;
|
|
5041
5133
|
const originalTmuxPane = process.env.TMUX_PANE;
|
|
@@ -5091,8 +5183,8 @@ esac
|
|
|
5091
5183
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
5092
5184
|
const tmuxCalls = await readFile(tmuxLog, "utf-8");
|
|
5093
5185
|
assert.match(tmuxCalls, /list-panes -t %1 -F/);
|
|
5094
|
-
assert.match(tmuxCalls,
|
|
5095
|
-
assert.
|
|
5186
|
+
assert.match(tmuxCalls, /split-window/);
|
|
5187
|
+
assert.match(tmuxCalls, new RegExp(`resize-pane -t %9 -y ${HUD_TMUX_HEIGHT_LINES}`));
|
|
5096
5188
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
|
|
5097
5189
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
|
|
5098
5190
|
} finally {
|
|
@@ -13784,7 +13876,7 @@ exit 0
|
|
|
13784
13876
|
|
|
13785
13877
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13786
13878
|
assert.equal(result.outputJson?.decision, "block");
|
|
13787
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
13879
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
13788
13880
|
assert.match(
|
|
13789
13881
|
String((result.outputJson?.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? ""),
|
|
13790
13882
|
/\$ultragoal.*\$team.*\$ralph/i,
|
|
@@ -13834,7 +13926,40 @@ exit 0
|
|
|
13834
13926
|
|
|
13835
13927
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13836
13928
|
assert.equal(result.outputJson?.decision, "block");
|
|
13837
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
13929
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
13930
|
+
} finally {
|
|
13931
|
+
await rm(cwd, { recursive: true, force: true });
|
|
13932
|
+
}
|
|
13933
|
+
});
|
|
13934
|
+
|
|
13935
|
+
it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => {
|
|
13936
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-"));
|
|
13937
|
+
try {
|
|
13938
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
13939
|
+
const sessionId = "sess-autopilot-ralplan-no-canonical";
|
|
13940
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
13941
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
13942
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
13943
|
+
active: true,
|
|
13944
|
+
mode: "autopilot",
|
|
13945
|
+
current_phase: "ralplan",
|
|
13946
|
+
session_id: sessionId,
|
|
13947
|
+
});
|
|
13948
|
+
|
|
13949
|
+
const result = await dispatchCodexNativeHook(
|
|
13950
|
+
{
|
|
13951
|
+
hook_event_name: "PreToolUse",
|
|
13952
|
+
cwd,
|
|
13953
|
+
session_id: sessionId,
|
|
13954
|
+
thread_id: "thread-autopilot-ralplan-no-canonical",
|
|
13955
|
+
tool_name: "Edit",
|
|
13956
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
13957
|
+
},
|
|
13958
|
+
{ cwd },
|
|
13959
|
+
);
|
|
13960
|
+
|
|
13961
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13962
|
+
assert.equal(result.outputJson, null);
|
|
13838
13963
|
} finally {
|
|
13839
13964
|
await rm(cwd, { recursive: true, force: true });
|
|
13840
13965
|
}
|
|
@@ -13925,12 +14050,486 @@ exit 0
|
|
|
13925
14050
|
|
|
13926
14051
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13927
14052
|
assert.equal(result.outputJson?.decision, "block");
|
|
13928
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
14053
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14054
|
+
} finally {
|
|
14055
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14056
|
+
}
|
|
14057
|
+
});
|
|
14058
|
+
|
|
14059
|
+
it("blocks implementation writes when ralplan and Autopilot ralplan are both active", async () => {
|
|
14060
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-autopilot-mixed-planning-"));
|
|
14061
|
+
try {
|
|
14062
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14063
|
+
const sessionId = "sess-ralplan-autopilot-mixed-planning";
|
|
14064
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14065
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14066
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14067
|
+
active: true,
|
|
14068
|
+
skill: "autopilot",
|
|
14069
|
+
phase: "ralplan",
|
|
14070
|
+
session_id: sessionId,
|
|
14071
|
+
active_skills: [
|
|
14072
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
14073
|
+
{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId },
|
|
14074
|
+
],
|
|
14075
|
+
});
|
|
14076
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14077
|
+
active: true,
|
|
14078
|
+
mode: "ralplan",
|
|
14079
|
+
current_phase: "planning",
|
|
14080
|
+
session_id: sessionId,
|
|
14081
|
+
});
|
|
14082
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14083
|
+
active: true,
|
|
14084
|
+
mode: "autopilot",
|
|
14085
|
+
current_phase: "ralplan",
|
|
14086
|
+
session_id: sessionId,
|
|
14087
|
+
});
|
|
14088
|
+
|
|
14089
|
+
const result = await dispatchCodexNativeHook(
|
|
14090
|
+
{
|
|
14091
|
+
hook_event_name: "PreToolUse",
|
|
14092
|
+
cwd,
|
|
14093
|
+
session_id: sessionId,
|
|
14094
|
+
thread_id: "thread-ralplan-autopilot-mixed-planning",
|
|
14095
|
+
tool_name: "Edit",
|
|
14096
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14097
|
+
},
|
|
14098
|
+
{ cwd },
|
|
14099
|
+
);
|
|
14100
|
+
|
|
14101
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14102
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14103
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14104
|
+
} finally {
|
|
14105
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14106
|
+
}
|
|
14107
|
+
});
|
|
14108
|
+
|
|
14109
|
+
it("blocks implementation writes while Autopilot is supervising replan without handoff", async () => {
|
|
14110
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-replan-pretool-block-"));
|
|
14111
|
+
try {
|
|
14112
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14113
|
+
const sessionId = "sess-autopilot-replan-pretool-block";
|
|
14114
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14115
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14116
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14117
|
+
active: true,
|
|
14118
|
+
skill: "autopilot",
|
|
14119
|
+
phase: "replan",
|
|
14120
|
+
session_id: sessionId,
|
|
14121
|
+
active_skills: [{ skill: "autopilot", phase: "replan", active: true, session_id: sessionId }],
|
|
14122
|
+
});
|
|
14123
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14124
|
+
active: true,
|
|
14125
|
+
mode: "autopilot",
|
|
14126
|
+
current_phase: "replan",
|
|
14127
|
+
session_id: sessionId,
|
|
14128
|
+
});
|
|
14129
|
+
|
|
14130
|
+
const result = await dispatchCodexNativeHook(
|
|
14131
|
+
{
|
|
14132
|
+
hook_event_name: "PreToolUse",
|
|
14133
|
+
cwd,
|
|
14134
|
+
session_id: sessionId,
|
|
14135
|
+
thread_id: "thread-autopilot-replan-pretool-block",
|
|
14136
|
+
tool_name: "Edit",
|
|
14137
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14138
|
+
},
|
|
14139
|
+
{ cwd },
|
|
14140
|
+
);
|
|
14141
|
+
|
|
14142
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14143
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14144
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14145
|
+
} finally {
|
|
14146
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14147
|
+
}
|
|
14148
|
+
});
|
|
14149
|
+
|
|
14150
|
+
it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
14151
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-block-"));
|
|
14152
|
+
try {
|
|
14153
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14154
|
+
const sessionId = "sess-autopilot-ralplan-native-map-block";
|
|
14155
|
+
const nativeSessionId = "019e-autopilot-ralplan-native";
|
|
14156
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14157
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14158
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14159
|
+
active: true,
|
|
14160
|
+
mode: "autopilot",
|
|
14161
|
+
current_phase: "ralplan",
|
|
14162
|
+
session_id: sessionId,
|
|
14163
|
+
});
|
|
14164
|
+
|
|
14165
|
+
const result = await dispatchCodexNativeHook(
|
|
14166
|
+
{
|
|
14167
|
+
hook_event_name: "PreToolUse",
|
|
14168
|
+
cwd,
|
|
14169
|
+
session_id: nativeSessionId,
|
|
14170
|
+
thread_id: "thread-autopilot-ralplan-native-map-block",
|
|
14171
|
+
tool_name: "apply_patch",
|
|
14172
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14173
|
+
},
|
|
14174
|
+
{ cwd },
|
|
14175
|
+
);
|
|
14176
|
+
|
|
14177
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14178
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14179
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14180
|
+
} finally {
|
|
14181
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14182
|
+
}
|
|
14183
|
+
});
|
|
14184
|
+
|
|
14185
|
+
it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
14186
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-bash-"));
|
|
14187
|
+
try {
|
|
14188
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14189
|
+
const sessionId = "sess-autopilot-ralplan-native-map-bash";
|
|
14190
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-bash";
|
|
14191
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14192
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14193
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14194
|
+
active: true,
|
|
14195
|
+
mode: "autopilot",
|
|
14196
|
+
current_phase: "ralplan",
|
|
14197
|
+
session_id: sessionId,
|
|
14198
|
+
});
|
|
14199
|
+
|
|
14200
|
+
const result = await dispatchCodexNativeHook(
|
|
14201
|
+
{
|
|
14202
|
+
hook_event_name: "PreToolUse",
|
|
14203
|
+
cwd,
|
|
14204
|
+
session_id: nativeSessionId,
|
|
14205
|
+
thread_id: "thread-autopilot-ralplan-native-map-bash",
|
|
14206
|
+
tool_name: "Bash",
|
|
14207
|
+
tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
|
|
14208
|
+
},
|
|
14209
|
+
{ cwd },
|
|
14210
|
+
);
|
|
14211
|
+
|
|
14212
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14213
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14214
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
13929
14215
|
} finally {
|
|
13930
14216
|
await rm(cwd, { recursive: true, force: true });
|
|
13931
14217
|
}
|
|
13932
14218
|
});
|
|
13933
14219
|
|
|
14220
|
+
it("blocks standalone ralplan writes when native Codex id maps to OMX session state", async () => {
|
|
14221
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-block-"));
|
|
14222
|
+
try {
|
|
14223
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14224
|
+
const sessionId = "sess-ralplan-native-map-block";
|
|
14225
|
+
const nativeSessionId = "019e-ralplan-native-map";
|
|
14226
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14227
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14228
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14229
|
+
active: true,
|
|
14230
|
+
mode: "ralplan",
|
|
14231
|
+
current_phase: "planning",
|
|
14232
|
+
session_id: sessionId,
|
|
14233
|
+
});
|
|
14234
|
+
|
|
14235
|
+
const result = await dispatchCodexNativeHook(
|
|
14236
|
+
{
|
|
14237
|
+
hook_event_name: "PreToolUse",
|
|
14238
|
+
cwd,
|
|
14239
|
+
session_id: nativeSessionId,
|
|
14240
|
+
thread_id: "thread-ralplan-native-map-block",
|
|
14241
|
+
tool_name: "Edit",
|
|
14242
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14243
|
+
},
|
|
14244
|
+
{ cwd },
|
|
14245
|
+
);
|
|
14246
|
+
|
|
14247
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14248
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14249
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14250
|
+
} finally {
|
|
14251
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14252
|
+
}
|
|
14253
|
+
});
|
|
14254
|
+
|
|
14255
|
+
it("blocks deep-interview writes when native Codex id maps to OMX session state", async () => {
|
|
14256
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-native-map-block-"));
|
|
14257
|
+
try {
|
|
14258
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14259
|
+
const sessionId = "sess-deep-interview-native-map-block";
|
|
14260
|
+
const nativeSessionId = "019e-deep-interview-native-map";
|
|
14261
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14262
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "deep-interview", "interview");
|
|
14263
|
+
await writeJson(join(stateDir, "sessions", sessionId, "deep-interview-state.json"), {
|
|
14264
|
+
active: true,
|
|
14265
|
+
mode: "deep-interview",
|
|
14266
|
+
current_phase: "interview",
|
|
14267
|
+
session_id: sessionId,
|
|
14268
|
+
});
|
|
14269
|
+
|
|
14270
|
+
const result = await dispatchCodexNativeHook(
|
|
14271
|
+
{
|
|
14272
|
+
hook_event_name: "PreToolUse",
|
|
14273
|
+
cwd,
|
|
14274
|
+
session_id: nativeSessionId,
|
|
14275
|
+
thread_id: "thread-deep-interview-native-map-block",
|
|
14276
|
+
tool_name: "Edit",
|
|
14277
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14278
|
+
},
|
|
14279
|
+
{ cwd },
|
|
14280
|
+
);
|
|
14281
|
+
|
|
14282
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14283
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14284
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i);
|
|
14285
|
+
} finally {
|
|
14286
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14287
|
+
}
|
|
14288
|
+
});
|
|
14289
|
+
|
|
14290
|
+
it("allows mapped ralplan planning artifact writes without execution handoff", async () => {
|
|
14291
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-"));
|
|
14292
|
+
try {
|
|
14293
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14294
|
+
const sessionId = "sess-ralplan-native-map-artifact";
|
|
14295
|
+
const nativeSessionId = "019e-ralplan-native-map-artifact";
|
|
14296
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14297
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14298
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14299
|
+
active: true,
|
|
14300
|
+
mode: "ralplan",
|
|
14301
|
+
current_phase: "planning",
|
|
14302
|
+
session_id: sessionId,
|
|
14303
|
+
});
|
|
14304
|
+
|
|
14305
|
+
const result = await dispatchCodexNativeHook(
|
|
14306
|
+
{
|
|
14307
|
+
hook_event_name: "PreToolUse",
|
|
14308
|
+
cwd,
|
|
14309
|
+
session_id: nativeSessionId,
|
|
14310
|
+
thread_id: "thread-ralplan-native-map-artifact",
|
|
14311
|
+
tool_name: "Bash",
|
|
14312
|
+
tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" },
|
|
14313
|
+
},
|
|
14314
|
+
{ cwd },
|
|
14315
|
+
);
|
|
14316
|
+
|
|
14317
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14318
|
+
assert.equal(result.outputJson, null);
|
|
14319
|
+
} finally {
|
|
14320
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14321
|
+
}
|
|
14322
|
+
});
|
|
14323
|
+
|
|
14324
|
+
it("allows mapped implementation writes when explicit execution handoff is active", async () => {
|
|
14325
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-handoff-"));
|
|
14326
|
+
try {
|
|
14327
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14328
|
+
const sessionId = "sess-ralplan-native-map-handoff";
|
|
14329
|
+
const nativeSessionId = "019e-ralplan-native-map-handoff";
|
|
14330
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14331
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14332
|
+
active: true,
|
|
14333
|
+
skill: "ultragoal",
|
|
14334
|
+
phase: "planning",
|
|
14335
|
+
session_id: sessionId,
|
|
14336
|
+
active_skills: [
|
|
14337
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
14338
|
+
{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId },
|
|
14339
|
+
],
|
|
14340
|
+
});
|
|
14341
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14342
|
+
active: true,
|
|
14343
|
+
mode: "ralplan",
|
|
14344
|
+
current_phase: "complete",
|
|
14345
|
+
session_id: sessionId,
|
|
14346
|
+
});
|
|
14347
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
14348
|
+
active: true,
|
|
14349
|
+
mode: "ultragoal",
|
|
14350
|
+
current_phase: "planning",
|
|
14351
|
+
session_id: sessionId,
|
|
14352
|
+
});
|
|
14353
|
+
|
|
14354
|
+
const result = await dispatchCodexNativeHook(
|
|
14355
|
+
{
|
|
14356
|
+
hook_event_name: "PreToolUse",
|
|
14357
|
+
cwd,
|
|
14358
|
+
session_id: nativeSessionId,
|
|
14359
|
+
thread_id: "thread-ralplan-native-map-handoff",
|
|
14360
|
+
tool_name: "Edit",
|
|
14361
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14362
|
+
},
|
|
14363
|
+
{ cwd },
|
|
14364
|
+
);
|
|
14365
|
+
|
|
14366
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14367
|
+
assert.equal(result.outputJson, null);
|
|
14368
|
+
} finally {
|
|
14369
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14370
|
+
}
|
|
14371
|
+
});
|
|
14372
|
+
|
|
14373
|
+
it("allows mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => {
|
|
14374
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-terminal-"));
|
|
14375
|
+
try {
|
|
14376
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14377
|
+
const sessionId = "sess-autopilot-ralplan-native-map-terminal";
|
|
14378
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-terminal";
|
|
14379
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14380
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14381
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14382
|
+
active: true,
|
|
14383
|
+
mode: "autopilot",
|
|
14384
|
+
current_phase: "ralplan",
|
|
14385
|
+
session_id: sessionId,
|
|
14386
|
+
});
|
|
14387
|
+
await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
|
|
14388
|
+
version: 1,
|
|
14389
|
+
active: false,
|
|
14390
|
+
mode: "autopilot",
|
|
14391
|
+
outcome: "finish",
|
|
14392
|
+
lifecycle_outcome: "finished",
|
|
14393
|
+
current_phase: "complete",
|
|
14394
|
+
completed_at: "2026-05-30T00:00:00.000Z",
|
|
14395
|
+
updated_at: "2026-05-30T00:00:00.000Z",
|
|
14396
|
+
});
|
|
14397
|
+
|
|
14398
|
+
const result = await dispatchCodexNativeHook(
|
|
14399
|
+
{
|
|
14400
|
+
hook_event_name: "PreToolUse",
|
|
14401
|
+
cwd,
|
|
14402
|
+
session_id: nativeSessionId,
|
|
14403
|
+
thread_id: "thread-autopilot-ralplan-native-map-terminal",
|
|
14404
|
+
tool_name: "Edit",
|
|
14405
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14406
|
+
},
|
|
14407
|
+
{ cwd },
|
|
14408
|
+
);
|
|
14409
|
+
|
|
14410
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14411
|
+
assert.equal(result.outputJson, null);
|
|
14412
|
+
} finally {
|
|
14413
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14414
|
+
}
|
|
14415
|
+
});
|
|
14416
|
+
|
|
14417
|
+
it("does not block unrelated native Codex ids when current OMX session mapping does not match", async () => {
|
|
14418
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-unrelated-"));
|
|
14419
|
+
try {
|
|
14420
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14421
|
+
const sessionId = "sess-ralplan-native-map-owner";
|
|
14422
|
+
const ownerNativeSessionId = "019e-ralplan-native-owner";
|
|
14423
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, ownerNativeSessionId);
|
|
14424
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14425
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14426
|
+
active: true,
|
|
14427
|
+
mode: "ralplan",
|
|
14428
|
+
current_phase: "planning",
|
|
14429
|
+
session_id: sessionId,
|
|
14430
|
+
});
|
|
14431
|
+
|
|
14432
|
+
const result = await dispatchCodexNativeHook(
|
|
14433
|
+
{
|
|
14434
|
+
hook_event_name: "PreToolUse",
|
|
14435
|
+
cwd,
|
|
14436
|
+
session_id: "019e-unrelated-native-session",
|
|
14437
|
+
thread_id: "thread-ralplan-native-map-unrelated",
|
|
14438
|
+
tool_name: "Edit",
|
|
14439
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14440
|
+
},
|
|
14441
|
+
{ cwd },
|
|
14442
|
+
);
|
|
14443
|
+
|
|
14444
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14445
|
+
assert.equal(result.outputJson, null);
|
|
14446
|
+
} finally {
|
|
14447
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14448
|
+
}
|
|
14449
|
+
});
|
|
14450
|
+
|
|
14451
|
+
it("blocks mapped Autopilot ralplan writes from the authoritative team state root", async () => {
|
|
14452
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-team-root-"));
|
|
14453
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
|
|
14454
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
14455
|
+
try {
|
|
14456
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
14457
|
+
const stateDir = teamStateRoot;
|
|
14458
|
+
const sessionId = "sess-autopilot-ralplan-team-root";
|
|
14459
|
+
const nativeSessionId = "019e-autopilot-ralplan-team-root";
|
|
14460
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14461
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14462
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14463
|
+
active: true,
|
|
14464
|
+
mode: "autopilot",
|
|
14465
|
+
current_phase: "ralplan",
|
|
14466
|
+
session_id: sessionId,
|
|
14467
|
+
});
|
|
14468
|
+
|
|
14469
|
+
const result = await dispatchCodexNativeHook(
|
|
14470
|
+
{
|
|
14471
|
+
hook_event_name: "PreToolUse",
|
|
14472
|
+
cwd,
|
|
14473
|
+
session_id: nativeSessionId,
|
|
14474
|
+
thread_id: "thread-autopilot-ralplan-team-root",
|
|
14475
|
+
tool_name: "Edit",
|
|
14476
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14477
|
+
},
|
|
14478
|
+
{ cwd },
|
|
14479
|
+
);
|
|
14480
|
+
|
|
14481
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14482
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14483
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14484
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "session.json")), false);
|
|
14485
|
+
} finally {
|
|
14486
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
14487
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
14488
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14489
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
14490
|
+
}
|
|
14491
|
+
});
|
|
14492
|
+
|
|
14493
|
+
it("does not block unrelated native Codex ids from the authoritative team state root", async () => {
|
|
14494
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-team-root-unrelated-"));
|
|
14495
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-unrelated-"));
|
|
14496
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
14497
|
+
try {
|
|
14498
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
14499
|
+
const stateDir = teamStateRoot;
|
|
14500
|
+
const sessionId = "sess-ralplan-team-root-owner";
|
|
14501
|
+
const nativeSessionId = "019e-ralplan-team-root-owner";
|
|
14502
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14503
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14504
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14505
|
+
active: true,
|
|
14506
|
+
mode: "ralplan",
|
|
14507
|
+
current_phase: "planning",
|
|
14508
|
+
session_id: sessionId,
|
|
14509
|
+
});
|
|
14510
|
+
|
|
14511
|
+
const result = await dispatchCodexNativeHook(
|
|
14512
|
+
{
|
|
14513
|
+
hook_event_name: "PreToolUse",
|
|
14514
|
+
cwd,
|
|
14515
|
+
session_id: "019e-unrelated-team-root-native",
|
|
14516
|
+
thread_id: "thread-ralplan-team-root-unrelated",
|
|
14517
|
+
tool_name: "Edit",
|
|
14518
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14519
|
+
},
|
|
14520
|
+
{ cwd },
|
|
14521
|
+
);
|
|
14522
|
+
|
|
14523
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14524
|
+
assert.equal(result.outputJson, null);
|
|
14525
|
+
} finally {
|
|
14526
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
14527
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
14528
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14529
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
14530
|
+
}
|
|
14531
|
+
});
|
|
14532
|
+
|
|
13934
14533
|
it("allows ralplan planning artifact writes without execution handoff", async () => {
|
|
13935
14534
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-"));
|
|
13936
14535
|
try {
|
|
@@ -14006,7 +14605,7 @@ exit 0
|
|
|
14006
14605
|
|
|
14007
14606
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14008
14607
|
assert.equal(result.outputJson?.decision, "block");
|
|
14009
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
14608
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14010
14609
|
} finally {
|
|
14011
14610
|
await rm(cwd, { recursive: true, force: true });
|
|
14012
14611
|
}
|
|
@@ -14730,6 +15329,80 @@ describe("codex native hook triage integration", () => {
|
|
|
14730
15329
|
}
|
|
14731
15330
|
});
|
|
14732
15331
|
|
|
15332
|
+
it("omits Team handoff guidance from autopilot prompt context when Team mode is disabled", async () => {
|
|
15333
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-"));
|
|
15334
|
+
try {
|
|
15335
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
15336
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
15337
|
+
scope: "project",
|
|
15338
|
+
teamMode: "disabled",
|
|
15339
|
+
});
|
|
15340
|
+
await writeSessionStart(cwd, "sess-autopilot-observable-no-team");
|
|
15341
|
+
|
|
15342
|
+
const result = await dispatchCodexNativeHook(
|
|
15343
|
+
{
|
|
15344
|
+
hook_event_name: "UserPromptSubmit",
|
|
15345
|
+
cwd,
|
|
15346
|
+
session_id: "sess-autopilot-observable-no-team",
|
|
15347
|
+
thread_id: "thread-autopilot-observable-no-team",
|
|
15348
|
+
turn_id: "turn-autopilot-observable-no-team",
|
|
15349
|
+
prompt: "$autopilot implement issue #2430",
|
|
15350
|
+
},
|
|
15351
|
+
{ cwd },
|
|
15352
|
+
);
|
|
15353
|
+
|
|
15354
|
+
assert.equal(result.skillState?.skill, "autopilot");
|
|
15355
|
+
const additionalContext = String(
|
|
15356
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
15357
|
+
);
|
|
15358
|
+
assert.match(additionalContext, /detected workflow keyword "\$autopilot" -> autopilot/);
|
|
15359
|
+
assert.match(additionalContext, /\$deep-interview -> \$ralplan -> \$ultragoal -> \$code-review -> \$ultraqa/);
|
|
15360
|
+
assert.doesNotMatch(additionalContext, /\$team/);
|
|
15361
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
15362
|
+
} finally {
|
|
15363
|
+
await rm(cwd, { recursive: true, force: true });
|
|
15364
|
+
}
|
|
15365
|
+
});
|
|
15366
|
+
|
|
15367
|
+
it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => {
|
|
15368
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-"));
|
|
15369
|
+
try {
|
|
15370
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
15371
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
15372
|
+
scope: "project",
|
|
15373
|
+
teamMode: "disabled",
|
|
15374
|
+
});
|
|
15375
|
+
await writeSessionStart(cwd, "sess-disabled-team-primary");
|
|
15376
|
+
|
|
15377
|
+
const result = await dispatchCodexNativeHook(
|
|
15378
|
+
{
|
|
15379
|
+
hook_event_name: "UserPromptSubmit",
|
|
15380
|
+
cwd,
|
|
15381
|
+
session_id: "sess-disabled-team-primary",
|
|
15382
|
+
thread_id: "thread-disabled-team-primary",
|
|
15383
|
+
turn_id: "turn-disabled-team-primary",
|
|
15384
|
+
prompt: "$team $ralph fix this",
|
|
15385
|
+
},
|
|
15386
|
+
{ cwd },
|
|
15387
|
+
);
|
|
15388
|
+
|
|
15389
|
+
assert.equal(result.skillState?.skill, "ralph");
|
|
15390
|
+
assert.equal(result.skillState?.transition_error, undefined);
|
|
15391
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
15392
|
+
assert.equal(
|
|
15393
|
+
existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ralph-state.json")),
|
|
15394
|
+
true,
|
|
15395
|
+
);
|
|
15396
|
+
const additionalContext = String(
|
|
15397
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
15398
|
+
);
|
|
15399
|
+
assert.match(additionalContext, /detected workflow keyword "\$ralph" -> ralph/);
|
|
15400
|
+
assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/);
|
|
15401
|
+
} finally {
|
|
15402
|
+
await rm(cwd, { recursive: true, force: true });
|
|
15403
|
+
}
|
|
15404
|
+
});
|
|
15405
|
+
|
|
14733
15406
|
it("makes bare autopilot command activation observable in state and prompt guidance", async () => {
|
|
14734
15407
|
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-"));
|
|
14735
15408
|
try {
|