oh-my-codex 0.18.14 → 0.18.16
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 +10 -7
- package/dist/agents/__tests__/native-config.test.js +37 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +10 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js +63 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +131 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +40 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
- package/dist/cli/__tests__/session-search-help.test.js +3 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +55 -1
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +35 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +80 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.d.ts +2 -0
- package/dist/cli/__tests__/url.test.d.ts.map +1 -0
- package/dist/cli/__tests__/url.test.js +82 -0
- package/dist/cli/__tests__/url.test.js.map +1 -0
- package/dist/cli/autoresearch-intake.d.ts.map +1 -1
- package/dist/cli/autoresearch-intake.js +2 -0
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/doctor.d.ts +17 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +203 -38
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +7 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +21 -62
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/session-search.d.ts +6 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +104 -4
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +20 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.d.ts +8 -0
- package/dist/cli/url.d.ts.map +1 -0
- package/dist/cli/url.js +69 -0
- package/dist/cli/url.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +20 -4
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +1 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +363 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +121 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +33 -2
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +1 -1
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +24 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +53 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +2 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -6
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +11 -5
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.js +9 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -1
- package/dist/notifications/http-client.js +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +600 -7
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +278 -8
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/session-history/__tests__/friction.test.d.ts +2 -0
- package/dist/session-history/__tests__/friction.test.d.ts.map +1 -0
- package/dist/session-history/__tests__/friction.test.js +107 -0
- package/dist/session-history/__tests__/friction.test.js.map +1 -0
- package/dist/session-history/friction.d.ts +68 -0
- package/dist/session-history/friction.d.ts.map +1 -0
- package/dist/session-history/friction.js +365 -0
- package/dist/session-history/friction.js.map +1 -0
- package/dist/sidecar/__tests__/args.test.d.ts +2 -0
- package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/args.test.js +18 -0
- package/dist/sidecar/__tests__/args.test.js.map +1 -0
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +12 -4
- package/dist/sidecar/index.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +150 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +8 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +108 -8
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
- package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
- package/dist/url-reader/__tests__/url-reader.test.js +433 -0
- package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
- package/dist/url-reader/index.d.ts +4 -0
- package/dist/url-reader/index.d.ts.map +1 -0
- package/dist/url-reader/index.js +522 -0
- package/dist/url-reader/index.js.map +1 -0
- package/dist/url-reader/types.d.ts +41 -0
- package/dist/url-reader/types.d.ts.map +1 -0
- package/dist/url-reader/types.js +2 -0
- package/dist/url-reader/types.js.map +1 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +3 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/platform-command.d.ts +7 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +12 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/vscode/__tests__/index.test.d.ts +2 -0
- package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
- package/dist/vscode/__tests__/index.test.js +236 -0
- package/dist/vscode/__tests__/index.test.js.map +1 -0
- package/dist/vscode/index.d.ts +76 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +196 -0
- package/dist/vscode/index.js.map +1 -0
- package/package.json +2 -2
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +692 -24
- package/src/scripts/codex-native-hook.ts +313 -7
- package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
|
@@ -2544,6 +2544,230 @@ describe("codex native hook dispatch", () => {
|
|
|
2544
2544
|
}
|
|
2545
2545
|
});
|
|
2546
2546
|
|
|
2547
|
+
it("allows same-session/current-thread Ralph Stop continuation", async () => {
|
|
2548
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-session-"));
|
|
2549
|
+
try {
|
|
2550
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2551
|
+
await writeNativeMappedSessionState(cwd, stateDir, "omx-current-ralph", "codex-current-ralph");
|
|
2552
|
+
await writeJson(join(stateDir, "sessions", "omx-current-ralph", "ralph-state.json"), {
|
|
2553
|
+
active: true,
|
|
2554
|
+
mode: "ralph",
|
|
2555
|
+
current_phase: "executing",
|
|
2556
|
+
owner_omx_session_id: "omx-current-ralph",
|
|
2557
|
+
owner_codex_session_id: "codex-current-ralph",
|
|
2558
|
+
owner_codex_thread_id: "thread-current-ralph",
|
|
2559
|
+
task_description: "Finish issue 2974 stale Ralph Stop-hook guard",
|
|
2560
|
+
});
|
|
2561
|
+
|
|
2562
|
+
const result = await dispatchCodexNativeHook(
|
|
2563
|
+
{
|
|
2564
|
+
hook_event_name: "Stop",
|
|
2565
|
+
cwd,
|
|
2566
|
+
session_id: "codex-current-ralph",
|
|
2567
|
+
thread_id: "thread-current-ralph",
|
|
2568
|
+
last_user_message: "continue the current ralph issue 2974 task",
|
|
2569
|
+
},
|
|
2570
|
+
{ cwd },
|
|
2571
|
+
);
|
|
2572
|
+
|
|
2573
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
2574
|
+
assert.equal(result.outputJson?.stopReason, "ralph_executing");
|
|
2575
|
+
assert.match(String(result.outputJson?.systemMessage), /Ralph is still active/);
|
|
2576
|
+
} finally {
|
|
2577
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2578
|
+
}
|
|
2579
|
+
});
|
|
2580
|
+
|
|
2581
|
+
it("does not resume stale different-session global Ralph Stop state", async () => {
|
|
2582
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-stale-global-"));
|
|
2583
|
+
try {
|
|
2584
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2585
|
+
active: true,
|
|
2586
|
+
mode: "ralph",
|
|
2587
|
+
current_phase: "executing",
|
|
2588
|
+
owner_codex_session_id: "codex-old-ralph",
|
|
2589
|
+
owner_codex_thread_id: "thread-old-ralph",
|
|
2590
|
+
task_description: "Finish issue 2974 stale Ralph Stop-hook guard",
|
|
2591
|
+
});
|
|
2592
|
+
|
|
2593
|
+
const result = await dispatchCodexNativeHook(
|
|
2594
|
+
{
|
|
2595
|
+
hook_event_name: "Stop",
|
|
2596
|
+
cwd,
|
|
2597
|
+
thread_id: "thread-new-ralph",
|
|
2598
|
+
last_user_message: "continue the current ralph issue 2974 task",
|
|
2599
|
+
},
|
|
2600
|
+
{ cwd },
|
|
2601
|
+
);
|
|
2602
|
+
|
|
2603
|
+
assert.equal(result.outputJson?.stopReason, undefined);
|
|
2604
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2605
|
+
} finally {
|
|
2606
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
|
|
2610
|
+
it("does not resume global Ralph Stop state for low-overlap unrelated tasks", async () => {
|
|
2611
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-low-overlap-"));
|
|
2612
|
+
try {
|
|
2613
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2614
|
+
active: true,
|
|
2615
|
+
mode: "ralph",
|
|
2616
|
+
current_phase: "executing",
|
|
2617
|
+
task_description: "Refactor documentation navigation and README examples",
|
|
2618
|
+
});
|
|
2619
|
+
|
|
2620
|
+
const result = await dispatchCodexNativeHook(
|
|
2621
|
+
{
|
|
2622
|
+
hook_event_name: "Stop",
|
|
2623
|
+
cwd,
|
|
2624
|
+
last_user_message: "continue auditing billing webhooks and payment retries",
|
|
2625
|
+
},
|
|
2626
|
+
{ cwd },
|
|
2627
|
+
);
|
|
2628
|
+
|
|
2629
|
+
assert.equal(result.outputJson?.stopReason, undefined);
|
|
2630
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2631
|
+
} finally {
|
|
2632
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2633
|
+
}
|
|
2634
|
+
});
|
|
2635
|
+
|
|
2636
|
+
it("allows explicit current user continuation for live-risk Ralph tasks when owner context is current", async () => {
|
|
2637
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-current-"));
|
|
2638
|
+
try {
|
|
2639
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2640
|
+
active: true,
|
|
2641
|
+
mode: "ralph",
|
|
2642
|
+
current_phase: "executing",
|
|
2643
|
+
owner_codex_thread_id: "thread-live-ralph",
|
|
2644
|
+
task_description: "Deploy billing migration rollback guard for production payments",
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
const result = await dispatchCodexNativeHook(
|
|
2648
|
+
{
|
|
2649
|
+
hook_event_name: "Stop",
|
|
2650
|
+
cwd,
|
|
2651
|
+
thread_id: "thread-live-ralph",
|
|
2652
|
+
last_user_message: "continue the current ralph deploy billing migration rollback guard",
|
|
2653
|
+
},
|
|
2654
|
+
{ cwd },
|
|
2655
|
+
);
|
|
2656
|
+
|
|
2657
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
2658
|
+
assert.equal(result.outputJson?.stopReason, "ralph_executing");
|
|
2659
|
+
} finally {
|
|
2660
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2661
|
+
}
|
|
2662
|
+
});
|
|
2663
|
+
|
|
2664
|
+
it("allows current-owner global Ralph Stop continuation with generic current-task wording", async () => {
|
|
2665
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-current-owner-generic-"));
|
|
2666
|
+
try {
|
|
2667
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2668
|
+
active: true,
|
|
2669
|
+
mode: "ralph",
|
|
2670
|
+
current_phase: "executing",
|
|
2671
|
+
owner_codex_thread_id: "thread-current-owner-generic",
|
|
2672
|
+
task_description: "Refactor documentation navigation and README examples",
|
|
2673
|
+
});
|
|
2674
|
+
|
|
2675
|
+
const result = await dispatchCodexNativeHook(
|
|
2676
|
+
{
|
|
2677
|
+
hook_event_name: "Stop",
|
|
2678
|
+
cwd,
|
|
2679
|
+
thread_id: "thread-current-owner-generic",
|
|
2680
|
+
last_user_message: "continue the current ralph task",
|
|
2681
|
+
},
|
|
2682
|
+
{ cwd },
|
|
2683
|
+
);
|
|
2684
|
+
|
|
2685
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
2686
|
+
assert.equal(result.outputJson?.stopReason, "ralph_executing");
|
|
2687
|
+
} finally {
|
|
2688
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2689
|
+
}
|
|
2690
|
+
});
|
|
2691
|
+
|
|
2692
|
+
it("allows same-thread global Ralph Stop continuation without payload text for non-live tasks", async () => {
|
|
2693
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-same-thread-no-text-"));
|
|
2694
|
+
try {
|
|
2695
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2696
|
+
active: true,
|
|
2697
|
+
mode: "ralph",
|
|
2698
|
+
current_phase: "executing",
|
|
2699
|
+
owner_codex_thread_id: "thread-same-thread-no-text",
|
|
2700
|
+
task_description: "Refactor documentation navigation and README examples",
|
|
2701
|
+
});
|
|
2702
|
+
|
|
2703
|
+
const result = await dispatchCodexNativeHook(
|
|
2704
|
+
{
|
|
2705
|
+
hook_event_name: "Stop",
|
|
2706
|
+
cwd,
|
|
2707
|
+
thread_id: "thread-same-thread-no-text",
|
|
2708
|
+
},
|
|
2709
|
+
{ cwd },
|
|
2710
|
+
);
|
|
2711
|
+
|
|
2712
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
2713
|
+
assert.equal(result.outputJson?.stopReason, "ralph_executing");
|
|
2714
|
+
} finally {
|
|
2715
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
|
|
2719
|
+
it("blocks no-text global Ralph Stop continuation for live-risk tasks", async () => {
|
|
2720
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-live-no-text-"));
|
|
2721
|
+
try {
|
|
2722
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2723
|
+
active: true,
|
|
2724
|
+
mode: "ralph",
|
|
2725
|
+
current_phase: "executing",
|
|
2726
|
+
owner_codex_thread_id: "thread-live-no-text",
|
|
2727
|
+
task_description: "Deploy billing migration rollback guard for production payments",
|
|
2728
|
+
});
|
|
2729
|
+
|
|
2730
|
+
const result = await dispatchCodexNativeHook(
|
|
2731
|
+
{
|
|
2732
|
+
hook_event_name: "Stop",
|
|
2733
|
+
cwd,
|
|
2734
|
+
thread_id: "thread-live-no-text",
|
|
2735
|
+
},
|
|
2736
|
+
{ cwd },
|
|
2737
|
+
);
|
|
2738
|
+
|
|
2739
|
+
assert.equal(result.outputJson?.stopReason, undefined);
|
|
2740
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2741
|
+
} finally {
|
|
2742
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2743
|
+
}
|
|
2744
|
+
});
|
|
2745
|
+
|
|
2746
|
+
it("blocks no-text global Ralph Stop continuation for issue 2974 operational live-risk tasks", async () => {
|
|
2747
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-stop-issue-2974-live-no-text-"));
|
|
2748
|
+
try {
|
|
2749
|
+
await writeJson(join(cwd, ".omx", "state", "ralph-state.json"), {
|
|
2750
|
+
active: true,
|
|
2751
|
+
mode: "ralph",
|
|
2752
|
+
current_phase: "executing",
|
|
2753
|
+
task_description: "Implement Telegram assistant GPT switch plan on VPS service with cron restart send notify workflow",
|
|
2754
|
+
});
|
|
2755
|
+
|
|
2756
|
+
const result = await dispatchCodexNativeHook(
|
|
2757
|
+
{
|
|
2758
|
+
hook_event_name: "Stop",
|
|
2759
|
+
cwd,
|
|
2760
|
+
},
|
|
2761
|
+
{ cwd },
|
|
2762
|
+
);
|
|
2763
|
+
|
|
2764
|
+
assert.equal(result.outputJson?.stopReason, undefined);
|
|
2765
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2766
|
+
} finally {
|
|
2767
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2768
|
+
}
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2547
2771
|
it("resolves the Codex owner from ancestry without mistaking codex-native-hook wrappers for Codex", () => {
|
|
2548
2772
|
const commands = new Map<number, string>([
|
|
2549
2773
|
[2100, 'sh -c node "/repo/dist/scripts/codex-native-hook.js"'],
|
|
@@ -3284,8 +3508,8 @@ standardMaxRounds = 15
|
|
|
3284
3508
|
}
|
|
3285
3509
|
});
|
|
3286
3510
|
|
|
3287
|
-
it("
|
|
3288
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-completed-
|
|
3511
|
+
it("does not block ordinary Stop after completed Ultragoal cleanup remains", async () => {
|
|
3512
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-completed-ultragoal-ordinary-stop-"));
|
|
3289
3513
|
try {
|
|
3290
3514
|
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
3291
3515
|
version: 1,
|
|
@@ -3296,17 +3520,103 @@ standardMaxRounds = 15
|
|
|
3296
3520
|
const result = await dispatchCodexNativeHook({
|
|
3297
3521
|
hook_event_name: "Stop",
|
|
3298
3522
|
cwd,
|
|
3299
|
-
session_id: "sess-completed-
|
|
3300
|
-
thread_id: "thread-completed-
|
|
3301
|
-
last_assistant_message: "
|
|
3523
|
+
session_id: "sess-completed-ultragoal-ordinary-stop",
|
|
3524
|
+
thread_id: "thread-completed-ultragoal-ordinary-stop",
|
|
3525
|
+
last_assistant_message: "Implemented the requested fix and verified the focused tests.",
|
|
3526
|
+
}, { cwd });
|
|
3527
|
+
|
|
3528
|
+
const output = JSON.stringify(result.outputJson);
|
|
3529
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
3530
|
+
assert.notEqual(result.outputJson?.stopReason, "completed_codex_goal_cleanup_required");
|
|
3531
|
+
assert.doesNotMatch(output, /run \/goal clear/);
|
|
3532
|
+
} finally {
|
|
3533
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
|
|
3537
|
+
it("does not treat explanatory create_goal warnings as completed-goal cleanup attempts", async () => {
|
|
3538
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-completed-goal-explainer-stop-"));
|
|
3539
|
+
try {
|
|
3540
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
3541
|
+
version: 1,
|
|
3542
|
+
aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z" },
|
|
3543
|
+
goals: [{ id: "G001-done", status: "complete", objective: "Done" }],
|
|
3544
|
+
});
|
|
3545
|
+
|
|
3546
|
+
for (const [index, last_assistant_message] of [
|
|
3547
|
+
"Do not call create_goal until the user has explicitly cleared the old goal state.",
|
|
3548
|
+
"No create_goal attempt was made; this is only the final summary.",
|
|
3549
|
+
"I am not calling create_goal; the completed work is summarized above.",
|
|
3550
|
+
"I am not starting another goal; cleanup is already documented.",
|
|
3551
|
+
"Do not start another goal until cleanup is explicit.",
|
|
3552
|
+
"Do not create a new ultragoal; this is only a final summary.",
|
|
3553
|
+
].entries()) {
|
|
3554
|
+
const result = await dispatchCodexNativeHook({
|
|
3555
|
+
hook_event_name: "Stop",
|
|
3556
|
+
cwd,
|
|
3557
|
+
session_id: `sess-completed-goal-explainer-stop-${index}`,
|
|
3558
|
+
thread_id: `thread-completed-goal-explainer-stop-${index}`,
|
|
3559
|
+
last_assistant_message,
|
|
3560
|
+
}, { cwd });
|
|
3561
|
+
|
|
3562
|
+
const output = JSON.stringify(result.outputJson);
|
|
3563
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
3564
|
+
assert.notEqual(result.outputJson?.stopReason, "completed_codex_goal_cleanup_required");
|
|
3565
|
+
assert.doesNotMatch(output, /run \/goal clear/);
|
|
3566
|
+
}
|
|
3567
|
+
} finally {
|
|
3568
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3569
|
+
}
|
|
3570
|
+
});
|
|
3571
|
+
|
|
3572
|
+
it("blocks explicit create_goal attempts when fresh native-goal cleanup evidence remains", async () => {
|
|
3573
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-completed-goal-mixed-stop-"));
|
|
3574
|
+
try {
|
|
3575
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
3576
|
+
version: 1,
|
|
3577
|
+
aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z" },
|
|
3578
|
+
goals: [{ id: "G001-done", status: "complete", objective: "Done" }],
|
|
3579
|
+
});
|
|
3580
|
+
|
|
3581
|
+
const result = await dispatchCodexNativeHook({
|
|
3582
|
+
hook_event_name: "Stop",
|
|
3583
|
+
cwd,
|
|
3584
|
+
session_id: "sess-completed-goal-mixed-stop",
|
|
3585
|
+
thread_id: "thread-completed-goal-mixed-stop",
|
|
3586
|
+
last_user_message: "get_goal reports a completed Codex goal still attached to this thread; do not call create_goal until cleanup is explicit.",
|
|
3587
|
+
last_assistant_message: "I am starting another run now; create_goal payload follows.",
|
|
3302
3588
|
}, { cwd });
|
|
3303
3589
|
|
|
3304
3590
|
const output = JSON.stringify(result.outputJson);
|
|
3305
3591
|
assert.equal(result.outputJson?.decision, "block");
|
|
3306
3592
|
assert.equal(result.outputJson?.stopReason, "completed_codex_goal_cleanup_required");
|
|
3307
3593
|
assert.match(output, /run \/goal clear/);
|
|
3308
|
-
|
|
3309
|
-
|
|
3594
|
+
} finally {
|
|
3595
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3596
|
+
}
|
|
3597
|
+
});
|
|
3598
|
+
|
|
3599
|
+
it("does not block Stop when completed durable history alone precedes another goal", async () => {
|
|
3600
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-completed-goal-stop-"));
|
|
3601
|
+
try {
|
|
3602
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
3603
|
+
version: 1,
|
|
3604
|
+
aggregateCompletion: { status: "complete", completedAt: "2026-05-20T00:00:00.000Z" },
|
|
3605
|
+
goals: [{ id: "G001-done", status: "complete", objective: "Done" }],
|
|
3606
|
+
});
|
|
3607
|
+
|
|
3608
|
+
const result = await dispatchCodexNativeHook({
|
|
3609
|
+
hook_event_name: "Stop",
|
|
3610
|
+
cwd,
|
|
3611
|
+
session_id: "sess-completed-goal-stop",
|
|
3612
|
+
thread_id: "thread-completed-goal-stop",
|
|
3613
|
+
last_assistant_message: "Starting another ultragoal now; create_goal payload follows after /goal clear already cleared native state.",
|
|
3614
|
+
}, { cwd });
|
|
3615
|
+
|
|
3616
|
+
const output = JSON.stringify(result.outputJson);
|
|
3617
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
3618
|
+
assert.notEqual(result.outputJson?.stopReason, "completed_codex_goal_cleanup_required");
|
|
3619
|
+
assert.doesNotMatch(output, /run \/goal clear/);
|
|
3310
3620
|
} finally {
|
|
3311
3621
|
await rm(cwd, { recursive: true, force: true });
|
|
3312
3622
|
}
|
|
@@ -12147,30 +12457,88 @@ exit 0
|
|
|
12147
12457
|
}
|
|
12148
12458
|
});
|
|
12149
12459
|
|
|
12150
|
-
it("does not block
|
|
12151
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-
|
|
12460
|
+
it("does not block Stop after owner-session ralplan CLI completion writes to native canonical state", async () => {
|
|
12461
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-owner-alias-complete-"));
|
|
12462
|
+
const previousSessionId = process.env.OMX_SESSION_ID;
|
|
12152
12463
|
try {
|
|
12153
12464
|
const stateDir = join(cwd, ".omx", "state");
|
|
12154
|
-
|
|
12155
|
-
|
|
12156
|
-
await
|
|
12465
|
+
const nativeSessionId = "native-id";
|
|
12466
|
+
const ownerSessionId = "omx-owner-id";
|
|
12467
|
+
await mkdir(join(stateDir, "sessions", nativeSessionId), { recursive: true });
|
|
12468
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
12469
|
+
session_id: nativeSessionId,
|
|
12470
|
+
native_session_id: nativeSessionId,
|
|
12471
|
+
owner_omx_session_id: ownerSessionId,
|
|
12472
|
+
cwd,
|
|
12473
|
+
});
|
|
12474
|
+
await writeJson(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json"), {
|
|
12157
12475
|
active: true,
|
|
12158
12476
|
skill: "ralplan",
|
|
12159
12477
|
phase: "planning",
|
|
12160
|
-
session_id:
|
|
12161
|
-
active_skills: [{
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12478
|
+
session_id: nativeSessionId,
|
|
12479
|
+
active_skills: [{ skill: "ralplan", phase: "planning", active: true, session_id: nativeSessionId }],
|
|
12480
|
+
});
|
|
12481
|
+
await writeJson(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json"), {
|
|
12482
|
+
active: true,
|
|
12483
|
+
mode: "ralplan",
|
|
12484
|
+
current_phase: "planning",
|
|
12485
|
+
session_id: nativeSessionId,
|
|
12167
12486
|
});
|
|
12487
|
+
process.env.OMX_SESSION_ID = ownerSessionId;
|
|
12168
12488
|
|
|
12169
|
-
const
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12489
|
+
const writeResult = await executeStateOperation("state_write", {
|
|
12490
|
+
mode: "ralplan",
|
|
12491
|
+
active: false,
|
|
12492
|
+
current_phase: "complete",
|
|
12493
|
+
status: "complete",
|
|
12494
|
+
terminal_state: "complete",
|
|
12495
|
+
workingDirectory: cwd,
|
|
12496
|
+
});
|
|
12497
|
+
|
|
12498
|
+
assert.notEqual(writeResult.isError, true);
|
|
12499
|
+
assert.equal(existsSync(join(stateDir, "sessions", ownerSessionId, "ralplan-state.json")), false);
|
|
12500
|
+
const result = await dispatchCodexNativeHook(
|
|
12501
|
+
{
|
|
12502
|
+
hook_event_name: "Stop",
|
|
12503
|
+
cwd,
|
|
12504
|
+
session_id: nativeSessionId,
|
|
12505
|
+
},
|
|
12506
|
+
{ cwd },
|
|
12507
|
+
);
|
|
12508
|
+
|
|
12509
|
+
assert.equal(result.omxEventName, "stop");
|
|
12510
|
+
assert.equal(result.outputJson, null);
|
|
12511
|
+
} finally {
|
|
12512
|
+
if (typeof previousSessionId === "string") process.env.OMX_SESSION_ID = previousSessionId;
|
|
12513
|
+
else delete process.env.OMX_SESSION_ID;
|
|
12514
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12515
|
+
}
|
|
12516
|
+
});
|
|
12517
|
+
|
|
12518
|
+
it("does not block on stale ralplan skill-active state when the matching mode state is absent", async () => {
|
|
12519
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-skill-"));
|
|
12520
|
+
try {
|
|
12521
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12522
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-stale-skill"), { recursive: true });
|
|
12523
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-stale-skill" });
|
|
12524
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-stale-skill", "skill-active-state.json"), {
|
|
12525
|
+
active: true,
|
|
12526
|
+
skill: "ralplan",
|
|
12527
|
+
phase: "planning",
|
|
12528
|
+
session_id: "sess-stop-stale-skill",
|
|
12529
|
+
active_skills: [{
|
|
12530
|
+
skill: "ralplan",
|
|
12531
|
+
phase: "planning",
|
|
12532
|
+
active: true,
|
|
12533
|
+
session_id: "sess-stop-stale-skill",
|
|
12534
|
+
}],
|
|
12535
|
+
});
|
|
12536
|
+
|
|
12537
|
+
const result = await dispatchCodexNativeHook(
|
|
12538
|
+
{
|
|
12539
|
+
hook_event_name: "Stop",
|
|
12540
|
+
cwd,
|
|
12541
|
+
session_id: "sess-stop-stale-skill",
|
|
12174
12542
|
},
|
|
12175
12543
|
{ cwd },
|
|
12176
12544
|
);
|
|
@@ -12200,6 +12568,7 @@ exit 0
|
|
|
12200
12568
|
mode: "ralplan",
|
|
12201
12569
|
current_phase: "complete",
|
|
12202
12570
|
session_id: sessionId,
|
|
12571
|
+
cwd,
|
|
12203
12572
|
});
|
|
12204
12573
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12205
12574
|
active: true,
|
|
@@ -12236,6 +12605,63 @@ exit 0
|
|
|
12236
12605
|
}
|
|
12237
12606
|
});
|
|
12238
12607
|
|
|
12608
|
+
it("keeps blocking current session ralplan when canonical root inactive ralplan state lacks project context", async () => {
|
|
12609
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-canonical-root-no-cwd-"));
|
|
12610
|
+
try {
|
|
12611
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12612
|
+
const sessionId = "sess-stop-ralplan-canonical-root-no-cwd";
|
|
12613
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
12614
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
12615
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
12616
|
+
active: false,
|
|
12617
|
+
skill: "ralplan",
|
|
12618
|
+
phase: "complete",
|
|
12619
|
+
session_id: sessionId,
|
|
12620
|
+
active_skills: [],
|
|
12621
|
+
});
|
|
12622
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
12623
|
+
active: false,
|
|
12624
|
+
mode: "ralplan",
|
|
12625
|
+
current_phase: "complete",
|
|
12626
|
+
session_id: sessionId,
|
|
12627
|
+
});
|
|
12628
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12629
|
+
active: true,
|
|
12630
|
+
skill: "ralplan",
|
|
12631
|
+
phase: "planning",
|
|
12632
|
+
session_id: sessionId,
|
|
12633
|
+
active_skills: [{
|
|
12634
|
+
skill: "ralplan",
|
|
12635
|
+
phase: "planning",
|
|
12636
|
+
active: true,
|
|
12637
|
+
session_id: sessionId,
|
|
12638
|
+
}],
|
|
12639
|
+
});
|
|
12640
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12641
|
+
active: true,
|
|
12642
|
+
mode: "ralplan",
|
|
12643
|
+
current_phase: "planning",
|
|
12644
|
+
session_id: sessionId,
|
|
12645
|
+
cwd,
|
|
12646
|
+
});
|
|
12647
|
+
|
|
12648
|
+
const result = await dispatchCodexNativeHook(
|
|
12649
|
+
{
|
|
12650
|
+
hook_event_name: "Stop",
|
|
12651
|
+
cwd,
|
|
12652
|
+
session_id: sessionId,
|
|
12653
|
+
},
|
|
12654
|
+
{ cwd },
|
|
12655
|
+
);
|
|
12656
|
+
|
|
12657
|
+
assert.equal(result.omxEventName, "stop");
|
|
12658
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
12659
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
12660
|
+
} finally {
|
|
12661
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12662
|
+
}
|
|
12663
|
+
});
|
|
12664
|
+
|
|
12239
12665
|
it("keeps blocking current session ralplan when root inactive ralplan state belongs to another session", async () => {
|
|
12240
12666
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-other-session-"));
|
|
12241
12667
|
try {
|
|
@@ -12346,6 +12772,247 @@ exit 0
|
|
|
12346
12772
|
}
|
|
12347
12773
|
});
|
|
12348
12774
|
|
|
12775
|
+
it("keeps blocking current session ralplan when unscoped root completion lacks a plan artifact", async () => {
|
|
12776
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-session-ralplan-root-no-plan-artifact-"));
|
|
12777
|
+
try {
|
|
12778
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12779
|
+
const sessionId = "sess-stop-unscoped-root-no-plan-artifact";
|
|
12780
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
12781
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
12782
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
12783
|
+
active: false,
|
|
12784
|
+
skill: "ralplan",
|
|
12785
|
+
phase: "complete",
|
|
12786
|
+
active_skills: [],
|
|
12787
|
+
});
|
|
12788
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
12789
|
+
active: false,
|
|
12790
|
+
mode: "ralplan",
|
|
12791
|
+
current_phase: "complete",
|
|
12792
|
+
planning_complete: true,
|
|
12793
|
+
updated_at: "2026-06-21T08:05:00.000Z",
|
|
12794
|
+
});
|
|
12795
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12796
|
+
active: true,
|
|
12797
|
+
skill: "ralplan",
|
|
12798
|
+
phase: "planning",
|
|
12799
|
+
session_id: sessionId,
|
|
12800
|
+
active_skills: [{
|
|
12801
|
+
skill: "ralplan",
|
|
12802
|
+
phase: "planning",
|
|
12803
|
+
active: true,
|
|
12804
|
+
session_id: sessionId,
|
|
12805
|
+
}],
|
|
12806
|
+
});
|
|
12807
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12808
|
+
active: true,
|
|
12809
|
+
mode: "ralplan",
|
|
12810
|
+
current_phase: "planning",
|
|
12811
|
+
session_id: sessionId,
|
|
12812
|
+
updated_at: "2026-06-21T08:00:00.000Z",
|
|
12813
|
+
});
|
|
12814
|
+
|
|
12815
|
+
const result = await dispatchCodexNativeHook(
|
|
12816
|
+
{
|
|
12817
|
+
hook_event_name: "Stop",
|
|
12818
|
+
cwd,
|
|
12819
|
+
session_id: sessionId,
|
|
12820
|
+
},
|
|
12821
|
+
{ cwd },
|
|
12822
|
+
);
|
|
12823
|
+
|
|
12824
|
+
assert.equal(result.omxEventName, "stop");
|
|
12825
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
12826
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
12827
|
+
} finally {
|
|
12828
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12829
|
+
}
|
|
12830
|
+
});
|
|
12831
|
+
|
|
12832
|
+
it("does not loop when newer unscoped root ralplan complete state shadows stale session planning", async () => {
|
|
12833
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-session-ralplan-newer-root-complete-"));
|
|
12834
|
+
try {
|
|
12835
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12836
|
+
const sessionId = "sess-stop-stale-session-ralplan-newer-root";
|
|
12837
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
12838
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
12839
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12840
|
+
active: true,
|
|
12841
|
+
skill: "ralplan",
|
|
12842
|
+
phase: "planning",
|
|
12843
|
+
session_id: sessionId,
|
|
12844
|
+
active_skills: [{
|
|
12845
|
+
skill: "ralplan",
|
|
12846
|
+
phase: "planning",
|
|
12847
|
+
active: true,
|
|
12848
|
+
session_id: sessionId,
|
|
12849
|
+
}],
|
|
12850
|
+
});
|
|
12851
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12852
|
+
active: true,
|
|
12853
|
+
mode: "ralplan",
|
|
12854
|
+
current_phase: "planning",
|
|
12855
|
+
session_id: sessionId,
|
|
12856
|
+
updated_at: "2026-06-21T08:00:00.000Z",
|
|
12857
|
+
});
|
|
12858
|
+
await mkdir(join(cwd, ".omx", "plans"), { recursive: true });
|
|
12859
|
+
await writeFile(join(cwd, ".omx", "plans", "prd-issue-2923.md"), "# PRD\n");
|
|
12860
|
+
await writeFile(join(cwd, ".omx", "plans", "test-spec-issue-2923.md"), "# Test spec\n");
|
|
12861
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
12862
|
+
active: false,
|
|
12863
|
+
skill: "ralplan",
|
|
12864
|
+
phase: "complete",
|
|
12865
|
+
active_skills: [],
|
|
12866
|
+
});
|
|
12867
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
12868
|
+
active: false,
|
|
12869
|
+
mode: "ralplan",
|
|
12870
|
+
current_phase: "complete",
|
|
12871
|
+
cwd,
|
|
12872
|
+
planning_complete: true,
|
|
12873
|
+
latest_plan_path: ".omx/plans/prd-issue-2923.md",
|
|
12874
|
+
updated_at: "2026-06-21T08:05:00.000Z",
|
|
12875
|
+
});
|
|
12876
|
+
|
|
12877
|
+
const result = await dispatchCodexNativeHook(
|
|
12878
|
+
{
|
|
12879
|
+
hook_event_name: "Stop",
|
|
12880
|
+
cwd,
|
|
12881
|
+
session_id: sessionId,
|
|
12882
|
+
tool_name: "apply_patch",
|
|
12883
|
+
},
|
|
12884
|
+
{ cwd },
|
|
12885
|
+
);
|
|
12886
|
+
|
|
12887
|
+
assert.equal(result.omxEventName, "stop");
|
|
12888
|
+
assert.equal(result.outputJson, null);
|
|
12889
|
+
} finally {
|
|
12890
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12891
|
+
}
|
|
12892
|
+
});
|
|
12893
|
+
|
|
12894
|
+
it("keeps blocking current session ralplan when newer unscoped root completion belongs to another worktree", async () => {
|
|
12895
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-cross-worktree-root-complete-"));
|
|
12896
|
+
try {
|
|
12897
|
+
const otherWorktree = join(tmpdir(), "omx-native-hook-other-worktree-2925");
|
|
12898
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12899
|
+
const sessionId = "sess-stop-ralplan-cross-worktree-root";
|
|
12900
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
12901
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
12902
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12903
|
+
active: true,
|
|
12904
|
+
skill: "ralplan",
|
|
12905
|
+
phase: "planning",
|
|
12906
|
+
session_id: sessionId,
|
|
12907
|
+
active_skills: [{
|
|
12908
|
+
skill: "ralplan",
|
|
12909
|
+
phase: "planning",
|
|
12910
|
+
active: true,
|
|
12911
|
+
session_id: sessionId,
|
|
12912
|
+
}],
|
|
12913
|
+
});
|
|
12914
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12915
|
+
active: true,
|
|
12916
|
+
mode: "ralplan",
|
|
12917
|
+
current_phase: "planning",
|
|
12918
|
+
session_id: sessionId,
|
|
12919
|
+
cwd,
|
|
12920
|
+
updated_at: "2026-06-21T08:00:00.000Z",
|
|
12921
|
+
});
|
|
12922
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
12923
|
+
active: false,
|
|
12924
|
+
skill: "ralplan",
|
|
12925
|
+
phase: "complete",
|
|
12926
|
+
active_skills: [],
|
|
12927
|
+
});
|
|
12928
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
12929
|
+
active: false,
|
|
12930
|
+
mode: "ralplan",
|
|
12931
|
+
current_phase: "complete",
|
|
12932
|
+
cwd: otherWorktree,
|
|
12933
|
+
planning_complete: true,
|
|
12934
|
+
latest_plan_path: ".omx/plans/prd-issue-2925.md",
|
|
12935
|
+
updated_at: "2026-06-21T08:05:00.000Z",
|
|
12936
|
+
});
|
|
12937
|
+
|
|
12938
|
+
const result = await dispatchCodexNativeHook(
|
|
12939
|
+
{
|
|
12940
|
+
hook_event_name: "Stop",
|
|
12941
|
+
cwd,
|
|
12942
|
+
session_id: sessionId,
|
|
12943
|
+
tool_name: "apply_patch",
|
|
12944
|
+
},
|
|
12945
|
+
{ cwd },
|
|
12946
|
+
);
|
|
12947
|
+
|
|
12948
|
+
assert.equal(result.omxEventName, "stop");
|
|
12949
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
12950
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
12951
|
+
} finally {
|
|
12952
|
+
await rm(cwd, { recursive: true, force: true });
|
|
12953
|
+
}
|
|
12954
|
+
});
|
|
12955
|
+
|
|
12956
|
+
it("keeps blocking current session ralplan when newer unscoped root completion lacks project context", async () => {
|
|
12957
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-root-complete-no-cwd-"));
|
|
12958
|
+
try {
|
|
12959
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
12960
|
+
const sessionId = "sess-stop-ralplan-root-complete-no-cwd";
|
|
12961
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
12962
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
12963
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12964
|
+
active: true,
|
|
12965
|
+
skill: "ralplan",
|
|
12966
|
+
phase: "planning",
|
|
12967
|
+
session_id: sessionId,
|
|
12968
|
+
active_skills: [{
|
|
12969
|
+
skill: "ralplan",
|
|
12970
|
+
phase: "planning",
|
|
12971
|
+
active: true,
|
|
12972
|
+
session_id: sessionId,
|
|
12973
|
+
}],
|
|
12974
|
+
});
|
|
12975
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
12976
|
+
active: true,
|
|
12977
|
+
mode: "ralplan",
|
|
12978
|
+
current_phase: "planning",
|
|
12979
|
+
session_id: sessionId,
|
|
12980
|
+
cwd,
|
|
12981
|
+
updated_at: "2026-06-21T08:00:00.000Z",
|
|
12982
|
+
});
|
|
12983
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
12984
|
+
active: false,
|
|
12985
|
+
skill: "ralplan",
|
|
12986
|
+
phase: "complete",
|
|
12987
|
+
active_skills: [],
|
|
12988
|
+
});
|
|
12989
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
12990
|
+
active: false,
|
|
12991
|
+
mode: "ralplan",
|
|
12992
|
+
current_phase: "complete",
|
|
12993
|
+
planning_complete: true,
|
|
12994
|
+
latest_plan_path: ".omx/plans/prd-issue-2925.md",
|
|
12995
|
+
updated_at: "2026-06-21T08:05:00.000Z",
|
|
12996
|
+
});
|
|
12997
|
+
|
|
12998
|
+
const result = await dispatchCodexNativeHook(
|
|
12999
|
+
{
|
|
13000
|
+
hook_event_name: "Stop",
|
|
13001
|
+
cwd,
|
|
13002
|
+
session_id: sessionId,
|
|
13003
|
+
tool_name: "apply_patch",
|
|
13004
|
+
},
|
|
13005
|
+
{ cwd },
|
|
13006
|
+
);
|
|
13007
|
+
|
|
13008
|
+
assert.equal(result.omxEventName, "stop");
|
|
13009
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
13010
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
13011
|
+
} finally {
|
|
13012
|
+
await rm(cwd, { recursive: true, force: true });
|
|
13013
|
+
}
|
|
13014
|
+
});
|
|
13015
|
+
|
|
12349
13016
|
it("does not block stale session ralplan when root ralplan is terminal and another root skill is active", async () => {
|
|
12350
13017
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-ralplan-other-root-skill-"));
|
|
12351
13018
|
try {
|
|
@@ -12370,6 +13037,7 @@ exit 0
|
|
|
12370
13037
|
mode: "ralplan",
|
|
12371
13038
|
current_phase: "complete",
|
|
12372
13039
|
session_id: sessionId,
|
|
13040
|
+
cwd,
|
|
12373
13041
|
});
|
|
12374
13042
|
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
12375
13043
|
active: true,
|