oh-my-codex 0.19.1 → 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 +3 -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__/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__/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__/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 +77 -19
- package/dist/cli/index.js.map +1 -1
- 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/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/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__/runtime.test.js +132 -0
- package/dist/ralplan/__tests__/runtime.test.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 +412 -0
- 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 +238 -12
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +450 -0
- package/dist/state/__tests__/operations.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 +72 -11
- package/dist/state/operations.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 +68 -1
- 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 +41 -9
- 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 +477 -0
- package/src/scripts/codex-native-hook.ts +272 -11
|
@@ -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 {
|
|
@@ -7841,6 +8104,70 @@ exit 0
|
|
|
7841
8104
|
);
|
|
7842
8105
|
}
|
|
7843
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
|
+
|
|
7844
8171
|
// Cross-mode non-terminal `omx state write` payloads are activations,
|
|
7845
8172
|
// because state_write normalizes them to active=true after the hook.
|
|
7846
8173
|
const blockedStateCliMutation = await preToolUse(
|
|
@@ -11490,6 +11817,38 @@ PY`,
|
|
|
11490
11817
|
assert.equal(blocker.tool_name, "multi_agent_v1.spawn_agent");
|
|
11491
11818
|
assert.match(String(blocker.error_summary), /agent thread limit reached/);
|
|
11492
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);
|
|
11821
|
+
} finally {
|
|
11822
|
+
await rm(cwd, { recursive: true, force: true });
|
|
11823
|
+
}
|
|
11824
|
+
});
|
|
11825
|
+
|
|
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-"));
|
|
11828
|
+
try {
|
|
11829
|
+
const result = await dispatchCodexNativeHook(
|
|
11830
|
+
{
|
|
11831
|
+
hook_event_name: "PostToolUse",
|
|
11832
|
+
cwd,
|
|
11833
|
+
session_id: "sess-subagent-support-record",
|
|
11834
|
+
thread_id: "thread-subagent-support-record",
|
|
11835
|
+
turn_id: "turn-subagent-support-record",
|
|
11836
|
+
tool_name: "multi_agent_v1.spawn_agent",
|
|
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/);
|
|
11493
11852
|
} finally {
|
|
11494
11853
|
await rm(cwd, { recursive: true, force: true });
|
|
11495
11854
|
}
|
|
@@ -21459,6 +21818,55 @@ PY`,
|
|
|
21459
21818
|
}
|
|
21460
21819
|
});
|
|
21461
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
|
+
|
|
21462
21870
|
it("blocks Main-root ralplan writes even when payload has only a typed agent_role", async () => {
|
|
21463
21871
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-agent-role-main-"));
|
|
21464
21872
|
try {
|
|
@@ -21884,6 +22292,75 @@ PY`,
|
|
|
21884
22292
|
}
|
|
21885
22293
|
});
|
|
21886
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
|
+
|
|
21887
22364
|
it("blocks common Bash file mutations in Main-root conductor states unless they target workflow metadata", async () => {
|
|
21888
22365
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-bash-mutations-"));
|
|
21889
22366
|
try {
|