oh-my-codex 0.18.7 → 0.18.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +12 -12
- 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/autopilot/__tests__/fsm.test.js +3 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
- package/dist/autopilot/fsm.js +2 -2
- package/dist/autopilot/fsm.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +4 -2
- package/dist/cli/__tests__/auth.test.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 +98 -6
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +28 -8
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +26 -9
- package/dist/cli/__tests__/question.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__/resume.test.js +50 -1
- package/dist/cli/__tests__/resume.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 +323 -18
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +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 +21 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +143 -28
- 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 +22 -3
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +312 -26
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +5 -9
- package/dist/cli/version.js.map +1 -1
- package/dist/compat/__tests__/doctor-contract.test.js +12 -1
- package/dist/compat/__tests__/doctor-contract.test.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 +12 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +30 -1
- package/dist/hooks/__tests__/deep-interview-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 +642 -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 +160 -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 +65 -80
- 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/question/__tests__/renderer.test.js +566 -1
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/renderer.d.ts +9 -1
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +246 -70
- package/dist/question/renderer.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +837 -101
- 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 +107 -39
- 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/utils/__tests__/platform-command.test.js +16 -1
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/__tests__/version.test.d.ts +2 -0
- package/dist/utils/__tests__/version.test.d.ts.map +1 -0
- package/dist/utils/__tests__/version.test.js +51 -0
- package/dist/utils/__tests__/version.test.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +16 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts +9 -0
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +15 -0
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/utils/version.d.ts +7 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +67 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js +89 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +16 -2
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/package.json +11 -10
- 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/deep-interview/SKILL.md +51 -11
- 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/deep-interview/SKILL.md +51 -11
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ultraqa/SKILL.md +9 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +946 -98
- 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 +123 -34
- 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/prepare-build.js +83 -0
- 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
- package/src/scripts/postinstall-bootstrap.js +0 -23
|
@@ -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
|
}
|
|
@@ -1830,6 +1867,51 @@ describe("codex native hook dispatch", () => {
|
|
|
1830
1867
|
}
|
|
1831
1868
|
});
|
|
1832
1869
|
|
|
1870
|
+
it("includes repo-local .omx project-memory during SessionStart when OMX_ROOT is boxed", async () => {
|
|
1871
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-boxed-memory-"));
|
|
1872
|
+
const boxedRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-boxed-root-"));
|
|
1873
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
1874
|
+
try {
|
|
1875
|
+
process.env.OMX_ROOT = boxedRoot;
|
|
1876
|
+
await writeJson(join(cwd, ".omx", "project-memory.json"), {
|
|
1877
|
+
techStack: "Repo-local CLI memory",
|
|
1878
|
+
conventions: "SessionStart should load CLI-written project memory",
|
|
1879
|
+
directives: [
|
|
1880
|
+
{ directive: "Prefer repo-local .omx project memory over boxed runtime fallback.", priority: "high" },
|
|
1881
|
+
],
|
|
1882
|
+
});
|
|
1883
|
+
await writeJson(join(boxedRoot, ".omx", "project-memory.json"), {
|
|
1884
|
+
techStack: "Boxed runtime memory should not win",
|
|
1885
|
+
notes: [{ category: "runtime", content: "stale boxed runtime note", timestamp: new Date().toISOString() }],
|
|
1886
|
+
});
|
|
1887
|
+
|
|
1888
|
+
const result = await dispatchCodexNativeHook(
|
|
1889
|
+
{
|
|
1890
|
+
hook_event_name: "SessionStart",
|
|
1891
|
+
cwd,
|
|
1892
|
+
session_id: "sess-boxed-memory-1",
|
|
1893
|
+
},
|
|
1894
|
+
{ cwd, sessionOwnerPid: 43210 },
|
|
1895
|
+
);
|
|
1896
|
+
|
|
1897
|
+
const additionalContext = String(
|
|
1898
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1899
|
+
);
|
|
1900
|
+
assert.match(additionalContext, /\[Project memory\]/);
|
|
1901
|
+
assert.match(additionalContext, /source: \.omx\/project-memory\.json/);
|
|
1902
|
+
assert.match(additionalContext, /Repo-local CLI memory/);
|
|
1903
|
+
assert.match(additionalContext, /SessionStart should load CLI-written project memory/);
|
|
1904
|
+
assert.match(additionalContext, /Prefer repo-local \.omx project memory over boxed runtime fallback\./);
|
|
1905
|
+
assert.doesNotMatch(additionalContext, /Boxed runtime memory should not win/);
|
|
1906
|
+
assert.doesNotMatch(additionalContext, /stale boxed runtime note/);
|
|
1907
|
+
} finally {
|
|
1908
|
+
if (previousOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
1909
|
+
else process.env.OMX_ROOT = previousOmxRoot;
|
|
1910
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1911
|
+
await rm(boxedRoot, { recursive: true, force: true });
|
|
1912
|
+
}
|
|
1913
|
+
});
|
|
1914
|
+
|
|
1833
1915
|
it("prefers repository project-memory.json during SessionStart while preserving legacy wiki guidance", async () => {
|
|
1834
1916
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-root-memory-legacy-wiki-"));
|
|
1835
1917
|
try {
|
|
@@ -3460,6 +3542,17 @@ standardMaxRounds = 15
|
|
|
3460
3542
|
assert.match(message, /Do not advance from deep-interview to ralplan merely because the first question was answered/);
|
|
3461
3543
|
assert.match(message, /Planner output has been reviewed sequentially by Architect and then Critic/);
|
|
3462
3544
|
assert.match(message, /do not hand off to Ultragoal or implementation until .*ralplan_architect_review.*ralplan_critic_review/);
|
|
3545
|
+
|
|
3546
|
+
const autopilotState = JSON.parse(await readFile(
|
|
3547
|
+
join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate", "autopilot-state.json"),
|
|
3548
|
+
"utf-8",
|
|
3549
|
+
)) as { state?: { handoff_artifacts?: { context_snapshot_path?: string } } };
|
|
3550
|
+
const snapshotPath = autopilotState.state?.handoff_artifacts?.context_snapshot_path ?? "";
|
|
3551
|
+
assert.match(snapshotPath, /^\.omx\/context\/implement-issue-2430-\d{8}T\d{6}Z\.md$/);
|
|
3552
|
+
const snapshot = await readFile(join(cwd, snapshotPath), "utf-8");
|
|
3553
|
+
assert.match(snapshot, /activation prompt \/ task seed: \$autopilot implement issue #2430/);
|
|
3554
|
+
assert.match(snapshot, /scope note: this seed captures the Autopilot activation prompt/);
|
|
3555
|
+
assert.match(snapshot, /constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa/);
|
|
3463
3556
|
} finally {
|
|
3464
3557
|
await rm(cwd, { recursive: true, force: true });
|
|
3465
3558
|
}
|
|
@@ -5035,7 +5128,51 @@ esac
|
|
|
5035
5128
|
}
|
|
5036
5129
|
});
|
|
5037
5130
|
|
|
5038
|
-
it("
|
|
5131
|
+
it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => {
|
|
5132
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"));
|
|
5133
|
+
const originalTmux = process.env.TMUX;
|
|
5134
|
+
const originalTmuxPane = process.env.TMUX_PANE;
|
|
5135
|
+
const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
5136
|
+
const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
5137
|
+
try {
|
|
5138
|
+
process.env.TMUX = "1";
|
|
5139
|
+
process.env.TMUX_PANE = "%1";
|
|
5140
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
5141
|
+
process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1";
|
|
5142
|
+
|
|
5143
|
+
let reconcileCalled = false;
|
|
5144
|
+
const result = await dispatchCodexNativeHook(
|
|
5145
|
+
{
|
|
5146
|
+
hook_event_name: "UserPromptSubmit",
|
|
5147
|
+
cwd,
|
|
5148
|
+
session_id: "omx-doctor-plugin-hook-smoke",
|
|
5149
|
+
prompt: "$ralplan doctor plugin hook smoke test",
|
|
5150
|
+
},
|
|
5151
|
+
{
|
|
5152
|
+
cwd,
|
|
5153
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
5154
|
+
reconcileCalled = true;
|
|
5155
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
5156
|
+
},
|
|
5157
|
+
},
|
|
5158
|
+
);
|
|
5159
|
+
|
|
5160
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
5161
|
+
assert.equal(reconcileCalled, false);
|
|
5162
|
+
} finally {
|
|
5163
|
+
if (originalTmux === undefined) delete process.env.TMUX;
|
|
5164
|
+
else process.env.TMUX = originalTmux;
|
|
5165
|
+
if (originalTmuxPane === undefined) delete process.env.TMUX_PANE;
|
|
5166
|
+
else process.env.TMUX_PANE = originalTmuxPane;
|
|
5167
|
+
if (originalHudOwner === undefined) delete process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
5168
|
+
else process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner;
|
|
5169
|
+
if (originalDoctorSmoke === undefined) delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
5170
|
+
else process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke;
|
|
5171
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5172
|
+
}
|
|
5173
|
+
});
|
|
5174
|
+
|
|
5175
|
+
it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
|
|
5039
5176
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-"));
|
|
5040
5177
|
const originalTmux = process.env.TMUX;
|
|
5041
5178
|
const originalTmuxPane = process.env.TMUX_PANE;
|
|
@@ -5091,8 +5228,8 @@ esac
|
|
|
5091
5228
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
5092
5229
|
const tmuxCalls = await readFile(tmuxLog, "utf-8");
|
|
5093
5230
|
assert.match(tmuxCalls, /list-panes -t %1 -F/);
|
|
5094
|
-
assert.match(tmuxCalls,
|
|
5095
|
-
assert.
|
|
5231
|
+
assert.match(tmuxCalls, /split-window/);
|
|
5232
|
+
assert.match(tmuxCalls, new RegExp(`resize-pane -t %9 -y ${HUD_TMUX_HEIGHT_LINES}`));
|
|
5096
5233
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
|
|
5097
5234
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
|
|
5098
5235
|
} finally {
|
|
@@ -13784,7 +13921,7 @@ exit 0
|
|
|
13784
13921
|
|
|
13785
13922
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13786
13923
|
assert.equal(result.outputJson?.decision, "block");
|
|
13787
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
13924
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
13788
13925
|
assert.match(
|
|
13789
13926
|
String((result.outputJson?.hookSpecificOutput as { additionalContext?: string } | undefined)?.additionalContext ?? ""),
|
|
13790
13927
|
/\$ultragoal.*\$team.*\$ralph/i,
|
|
@@ -13834,41 +13971,36 @@ exit 0
|
|
|
13834
13971
|
|
|
13835
13972
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13836
13973
|
assert.equal(result.outputJson?.decision, "block");
|
|
13837
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
13974
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
13838
13975
|
} finally {
|
|
13839
13976
|
await rm(cwd, { recursive: true, force: true });
|
|
13840
13977
|
}
|
|
13841
13978
|
});
|
|
13842
13979
|
|
|
13843
|
-
it("
|
|
13844
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-
|
|
13980
|
+
it("blocks implementation writes when Autopilot ralplan is visible only in skill-active phase", async () => {
|
|
13981
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-skill-ralplan-pretool-block-"));
|
|
13845
13982
|
try {
|
|
13846
13983
|
const stateDir = join(cwd, ".omx", "state");
|
|
13847
|
-
const sessionId = "sess-autopilot-ralplan-
|
|
13984
|
+
const sessionId = "sess-autopilot-skill-ralplan-pretool-block";
|
|
13848
13985
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
13849
13986
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
13850
13987
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
13851
13988
|
active: true,
|
|
13852
13989
|
skill: "autopilot",
|
|
13853
|
-
phase: "ralplan",
|
|
13990
|
+
phase: "autopilot:ralplan",
|
|
13854
13991
|
session_id: sessionId,
|
|
13855
|
-
active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }],
|
|
13992
|
+
active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }],
|
|
13856
13993
|
});
|
|
13857
13994
|
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
13858
13995
|
active: true,
|
|
13859
13996
|
mode: "autopilot",
|
|
13860
|
-
current_phase: "
|
|
13997
|
+
current_phase: "planning",
|
|
13861
13998
|
session_id: sessionId,
|
|
13862
|
-
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
outcome: "finish",
|
|
13868
|
-
lifecycle_outcome: "finished",
|
|
13869
|
-
current_phase: "complete",
|
|
13870
|
-
completed_at: "2026-05-30T00:00:00.000Z",
|
|
13871
|
-
updated_at: "2026-05-30T00:00:00.000Z",
|
|
13999
|
+
state: {
|
|
14000
|
+
handoff_artifacts: {
|
|
14001
|
+
ralplan_consensus_gate: { required: true, complete: false },
|
|
14002
|
+
},
|
|
14003
|
+
},
|
|
13872
14004
|
});
|
|
13873
14005
|
|
|
13874
14006
|
const result = await dispatchCodexNativeHook(
|
|
@@ -13876,7 +14008,7 @@ exit 0
|
|
|
13876
14008
|
hook_event_name: "PreToolUse",
|
|
13877
14009
|
cwd,
|
|
13878
14010
|
session_id: sessionId,
|
|
13879
|
-
thread_id: "thread-autopilot-ralplan-
|
|
14011
|
+
thread_id: "thread-autopilot-skill-ralplan-pretool-block",
|
|
13880
14012
|
tool_name: "Edit",
|
|
13881
14013
|
tool_input: { file_path: "src/runtime.ts" },
|
|
13882
14014
|
},
|
|
@@ -13884,30 +14016,74 @@ exit 0
|
|
|
13884
14016
|
);
|
|
13885
14017
|
|
|
13886
14018
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13887
|
-
assert.equal(result.outputJson,
|
|
14019
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14020
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Autopilot planning is active .*implementation\/write tools are blocked/i);
|
|
13888
14021
|
} finally {
|
|
13889
14022
|
await rm(cwd, { recursive: true, force: true });
|
|
13890
14023
|
}
|
|
13891
14024
|
});
|
|
13892
14025
|
|
|
13893
|
-
it("
|
|
13894
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-
|
|
14026
|
+
it("ignores stale Autopilot ralplan skill mirrors after detail state leaves planning", async () => {
|
|
14027
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-stale-ralplan-mirror-"));
|
|
13895
14028
|
try {
|
|
13896
14029
|
const stateDir = join(cwd, ".omx", "state");
|
|
13897
|
-
const sessionId = "sess-autopilot-ralplan-
|
|
14030
|
+
const sessionId = "sess-autopilot-stale-ralplan-mirror";
|
|
13898
14031
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
13899
14032
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
13900
14033
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
13901
14034
|
active: true,
|
|
13902
14035
|
skill: "autopilot",
|
|
13903
|
-
phase: "ralplan",
|
|
14036
|
+
phase: "autopilot:ralplan",
|
|
13904
14037
|
session_id: sessionId,
|
|
13905
|
-
active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }],
|
|
14038
|
+
active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }],
|
|
14039
|
+
});
|
|
14040
|
+
|
|
14041
|
+
for (const phase of ["ultragoal", "code-review", "completing", "complete"]) {
|
|
14042
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14043
|
+
active: true,
|
|
14044
|
+
mode: "autopilot",
|
|
14045
|
+
current_phase: phase,
|
|
14046
|
+
session_id: sessionId,
|
|
14047
|
+
});
|
|
14048
|
+
|
|
14049
|
+
const result = await dispatchCodexNativeHook(
|
|
14050
|
+
{
|
|
14051
|
+
hook_event_name: "PreToolUse",
|
|
14052
|
+
cwd,
|
|
14053
|
+
session_id: sessionId,
|
|
14054
|
+
thread_id: "thread-autopilot-stale-ralplan-mirror",
|
|
14055
|
+
tool_name: "Edit",
|
|
14056
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14057
|
+
},
|
|
14058
|
+
{ cwd },
|
|
14059
|
+
);
|
|
14060
|
+
|
|
14061
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14062
|
+
assert.equal(result.outputJson, null, `stale skill-active ralplan mirror must not block when Autopilot detail phase is ${phase}`);
|
|
14063
|
+
}
|
|
14064
|
+
} finally {
|
|
14065
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14066
|
+
}
|
|
14067
|
+
});
|
|
14068
|
+
|
|
14069
|
+
it("allows explicit blank Autopilot detail phase to use a ralplan skill mirror", async () => {
|
|
14070
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-blank-phase-mirror-"));
|
|
14071
|
+
try {
|
|
14072
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14073
|
+
const sessionId = "sess-autopilot-blank-phase-mirror";
|
|
14074
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14075
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14076
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14077
|
+
active: true,
|
|
14078
|
+
skill: "autopilot",
|
|
14079
|
+
phase: "autopilot:ralplan",
|
|
14080
|
+
session_id: sessionId,
|
|
14081
|
+
active_skills: [{ skill: "autopilot", phase: "autopilot:ralplan", active: true, session_id: sessionId }],
|
|
13906
14082
|
});
|
|
13907
14083
|
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
13908
14084
|
active: true,
|
|
13909
14085
|
mode: "autopilot",
|
|
13910
|
-
current_phase: "
|
|
14086
|
+
current_phase: "",
|
|
13911
14087
|
session_id: sessionId,
|
|
13912
14088
|
});
|
|
13913
14089
|
|
|
@@ -13916,39 +14092,32 @@ exit 0
|
|
|
13916
14092
|
hook_event_name: "PreToolUse",
|
|
13917
14093
|
cwd,
|
|
13918
14094
|
session_id: sessionId,
|
|
13919
|
-
thread_id: "thread-autopilot-
|
|
13920
|
-
tool_name: "
|
|
13921
|
-
tool_input: {
|
|
14095
|
+
thread_id: "thread-autopilot-blank-phase-mirror",
|
|
14096
|
+
tool_name: "Edit",
|
|
14097
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
13922
14098
|
},
|
|
13923
14099
|
{ cwd },
|
|
13924
14100
|
);
|
|
13925
14101
|
|
|
13926
14102
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
13927
14103
|
assert.equal(result.outputJson?.decision, "block");
|
|
13928
|
-
assert.match(String(result.outputJson?.reason ?? ""), /
|
|
14104
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Autopilot planning is active .*implementation\/write tools are blocked/i);
|
|
13929
14105
|
} finally {
|
|
13930
14106
|
await rm(cwd, { recursive: true, force: true });
|
|
13931
14107
|
}
|
|
13932
14108
|
});
|
|
13933
14109
|
|
|
13934
|
-
it("
|
|
13935
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-
|
|
14110
|
+
it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => {
|
|
14111
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-"));
|
|
13936
14112
|
try {
|
|
13937
14113
|
const stateDir = join(cwd, ".omx", "state");
|
|
13938
|
-
const sessionId = "sess-ralplan-
|
|
14114
|
+
const sessionId = "sess-autopilot-ralplan-no-canonical";
|
|
13939
14115
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
13940
14116
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
13941
|
-
await writeJson(join(stateDir, "sessions", sessionId, "
|
|
13942
|
-
active: true,
|
|
13943
|
-
skill: "ralplan",
|
|
13944
|
-
phase: "planning",
|
|
13945
|
-
session_id: sessionId,
|
|
13946
|
-
active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }],
|
|
13947
|
-
});
|
|
13948
|
-
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14117
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
13949
14118
|
active: true,
|
|
13950
|
-
mode: "
|
|
13951
|
-
current_phase: "
|
|
14119
|
+
mode: "autopilot",
|
|
14120
|
+
current_phase: "ralplan",
|
|
13952
14121
|
session_id: sessionId,
|
|
13953
14122
|
});
|
|
13954
14123
|
|
|
@@ -13957,9 +14126,9 @@ exit 0
|
|
|
13957
14126
|
hook_event_name: "PreToolUse",
|
|
13958
14127
|
cwd,
|
|
13959
14128
|
session_id: sessionId,
|
|
13960
|
-
thread_id: "thread-ralplan-
|
|
13961
|
-
tool_name: "
|
|
13962
|
-
tool_input: { file_path: "
|
|
14129
|
+
thread_id: "thread-autopilot-ralplan-no-canonical",
|
|
14130
|
+
tool_name: "Edit",
|
|
14131
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
13963
14132
|
},
|
|
13964
14133
|
{ cwd },
|
|
13965
14134
|
);
|
|
@@ -13971,65 +14140,74 @@ exit 0
|
|
|
13971
14140
|
}
|
|
13972
14141
|
});
|
|
13973
14142
|
|
|
13974
|
-
it("
|
|
13975
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-
|
|
14143
|
+
it("allows implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => {
|
|
14144
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-terminal-pretool-"));
|
|
13976
14145
|
try {
|
|
13977
14146
|
const stateDir = join(cwd, ".omx", "state");
|
|
13978
|
-
const sessionId = "sess-ralplan-pretool
|
|
14147
|
+
const sessionId = "sess-autopilot-ralplan-terminal-pretool";
|
|
13979
14148
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
13980
14149
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
13981
14150
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
13982
14151
|
active: true,
|
|
13983
|
-
skill: "
|
|
13984
|
-
phase: "
|
|
14152
|
+
skill: "autopilot",
|
|
14153
|
+
phase: "ralplan",
|
|
13985
14154
|
session_id: sessionId,
|
|
13986
|
-
active_skills: [{ skill: "
|
|
14155
|
+
active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }],
|
|
13987
14156
|
});
|
|
13988
|
-
await writeJson(join(stateDir, "sessions", sessionId, "
|
|
14157
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
13989
14158
|
active: true,
|
|
13990
|
-
mode: "
|
|
13991
|
-
current_phase: "
|
|
14159
|
+
mode: "autopilot",
|
|
14160
|
+
current_phase: "ralplan",
|
|
13992
14161
|
session_id: sessionId,
|
|
13993
14162
|
});
|
|
14163
|
+
await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
|
|
14164
|
+
version: 1,
|
|
14165
|
+
active: false,
|
|
14166
|
+
mode: "autopilot",
|
|
14167
|
+
outcome: "finish",
|
|
14168
|
+
lifecycle_outcome: "finished",
|
|
14169
|
+
current_phase: "complete",
|
|
14170
|
+
completed_at: "2026-05-30T00:00:00.000Z",
|
|
14171
|
+
updated_at: "2026-05-30T00:00:00.000Z",
|
|
14172
|
+
});
|
|
13994
14173
|
|
|
13995
14174
|
const result = await dispatchCodexNativeHook(
|
|
13996
14175
|
{
|
|
13997
14176
|
hook_event_name: "PreToolUse",
|
|
13998
14177
|
cwd,
|
|
13999
14178
|
session_id: sessionId,
|
|
14000
|
-
thread_id: "thread-ralplan-pretool
|
|
14001
|
-
tool_name: "
|
|
14002
|
-
tool_input: {
|
|
14179
|
+
thread_id: "thread-autopilot-ralplan-terminal-pretool",
|
|
14180
|
+
tool_name: "Edit",
|
|
14181
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14003
14182
|
},
|
|
14004
14183
|
{ cwd },
|
|
14005
14184
|
);
|
|
14006
14185
|
|
|
14007
14186
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14008
|
-
assert.equal(result.outputJson
|
|
14009
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
14187
|
+
assert.equal(result.outputJson, null);
|
|
14010
14188
|
} finally {
|
|
14011
14189
|
await rm(cwd, { recursive: true, force: true });
|
|
14012
14190
|
}
|
|
14013
14191
|
});
|
|
14014
14192
|
|
|
14015
|
-
it("
|
|
14016
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-
|
|
14193
|
+
it("blocks bash implementation writes while Autopilot is supervising ralplan without handoff", async () => {
|
|
14194
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-pretool-bash-block-"));
|
|
14017
14195
|
try {
|
|
14018
14196
|
const stateDir = join(cwd, ".omx", "state");
|
|
14019
|
-
const sessionId = "sess-ralplan-pretool-bash-
|
|
14197
|
+
const sessionId = "sess-autopilot-ralplan-pretool-bash-block";
|
|
14020
14198
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14021
14199
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14022
14200
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14023
14201
|
active: true,
|
|
14024
|
-
skill: "
|
|
14025
|
-
phase: "
|
|
14202
|
+
skill: "autopilot",
|
|
14203
|
+
phase: "ralplan",
|
|
14026
14204
|
session_id: sessionId,
|
|
14027
|
-
active_skills: [{ skill: "
|
|
14205
|
+
active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }],
|
|
14028
14206
|
});
|
|
14029
|
-
await writeJson(join(stateDir, "sessions", sessionId, "
|
|
14207
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14030
14208
|
active: true,
|
|
14031
|
-
mode: "
|
|
14032
|
-
current_phase: "
|
|
14209
|
+
mode: "autopilot",
|
|
14210
|
+
current_phase: "ralplan",
|
|
14033
14211
|
session_id: sessionId,
|
|
14034
14212
|
});
|
|
14035
14213
|
|
|
@@ -14038,47 +14216,48 @@ exit 0
|
|
|
14038
14216
|
hook_event_name: "PreToolUse",
|
|
14039
14217
|
cwd,
|
|
14040
14218
|
session_id: sessionId,
|
|
14041
|
-
thread_id: "thread-ralplan-pretool-bash-
|
|
14219
|
+
thread_id: "thread-autopilot-ralplan-pretool-bash-block",
|
|
14042
14220
|
tool_name: "Bash",
|
|
14043
|
-
tool_input: { command: "cat <<'EOF' >
|
|
14221
|
+
tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
|
|
14044
14222
|
},
|
|
14045
14223
|
{ cwd },
|
|
14046
14224
|
);
|
|
14047
14225
|
|
|
14048
14226
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14049
|
-
assert.equal(result.outputJson,
|
|
14227
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14228
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14050
14229
|
} finally {
|
|
14051
14230
|
await rm(cwd, { recursive: true, force: true });
|
|
14052
14231
|
}
|
|
14053
14232
|
});
|
|
14054
14233
|
|
|
14055
|
-
it("
|
|
14056
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-
|
|
14234
|
+
it("blocks implementation writes when ralplan and Autopilot ralplan are both active", async () => {
|
|
14235
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-autopilot-mixed-planning-"));
|
|
14057
14236
|
try {
|
|
14058
14237
|
const stateDir = join(cwd, ".omx", "state");
|
|
14059
|
-
const sessionId = "sess-ralplan-
|
|
14238
|
+
const sessionId = "sess-ralplan-autopilot-mixed-planning";
|
|
14060
14239
|
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14061
14240
|
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14062
14241
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14063
14242
|
active: true,
|
|
14064
|
-
skill: "
|
|
14065
|
-
phase: "
|
|
14243
|
+
skill: "autopilot",
|
|
14244
|
+
phase: "ralplan",
|
|
14066
14245
|
session_id: sessionId,
|
|
14067
14246
|
active_skills: [
|
|
14068
14247
|
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
14069
|
-
{ skill: "
|
|
14248
|
+
{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId },
|
|
14070
14249
|
],
|
|
14071
14250
|
});
|
|
14072
14251
|
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14073
14252
|
active: true,
|
|
14074
14253
|
mode: "ralplan",
|
|
14075
|
-
current_phase: "
|
|
14254
|
+
current_phase: "planning",
|
|
14076
14255
|
session_id: sessionId,
|
|
14077
14256
|
});
|
|
14078
|
-
await writeJson(join(stateDir, "sessions", sessionId, "
|
|
14257
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14079
14258
|
active: true,
|
|
14080
|
-
mode: "
|
|
14081
|
-
current_phase: "
|
|
14259
|
+
mode: "autopilot",
|
|
14260
|
+
current_phase: "ralplan",
|
|
14082
14261
|
session_id: sessionId,
|
|
14083
14262
|
});
|
|
14084
14263
|
|
|
@@ -14087,7 +14266,7 @@ exit 0
|
|
|
14087
14266
|
hook_event_name: "PreToolUse",
|
|
14088
14267
|
cwd,
|
|
14089
14268
|
session_id: sessionId,
|
|
14090
|
-
thread_id: "thread-ralplan-
|
|
14269
|
+
thread_id: "thread-ralplan-autopilot-mixed-planning",
|
|
14091
14270
|
tool_name: "Edit",
|
|
14092
14271
|
tool_input: { file_path: "src/runtime.ts" },
|
|
14093
14272
|
},
|
|
@@ -14095,19 +14274,614 @@ exit 0
|
|
|
14095
14274
|
);
|
|
14096
14275
|
|
|
14097
14276
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14098
|
-
assert.equal(result.outputJson,
|
|
14277
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14278
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14099
14279
|
} finally {
|
|
14100
14280
|
await rm(cwd, { recursive: true, force: true });
|
|
14101
14281
|
}
|
|
14102
14282
|
});
|
|
14103
14283
|
|
|
14104
|
-
it("
|
|
14105
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-
|
|
14284
|
+
it("blocks implementation writes while Autopilot is supervising replan without handoff", async () => {
|
|
14285
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-replan-pretool-block-"));
|
|
14106
14286
|
try {
|
|
14107
14287
|
const stateDir = join(cwd, ".omx", "state");
|
|
14108
|
-
|
|
14109
|
-
await
|
|
14110
|
-
|
|
14288
|
+
const sessionId = "sess-autopilot-replan-pretool-block";
|
|
14289
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14290
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14291
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14292
|
+
active: true,
|
|
14293
|
+
skill: "autopilot",
|
|
14294
|
+
phase: "replan",
|
|
14295
|
+
session_id: sessionId,
|
|
14296
|
+
active_skills: [{ skill: "autopilot", phase: "replan", active: true, session_id: sessionId }],
|
|
14297
|
+
});
|
|
14298
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14299
|
+
active: true,
|
|
14300
|
+
mode: "autopilot",
|
|
14301
|
+
current_phase: "replan",
|
|
14302
|
+
session_id: sessionId,
|
|
14303
|
+
});
|
|
14304
|
+
|
|
14305
|
+
const result = await dispatchCodexNativeHook(
|
|
14306
|
+
{
|
|
14307
|
+
hook_event_name: "PreToolUse",
|
|
14308
|
+
cwd,
|
|
14309
|
+
session_id: sessionId,
|
|
14310
|
+
thread_id: "thread-autopilot-replan-pretool-block",
|
|
14311
|
+
tool_name: "Edit",
|
|
14312
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14313
|
+
},
|
|
14314
|
+
{ cwd },
|
|
14315
|
+
);
|
|
14316
|
+
|
|
14317
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14318
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14319
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14320
|
+
} finally {
|
|
14321
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14322
|
+
}
|
|
14323
|
+
});
|
|
14324
|
+
|
|
14325
|
+
it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
14326
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-block-"));
|
|
14327
|
+
try {
|
|
14328
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14329
|
+
const sessionId = "sess-autopilot-ralplan-native-map-block";
|
|
14330
|
+
const nativeSessionId = "019e-autopilot-ralplan-native";
|
|
14331
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14332
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14333
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14334
|
+
active: true,
|
|
14335
|
+
mode: "autopilot",
|
|
14336
|
+
current_phase: "ralplan",
|
|
14337
|
+
session_id: sessionId,
|
|
14338
|
+
});
|
|
14339
|
+
|
|
14340
|
+
const result = await dispatchCodexNativeHook(
|
|
14341
|
+
{
|
|
14342
|
+
hook_event_name: "PreToolUse",
|
|
14343
|
+
cwd,
|
|
14344
|
+
session_id: nativeSessionId,
|
|
14345
|
+
thread_id: "thread-autopilot-ralplan-native-map-block",
|
|
14346
|
+
tool_name: "apply_patch",
|
|
14347
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14348
|
+
},
|
|
14349
|
+
{ cwd },
|
|
14350
|
+
);
|
|
14351
|
+
|
|
14352
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14353
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14354
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14355
|
+
} finally {
|
|
14356
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14357
|
+
}
|
|
14358
|
+
});
|
|
14359
|
+
|
|
14360
|
+
it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
14361
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-bash-"));
|
|
14362
|
+
try {
|
|
14363
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14364
|
+
const sessionId = "sess-autopilot-ralplan-native-map-bash";
|
|
14365
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-bash";
|
|
14366
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14367
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14368
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14369
|
+
active: true,
|
|
14370
|
+
mode: "autopilot",
|
|
14371
|
+
current_phase: "ralplan",
|
|
14372
|
+
session_id: sessionId,
|
|
14373
|
+
});
|
|
14374
|
+
|
|
14375
|
+
const result = await dispatchCodexNativeHook(
|
|
14376
|
+
{
|
|
14377
|
+
hook_event_name: "PreToolUse",
|
|
14378
|
+
cwd,
|
|
14379
|
+
session_id: nativeSessionId,
|
|
14380
|
+
thread_id: "thread-autopilot-ralplan-native-map-bash",
|
|
14381
|
+
tool_name: "Bash",
|
|
14382
|
+
tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
|
|
14383
|
+
},
|
|
14384
|
+
{ cwd },
|
|
14385
|
+
);
|
|
14386
|
+
|
|
14387
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14388
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14389
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14390
|
+
} finally {
|
|
14391
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14392
|
+
}
|
|
14393
|
+
});
|
|
14394
|
+
|
|
14395
|
+
it("blocks standalone ralplan writes when native Codex id maps to OMX session state", async () => {
|
|
14396
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-block-"));
|
|
14397
|
+
try {
|
|
14398
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14399
|
+
const sessionId = "sess-ralplan-native-map-block";
|
|
14400
|
+
const nativeSessionId = "019e-ralplan-native-map";
|
|
14401
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14402
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14403
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14404
|
+
active: true,
|
|
14405
|
+
mode: "ralplan",
|
|
14406
|
+
current_phase: "planning",
|
|
14407
|
+
session_id: sessionId,
|
|
14408
|
+
});
|
|
14409
|
+
|
|
14410
|
+
const result = await dispatchCodexNativeHook(
|
|
14411
|
+
{
|
|
14412
|
+
hook_event_name: "PreToolUse",
|
|
14413
|
+
cwd,
|
|
14414
|
+
session_id: nativeSessionId,
|
|
14415
|
+
thread_id: "thread-ralplan-native-map-block",
|
|
14416
|
+
tool_name: "Edit",
|
|
14417
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14418
|
+
},
|
|
14419
|
+
{ cwd },
|
|
14420
|
+
);
|
|
14421
|
+
|
|
14422
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14423
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14424
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14425
|
+
} finally {
|
|
14426
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14427
|
+
}
|
|
14428
|
+
});
|
|
14429
|
+
|
|
14430
|
+
it("blocks deep-interview writes when native Codex id maps to OMX session state", async () => {
|
|
14431
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-native-map-block-"));
|
|
14432
|
+
try {
|
|
14433
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14434
|
+
const sessionId = "sess-deep-interview-native-map-block";
|
|
14435
|
+
const nativeSessionId = "019e-deep-interview-native-map";
|
|
14436
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14437
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "deep-interview", "interview");
|
|
14438
|
+
await writeJson(join(stateDir, "sessions", sessionId, "deep-interview-state.json"), {
|
|
14439
|
+
active: true,
|
|
14440
|
+
mode: "deep-interview",
|
|
14441
|
+
current_phase: "interview",
|
|
14442
|
+
session_id: sessionId,
|
|
14443
|
+
});
|
|
14444
|
+
|
|
14445
|
+
const result = await dispatchCodexNativeHook(
|
|
14446
|
+
{
|
|
14447
|
+
hook_event_name: "PreToolUse",
|
|
14448
|
+
cwd,
|
|
14449
|
+
session_id: nativeSessionId,
|
|
14450
|
+
thread_id: "thread-deep-interview-native-map-block",
|
|
14451
|
+
tool_name: "Edit",
|
|
14452
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14453
|
+
},
|
|
14454
|
+
{ cwd },
|
|
14455
|
+
);
|
|
14456
|
+
|
|
14457
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14458
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14459
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i);
|
|
14460
|
+
} finally {
|
|
14461
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14462
|
+
}
|
|
14463
|
+
});
|
|
14464
|
+
|
|
14465
|
+
it("allows mapped ralplan planning artifact writes without execution handoff", async () => {
|
|
14466
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-"));
|
|
14467
|
+
try {
|
|
14468
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14469
|
+
const sessionId = "sess-ralplan-native-map-artifact";
|
|
14470
|
+
const nativeSessionId = "019e-ralplan-native-map-artifact";
|
|
14471
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14472
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14473
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14474
|
+
active: true,
|
|
14475
|
+
mode: "ralplan",
|
|
14476
|
+
current_phase: "planning",
|
|
14477
|
+
session_id: sessionId,
|
|
14478
|
+
});
|
|
14479
|
+
|
|
14480
|
+
const result = await dispatchCodexNativeHook(
|
|
14481
|
+
{
|
|
14482
|
+
hook_event_name: "PreToolUse",
|
|
14483
|
+
cwd,
|
|
14484
|
+
session_id: nativeSessionId,
|
|
14485
|
+
thread_id: "thread-ralplan-native-map-artifact",
|
|
14486
|
+
tool_name: "Bash",
|
|
14487
|
+
tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" },
|
|
14488
|
+
},
|
|
14489
|
+
{ cwd },
|
|
14490
|
+
);
|
|
14491
|
+
|
|
14492
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14493
|
+
assert.equal(result.outputJson, null);
|
|
14494
|
+
} finally {
|
|
14495
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14496
|
+
}
|
|
14497
|
+
});
|
|
14498
|
+
|
|
14499
|
+
it("allows mapped implementation writes when explicit execution handoff is active", async () => {
|
|
14500
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-handoff-"));
|
|
14501
|
+
try {
|
|
14502
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14503
|
+
const sessionId = "sess-ralplan-native-map-handoff";
|
|
14504
|
+
const nativeSessionId = "019e-ralplan-native-map-handoff";
|
|
14505
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14506
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14507
|
+
active: true,
|
|
14508
|
+
skill: "ultragoal",
|
|
14509
|
+
phase: "planning",
|
|
14510
|
+
session_id: sessionId,
|
|
14511
|
+
active_skills: [
|
|
14512
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
14513
|
+
{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId },
|
|
14514
|
+
],
|
|
14515
|
+
});
|
|
14516
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14517
|
+
active: true,
|
|
14518
|
+
mode: "ralplan",
|
|
14519
|
+
current_phase: "complete",
|
|
14520
|
+
session_id: sessionId,
|
|
14521
|
+
});
|
|
14522
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
14523
|
+
active: true,
|
|
14524
|
+
mode: "ultragoal",
|
|
14525
|
+
current_phase: "planning",
|
|
14526
|
+
session_id: sessionId,
|
|
14527
|
+
});
|
|
14528
|
+
|
|
14529
|
+
const result = await dispatchCodexNativeHook(
|
|
14530
|
+
{
|
|
14531
|
+
hook_event_name: "PreToolUse",
|
|
14532
|
+
cwd,
|
|
14533
|
+
session_id: nativeSessionId,
|
|
14534
|
+
thread_id: "thread-ralplan-native-map-handoff",
|
|
14535
|
+
tool_name: "Edit",
|
|
14536
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14537
|
+
},
|
|
14538
|
+
{ cwd },
|
|
14539
|
+
);
|
|
14540
|
+
|
|
14541
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14542
|
+
assert.equal(result.outputJson, null);
|
|
14543
|
+
} finally {
|
|
14544
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14545
|
+
}
|
|
14546
|
+
});
|
|
14547
|
+
|
|
14548
|
+
it("allows mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => {
|
|
14549
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-terminal-"));
|
|
14550
|
+
try {
|
|
14551
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14552
|
+
const sessionId = "sess-autopilot-ralplan-native-map-terminal";
|
|
14553
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-terminal";
|
|
14554
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14555
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14556
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14557
|
+
active: true,
|
|
14558
|
+
mode: "autopilot",
|
|
14559
|
+
current_phase: "ralplan",
|
|
14560
|
+
session_id: sessionId,
|
|
14561
|
+
});
|
|
14562
|
+
await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
|
|
14563
|
+
version: 1,
|
|
14564
|
+
active: false,
|
|
14565
|
+
mode: "autopilot",
|
|
14566
|
+
outcome: "finish",
|
|
14567
|
+
lifecycle_outcome: "finished",
|
|
14568
|
+
current_phase: "complete",
|
|
14569
|
+
completed_at: "2026-05-30T00:00:00.000Z",
|
|
14570
|
+
updated_at: "2026-05-30T00:00:00.000Z",
|
|
14571
|
+
});
|
|
14572
|
+
|
|
14573
|
+
const result = await dispatchCodexNativeHook(
|
|
14574
|
+
{
|
|
14575
|
+
hook_event_name: "PreToolUse",
|
|
14576
|
+
cwd,
|
|
14577
|
+
session_id: nativeSessionId,
|
|
14578
|
+
thread_id: "thread-autopilot-ralplan-native-map-terminal",
|
|
14579
|
+
tool_name: "Edit",
|
|
14580
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14581
|
+
},
|
|
14582
|
+
{ cwd },
|
|
14583
|
+
);
|
|
14584
|
+
|
|
14585
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14586
|
+
assert.equal(result.outputJson, null);
|
|
14587
|
+
} finally {
|
|
14588
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14589
|
+
}
|
|
14590
|
+
});
|
|
14591
|
+
|
|
14592
|
+
it("does not block unrelated native Codex ids when current OMX session mapping does not match", async () => {
|
|
14593
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-unrelated-"));
|
|
14594
|
+
try {
|
|
14595
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14596
|
+
const sessionId = "sess-ralplan-native-map-owner";
|
|
14597
|
+
const ownerNativeSessionId = "019e-ralplan-native-owner";
|
|
14598
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, ownerNativeSessionId);
|
|
14599
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14600
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14601
|
+
active: true,
|
|
14602
|
+
mode: "ralplan",
|
|
14603
|
+
current_phase: "planning",
|
|
14604
|
+
session_id: sessionId,
|
|
14605
|
+
});
|
|
14606
|
+
|
|
14607
|
+
const result = await dispatchCodexNativeHook(
|
|
14608
|
+
{
|
|
14609
|
+
hook_event_name: "PreToolUse",
|
|
14610
|
+
cwd,
|
|
14611
|
+
session_id: "019e-unrelated-native-session",
|
|
14612
|
+
thread_id: "thread-ralplan-native-map-unrelated",
|
|
14613
|
+
tool_name: "Edit",
|
|
14614
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14615
|
+
},
|
|
14616
|
+
{ cwd },
|
|
14617
|
+
);
|
|
14618
|
+
|
|
14619
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14620
|
+
assert.equal(result.outputJson, null);
|
|
14621
|
+
} finally {
|
|
14622
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14623
|
+
}
|
|
14624
|
+
});
|
|
14625
|
+
|
|
14626
|
+
it("blocks mapped Autopilot ralplan writes from the authoritative team state root", async () => {
|
|
14627
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-team-root-"));
|
|
14628
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
|
|
14629
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
14630
|
+
try {
|
|
14631
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
14632
|
+
const stateDir = teamStateRoot;
|
|
14633
|
+
const sessionId = "sess-autopilot-ralplan-team-root";
|
|
14634
|
+
const nativeSessionId = "019e-autopilot-ralplan-team-root";
|
|
14635
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14636
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
14637
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
14638
|
+
active: true,
|
|
14639
|
+
mode: "autopilot",
|
|
14640
|
+
current_phase: "ralplan",
|
|
14641
|
+
session_id: sessionId,
|
|
14642
|
+
});
|
|
14643
|
+
|
|
14644
|
+
const result = await dispatchCodexNativeHook(
|
|
14645
|
+
{
|
|
14646
|
+
hook_event_name: "PreToolUse",
|
|
14647
|
+
cwd,
|
|
14648
|
+
session_id: nativeSessionId,
|
|
14649
|
+
thread_id: "thread-autopilot-ralplan-team-root",
|
|
14650
|
+
tool_name: "Edit",
|
|
14651
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14652
|
+
},
|
|
14653
|
+
{ cwd },
|
|
14654
|
+
);
|
|
14655
|
+
|
|
14656
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14657
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14658
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14659
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "session.json")), false);
|
|
14660
|
+
} finally {
|
|
14661
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
14662
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
14663
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14664
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
14665
|
+
}
|
|
14666
|
+
});
|
|
14667
|
+
|
|
14668
|
+
it("does not block unrelated native Codex ids from the authoritative team state root", async () => {
|
|
14669
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-team-root-unrelated-"));
|
|
14670
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-unrelated-"));
|
|
14671
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
14672
|
+
try {
|
|
14673
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
14674
|
+
const stateDir = teamStateRoot;
|
|
14675
|
+
const sessionId = "sess-ralplan-team-root-owner";
|
|
14676
|
+
const nativeSessionId = "019e-ralplan-team-root-owner";
|
|
14677
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
14678
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
14679
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14680
|
+
active: true,
|
|
14681
|
+
mode: "ralplan",
|
|
14682
|
+
current_phase: "planning",
|
|
14683
|
+
session_id: sessionId,
|
|
14684
|
+
});
|
|
14685
|
+
|
|
14686
|
+
const result = await dispatchCodexNativeHook(
|
|
14687
|
+
{
|
|
14688
|
+
hook_event_name: "PreToolUse",
|
|
14689
|
+
cwd,
|
|
14690
|
+
session_id: "019e-unrelated-team-root-native",
|
|
14691
|
+
thread_id: "thread-ralplan-team-root-unrelated",
|
|
14692
|
+
tool_name: "Edit",
|
|
14693
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14694
|
+
},
|
|
14695
|
+
{ cwd },
|
|
14696
|
+
);
|
|
14697
|
+
|
|
14698
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14699
|
+
assert.equal(result.outputJson, null);
|
|
14700
|
+
} finally {
|
|
14701
|
+
if (typeof previousTeamStateRoot === "string") process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
14702
|
+
else delete process.env.OMX_TEAM_STATE_ROOT;
|
|
14703
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14704
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
14705
|
+
}
|
|
14706
|
+
});
|
|
14707
|
+
|
|
14708
|
+
it("allows ralplan planning artifact writes without execution handoff", async () => {
|
|
14709
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-"));
|
|
14710
|
+
try {
|
|
14711
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14712
|
+
const sessionId = "sess-ralplan-pretool-artifact";
|
|
14713
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14714
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14715
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14716
|
+
active: true,
|
|
14717
|
+
skill: "ralplan",
|
|
14718
|
+
phase: "planning",
|
|
14719
|
+
session_id: sessionId,
|
|
14720
|
+
active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }],
|
|
14721
|
+
});
|
|
14722
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14723
|
+
active: true,
|
|
14724
|
+
mode: "ralplan",
|
|
14725
|
+
current_phase: "planning",
|
|
14726
|
+
session_id: sessionId,
|
|
14727
|
+
});
|
|
14728
|
+
|
|
14729
|
+
const result = await dispatchCodexNativeHook(
|
|
14730
|
+
{
|
|
14731
|
+
hook_event_name: "PreToolUse",
|
|
14732
|
+
cwd,
|
|
14733
|
+
session_id: sessionId,
|
|
14734
|
+
thread_id: "thread-ralplan-pretool-artifact",
|
|
14735
|
+
tool_name: "Write",
|
|
14736
|
+
tool_input: { file_path: ".omx/plans/prd-issue-2603.md" },
|
|
14737
|
+
},
|
|
14738
|
+
{ cwd },
|
|
14739
|
+
);
|
|
14740
|
+
|
|
14741
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14742
|
+
assert.equal(result.outputJson, null);
|
|
14743
|
+
} finally {
|
|
14744
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14745
|
+
}
|
|
14746
|
+
});
|
|
14747
|
+
|
|
14748
|
+
it("blocks bash implementation writes while ralplan is active without execution handoff", async () => {
|
|
14749
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-block-"));
|
|
14750
|
+
try {
|
|
14751
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14752
|
+
const sessionId = "sess-ralplan-pretool-bash-block";
|
|
14753
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14754
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14755
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14756
|
+
active: true,
|
|
14757
|
+
skill: "ralplan",
|
|
14758
|
+
phase: "planning",
|
|
14759
|
+
session_id: sessionId,
|
|
14760
|
+
active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }],
|
|
14761
|
+
});
|
|
14762
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14763
|
+
active: true,
|
|
14764
|
+
mode: "ralplan",
|
|
14765
|
+
current_phase: "planning",
|
|
14766
|
+
session_id: sessionId,
|
|
14767
|
+
});
|
|
14768
|
+
|
|
14769
|
+
const result = await dispatchCodexNativeHook(
|
|
14770
|
+
{
|
|
14771
|
+
hook_event_name: "PreToolUse",
|
|
14772
|
+
cwd,
|
|
14773
|
+
session_id: sessionId,
|
|
14774
|
+
thread_id: "thread-ralplan-pretool-bash-block",
|
|
14775
|
+
tool_name: "Bash",
|
|
14776
|
+
tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
|
|
14777
|
+
},
|
|
14778
|
+
{ cwd },
|
|
14779
|
+
);
|
|
14780
|
+
|
|
14781
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14782
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
14783
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
14784
|
+
} finally {
|
|
14785
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14786
|
+
}
|
|
14787
|
+
});
|
|
14788
|
+
|
|
14789
|
+
it("allows bash planning artifact writes while ralplan is active without execution handoff", async () => {
|
|
14790
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-bash-artifact-"));
|
|
14791
|
+
try {
|
|
14792
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14793
|
+
const sessionId = "sess-ralplan-pretool-bash-artifact";
|
|
14794
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14795
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14796
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14797
|
+
active: true,
|
|
14798
|
+
skill: "ralplan",
|
|
14799
|
+
phase: "planning",
|
|
14800
|
+
session_id: sessionId,
|
|
14801
|
+
active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId }],
|
|
14802
|
+
});
|
|
14803
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14804
|
+
active: true,
|
|
14805
|
+
mode: "ralplan",
|
|
14806
|
+
current_phase: "planning",
|
|
14807
|
+
session_id: sessionId,
|
|
14808
|
+
});
|
|
14809
|
+
|
|
14810
|
+
const result = await dispatchCodexNativeHook(
|
|
14811
|
+
{
|
|
14812
|
+
hook_event_name: "PreToolUse",
|
|
14813
|
+
cwd,
|
|
14814
|
+
session_id: sessionId,
|
|
14815
|
+
thread_id: "thread-ralplan-pretool-bash-artifact",
|
|
14816
|
+
tool_name: "Bash",
|
|
14817
|
+
tool_input: { command: "cat <<'EOF' > .omx/plans/prd-issue-2603.md\nplanning\nEOF" },
|
|
14818
|
+
},
|
|
14819
|
+
{ cwd },
|
|
14820
|
+
);
|
|
14821
|
+
|
|
14822
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14823
|
+
assert.equal(result.outputJson, null);
|
|
14824
|
+
} finally {
|
|
14825
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14826
|
+
}
|
|
14827
|
+
});
|
|
14828
|
+
|
|
14829
|
+
it("allows implementation writes when an explicit execution handoff is active", async () => {
|
|
14830
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-handoff-"));
|
|
14831
|
+
try {
|
|
14832
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14833
|
+
const sessionId = "sess-ralplan-pretool-handoff";
|
|
14834
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
14835
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
14836
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
14837
|
+
active: true,
|
|
14838
|
+
skill: "ultragoal",
|
|
14839
|
+
phase: "planning",
|
|
14840
|
+
session_id: sessionId,
|
|
14841
|
+
active_skills: [
|
|
14842
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
14843
|
+
{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId },
|
|
14844
|
+
],
|
|
14845
|
+
});
|
|
14846
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
14847
|
+
active: true,
|
|
14848
|
+
mode: "ralplan",
|
|
14849
|
+
current_phase: "complete",
|
|
14850
|
+
session_id: sessionId,
|
|
14851
|
+
});
|
|
14852
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
14853
|
+
active: true,
|
|
14854
|
+
mode: "ultragoal",
|
|
14855
|
+
current_phase: "planning",
|
|
14856
|
+
session_id: sessionId,
|
|
14857
|
+
});
|
|
14858
|
+
|
|
14859
|
+
const result = await dispatchCodexNativeHook(
|
|
14860
|
+
{
|
|
14861
|
+
hook_event_name: "PreToolUse",
|
|
14862
|
+
cwd,
|
|
14863
|
+
session_id: sessionId,
|
|
14864
|
+
thread_id: "thread-ralplan-pretool-handoff",
|
|
14865
|
+
tool_name: "Edit",
|
|
14866
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
14867
|
+
},
|
|
14868
|
+
{ cwd },
|
|
14869
|
+
);
|
|
14870
|
+
|
|
14871
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
14872
|
+
assert.equal(result.outputJson, null);
|
|
14873
|
+
} finally {
|
|
14874
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14875
|
+
}
|
|
14876
|
+
});
|
|
14877
|
+
|
|
14878
|
+
it("does not block Stop from root team state without team_name when no session is known", async () => {
|
|
14879
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-team-no-session-no-name-"));
|
|
14880
|
+
try {
|
|
14881
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
14882
|
+
await mkdir(stateDir, { recursive: true });
|
|
14883
|
+
await writeJson(join(stateDir, "team-state.json"), {
|
|
14884
|
+
active: true,
|
|
14111
14885
|
mode: "team",
|
|
14112
14886
|
current_phase: "starting",
|
|
14113
14887
|
});
|
|
@@ -14730,6 +15504,80 @@ describe("codex native hook triage integration", () => {
|
|
|
14730
15504
|
}
|
|
14731
15505
|
});
|
|
14732
15506
|
|
|
15507
|
+
it("omits Team handoff guidance from autopilot prompt context when Team mode is disabled", async () => {
|
|
15508
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-"));
|
|
15509
|
+
try {
|
|
15510
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
15511
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
15512
|
+
scope: "project",
|
|
15513
|
+
teamMode: "disabled",
|
|
15514
|
+
});
|
|
15515
|
+
await writeSessionStart(cwd, "sess-autopilot-observable-no-team");
|
|
15516
|
+
|
|
15517
|
+
const result = await dispatchCodexNativeHook(
|
|
15518
|
+
{
|
|
15519
|
+
hook_event_name: "UserPromptSubmit",
|
|
15520
|
+
cwd,
|
|
15521
|
+
session_id: "sess-autopilot-observable-no-team",
|
|
15522
|
+
thread_id: "thread-autopilot-observable-no-team",
|
|
15523
|
+
turn_id: "turn-autopilot-observable-no-team",
|
|
15524
|
+
prompt: "$autopilot implement issue #2430",
|
|
15525
|
+
},
|
|
15526
|
+
{ cwd },
|
|
15527
|
+
);
|
|
15528
|
+
|
|
15529
|
+
assert.equal(result.skillState?.skill, "autopilot");
|
|
15530
|
+
const additionalContext = String(
|
|
15531
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
15532
|
+
);
|
|
15533
|
+
assert.match(additionalContext, /detected workflow keyword "\$autopilot" -> autopilot/);
|
|
15534
|
+
assert.match(additionalContext, /\$deep-interview -> \$ralplan -> \$ultragoal -> \$code-review -> \$ultraqa/);
|
|
15535
|
+
assert.doesNotMatch(additionalContext, /\$team/);
|
|
15536
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
15537
|
+
} finally {
|
|
15538
|
+
await rm(cwd, { recursive: true, force: true });
|
|
15539
|
+
}
|
|
15540
|
+
});
|
|
15541
|
+
|
|
15542
|
+
it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => {
|
|
15543
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-"));
|
|
15544
|
+
try {
|
|
15545
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
15546
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
15547
|
+
scope: "project",
|
|
15548
|
+
teamMode: "disabled",
|
|
15549
|
+
});
|
|
15550
|
+
await writeSessionStart(cwd, "sess-disabled-team-primary");
|
|
15551
|
+
|
|
15552
|
+
const result = await dispatchCodexNativeHook(
|
|
15553
|
+
{
|
|
15554
|
+
hook_event_name: "UserPromptSubmit",
|
|
15555
|
+
cwd,
|
|
15556
|
+
session_id: "sess-disabled-team-primary",
|
|
15557
|
+
thread_id: "thread-disabled-team-primary",
|
|
15558
|
+
turn_id: "turn-disabled-team-primary",
|
|
15559
|
+
prompt: "$team $ralph fix this",
|
|
15560
|
+
},
|
|
15561
|
+
{ cwd },
|
|
15562
|
+
);
|
|
15563
|
+
|
|
15564
|
+
assert.equal(result.skillState?.skill, "ralph");
|
|
15565
|
+
assert.equal(result.skillState?.transition_error, undefined);
|
|
15566
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
15567
|
+
assert.equal(
|
|
15568
|
+
existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ralph-state.json")),
|
|
15569
|
+
true,
|
|
15570
|
+
);
|
|
15571
|
+
const additionalContext = String(
|
|
15572
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
15573
|
+
);
|
|
15574
|
+
assert.match(additionalContext, /detected workflow keyword "\$ralph" -> ralph/);
|
|
15575
|
+
assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/);
|
|
15576
|
+
} finally {
|
|
15577
|
+
await rm(cwd, { recursive: true, force: true });
|
|
15578
|
+
}
|
|
15579
|
+
});
|
|
15580
|
+
|
|
14733
15581
|
it("makes bare autopilot command activation observable in state and prompt guidance", async () => {
|
|
14734
15582
|
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-"));
|
|
14735
15583
|
try {
|