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
|
@@ -46,6 +46,23 @@ async function writeJson(path, value) {
|
|
|
46
46
|
await mkdir(dirname(path), { recursive: true }).catch(() => { });
|
|
47
47
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
48
48
|
}
|
|
49
|
+
async function writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId) {
|
|
50
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
51
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
52
|
+
session_id: sessionId,
|
|
53
|
+
native_session_id: nativeSessionId,
|
|
54
|
+
cwd,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async function writeSessionSkillActiveState(stateDir, sessionId, skill, phase) {
|
|
58
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
59
|
+
active: true,
|
|
60
|
+
skill,
|
|
61
|
+
phase,
|
|
62
|
+
session_id: sessionId,
|
|
63
|
+
active_skills: [{ skill, phase, active: true, session_id: sessionId }],
|
|
64
|
+
});
|
|
65
|
+
}
|
|
49
66
|
async function setTeamPaneIds(cwd, teamName, paneIds) {
|
|
50
67
|
for (const fileName of ["config.json", "manifest.v2.json"]) {
|
|
51
68
|
const filePath = join(cwd, ".omx", "state", "team", teamName, fileName);
|
|
@@ -1280,12 +1297,16 @@ describe("codex native hook dispatch", () => {
|
|
|
1280
1297
|
}, {
|
|
1281
1298
|
cwd,
|
|
1282
1299
|
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
1283
|
-
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
1300
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
|
|
1284
1301
|
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
1285
1302
|
},
|
|
1286
1303
|
});
|
|
1287
1304
|
assert.equal(promptResult.omxEventName, "keyword-detector");
|
|
1288
|
-
assert.deepEqual(reconcileCall, {
|
|
1305
|
+
assert.deepEqual(reconcileCall, {
|
|
1306
|
+
cwd,
|
|
1307
|
+
sessionId: ownerSessionId,
|
|
1308
|
+
sessionIds: [ownerSessionId, nativeSessionId],
|
|
1309
|
+
});
|
|
1289
1310
|
}
|
|
1290
1311
|
finally {
|
|
1291
1312
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1315,12 +1336,16 @@ describe("codex native hook dispatch", () => {
|
|
|
1315
1336
|
}, {
|
|
1316
1337
|
cwd,
|
|
1317
1338
|
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
1318
|
-
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
1339
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
|
|
1319
1340
|
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
1320
1341
|
},
|
|
1321
1342
|
});
|
|
1322
1343
|
assert.equal(promptResult.omxEventName, "keyword-detector");
|
|
1323
|
-
assert.deepEqual(reconcileCall, {
|
|
1344
|
+
assert.deepEqual(reconcileCall, {
|
|
1345
|
+
cwd,
|
|
1346
|
+
sessionId: canonicalSessionId,
|
|
1347
|
+
sessionIds: [canonicalSessionId, nativeSessionId],
|
|
1348
|
+
});
|
|
1324
1349
|
}
|
|
1325
1350
|
finally {
|
|
1326
1351
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -2853,6 +2878,13 @@ standardMaxRounds = 15
|
|
|
2853
2878
|
assert.match(message, /Do not advance from deep-interview to ralplan merely because the first question was answered/);
|
|
2854
2879
|
assert.match(message, /Planner output has been reviewed sequentially by Architect and then Critic/);
|
|
2855
2880
|
assert.match(message, /do not hand off to Ultragoal or implementation until .*ralplan_architect_review.*ralplan_critic_review/);
|
|
2881
|
+
const autopilotState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate", "autopilot-state.json"), "utf-8"));
|
|
2882
|
+
const snapshotPath = autopilotState.state?.handoff_artifacts?.context_snapshot_path ?? "";
|
|
2883
|
+
assert.match(snapshotPath, /^\.omx\/context\/implement-issue-2430-\d{8}T\d{6}Z\.md$/);
|
|
2884
|
+
const snapshot = await readFile(join(cwd, snapshotPath), "utf-8");
|
|
2885
|
+
assert.match(snapshot, /activation prompt \/ task seed: \$autopilot implement issue #2430/);
|
|
2886
|
+
assert.match(snapshot, /scope note: this seed captures the Autopilot activation prompt/);
|
|
2887
|
+
assert.match(snapshot, /constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa/);
|
|
2856
2888
|
}
|
|
2857
2889
|
finally {
|
|
2858
2890
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -4172,7 +4204,54 @@ esac
|
|
|
4172
4204
|
await rm(cwd, { recursive: true, force: true });
|
|
4173
4205
|
}
|
|
4174
4206
|
});
|
|
4175
|
-
it("
|
|
4207
|
+
it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => {
|
|
4208
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"));
|
|
4209
|
+
const originalTmux = process.env.TMUX;
|
|
4210
|
+
const originalTmuxPane = process.env.TMUX_PANE;
|
|
4211
|
+
const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
4212
|
+
const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
4213
|
+
try {
|
|
4214
|
+
process.env.TMUX = "1";
|
|
4215
|
+
process.env.TMUX_PANE = "%1";
|
|
4216
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
4217
|
+
process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1";
|
|
4218
|
+
let reconcileCalled = false;
|
|
4219
|
+
const result = await dispatchCodexNativeHook({
|
|
4220
|
+
hook_event_name: "UserPromptSubmit",
|
|
4221
|
+
cwd,
|
|
4222
|
+
session_id: "omx-doctor-plugin-hook-smoke",
|
|
4223
|
+
prompt: "$ralplan doctor plugin hook smoke test",
|
|
4224
|
+
}, {
|
|
4225
|
+
cwd,
|
|
4226
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
4227
|
+
reconcileCalled = true;
|
|
4228
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
4229
|
+
},
|
|
4230
|
+
});
|
|
4231
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
4232
|
+
assert.equal(reconcileCalled, false);
|
|
4233
|
+
}
|
|
4234
|
+
finally {
|
|
4235
|
+
if (originalTmux === undefined)
|
|
4236
|
+
delete process.env.TMUX;
|
|
4237
|
+
else
|
|
4238
|
+
process.env.TMUX = originalTmux;
|
|
4239
|
+
if (originalTmuxPane === undefined)
|
|
4240
|
+
delete process.env.TMUX_PANE;
|
|
4241
|
+
else
|
|
4242
|
+
process.env.TMUX_PANE = originalTmuxPane;
|
|
4243
|
+
if (originalHudOwner === undefined)
|
|
4244
|
+
delete process.env[OMX_TMUX_HUD_OWNER_ENV];
|
|
4245
|
+
else
|
|
4246
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner;
|
|
4247
|
+
if (originalDoctorSmoke === undefined)
|
|
4248
|
+
delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
|
|
4249
|
+
else
|
|
4250
|
+
process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke;
|
|
4251
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4252
|
+
}
|
|
4253
|
+
});
|
|
4254
|
+
it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
|
|
4176
4255
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-"));
|
|
4177
4256
|
const originalTmux = process.env.TMUX;
|
|
4178
4257
|
const originalTmuxPane = process.env.TMUX_PANE;
|
|
@@ -4219,8 +4298,8 @@ esac
|
|
|
4219
4298
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
4220
4299
|
const tmuxCalls = await readFile(tmuxLog, "utf-8");
|
|
4221
4300
|
assert.match(tmuxCalls, /list-panes -t %1 -F/);
|
|
4222
|
-
assert.match(tmuxCalls,
|
|
4223
|
-
assert.
|
|
4301
|
+
assert.match(tmuxCalls, /split-window/);
|
|
4302
|
+
assert.match(tmuxCalls, new RegExp(`resize-pane -t %9 -y ${HUD_TMUX_HEIGHT_LINES}`));
|
|
4224
4303
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
|
|
4225
4304
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
|
|
4226
4305
|
}
|
|
@@ -11483,7 +11562,7 @@ exit 0
|
|
|
11483
11562
|
}, { cwd });
|
|
11484
11563
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11485
11564
|
assert.equal(result.outputJson?.decision, "block");
|
|
11486
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
11565
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11487
11566
|
assert.match(String(result.outputJson?.hookSpecificOutput?.additionalContext ?? ""), /\$ultragoal.*\$team.*\$ralph/i);
|
|
11488
11567
|
}
|
|
11489
11568
|
finally {
|
|
@@ -11525,7 +11604,35 @@ exit 0
|
|
|
11525
11604
|
}, { cwd });
|
|
11526
11605
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11527
11606
|
assert.equal(result.outputJson?.decision, "block");
|
|
11528
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
11607
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11608
|
+
}
|
|
11609
|
+
finally {
|
|
11610
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11611
|
+
}
|
|
11612
|
+
});
|
|
11613
|
+
it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => {
|
|
11614
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-"));
|
|
11615
|
+
try {
|
|
11616
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11617
|
+
const sessionId = "sess-autopilot-ralplan-no-canonical";
|
|
11618
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
11619
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
11620
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
11621
|
+
active: true,
|
|
11622
|
+
mode: "autopilot",
|
|
11623
|
+
current_phase: "ralplan",
|
|
11624
|
+
session_id: sessionId,
|
|
11625
|
+
});
|
|
11626
|
+
const result = await dispatchCodexNativeHook({
|
|
11627
|
+
hook_event_name: "PreToolUse",
|
|
11628
|
+
cwd,
|
|
11629
|
+
session_id: sessionId,
|
|
11630
|
+
thread_id: "thread-autopilot-ralplan-no-canonical",
|
|
11631
|
+
tool_name: "Edit",
|
|
11632
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11633
|
+
}, { cwd });
|
|
11634
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11635
|
+
assert.equal(result.outputJson, null);
|
|
11529
11636
|
}
|
|
11530
11637
|
finally {
|
|
11531
11638
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -11606,12 +11713,430 @@ exit 0
|
|
|
11606
11713
|
}, { cwd });
|
|
11607
11714
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11608
11715
|
assert.equal(result.outputJson?.decision, "block");
|
|
11609
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
11716
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11610
11717
|
}
|
|
11611
11718
|
finally {
|
|
11612
11719
|
await rm(cwd, { recursive: true, force: true });
|
|
11613
11720
|
}
|
|
11614
11721
|
});
|
|
11722
|
+
it("blocks implementation writes when ralplan and Autopilot ralplan are both active", async () => {
|
|
11723
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-autopilot-mixed-planning-"));
|
|
11724
|
+
try {
|
|
11725
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11726
|
+
const sessionId = "sess-ralplan-autopilot-mixed-planning";
|
|
11727
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
11728
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
11729
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
11730
|
+
active: true,
|
|
11731
|
+
skill: "autopilot",
|
|
11732
|
+
phase: "ralplan",
|
|
11733
|
+
session_id: sessionId,
|
|
11734
|
+
active_skills: [
|
|
11735
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
11736
|
+
{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId },
|
|
11737
|
+
],
|
|
11738
|
+
});
|
|
11739
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
11740
|
+
active: true,
|
|
11741
|
+
mode: "ralplan",
|
|
11742
|
+
current_phase: "planning",
|
|
11743
|
+
session_id: sessionId,
|
|
11744
|
+
});
|
|
11745
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
11746
|
+
active: true,
|
|
11747
|
+
mode: "autopilot",
|
|
11748
|
+
current_phase: "ralplan",
|
|
11749
|
+
session_id: sessionId,
|
|
11750
|
+
});
|
|
11751
|
+
const result = await dispatchCodexNativeHook({
|
|
11752
|
+
hook_event_name: "PreToolUse",
|
|
11753
|
+
cwd,
|
|
11754
|
+
session_id: sessionId,
|
|
11755
|
+
thread_id: "thread-ralplan-autopilot-mixed-planning",
|
|
11756
|
+
tool_name: "Edit",
|
|
11757
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11758
|
+
}, { cwd });
|
|
11759
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11760
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11761
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11762
|
+
}
|
|
11763
|
+
finally {
|
|
11764
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11765
|
+
}
|
|
11766
|
+
});
|
|
11767
|
+
it("blocks implementation writes while Autopilot is supervising replan without handoff", async () => {
|
|
11768
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-replan-pretool-block-"));
|
|
11769
|
+
try {
|
|
11770
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11771
|
+
const sessionId = "sess-autopilot-replan-pretool-block";
|
|
11772
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
11773
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
11774
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
11775
|
+
active: true,
|
|
11776
|
+
skill: "autopilot",
|
|
11777
|
+
phase: "replan",
|
|
11778
|
+
session_id: sessionId,
|
|
11779
|
+
active_skills: [{ skill: "autopilot", phase: "replan", active: true, session_id: sessionId }],
|
|
11780
|
+
});
|
|
11781
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
11782
|
+
active: true,
|
|
11783
|
+
mode: "autopilot",
|
|
11784
|
+
current_phase: "replan",
|
|
11785
|
+
session_id: sessionId,
|
|
11786
|
+
});
|
|
11787
|
+
const result = await dispatchCodexNativeHook({
|
|
11788
|
+
hook_event_name: "PreToolUse",
|
|
11789
|
+
cwd,
|
|
11790
|
+
session_id: sessionId,
|
|
11791
|
+
thread_id: "thread-autopilot-replan-pretool-block",
|
|
11792
|
+
tool_name: "Edit",
|
|
11793
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11794
|
+
}, { cwd });
|
|
11795
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11796
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11797
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11798
|
+
}
|
|
11799
|
+
finally {
|
|
11800
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11801
|
+
}
|
|
11802
|
+
});
|
|
11803
|
+
it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
11804
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-block-"));
|
|
11805
|
+
try {
|
|
11806
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11807
|
+
const sessionId = "sess-autopilot-ralplan-native-map-block";
|
|
11808
|
+
const nativeSessionId = "019e-autopilot-ralplan-native";
|
|
11809
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11810
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
11811
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
11812
|
+
active: true,
|
|
11813
|
+
mode: "autopilot",
|
|
11814
|
+
current_phase: "ralplan",
|
|
11815
|
+
session_id: sessionId,
|
|
11816
|
+
});
|
|
11817
|
+
const result = await dispatchCodexNativeHook({
|
|
11818
|
+
hook_event_name: "PreToolUse",
|
|
11819
|
+
cwd,
|
|
11820
|
+
session_id: nativeSessionId,
|
|
11821
|
+
thread_id: "thread-autopilot-ralplan-native-map-block",
|
|
11822
|
+
tool_name: "apply_patch",
|
|
11823
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11824
|
+
}, { cwd });
|
|
11825
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11826
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11827
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11828
|
+
}
|
|
11829
|
+
finally {
|
|
11830
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11831
|
+
}
|
|
11832
|
+
});
|
|
11833
|
+
it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
|
|
11834
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-bash-"));
|
|
11835
|
+
try {
|
|
11836
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11837
|
+
const sessionId = "sess-autopilot-ralplan-native-map-bash";
|
|
11838
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-bash";
|
|
11839
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11840
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
11841
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
11842
|
+
active: true,
|
|
11843
|
+
mode: "autopilot",
|
|
11844
|
+
current_phase: "ralplan",
|
|
11845
|
+
session_id: sessionId,
|
|
11846
|
+
});
|
|
11847
|
+
const result = await dispatchCodexNativeHook({
|
|
11848
|
+
hook_event_name: "PreToolUse",
|
|
11849
|
+
cwd,
|
|
11850
|
+
session_id: nativeSessionId,
|
|
11851
|
+
thread_id: "thread-autopilot-ralplan-native-map-bash",
|
|
11852
|
+
tool_name: "Bash",
|
|
11853
|
+
tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
|
|
11854
|
+
}, { cwd });
|
|
11855
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11856
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11857
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11858
|
+
}
|
|
11859
|
+
finally {
|
|
11860
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11861
|
+
}
|
|
11862
|
+
});
|
|
11863
|
+
it("blocks standalone ralplan writes when native Codex id maps to OMX session state", async () => {
|
|
11864
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-block-"));
|
|
11865
|
+
try {
|
|
11866
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11867
|
+
const sessionId = "sess-ralplan-native-map-block";
|
|
11868
|
+
const nativeSessionId = "019e-ralplan-native-map";
|
|
11869
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11870
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
11871
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
11872
|
+
active: true,
|
|
11873
|
+
mode: "ralplan",
|
|
11874
|
+
current_phase: "planning",
|
|
11875
|
+
session_id: sessionId,
|
|
11876
|
+
});
|
|
11877
|
+
const result = await dispatchCodexNativeHook({
|
|
11878
|
+
hook_event_name: "PreToolUse",
|
|
11879
|
+
cwd,
|
|
11880
|
+
session_id: nativeSessionId,
|
|
11881
|
+
thread_id: "thread-ralplan-native-map-block",
|
|
11882
|
+
tool_name: "Edit",
|
|
11883
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11884
|
+
}, { cwd });
|
|
11885
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11886
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11887
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11888
|
+
}
|
|
11889
|
+
finally {
|
|
11890
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11891
|
+
}
|
|
11892
|
+
});
|
|
11893
|
+
it("blocks deep-interview writes when native Codex id maps to OMX session state", async () => {
|
|
11894
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-native-map-block-"));
|
|
11895
|
+
try {
|
|
11896
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11897
|
+
const sessionId = "sess-deep-interview-native-map-block";
|
|
11898
|
+
const nativeSessionId = "019e-deep-interview-native-map";
|
|
11899
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11900
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "deep-interview", "interview");
|
|
11901
|
+
await writeJson(join(stateDir, "sessions", sessionId, "deep-interview-state.json"), {
|
|
11902
|
+
active: true,
|
|
11903
|
+
mode: "deep-interview",
|
|
11904
|
+
current_phase: "interview",
|
|
11905
|
+
session_id: sessionId,
|
|
11906
|
+
});
|
|
11907
|
+
const result = await dispatchCodexNativeHook({
|
|
11908
|
+
hook_event_name: "PreToolUse",
|
|
11909
|
+
cwd,
|
|
11910
|
+
session_id: nativeSessionId,
|
|
11911
|
+
thread_id: "thread-deep-interview-native-map-block",
|
|
11912
|
+
tool_name: "Edit",
|
|
11913
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11914
|
+
}, { cwd });
|
|
11915
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11916
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
11917
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i);
|
|
11918
|
+
}
|
|
11919
|
+
finally {
|
|
11920
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11921
|
+
}
|
|
11922
|
+
});
|
|
11923
|
+
it("allows mapped ralplan planning artifact writes without execution handoff", async () => {
|
|
11924
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-"));
|
|
11925
|
+
try {
|
|
11926
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11927
|
+
const sessionId = "sess-ralplan-native-map-artifact";
|
|
11928
|
+
const nativeSessionId = "019e-ralplan-native-map-artifact";
|
|
11929
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11930
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
11931
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
11932
|
+
active: true,
|
|
11933
|
+
mode: "ralplan",
|
|
11934
|
+
current_phase: "planning",
|
|
11935
|
+
session_id: sessionId,
|
|
11936
|
+
});
|
|
11937
|
+
const result = await dispatchCodexNativeHook({
|
|
11938
|
+
hook_event_name: "PreToolUse",
|
|
11939
|
+
cwd,
|
|
11940
|
+
session_id: nativeSessionId,
|
|
11941
|
+
thread_id: "thread-ralplan-native-map-artifact",
|
|
11942
|
+
tool_name: "Bash",
|
|
11943
|
+
tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" },
|
|
11944
|
+
}, { cwd });
|
|
11945
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11946
|
+
assert.equal(result.outputJson, null);
|
|
11947
|
+
}
|
|
11948
|
+
finally {
|
|
11949
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11950
|
+
}
|
|
11951
|
+
});
|
|
11952
|
+
it("allows mapped implementation writes when explicit execution handoff is active", async () => {
|
|
11953
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-handoff-"));
|
|
11954
|
+
try {
|
|
11955
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
11956
|
+
const sessionId = "sess-ralplan-native-map-handoff";
|
|
11957
|
+
const nativeSessionId = "019e-ralplan-native-map-handoff";
|
|
11958
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
11959
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
11960
|
+
active: true,
|
|
11961
|
+
skill: "ultragoal",
|
|
11962
|
+
phase: "planning",
|
|
11963
|
+
session_id: sessionId,
|
|
11964
|
+
active_skills: [
|
|
11965
|
+
{ skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
|
|
11966
|
+
{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId },
|
|
11967
|
+
],
|
|
11968
|
+
});
|
|
11969
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
11970
|
+
active: true,
|
|
11971
|
+
mode: "ralplan",
|
|
11972
|
+
current_phase: "complete",
|
|
11973
|
+
session_id: sessionId,
|
|
11974
|
+
});
|
|
11975
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
11976
|
+
active: true,
|
|
11977
|
+
mode: "ultragoal",
|
|
11978
|
+
current_phase: "planning",
|
|
11979
|
+
session_id: sessionId,
|
|
11980
|
+
});
|
|
11981
|
+
const result = await dispatchCodexNativeHook({
|
|
11982
|
+
hook_event_name: "PreToolUse",
|
|
11983
|
+
cwd,
|
|
11984
|
+
session_id: nativeSessionId,
|
|
11985
|
+
thread_id: "thread-ralplan-native-map-handoff",
|
|
11986
|
+
tool_name: "Edit",
|
|
11987
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
11988
|
+
}, { cwd });
|
|
11989
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11990
|
+
assert.equal(result.outputJson, null);
|
|
11991
|
+
}
|
|
11992
|
+
finally {
|
|
11993
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11994
|
+
}
|
|
11995
|
+
});
|
|
11996
|
+
it("allows mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => {
|
|
11997
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-terminal-"));
|
|
11998
|
+
try {
|
|
11999
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12000
|
+
const sessionId = "sess-autopilot-ralplan-native-map-terminal";
|
|
12001
|
+
const nativeSessionId = "019e-autopilot-ralplan-native-terminal";
|
|
12002
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
12003
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
12004
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
12005
|
+
active: true,
|
|
12006
|
+
mode: "autopilot",
|
|
12007
|
+
current_phase: "ralplan",
|
|
12008
|
+
session_id: sessionId,
|
|
12009
|
+
});
|
|
12010
|
+
await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
|
|
12011
|
+
version: 1,
|
|
12012
|
+
active: false,
|
|
12013
|
+
mode: "autopilot",
|
|
12014
|
+
outcome: "finish",
|
|
12015
|
+
lifecycle_outcome: "finished",
|
|
12016
|
+
current_phase: "complete",
|
|
12017
|
+
completed_at: "2026-05-30T00:00:00.000Z",
|
|
12018
|
+
updated_at: "2026-05-30T00:00:00.000Z",
|
|
12019
|
+
});
|
|
12020
|
+
const result = await dispatchCodexNativeHook({
|
|
12021
|
+
hook_event_name: "PreToolUse",
|
|
12022
|
+
cwd,
|
|
12023
|
+
session_id: nativeSessionId,
|
|
12024
|
+
thread_id: "thread-autopilot-ralplan-native-map-terminal",
|
|
12025
|
+
tool_name: "Edit",
|
|
12026
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
12027
|
+
}, { cwd });
|
|
12028
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
12029
|
+
assert.equal(result.outputJson, null);
|
|
12030
|
+
}
|
|
12031
|
+
finally {
|
|
12032
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12033
|
+
}
|
|
12034
|
+
});
|
|
12035
|
+
it("does not block unrelated native Codex ids when current OMX session mapping does not match", async () => {
|
|
12036
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-unrelated-"));
|
|
12037
|
+
try {
|
|
12038
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12039
|
+
const sessionId = "sess-ralplan-native-map-owner";
|
|
12040
|
+
const ownerNativeSessionId = "019e-ralplan-native-owner";
|
|
12041
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, ownerNativeSessionId);
|
|
12042
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
12043
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12044
|
+
active: true,
|
|
12045
|
+
mode: "ralplan",
|
|
12046
|
+
current_phase: "planning",
|
|
12047
|
+
session_id: sessionId,
|
|
12048
|
+
});
|
|
12049
|
+
const result = await dispatchCodexNativeHook({
|
|
12050
|
+
hook_event_name: "PreToolUse",
|
|
12051
|
+
cwd,
|
|
12052
|
+
session_id: "019e-unrelated-native-session",
|
|
12053
|
+
thread_id: "thread-ralplan-native-map-unrelated",
|
|
12054
|
+
tool_name: "Edit",
|
|
12055
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
12056
|
+
}, { cwd });
|
|
12057
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
12058
|
+
assert.equal(result.outputJson, null);
|
|
12059
|
+
}
|
|
12060
|
+
finally {
|
|
12061
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12062
|
+
}
|
|
12063
|
+
});
|
|
12064
|
+
it("blocks mapped Autopilot ralplan writes from the authoritative team state root", async () => {
|
|
12065
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-team-root-"));
|
|
12066
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
|
|
12067
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
12068
|
+
try {
|
|
12069
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
12070
|
+
const stateDir = teamStateRoot;
|
|
12071
|
+
const sessionId = "sess-autopilot-ralplan-team-root";
|
|
12072
|
+
const nativeSessionId = "019e-autopilot-ralplan-team-root";
|
|
12073
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
12074
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
|
|
12075
|
+
await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
|
|
12076
|
+
active: true,
|
|
12077
|
+
mode: "autopilot",
|
|
12078
|
+
current_phase: "ralplan",
|
|
12079
|
+
session_id: sessionId,
|
|
12080
|
+
});
|
|
12081
|
+
const result = await dispatchCodexNativeHook({
|
|
12082
|
+
hook_event_name: "PreToolUse",
|
|
12083
|
+
cwd,
|
|
12084
|
+
session_id: nativeSessionId,
|
|
12085
|
+
thread_id: "thread-autopilot-ralplan-team-root",
|
|
12086
|
+
tool_name: "Edit",
|
|
12087
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
12088
|
+
}, { cwd });
|
|
12089
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
12090
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
12091
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
12092
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "session.json")), false);
|
|
12093
|
+
}
|
|
12094
|
+
finally {
|
|
12095
|
+
if (typeof previousTeamStateRoot === "string")
|
|
12096
|
+
process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
12097
|
+
else
|
|
12098
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
12099
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12100
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
12101
|
+
}
|
|
12102
|
+
});
|
|
12103
|
+
it("does not block unrelated native Codex ids from the authoritative team state root", async () => {
|
|
12104
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-team-root-unrelated-"));
|
|
12105
|
+
const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-unrelated-"));
|
|
12106
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
12107
|
+
try {
|
|
12108
|
+
process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
|
|
12109
|
+
const stateDir = teamStateRoot;
|
|
12110
|
+
const sessionId = "sess-ralplan-team-root-owner";
|
|
12111
|
+
const nativeSessionId = "019e-ralplan-team-root-owner";
|
|
12112
|
+
await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
|
|
12113
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
|
|
12114
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12115
|
+
active: true,
|
|
12116
|
+
mode: "ralplan",
|
|
12117
|
+
current_phase: "planning",
|
|
12118
|
+
session_id: sessionId,
|
|
12119
|
+
});
|
|
12120
|
+
const result = await dispatchCodexNativeHook({
|
|
12121
|
+
hook_event_name: "PreToolUse",
|
|
12122
|
+
cwd,
|
|
12123
|
+
session_id: "019e-unrelated-team-root-native",
|
|
12124
|
+
thread_id: "thread-ralplan-team-root-unrelated",
|
|
12125
|
+
tool_name: "Edit",
|
|
12126
|
+
tool_input: { file_path: "src/runtime.ts" },
|
|
12127
|
+
}, { cwd });
|
|
12128
|
+
assert.equal(result.omxEventName, "pre-tool-use");
|
|
12129
|
+
assert.equal(result.outputJson, null);
|
|
12130
|
+
}
|
|
12131
|
+
finally {
|
|
12132
|
+
if (typeof previousTeamStateRoot === "string")
|
|
12133
|
+
process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
12134
|
+
else
|
|
12135
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
12136
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12137
|
+
await rm(teamStateRoot, { recursive: true, force: true });
|
|
12138
|
+
}
|
|
12139
|
+
});
|
|
11615
12140
|
it("allows ralplan planning artifact writes without execution handoff", async () => {
|
|
11616
12141
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-"));
|
|
11617
12142
|
try {
|
|
@@ -11677,7 +12202,7 @@ exit 0
|
|
|
11677
12202
|
}, { cwd });
|
|
11678
12203
|
assert.equal(result.omxEventName, "pre-tool-use");
|
|
11679
12204
|
assert.equal(result.outputJson?.decision, "block");
|
|
11680
|
-
assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
|
|
12205
|
+
assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
|
|
11681
12206
|
}
|
|
11682
12207
|
finally {
|
|
11683
12208
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -12291,6 +12816,63 @@ describe("codex native hook triage integration", () => {
|
|
|
12291
12816
|
await rm(cwd, { recursive: true, force: true });
|
|
12292
12817
|
}
|
|
12293
12818
|
});
|
|
12819
|
+
it("omits Team handoff guidance from autopilot prompt context when Team mode is disabled", async () => {
|
|
12820
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-"));
|
|
12821
|
+
try {
|
|
12822
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
12823
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
12824
|
+
scope: "project",
|
|
12825
|
+
teamMode: "disabled",
|
|
12826
|
+
});
|
|
12827
|
+
await writeSessionStart(cwd, "sess-autopilot-observable-no-team");
|
|
12828
|
+
const result = await dispatchCodexNativeHook({
|
|
12829
|
+
hook_event_name: "UserPromptSubmit",
|
|
12830
|
+
cwd,
|
|
12831
|
+
session_id: "sess-autopilot-observable-no-team",
|
|
12832
|
+
thread_id: "thread-autopilot-observable-no-team",
|
|
12833
|
+
turn_id: "turn-autopilot-observable-no-team",
|
|
12834
|
+
prompt: "$autopilot implement issue #2430",
|
|
12835
|
+
}, { cwd });
|
|
12836
|
+
assert.equal(result.skillState?.skill, "autopilot");
|
|
12837
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
12838
|
+
assert.match(additionalContext, /detected workflow keyword "\$autopilot" -> autopilot/);
|
|
12839
|
+
assert.match(additionalContext, /\$deep-interview -> \$ralplan -> \$ultragoal -> \$code-review -> \$ultraqa/);
|
|
12840
|
+
assert.doesNotMatch(additionalContext, /\$team/);
|
|
12841
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
12842
|
+
}
|
|
12843
|
+
finally {
|
|
12844
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12845
|
+
}
|
|
12846
|
+
});
|
|
12847
|
+
it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => {
|
|
12848
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-"));
|
|
12849
|
+
try {
|
|
12850
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
12851
|
+
await writeJson(join(cwd, ".omx", "setup-scope.json"), {
|
|
12852
|
+
scope: "project",
|
|
12853
|
+
teamMode: "disabled",
|
|
12854
|
+
});
|
|
12855
|
+
await writeSessionStart(cwd, "sess-disabled-team-primary");
|
|
12856
|
+
const result = await dispatchCodexNativeHook({
|
|
12857
|
+
hook_event_name: "UserPromptSubmit",
|
|
12858
|
+
cwd,
|
|
12859
|
+
session_id: "sess-disabled-team-primary",
|
|
12860
|
+
thread_id: "thread-disabled-team-primary",
|
|
12861
|
+
turn_id: "turn-disabled-team-primary",
|
|
12862
|
+
prompt: "$team $ralph fix this",
|
|
12863
|
+
}, { cwd });
|
|
12864
|
+
assert.equal(result.skillState?.skill, "ralph");
|
|
12865
|
+
assert.equal(result.skillState?.transition_error, undefined);
|
|
12866
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
|
|
12867
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ralph-state.json")), true);
|
|
12868
|
+
const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
|
|
12869
|
+
assert.match(additionalContext, /detected workflow keyword "\$ralph" -> ralph/);
|
|
12870
|
+
assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/);
|
|
12871
|
+
}
|
|
12872
|
+
finally {
|
|
12873
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12874
|
+
}
|
|
12875
|
+
});
|
|
12294
12876
|
it("makes bare autopilot command activation observable in state and prompt guidance", async () => {
|
|
12295
12877
|
const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-"));
|
|
12296
12878
|
try {
|