oh-my-codex 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +8 -1
- package/crates/omx-api/src/lib.rs +3 -3
- package/crates/omx-api/tests/cli.rs +4 -4
- package/crates/omx-explore/src/main.rs +4 -4
- package/crates/omx-sparkshell/src/codex_bridge.rs +2 -2
- package/dist/agents/__tests__/definitions.test.js +3 -3
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +71 -42
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +3 -3
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -2
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +7 -5
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.js +19 -12
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +151 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts.map +1 -1
- package/dist/autopilot/planner-routing.js +4 -2
- package/dist/autopilot/planner-routing.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +38 -2
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts +2 -0
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +8 -0
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/capabilities/__tests__/lockfile.test.d.ts +2 -0
- package/dist/capabilities/__tests__/lockfile.test.d.ts.map +1 -0
- package/dist/capabilities/__tests__/lockfile.test.js +115 -0
- package/dist/capabilities/__tests__/lockfile.test.js.map +1 -0
- package/dist/capabilities/lockfile.d.ts +102 -0
- package/dist/capabilities/lockfile.d.ts.map +1 -0
- package/dist/capabilities/lockfile.js +334 -0
- package/dist/capabilities/lockfile.js.map +1 -0
- package/dist/cli/__tests__/agents.test.js +5 -5
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/capabilities.test.d.ts +2 -0
- package/dist/cli/__tests__/capabilities.test.d.ts.map +1 -0
- package/dist/cli/__tests__/capabilities.test.js +135 -0
- package/dist/cli/__tests__/capabilities.test.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +237 -6
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +9 -9
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +1 -1
- package/dist/cli/__tests__/doctor-spark-routing.test.js +22 -6
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +53 -18
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mission.test.d.ts +2 -0
- package/dist/cli/__tests__/mission.test.d.ts.map +1 -0
- package/dist/cli/__tests__/mission.test.js +239 -0
- package/dist/cli/__tests__/mission.test.js.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +3 -3
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +95 -6
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +67 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +46 -18
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +3 -3
- package/dist/cli/__tests__/setup-refresh.test.js +29 -7
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +1 -1
- package/dist/cli/__tests__/uninstall.test.js +4 -4
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/agents.js +1 -1
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/capabilities.d.ts +3 -0
- package/dist/cli/capabilities.d.ts.map +1 -0
- package/dist/cli/capabilities.js +118 -0
- package/dist/cli/capabilities.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +7 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +107 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mission.d.ts +36 -0
- package/dist/cli/mission.d.ts.map +1 -0
- package/dist/cli/mission.js +443 -0
- package/dist/cli/mission.js.map +1 -0
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +31 -0
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +7 -4
- package/dist/cli/setup.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +23 -23
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +9 -9
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +51 -26
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/models.d.ts +16 -6
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +19 -8
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +12 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +2 -2
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +2 -2
- package/dist/hud/__tests__/state.test.js +31 -6
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +7 -3
- package/dist/hud/state.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +108 -1
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +26 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +170 -0
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +32 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +4 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +6 -15
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/base.d.ts +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +27 -1
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +2 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +1 -0
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.d.ts +5 -1
- package/dist/pipeline/stages/ultragoal.d.ts.map +1 -1
- package/dist/pipeline/stages/ultragoal.js +14 -4
- package/dist/pipeline/stages/ultragoal.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +193 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +132 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.js +24 -3
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +17 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +97 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +762 -20
- 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 +306 -22
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/generate-catalog-docs.js +1 -0
- package/dist/scripts/generate-catalog-docs.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +737 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +59 -0
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +14 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +80 -17
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.js +3 -3
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/terminal-normalization.d.ts +8 -0
- package/dist/state/terminal-normalization.d.ts.map +1 -0
- package/dist/state/terminal-normalization.js +127 -0
- package/dist/state/terminal-normalization.js.map +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +3 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/delegation-policy.test.js +2 -2
- package/dist/team/__tests__/delegation-policy.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +25 -25
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +28 -28
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +7 -7
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js +5 -5
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +12 -12
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +57 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +3 -3
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +11 -0
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +2 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +6 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +82 -5
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +5 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +111 -14
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +7 -7
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts +2 -0
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts.map +1 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js +118 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js.map +1 -0
- package/dist/utils/worktree-tool-context.d.ts +25 -0
- package/dist/utils/worktree-tool-context.d.ts.map +1 -0
- package/dist/utils/worktree-tool-context.js +116 -0
- package/dist/utils/worktree-tool-context.js.map +1 -0
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +96 -10
- package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +2 -2
- package/prompts/prometheus-strict-metis.md +1 -1
- package/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/skills/code-review/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +1 -1
- package/skills/omx-setup/SKILL.md +5 -5
- package/skills/prometheus-strict/SKILL.md +3 -3
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/team/SKILL.md +1 -1
- package/skills/ultrawork/references/agent-tiers.md +2 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +824 -7
- package/src/scripts/codex-native-hook.ts +347 -20
- package/src/scripts/generate-catalog-docs.ts +1 -0
|
@@ -1819,6 +1819,193 @@ PY`,
|
|
|
1819
1819
|
}
|
|
1820
1820
|
});
|
|
1821
1821
|
|
|
1822
|
+
it("adds resume-by-id instructions for persisted subagents on SessionStart resume", async () => {
|
|
1823
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-reopen-"));
|
|
1824
|
+
try {
|
|
1825
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1826
|
+
const sessionId = "omx-reopen-session";
|
|
1827
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reopen", pid: process.pid });
|
|
1828
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1829
|
+
schemaVersion: 1,
|
|
1830
|
+
sessions: {
|
|
1831
|
+
[sessionId]: {
|
|
1832
|
+
session_id: sessionId,
|
|
1833
|
+
leader_thread_id: "codex-leader-reopen",
|
|
1834
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1835
|
+
threads: {
|
|
1836
|
+
"codex-leader-reopen": {
|
|
1837
|
+
thread_id: "codex-leader-reopen",
|
|
1838
|
+
kind: "leader",
|
|
1839
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1840
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1841
|
+
turn_count: 1,
|
|
1842
|
+
},
|
|
1843
|
+
"thread-architect-reopen": {
|
|
1844
|
+
thread_id: "thread-architect-reopen",
|
|
1845
|
+
kind: "subagent",
|
|
1846
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1847
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1848
|
+
turn_count: 2,
|
|
1849
|
+
role: "architect",
|
|
1850
|
+
lane_id: "plan-review",
|
|
1851
|
+
scope: "SessionStart reopen",
|
|
1852
|
+
status: "available",
|
|
1853
|
+
last_handoff_summary: "reviewed the restart plan",
|
|
1854
|
+
},
|
|
1855
|
+
},
|
|
1856
|
+
},
|
|
1857
|
+
},
|
|
1858
|
+
});
|
|
1859
|
+
|
|
1860
|
+
const result = await dispatchCodexNativeHook(
|
|
1861
|
+
{
|
|
1862
|
+
hook_event_name: "SessionStart",
|
|
1863
|
+
cwd,
|
|
1864
|
+
session_id: "codex-leader-reopen",
|
|
1865
|
+
source: "resume",
|
|
1866
|
+
},
|
|
1867
|
+
{ cwd, sessionOwnerPid: process.pid },
|
|
1868
|
+
);
|
|
1869
|
+
|
|
1870
|
+
const additionalContext = String(
|
|
1871
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1872
|
+
);
|
|
1873
|
+
assert.match(additionalContext, /\[Persisted subagent reopen\]/);
|
|
1874
|
+
assert.match(additionalContext, /resume_agent\("thread-architect-reopen"\)/);
|
|
1875
|
+
assert.match(additionalContext, /role: architect; lane: plan-review; scope: SessionStart reopen; status: available/);
|
|
1876
|
+
assert.match(additionalContext, /saved subagent ids found: 1/);
|
|
1877
|
+
|
|
1878
|
+
const tracking = JSON.parse(await readFile(join(stateDir, "subagent-tracking.json"), "utf-8")) as {
|
|
1879
|
+
sessions?: Record<string, { threads?: Record<string, { resume_requested_at?: string }> }>;
|
|
1880
|
+
};
|
|
1881
|
+
assert.match(
|
|
1882
|
+
tracking.sessions?.[sessionId]?.threads?.["thread-architect-reopen"]?.resume_requested_at ?? "",
|
|
1883
|
+
/^\d{4}-\d{2}-\d{2}T/,
|
|
1884
|
+
);
|
|
1885
|
+
} finally {
|
|
1886
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1887
|
+
}
|
|
1888
|
+
});
|
|
1889
|
+
|
|
1890
|
+
it("does not suggest duplicate same-role subagent spawns when reopen ids exist", async () => {
|
|
1891
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-no-duplicates-"));
|
|
1892
|
+
try {
|
|
1893
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1894
|
+
const sessionId = "omx-reuse-session";
|
|
1895
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-reuse", pid: process.pid });
|
|
1896
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1897
|
+
schemaVersion: 1,
|
|
1898
|
+
sessions: {
|
|
1899
|
+
[sessionId]: {
|
|
1900
|
+
session_id: sessionId,
|
|
1901
|
+
leader_thread_id: "codex-leader-reuse",
|
|
1902
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1903
|
+
threads: {
|
|
1904
|
+
"codex-leader-reuse": {
|
|
1905
|
+
thread_id: "codex-leader-reuse",
|
|
1906
|
+
kind: "leader",
|
|
1907
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1908
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1909
|
+
turn_count: 1,
|
|
1910
|
+
},
|
|
1911
|
+
"thread-critic-reuse": {
|
|
1912
|
+
thread_id: "thread-critic-reuse",
|
|
1913
|
+
kind: "subagent",
|
|
1914
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1915
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1916
|
+
turn_count: 1,
|
|
1917
|
+
role: "critic",
|
|
1918
|
+
lane_id: "risk-review",
|
|
1919
|
+
status: "closed",
|
|
1920
|
+
},
|
|
1921
|
+
},
|
|
1922
|
+
},
|
|
1923
|
+
},
|
|
1924
|
+
});
|
|
1925
|
+
|
|
1926
|
+
const result = await dispatchCodexNativeHook(
|
|
1927
|
+
{
|
|
1928
|
+
hook_event_name: "SessionStart",
|
|
1929
|
+
cwd,
|
|
1930
|
+
session_id: "codex-leader-reuse",
|
|
1931
|
+
source: "startup",
|
|
1932
|
+
},
|
|
1933
|
+
{ cwd, sessionOwnerPid: process.pid },
|
|
1934
|
+
);
|
|
1935
|
+
|
|
1936
|
+
const additionalContext = String(
|
|
1937
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1938
|
+
);
|
|
1939
|
+
assert.match(additionalContext, /resume_agent\("thread-critic-reuse"\)/);
|
|
1940
|
+
assert.match(additionalContext, /avoid duplicate same-type subagent spawns/);
|
|
1941
|
+
assert.match(additionalContext, /do not spawn a new agent solely because reopen failed/);
|
|
1942
|
+
assert.doesNotMatch(additionalContext, /spawn.*critic.*replacement/i);
|
|
1943
|
+
} finally {
|
|
1944
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1948
|
+
it("surfaces clear warnings for unavailable persisted subagents without spawning replacements", async () => {
|
|
1949
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-warning-"));
|
|
1950
|
+
try {
|
|
1951
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1952
|
+
const sessionId = "omx-warning-session";
|
|
1953
|
+
await writeSessionStart(cwd, sessionId, { nativeSessionId: "codex-leader-warning", pid: process.pid });
|
|
1954
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
1955
|
+
schemaVersion: 1,
|
|
1956
|
+
sessions: {
|
|
1957
|
+
[sessionId]: {
|
|
1958
|
+
session_id: sessionId,
|
|
1959
|
+
leader_thread_id: "codex-leader-warning",
|
|
1960
|
+
updated_at: "2026-07-09T00:00:00.000Z",
|
|
1961
|
+
threads: {
|
|
1962
|
+
"codex-leader-warning": {
|
|
1963
|
+
thread_id: "codex-leader-warning",
|
|
1964
|
+
kind: "leader",
|
|
1965
|
+
first_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1966
|
+
last_seen_at: "2026-07-09T00:00:00.000Z",
|
|
1967
|
+
turn_count: 1,
|
|
1968
|
+
},
|
|
1969
|
+
"thread-executor-unavailable": {
|
|
1970
|
+
thread_id: "thread-executor-unavailable",
|
|
1971
|
+
kind: "subagent",
|
|
1972
|
+
first_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1973
|
+
last_seen_at: "2026-07-09T00:01:00.000Z",
|
|
1974
|
+
turn_count: 1,
|
|
1975
|
+
role: "executor",
|
|
1976
|
+
lane_id: "implementation",
|
|
1977
|
+
status: "unavailable",
|
|
1978
|
+
resume_failed_at: "2026-07-09T00:02:00.000Z",
|
|
1979
|
+
resume_failure_reason: "Codex reported missing thread id",
|
|
1980
|
+
},
|
|
1981
|
+
},
|
|
1982
|
+
},
|
|
1983
|
+
},
|
|
1984
|
+
});
|
|
1985
|
+
|
|
1986
|
+
const result = await dispatchCodexNativeHook(
|
|
1987
|
+
{
|
|
1988
|
+
hook_event_name: "SessionStart",
|
|
1989
|
+
cwd,
|
|
1990
|
+
session_id: "codex-leader-warning",
|
|
1991
|
+
source: "resume",
|
|
1992
|
+
},
|
|
1993
|
+
{ cwd, sessionOwnerPid: process.pid },
|
|
1994
|
+
);
|
|
1995
|
+
|
|
1996
|
+
const additionalContext = String(
|
|
1997
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext ?? "",
|
|
1998
|
+
);
|
|
1999
|
+
assert.match(additionalContext, /No compatible saved subagent id is currently marked reopenable/);
|
|
2000
|
+
assert.match(additionalContext, /Reopen warnings:/);
|
|
2001
|
+
assert.match(additionalContext, /thread-executor-unavailable/);
|
|
2002
|
+
assert.match(additionalContext, /last failure: Codex reported missing thread id/);
|
|
2003
|
+
assert.doesNotMatch(additionalContext, /resume_agent\("thread-executor-unavailable"\)/);
|
|
2004
|
+
} finally {
|
|
2005
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
|
|
1822
2009
|
it("preserves canonical OMX session scope when native SessionStart arrives with a different id", async () => {
|
|
1823
2010
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-reconcile-"));
|
|
1824
2011
|
try {
|
|
@@ -5039,6 +5226,82 @@ standardMaxRounds = 15
|
|
|
5039
5226
|
}
|
|
5040
5227
|
});
|
|
5041
5228
|
|
|
5229
|
+
it("keeps conductor guidance on autopilot activation after capacity-only native subagent evidence", async () => {
|
|
5230
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-capacity-native-"));
|
|
5231
|
+
try {
|
|
5232
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
5233
|
+
await dispatchCodexNativeHook(
|
|
5234
|
+
{
|
|
5235
|
+
hook_event_name: "PostToolUse",
|
|
5236
|
+
cwd,
|
|
5237
|
+
session_id: "sess-autopilot-capacity-native",
|
|
5238
|
+
thread_id: "thread-autopilot-capacity-native",
|
|
5239
|
+
turn_id: "turn-autopilot-capacity-native-spawn",
|
|
5240
|
+
tool_name: "multi_agent_v1.spawn_agent",
|
|
5241
|
+
tool_response: { error: "collab spawn failed: agent thread limit reached" },
|
|
5242
|
+
},
|
|
5243
|
+
{ cwd },
|
|
5244
|
+
);
|
|
5245
|
+
|
|
5246
|
+
const result = await dispatchCodexNativeHook(
|
|
5247
|
+
{
|
|
5248
|
+
hook_event_name: "UserPromptSubmit",
|
|
5249
|
+
cwd,
|
|
5250
|
+
session_id: "sess-autopilot-capacity-native",
|
|
5251
|
+
thread_id: "thread-autopilot-capacity-native",
|
|
5252
|
+
turn_id: "turn-autopilot-capacity-native-prompt",
|
|
5253
|
+
prompt: "$autopilot implement issue #3078",
|
|
5254
|
+
},
|
|
5255
|
+
{ cwd },
|
|
5256
|
+
);
|
|
5257
|
+
|
|
5258
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
5259
|
+
assert.equal(result.skillState?.skill, "autopilot");
|
|
5260
|
+
const message = String(
|
|
5261
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
5262
|
+
);
|
|
5263
|
+
assert.match(message, /Autopilot protocol:/);
|
|
5264
|
+
assert.match(message, /Conductor mode contract:/);
|
|
5265
|
+
assert.match(message, /Golden Rule: When the Main agent is acting in Conductor mode/);
|
|
5266
|
+
assert.match(message, /Conductor reuse and ledger guidance:/);
|
|
5267
|
+
assert.doesNotMatch(message, /Native subagent support is unavailable in this environment/);
|
|
5268
|
+
assert.doesNotMatch(message, /Reason: agent_thread_limit_reached/);
|
|
5269
|
+
} finally {
|
|
5270
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5271
|
+
}
|
|
5272
|
+
});
|
|
5273
|
+
|
|
5274
|
+
it("omits conductor block and emits unsupported native guidance for unsupported autopilot first-run payload", async () => {
|
|
5275
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-unsupported-native-"));
|
|
5276
|
+
try {
|
|
5277
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
5278
|
+
const result = await dispatchCodexNativeHook(
|
|
5279
|
+
{
|
|
5280
|
+
hook_event_name: "UserPromptSubmit",
|
|
5281
|
+
cwd,
|
|
5282
|
+
session_id: "sess-autopilot-unsupported-native",
|
|
5283
|
+
thread_id: "thread-autopilot-unsupported-native",
|
|
5284
|
+
turn_id: "turn-autopilot-unsupported-native",
|
|
5285
|
+
prompt: "$autopilot implement issue #3078",
|
|
5286
|
+
capabilities: { native_subagents: false, multi_agent_v1: false },
|
|
5287
|
+
},
|
|
5288
|
+
{ cwd },
|
|
5289
|
+
);
|
|
5290
|
+
|
|
5291
|
+
const message = String(
|
|
5292
|
+
(result.outputJson as { hookSpecificOutput?: { additionalContext?: string } })?.hookSpecificOutput?.additionalContext || "",
|
|
5293
|
+
);
|
|
5294
|
+
assert.match(message, /Autopilot protocol:/);
|
|
5295
|
+
assert.doesNotMatch(message, /Conductor mode contract:/);
|
|
5296
|
+
assert.doesNotMatch(message, /Conductor reuse and ledger guidance:/);
|
|
5297
|
+
assert.match(message, /Native subagent support is unavailable in this environment/);
|
|
5298
|
+
assert.match(message, /Reason: native_subagents_unsupported/);
|
|
5299
|
+
assert.match(message, /Do not call multi_agent_v1\.close_agent/);
|
|
5300
|
+
} finally {
|
|
5301
|
+
await rm(cwd, { recursive: true, force: true });
|
|
5302
|
+
}
|
|
5303
|
+
});
|
|
5304
|
+
|
|
5042
5305
|
it("records ultragoal prompt skill activation with goal-tool handoff guidance", async () => {
|
|
5043
5306
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-"));
|
|
5044
5307
|
try {
|
|
@@ -7253,6 +7516,79 @@ exit 0
|
|
|
7253
7516
|
}
|
|
7254
7517
|
});
|
|
7255
7518
|
|
|
7519
|
+
it("does not self-lock PreToolUse after completed deep-interview handoff leaves Autopilot in deep-interview phase", async () => {
|
|
7520
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-completed-autopilot-"));
|
|
7521
|
+
try {
|
|
7522
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
7523
|
+
const sessionId = "sess-di-completed-autopilot";
|
|
7524
|
+
const threadId = "thread-di-completed-autopilot";
|
|
7525
|
+
const sessionDir = join(stateDir, "sessions", sessionId);
|
|
7526
|
+
await mkdir(sessionDir, { recursive: true });
|
|
7527
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
|
|
7528
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
7529
|
+
version: 1,
|
|
7530
|
+
active: true,
|
|
7531
|
+
skill: "autopilot",
|
|
7532
|
+
keyword: "$autopilot",
|
|
7533
|
+
phase: "deep-interview",
|
|
7534
|
+
initialized_mode: "autopilot",
|
|
7535
|
+
initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`,
|
|
7536
|
+
session_id: sessionId,
|
|
7537
|
+
thread_id: threadId,
|
|
7538
|
+
active_skills: [{ skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId }],
|
|
7539
|
+
});
|
|
7540
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
7541
|
+
active: false,
|
|
7542
|
+
mode: "deep-interview",
|
|
7543
|
+
current_phase: "completed",
|
|
7544
|
+
session_id: sessionId,
|
|
7545
|
+
thread_id: threadId,
|
|
7546
|
+
deep_interview_gate: {
|
|
7547
|
+
status: "complete",
|
|
7548
|
+
handoff_summary: "Requirements were clarified before the Autopilot handoff.",
|
|
7549
|
+
},
|
|
7550
|
+
});
|
|
7551
|
+
await writeJson(join(sessionDir, "autopilot-state.json"), {
|
|
7552
|
+
active: true,
|
|
7553
|
+
mode: "autopilot",
|
|
7554
|
+
current_phase: "deep-interview",
|
|
7555
|
+
session_id: sessionId,
|
|
7556
|
+
thread_id: threadId,
|
|
7557
|
+
});
|
|
7558
|
+
|
|
7559
|
+
const implementationEdit = await dispatchCodexNativeHook(
|
|
7560
|
+
{
|
|
7561
|
+
hook_event_name: "PreToolUse",
|
|
7562
|
+
cwd,
|
|
7563
|
+
session_id: sessionId,
|
|
7564
|
+
thread_id: threadId,
|
|
7565
|
+
tool_name: "Edit",
|
|
7566
|
+
tool_use_id: "tool-di-completed-autopilot-edit",
|
|
7567
|
+
tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b" },
|
|
7568
|
+
},
|
|
7569
|
+
{ cwd },
|
|
7570
|
+
);
|
|
7571
|
+
assert.equal(implementationEdit.omxEventName, "pre-tool-use");
|
|
7572
|
+
assert.equal(implementationEdit.outputJson, null);
|
|
7573
|
+
|
|
7574
|
+
const stateRepair = await dispatchCodexNativeHook(
|
|
7575
|
+
{
|
|
7576
|
+
hook_event_name: "PreToolUse",
|
|
7577
|
+
cwd,
|
|
7578
|
+
session_id: sessionId,
|
|
7579
|
+
thread_id: threadId,
|
|
7580
|
+
tool_name: "mcp__omx_state__state_write",
|
|
7581
|
+
tool_use_id: "tool-di-completed-autopilot-state-repair",
|
|
7582
|
+
tool_input: { mode: "autopilot", current_phase: "ralplan", active: true },
|
|
7583
|
+
},
|
|
7584
|
+
{ cwd },
|
|
7585
|
+
);
|
|
7586
|
+
assert.equal(stateRepair.outputJson, null);
|
|
7587
|
+
} finally {
|
|
7588
|
+
await rm(cwd, { recursive: true, force: true });
|
|
7589
|
+
}
|
|
7590
|
+
});
|
|
7591
|
+
|
|
7256
7592
|
it("allows deep-interview artifact and state writes while blocking implementation Bash writes", async () => {
|
|
7257
7593
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-artifact-"));
|
|
7258
7594
|
try {
|
|
@@ -7768,6 +8104,70 @@ exit 0
|
|
|
7768
8104
|
);
|
|
7769
8105
|
}
|
|
7770
8106
|
|
|
8107
|
+
const runtimeRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-runtime-root-"));
|
|
8108
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
8109
|
+
const runtimeSessionId = "sess-di-runtime-artifact";
|
|
8110
|
+
const runtimeStateDir = join(runtimeRoot, ".omx", "state");
|
|
8111
|
+
const runtimeSessionDir = join(runtimeStateDir, "sessions", runtimeSessionId);
|
|
8112
|
+
try {
|
|
8113
|
+
process.env.OMX_ROOT = runtimeRoot;
|
|
8114
|
+
await mkdir(runtimeSessionDir, { recursive: true });
|
|
8115
|
+
await writeJson(join(runtimeStateDir, "session.json"), { session_id: runtimeSessionId, cwd });
|
|
8116
|
+
await writeJson(join(runtimeSessionDir, "skill-active-state.json"), {
|
|
8117
|
+
version: 1,
|
|
8118
|
+
active: true,
|
|
8119
|
+
skill: "deep-interview",
|
|
8120
|
+
phase: "planning",
|
|
8121
|
+
session_id: runtimeSessionId,
|
|
8122
|
+
thread_id: threadId,
|
|
8123
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: runtimeSessionId, thread_id: threadId }],
|
|
8124
|
+
});
|
|
8125
|
+
await writeJson(join(runtimeSessionDir, "deep-interview-state.json"), {
|
|
8126
|
+
active: true,
|
|
8127
|
+
mode: "deep-interview",
|
|
8128
|
+
current_phase: "intent-first",
|
|
8129
|
+
session_id: runtimeSessionId,
|
|
8130
|
+
thread_id: threadId,
|
|
8131
|
+
});
|
|
8132
|
+
|
|
8133
|
+
const allowedRuntimeStateWrite = await dispatchCodexNativeHook({
|
|
8134
|
+
hook_event_name: "PreToolUse",
|
|
8135
|
+
cwd,
|
|
8136
|
+
session_id: runtimeSessionId,
|
|
8137
|
+
thread_id: threadId,
|
|
8138
|
+
tool_name: "Write",
|
|
8139
|
+
tool_use_id: "tool-di-runtime-state-write",
|
|
8140
|
+
tool_input: { file_path: join(runtimeSessionDir, "deep-interview-state.json"), content: "{}\n" },
|
|
8141
|
+
}, { cwd });
|
|
8142
|
+
assert.equal(allowedRuntimeStateWrite.outputJson, null);
|
|
8143
|
+
|
|
8144
|
+
const blockedRuntimeStatePeerWrite = await dispatchCodexNativeHook({
|
|
8145
|
+
hook_event_name: "PreToolUse",
|
|
8146
|
+
cwd,
|
|
8147
|
+
session_id: runtimeSessionId,
|
|
8148
|
+
thread_id: threadId,
|
|
8149
|
+
tool_name: "Write",
|
|
8150
|
+
tool_use_id: "tool-di-runtime-peer-state-write",
|
|
8151
|
+
tool_input: { file_path: join(runtimeRoot, ".omx", "state", "sessions", "../outside", "deep-interview-state.json"), content: "{}\n" },
|
|
8152
|
+
}, { cwd });
|
|
8153
|
+
assert.equal((blockedRuntimeStatePeerWrite.outputJson as { decision?: string } | null)?.decision, "block");
|
|
8154
|
+
|
|
8155
|
+
const blockedRuntimeUnrelatedWrite = await dispatchCodexNativeHook({
|
|
8156
|
+
hook_event_name: "PreToolUse",
|
|
8157
|
+
cwd,
|
|
8158
|
+
session_id: runtimeSessionId,
|
|
8159
|
+
thread_id: threadId,
|
|
8160
|
+
tool_name: "Write",
|
|
8161
|
+
tool_use_id: "tool-di-runtime-unrelated-write",
|
|
8162
|
+
tool_input: { file_path: join(runtimeRoot, "unrelated", "deep-interview-state.json"), content: "{}\n" },
|
|
8163
|
+
}, { cwd });
|
|
8164
|
+
assert.equal((blockedRuntimeUnrelatedWrite.outputJson as { decision?: string } | null)?.decision, "block");
|
|
8165
|
+
} finally {
|
|
8166
|
+
if (typeof previousOmxRoot === "string") process.env.OMX_ROOT = previousOmxRoot;
|
|
8167
|
+
else delete process.env.OMX_ROOT;
|
|
8168
|
+
await rm(runtimeRoot, { recursive: true, force: true });
|
|
8169
|
+
}
|
|
8170
|
+
|
|
7771
8171
|
// Cross-mode non-terminal `omx state write` payloads are activations,
|
|
7772
8172
|
// because state_write normalizes them to active=true after the hook.
|
|
7773
8173
|
const blockedStateCliMutation = await preToolUse(
|
|
@@ -11417,23 +11817,55 @@ PY`,
|
|
|
11417
11817
|
assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent");
|
|
11418
11818
|
assert.match(String(blocker.error_summary), /agent thread limit reached/);
|
|
11419
11819
|
assert.ok(Date.parse(String(blocker.expires_at)) > Date.parse(String(blocker.observed_at)));
|
|
11820
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "native-subagent-support.json")), false);
|
|
11420
11821
|
} finally {
|
|
11421
11822
|
await rm(cwd, { recursive: true, force: true });
|
|
11422
11823
|
}
|
|
11423
11824
|
});
|
|
11424
11825
|
|
|
11425
|
-
it("
|
|
11426
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-
|
|
11826
|
+
it("records unsupported native subagent support blocker from spawn_agent PostToolUse output", async () => {
|
|
11827
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-support-record-"));
|
|
11427
11828
|
try {
|
|
11428
|
-
await dispatchCodexNativeHook(
|
|
11829
|
+
const result = await dispatchCodexNativeHook(
|
|
11429
11830
|
{
|
|
11430
11831
|
hook_event_name: "PostToolUse",
|
|
11431
11832
|
cwd,
|
|
11432
|
-
session_id: "sess-subagent-
|
|
11433
|
-
thread_id: "thread-subagent-
|
|
11434
|
-
turn_id: "turn-subagent-
|
|
11833
|
+
session_id: "sess-subagent-support-record",
|
|
11834
|
+
thread_id: "thread-subagent-support-record",
|
|
11835
|
+
turn_id: "turn-subagent-support-record",
|
|
11435
11836
|
tool_name: "multi_agent_v1.spawn_agent",
|
|
11436
|
-
tool_response: "
|
|
11837
|
+
tool_response: { error: "unknown tool: multi_agent_v1.spawn_agent is unavailable" },
|
|
11838
|
+
},
|
|
11839
|
+
{ cwd },
|
|
11840
|
+
);
|
|
11841
|
+
|
|
11842
|
+
assert.equal(result.omxEventName, "post-tool-use");
|
|
11843
|
+
const blocker = JSON.parse(
|
|
11844
|
+
await readFile(join(cwd, ".omx", "state", "native-subagent-support.json"), "utf-8"),
|
|
11845
|
+
) as Record<string, unknown>;
|
|
11846
|
+
assert.equal(blocker.status, "unsupported");
|
|
11847
|
+
assert.equal(blocker.reason, "multi_agent_v1_unavailable");
|
|
11848
|
+
assert.equal(blocker.session_id, "sess-subagent-support-record");
|
|
11849
|
+
assert.equal(blocker.thread_id, "thread-subagent-support-record");
|
|
11850
|
+
assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent");
|
|
11851
|
+
assert.match(String(blocker.evidence), /unknown tool/);
|
|
11852
|
+
} finally {
|
|
11853
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11854
|
+
}
|
|
11855
|
+
});
|
|
11856
|
+
|
|
11857
|
+
it("blocks close_agent cleanup after recent native subagent capacity exhaustion", async () => {
|
|
11858
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-subagent-capacity-close-block-"));
|
|
11859
|
+
try {
|
|
11860
|
+
await dispatchCodexNativeHook(
|
|
11861
|
+
{
|
|
11862
|
+
hook_event_name: "PostToolUse",
|
|
11863
|
+
cwd,
|
|
11864
|
+
session_id: "sess-subagent-capacity-close-block",
|
|
11865
|
+
thread_id: "thread-subagent-capacity-close-block",
|
|
11866
|
+
turn_id: "turn-subagent-capacity-close-block",
|
|
11867
|
+
tool_name: "multi_agent_v1.spawn_agent",
|
|
11868
|
+
tool_response: "collab spawn failed: agent thread limit reached",
|
|
11437
11869
|
},
|
|
11438
11870
|
{ cwd },
|
|
11439
11871
|
);
|
|
@@ -16368,6 +16800,79 @@ PY`,
|
|
|
16368
16800
|
}
|
|
16369
16801
|
});
|
|
16370
16802
|
|
|
16803
|
+
it("clears stale ralplan Stop cache when authoritative state is terminal inactive", async () => {
|
|
16804
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-terminal-cache-"));
|
|
16805
|
+
try {
|
|
16806
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
16807
|
+
const sessionId = "sess-stop-ralplan-terminal-cache";
|
|
16808
|
+
const threadId = "thread-stop-ralplan-terminal-cache";
|
|
16809
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
16810
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
|
|
16811
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
16812
|
+
active: false,
|
|
16813
|
+
skill: "ralplan",
|
|
16814
|
+
phase: "complete",
|
|
16815
|
+
session_id: sessionId,
|
|
16816
|
+
active_skills: [],
|
|
16817
|
+
});
|
|
16818
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
16819
|
+
active: false,
|
|
16820
|
+
mode: "ralplan",
|
|
16821
|
+
current_phase: "complete",
|
|
16822
|
+
status: "complete",
|
|
16823
|
+
run_outcome: "complete",
|
|
16824
|
+
session_id: sessionId,
|
|
16825
|
+
cwd,
|
|
16826
|
+
});
|
|
16827
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
16828
|
+
active: false,
|
|
16829
|
+
skill: "ralplan",
|
|
16830
|
+
phase: "complete",
|
|
16831
|
+
session_id: sessionId,
|
|
16832
|
+
active_skills: [],
|
|
16833
|
+
});
|
|
16834
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
16835
|
+
active: false,
|
|
16836
|
+
mode: "ralplan",
|
|
16837
|
+
current_phase: "complete",
|
|
16838
|
+
status: "complete",
|
|
16839
|
+
run_outcome: "complete",
|
|
16840
|
+
session_id: sessionId,
|
|
16841
|
+
cwd,
|
|
16842
|
+
});
|
|
16843
|
+
await writeJson(join(stateDir, "native-stop-state.json"), {
|
|
16844
|
+
sessions: {
|
|
16845
|
+
[sessionId]: {
|
|
16846
|
+
last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`,
|
|
16847
|
+
updated_at: "2026-07-04T00:00:00.000Z",
|
|
16848
|
+
},
|
|
16849
|
+
[threadId]: {
|
|
16850
|
+
last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`,
|
|
16851
|
+
updated_at: "2026-07-04T00:00:00.000Z",
|
|
16852
|
+
},
|
|
16853
|
+
},
|
|
16854
|
+
});
|
|
16855
|
+
|
|
16856
|
+
const result = await dispatchCodexNativeHook(
|
|
16857
|
+
{
|
|
16858
|
+
hook_event_name: "Stop",
|
|
16859
|
+
cwd,
|
|
16860
|
+
session_id: sessionId,
|
|
16861
|
+
thread_id: threadId,
|
|
16862
|
+
stop_hook_active: true,
|
|
16863
|
+
},
|
|
16864
|
+
{ cwd },
|
|
16865
|
+
);
|
|
16866
|
+
|
|
16867
|
+
assert.equal(result.omxEventName, "stop");
|
|
16868
|
+
assert.equal(result.outputJson, null);
|
|
16869
|
+
const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as { sessions?: Record<string, unknown> };
|
|
16870
|
+
assert.deepEqual(stopState.sessions, {});
|
|
16871
|
+
} finally {
|
|
16872
|
+
await rm(cwd, { recursive: true, force: true });
|
|
16873
|
+
}
|
|
16874
|
+
});
|
|
16875
|
+
|
|
16371
16876
|
it("keeps blocking current session ralplan when canonical root inactive ralplan state lacks project context", async () => {
|
|
16372
16877
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-canonical-root-no-cwd-"));
|
|
16373
16878
|
try {
|
|
@@ -21313,6 +21818,55 @@ PY`,
|
|
|
21313
21818
|
}
|
|
21314
21819
|
});
|
|
21315
21820
|
|
|
21821
|
+
it("blocks unsupported active conductor source edits with native delegation recovery guidance", async () => {
|
|
21822
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unsupported-conductor-source-"));
|
|
21823
|
+
try {
|
|
21824
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
21825
|
+
const sessionId = "sess-unsupported-conductor-source";
|
|
21826
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
21827
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
21828
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
21829
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
21830
|
+
active: true,
|
|
21831
|
+
mode: "ultragoal",
|
|
21832
|
+
current_phase: "planning",
|
|
21833
|
+
session_id: sessionId,
|
|
21834
|
+
});
|
|
21835
|
+
await writeJson(join(stateDir, "native-subagent-support.json"), {
|
|
21836
|
+
schema_version: 1,
|
|
21837
|
+
status: "unsupported",
|
|
21838
|
+
reason: "multi_agent_v1_unavailable",
|
|
21839
|
+
session_id: sessionId,
|
|
21840
|
+
evidence: "unknown tool: multi_agent_v1.spawn_agent",
|
|
21841
|
+
observed_at: new Date().toISOString(),
|
|
21842
|
+
cwd,
|
|
21843
|
+
});
|
|
21844
|
+
|
|
21845
|
+
const result = await dispatchCodexNativeHook(
|
|
21846
|
+
{
|
|
21847
|
+
hook_event_name: "PreToolUse",
|
|
21848
|
+
cwd,
|
|
21849
|
+
session_id: sessionId,
|
|
21850
|
+
thread_id: "thread-unsupported-conductor-source",
|
|
21851
|
+
tool_name: "Write",
|
|
21852
|
+
tool_input: { file_path: "src/runtime.ts", content: "export const value = 1;\n" },
|
|
21853
|
+
},
|
|
21854
|
+
{ cwd },
|
|
21855
|
+
);
|
|
21856
|
+
|
|
21857
|
+
const output = result.outputJson as { decision?: string; hookSpecificOutput?: { additionalContext?: string } } | null;
|
|
21858
|
+
const context = String(output?.hookSpecificOutput?.additionalContext ?? "");
|
|
21859
|
+
assert.equal(output?.decision, "block");
|
|
21860
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: planning\)/);
|
|
21861
|
+
assert.match(context, /Native subagent support is unavailable in this environment/);
|
|
21862
|
+
assert.match(context, /Reason: multi_agent_v1_unavailable/);
|
|
21863
|
+
assert.match(context, /blocked\/cancelled/);
|
|
21864
|
+
assert.match(context, /do not call multi_agent_v1\.close_agent/i);
|
|
21865
|
+
} finally {
|
|
21866
|
+
await rm(cwd, { recursive: true, force: true });
|
|
21867
|
+
}
|
|
21868
|
+
});
|
|
21869
|
+
|
|
21316
21870
|
it("blocks Main-root ralplan writes even when payload has only a typed agent_role", async () => {
|
|
21317
21871
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-agent-role-main-"));
|
|
21318
21872
|
try {
|
|
@@ -21453,6 +22007,200 @@ PY`,
|
|
|
21453
22007
|
}
|
|
21454
22008
|
});
|
|
21455
22009
|
|
|
22010
|
+
it("allows conductor writes from tracked typed native subagents when PreToolUse omits thread_spawn source", async () => {
|
|
22011
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-tracked-subagent-no-source-"));
|
|
22012
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
22013
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
22014
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
22015
|
+
try {
|
|
22016
|
+
process.env.OMX_ROOT = cwd;
|
|
22017
|
+
delete process.env.OMX_STATE_ROOT;
|
|
22018
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22019
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
22020
|
+
const sessionId = "sess-conductor-tracked-subagent-no-source";
|
|
22021
|
+
const leaderThreadId = "thread-conductor-leader-no-source";
|
|
22022
|
+
const childThreadId = "thread-conductor-child-no-source";
|
|
22023
|
+
const nowIso = new Date().toISOString();
|
|
22024
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
22025
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
22026
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
22027
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
22028
|
+
active: true,
|
|
22029
|
+
mode: "ultragoal",
|
|
22030
|
+
current_phase: "executing",
|
|
22031
|
+
session_id: sessionId,
|
|
22032
|
+
});
|
|
22033
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
22034
|
+
schemaVersion: 1,
|
|
22035
|
+
sessions: {
|
|
22036
|
+
[sessionId]: {
|
|
22037
|
+
session_id: sessionId,
|
|
22038
|
+
leader_thread_id: leaderThreadId,
|
|
22039
|
+
updated_at: nowIso,
|
|
22040
|
+
threads: {
|
|
22041
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
22042
|
+
[childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
22043
|
+
},
|
|
22044
|
+
},
|
|
22045
|
+
},
|
|
22046
|
+
});
|
|
22047
|
+
|
|
22048
|
+
const result = await dispatchCodexNativeHook(
|
|
22049
|
+
{
|
|
22050
|
+
hook_event_name: "PreToolUse",
|
|
22051
|
+
cwd,
|
|
22052
|
+
session_id: sessionId,
|
|
22053
|
+
thread_id: childThreadId,
|
|
22054
|
+
agent_role: "executor",
|
|
22055
|
+
tool_name: "apply_patch",
|
|
22056
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
22057
|
+
},
|
|
22058
|
+
{ cwd },
|
|
22059
|
+
);
|
|
22060
|
+
|
|
22061
|
+
assert.equal(result.outputJson, null);
|
|
22062
|
+
} finally {
|
|
22063
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
22064
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
22065
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
22066
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
22067
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22068
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
22069
|
+
await rm(cwd, { recursive: true, force: true });
|
|
22070
|
+
}
|
|
22071
|
+
});
|
|
22072
|
+
|
|
22073
|
+
it("blocks conductor writes when corrupt tracker state labels the leader as a subagent", async () => {
|
|
22074
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-corrupt-leader-subagent-"));
|
|
22075
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
22076
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
22077
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
22078
|
+
try {
|
|
22079
|
+
process.env.OMX_ROOT = cwd;
|
|
22080
|
+
delete process.env.OMX_STATE_ROOT;
|
|
22081
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22082
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
22083
|
+
const sessionId = "sess-conductor-corrupt-leader-subagent";
|
|
22084
|
+
const leaderThreadId = "thread-conductor-corrupt-leader";
|
|
22085
|
+
const nowIso = new Date().toISOString();
|
|
22086
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
22087
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
22088
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
22089
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
22090
|
+
active: true,
|
|
22091
|
+
mode: "ultragoal",
|
|
22092
|
+
current_phase: "executing",
|
|
22093
|
+
session_id: sessionId,
|
|
22094
|
+
});
|
|
22095
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
22096
|
+
schemaVersion: 1,
|
|
22097
|
+
sessions: {
|
|
22098
|
+
[sessionId]: {
|
|
22099
|
+
session_id: sessionId,
|
|
22100
|
+
leader_thread_id: leaderThreadId,
|
|
22101
|
+
updated_at: nowIso,
|
|
22102
|
+
threads: {
|
|
22103
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
22104
|
+
},
|
|
22105
|
+
},
|
|
22106
|
+
},
|
|
22107
|
+
});
|
|
22108
|
+
|
|
22109
|
+
const result = await dispatchCodexNativeHook(
|
|
22110
|
+
{
|
|
22111
|
+
hook_event_name: "PreToolUse",
|
|
22112
|
+
cwd,
|
|
22113
|
+
session_id: sessionId,
|
|
22114
|
+
thread_id: leaderThreadId,
|
|
22115
|
+
agent_role: "executor",
|
|
22116
|
+
tool_name: "apply_patch",
|
|
22117
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
22118
|
+
},
|
|
22119
|
+
{ cwd },
|
|
22120
|
+
);
|
|
22121
|
+
|
|
22122
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
22123
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/);
|
|
22124
|
+
} finally {
|
|
22125
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
22126
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
22127
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
22128
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
22129
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22130
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
22131
|
+
await rm(cwd, { recursive: true, force: true });
|
|
22132
|
+
}
|
|
22133
|
+
});
|
|
22134
|
+
|
|
22135
|
+
it("blocks conductor writes when thread_spawn provenance is attached to the leader thread", async () => {
|
|
22136
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-thread-spawn-leader-"));
|
|
22137
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
22138
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
22139
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
22140
|
+
try {
|
|
22141
|
+
process.env.OMX_ROOT = cwd;
|
|
22142
|
+
delete process.env.OMX_STATE_ROOT;
|
|
22143
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22144
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
22145
|
+
const sessionId = "sess-conductor-thread-spawn-leader";
|
|
22146
|
+
const leaderThreadId = "thread-conductor-thread-spawn-leader";
|
|
22147
|
+
const nowIso = new Date().toISOString();
|
|
22148
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
22149
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
22150
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
22151
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
22152
|
+
active: true,
|
|
22153
|
+
mode: "ultragoal",
|
|
22154
|
+
current_phase: "executing",
|
|
22155
|
+
session_id: sessionId,
|
|
22156
|
+
});
|
|
22157
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
22158
|
+
schemaVersion: 1,
|
|
22159
|
+
sessions: {
|
|
22160
|
+
[sessionId]: {
|
|
22161
|
+
session_id: sessionId,
|
|
22162
|
+
leader_thread_id: leaderThreadId,
|
|
22163
|
+
updated_at: nowIso,
|
|
22164
|
+
threads: {
|
|
22165
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
22166
|
+
},
|
|
22167
|
+
},
|
|
22168
|
+
},
|
|
22169
|
+
});
|
|
22170
|
+
|
|
22171
|
+
const result = await dispatchCodexNativeHook(
|
|
22172
|
+
{
|
|
22173
|
+
hook_event_name: "PreToolUse",
|
|
22174
|
+
cwd,
|
|
22175
|
+
session_id: sessionId,
|
|
22176
|
+
thread_id: leaderThreadId,
|
|
22177
|
+
agent_role: "executor",
|
|
22178
|
+
source: {
|
|
22179
|
+
subagent: {
|
|
22180
|
+
thread_spawn: {
|
|
22181
|
+
parent_thread_id: leaderThreadId,
|
|
22182
|
+
},
|
|
22183
|
+
},
|
|
22184
|
+
},
|
|
22185
|
+
tool_name: "apply_patch",
|
|
22186
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
22187
|
+
},
|
|
22188
|
+
{ cwd },
|
|
22189
|
+
);
|
|
22190
|
+
|
|
22191
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
22192
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/);
|
|
22193
|
+
} finally {
|
|
22194
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
22195
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
22196
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
22197
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
22198
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
22199
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
22200
|
+
await rm(cwd, { recursive: true, force: true });
|
|
22201
|
+
}
|
|
22202
|
+
});
|
|
22203
|
+
|
|
21456
22204
|
it("blocks Main-root ralph conductor source and planning artifact writes while allowing .omx workflow state writes", async () => {
|
|
21457
22205
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-write-"));
|
|
21458
22206
|
try {
|
|
@@ -21544,6 +22292,75 @@ PY`,
|
|
|
21544
22292
|
}
|
|
21545
22293
|
});
|
|
21546
22294
|
|
|
22295
|
+
it("allows structured ultragoal steering cleanup through dynamic nested shell loops", async () => {
|
|
22296
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-steer-cleanup-"));
|
|
22297
|
+
try {
|
|
22298
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
22299
|
+
const sessionId = "sess-ultragoal-steer-cleanup";
|
|
22300
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
22301
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
22302
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
22303
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
22304
|
+
active: true,
|
|
22305
|
+
mode: "ultragoal",
|
|
22306
|
+
current_phase: "planning",
|
|
22307
|
+
session_id: sessionId,
|
|
22308
|
+
});
|
|
22309
|
+
|
|
22310
|
+
const result = await dispatchCodexNativeHook(
|
|
22311
|
+
{
|
|
22312
|
+
hook_event_name: "PreToolUse",
|
|
22313
|
+
cwd,
|
|
22314
|
+
session_id: sessionId,
|
|
22315
|
+
thread_id: "thread-ultragoal-steer-cleanup",
|
|
22316
|
+
tool_name: "Bash",
|
|
22317
|
+
tool_input: {
|
|
22318
|
+
command: `bash -lc 'for goal_id in G001-atomized G002-atomized; do omx ultragoal steer --kind mark_blocked_superseded --target-goal-id "$goal_id" --evidence ".omx/ultragoal cleanup supersedes atomized pseudo-goals." --rationale "Structured steering cleanup keeps durable Ultragoal metadata auditable." --json; done'`,
|
|
22319
|
+
},
|
|
22320
|
+
},
|
|
22321
|
+
{ cwd },
|
|
22322
|
+
);
|
|
22323
|
+
|
|
22324
|
+
assert.equal(result.outputJson, null);
|
|
22325
|
+
} finally {
|
|
22326
|
+
await rm(cwd, { recursive: true, force: true });
|
|
22327
|
+
}
|
|
22328
|
+
});
|
|
22329
|
+
|
|
22330
|
+
it("blocks unsafe dynamic nested shell writes in conductor mode", async () => {
|
|
22331
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-dynamic-shell-write-block-"));
|
|
22332
|
+
try {
|
|
22333
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
22334
|
+
const sessionId = "sess-dynamic-shell-write-block";
|
|
22335
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
22336
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
|
|
22337
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning");
|
|
22338
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
22339
|
+
active: true,
|
|
22340
|
+
mode: "ultragoal",
|
|
22341
|
+
current_phase: "planning",
|
|
22342
|
+
session_id: sessionId,
|
|
22343
|
+
});
|
|
22344
|
+
|
|
22345
|
+
const result = await dispatchCodexNativeHook(
|
|
22346
|
+
{
|
|
22347
|
+
hook_event_name: "PreToolUse",
|
|
22348
|
+
cwd,
|
|
22349
|
+
session_id: sessionId,
|
|
22350
|
+
thread_id: "thread-dynamic-shell-write-block",
|
|
22351
|
+
tool_name: "Bash",
|
|
22352
|
+
tool_input: { command: `bash -lc 'cp "$SOURCE_FILE" src/runtime.ts'` },
|
|
22353
|
+
},
|
|
22354
|
+
{ cwd },
|
|
22355
|
+
);
|
|
22356
|
+
|
|
22357
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
22358
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Bash nested shell execution is dynamic and cannot be validated/);
|
|
22359
|
+
} finally {
|
|
22360
|
+
await rm(cwd, { recursive: true, force: true });
|
|
22361
|
+
}
|
|
22362
|
+
});
|
|
22363
|
+
|
|
21547
22364
|
it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata", async () => {
|
|
21548
22365
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-"));
|
|
21549
22366
|
try {
|