oh-my-codex 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +5 -0
- 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__/session-scoped-runtime.test.js +67 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +30 -4
- 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/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/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/ralplan/__tests__/consensus-gate.test.js +193 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.js +24 -3
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +330 -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 +68 -10
- 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 +287 -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/operations.d.ts.map +1 -1
- package/dist/state/operations.js +8 -6
- 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/ultragoal/__tests__/artifacts.test.js +14 -4
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +70 -5
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/src/scripts/__tests__/codex-native-hook.test.ts +340 -0
- package/src/scripts/codex-native-hook.ts +75 -9
- package/src/scripts/generate-catalog-docs.ts +1 -0
|
@@ -7253,6 +7253,79 @@ exit 0
|
|
|
7253
7253
|
}
|
|
7254
7254
|
});
|
|
7255
7255
|
|
|
7256
|
+
it("does not self-lock PreToolUse after completed deep-interview handoff leaves Autopilot in deep-interview phase", async () => {
|
|
7257
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-completed-autopilot-"));
|
|
7258
|
+
try {
|
|
7259
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
7260
|
+
const sessionId = "sess-di-completed-autopilot";
|
|
7261
|
+
const threadId = "thread-di-completed-autopilot";
|
|
7262
|
+
const sessionDir = join(stateDir, "sessions", sessionId);
|
|
7263
|
+
await mkdir(sessionDir, { recursive: true });
|
|
7264
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
|
|
7265
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
7266
|
+
version: 1,
|
|
7267
|
+
active: true,
|
|
7268
|
+
skill: "autopilot",
|
|
7269
|
+
keyword: "$autopilot",
|
|
7270
|
+
phase: "deep-interview",
|
|
7271
|
+
initialized_mode: "autopilot",
|
|
7272
|
+
initialized_state_path: `.omx/state/sessions/${sessionId}/autopilot-state.json`,
|
|
7273
|
+
session_id: sessionId,
|
|
7274
|
+
thread_id: threadId,
|
|
7275
|
+
active_skills: [{ skill: "autopilot", phase: "deep-interview", active: true, session_id: sessionId, thread_id: threadId }],
|
|
7276
|
+
});
|
|
7277
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
7278
|
+
active: false,
|
|
7279
|
+
mode: "deep-interview",
|
|
7280
|
+
current_phase: "completed",
|
|
7281
|
+
session_id: sessionId,
|
|
7282
|
+
thread_id: threadId,
|
|
7283
|
+
deep_interview_gate: {
|
|
7284
|
+
status: "complete",
|
|
7285
|
+
handoff_summary: "Requirements were clarified before the Autopilot handoff.",
|
|
7286
|
+
},
|
|
7287
|
+
});
|
|
7288
|
+
await writeJson(join(sessionDir, "autopilot-state.json"), {
|
|
7289
|
+
active: true,
|
|
7290
|
+
mode: "autopilot",
|
|
7291
|
+
current_phase: "deep-interview",
|
|
7292
|
+
session_id: sessionId,
|
|
7293
|
+
thread_id: threadId,
|
|
7294
|
+
});
|
|
7295
|
+
|
|
7296
|
+
const implementationEdit = await dispatchCodexNativeHook(
|
|
7297
|
+
{
|
|
7298
|
+
hook_event_name: "PreToolUse",
|
|
7299
|
+
cwd,
|
|
7300
|
+
session_id: sessionId,
|
|
7301
|
+
thread_id: threadId,
|
|
7302
|
+
tool_name: "Edit",
|
|
7303
|
+
tool_use_id: "tool-di-completed-autopilot-edit",
|
|
7304
|
+
tool_input: { file_path: "src/runtime.ts", old_string: "a", new_string: "b" },
|
|
7305
|
+
},
|
|
7306
|
+
{ cwd },
|
|
7307
|
+
);
|
|
7308
|
+
assert.equal(implementationEdit.omxEventName, "pre-tool-use");
|
|
7309
|
+
assert.equal(implementationEdit.outputJson, null);
|
|
7310
|
+
|
|
7311
|
+
const stateRepair = await dispatchCodexNativeHook(
|
|
7312
|
+
{
|
|
7313
|
+
hook_event_name: "PreToolUse",
|
|
7314
|
+
cwd,
|
|
7315
|
+
session_id: sessionId,
|
|
7316
|
+
thread_id: threadId,
|
|
7317
|
+
tool_name: "mcp__omx_state__state_write",
|
|
7318
|
+
tool_use_id: "tool-di-completed-autopilot-state-repair",
|
|
7319
|
+
tool_input: { mode: "autopilot", current_phase: "ralplan", active: true },
|
|
7320
|
+
},
|
|
7321
|
+
{ cwd },
|
|
7322
|
+
);
|
|
7323
|
+
assert.equal(stateRepair.outputJson, null);
|
|
7324
|
+
} finally {
|
|
7325
|
+
await rm(cwd, { recursive: true, force: true });
|
|
7326
|
+
}
|
|
7327
|
+
});
|
|
7328
|
+
|
|
7256
7329
|
it("allows deep-interview artifact and state writes while blocking implementation Bash writes", async () => {
|
|
7257
7330
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-pretool-deep-interview-artifact-"));
|
|
7258
7331
|
try {
|
|
@@ -16368,6 +16441,79 @@ PY`,
|
|
|
16368
16441
|
}
|
|
16369
16442
|
});
|
|
16370
16443
|
|
|
16444
|
+
it("clears stale ralplan Stop cache when authoritative state is terminal inactive", async () => {
|
|
16445
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-terminal-cache-"));
|
|
16446
|
+
try {
|
|
16447
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
16448
|
+
const sessionId = "sess-stop-ralplan-terminal-cache";
|
|
16449
|
+
const threadId = "thread-stop-ralplan-terminal-cache";
|
|
16450
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
16451
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, cwd });
|
|
16452
|
+
await writeJson(join(stateDir, "skill-active-state.json"), {
|
|
16453
|
+
active: false,
|
|
16454
|
+
skill: "ralplan",
|
|
16455
|
+
phase: "complete",
|
|
16456
|
+
session_id: sessionId,
|
|
16457
|
+
active_skills: [],
|
|
16458
|
+
});
|
|
16459
|
+
await writeJson(join(stateDir, "ralplan-state.json"), {
|
|
16460
|
+
active: false,
|
|
16461
|
+
mode: "ralplan",
|
|
16462
|
+
current_phase: "complete",
|
|
16463
|
+
status: "complete",
|
|
16464
|
+
run_outcome: "complete",
|
|
16465
|
+
session_id: sessionId,
|
|
16466
|
+
cwd,
|
|
16467
|
+
});
|
|
16468
|
+
await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
|
|
16469
|
+
active: false,
|
|
16470
|
+
skill: "ralplan",
|
|
16471
|
+
phase: "complete",
|
|
16472
|
+
session_id: sessionId,
|
|
16473
|
+
active_skills: [],
|
|
16474
|
+
});
|
|
16475
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
|
|
16476
|
+
active: false,
|
|
16477
|
+
mode: "ralplan",
|
|
16478
|
+
current_phase: "complete",
|
|
16479
|
+
status: "complete",
|
|
16480
|
+
run_outcome: "complete",
|
|
16481
|
+
session_id: sessionId,
|
|
16482
|
+
cwd,
|
|
16483
|
+
});
|
|
16484
|
+
await writeJson(join(stateDir, "native-stop-state.json"), {
|
|
16485
|
+
sessions: {
|
|
16486
|
+
[sessionId]: {
|
|
16487
|
+
last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`,
|
|
16488
|
+
updated_at: "2026-07-04T00:00:00.000Z",
|
|
16489
|
+
},
|
|
16490
|
+
[threadId]: {
|
|
16491
|
+
last_signature: `skill-stop|${sessionId}|${threadId}|no-message|skill_ralplan_planning_continue_artifact`,
|
|
16492
|
+
updated_at: "2026-07-04T00:00:00.000Z",
|
|
16493
|
+
},
|
|
16494
|
+
},
|
|
16495
|
+
});
|
|
16496
|
+
|
|
16497
|
+
const result = await dispatchCodexNativeHook(
|
|
16498
|
+
{
|
|
16499
|
+
hook_event_name: "Stop",
|
|
16500
|
+
cwd,
|
|
16501
|
+
session_id: sessionId,
|
|
16502
|
+
thread_id: threadId,
|
|
16503
|
+
stop_hook_active: true,
|
|
16504
|
+
},
|
|
16505
|
+
{ cwd },
|
|
16506
|
+
);
|
|
16507
|
+
|
|
16508
|
+
assert.equal(result.omxEventName, "stop");
|
|
16509
|
+
assert.equal(result.outputJson, null);
|
|
16510
|
+
const stopState = JSON.parse(await readFile(join(stateDir, "native-stop-state.json"), "utf-8")) as { sessions?: Record<string, unknown> };
|
|
16511
|
+
assert.deepEqual(stopState.sessions, {});
|
|
16512
|
+
} finally {
|
|
16513
|
+
await rm(cwd, { recursive: true, force: true });
|
|
16514
|
+
}
|
|
16515
|
+
});
|
|
16516
|
+
|
|
16371
16517
|
it("keeps blocking current session ralplan when canonical root inactive ralplan state lacks project context", async () => {
|
|
16372
16518
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralplan-canonical-root-no-cwd-"));
|
|
16373
16519
|
try {
|
|
@@ -21453,6 +21599,200 @@ PY`,
|
|
|
21453
21599
|
}
|
|
21454
21600
|
});
|
|
21455
21601
|
|
|
21602
|
+
it("allows conductor writes from tracked typed native subagents when PreToolUse omits thread_spawn source", async () => {
|
|
21603
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-tracked-subagent-no-source-"));
|
|
21604
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
21605
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
21606
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
21607
|
+
try {
|
|
21608
|
+
process.env.OMX_ROOT = cwd;
|
|
21609
|
+
delete process.env.OMX_STATE_ROOT;
|
|
21610
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21611
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
21612
|
+
const sessionId = "sess-conductor-tracked-subagent-no-source";
|
|
21613
|
+
const leaderThreadId = "thread-conductor-leader-no-source";
|
|
21614
|
+
const childThreadId = "thread-conductor-child-no-source";
|
|
21615
|
+
const nowIso = new Date().toISOString();
|
|
21616
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
21617
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
21618
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
21619
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
21620
|
+
active: true,
|
|
21621
|
+
mode: "ultragoal",
|
|
21622
|
+
current_phase: "executing",
|
|
21623
|
+
session_id: sessionId,
|
|
21624
|
+
});
|
|
21625
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
21626
|
+
schemaVersion: 1,
|
|
21627
|
+
sessions: {
|
|
21628
|
+
[sessionId]: {
|
|
21629
|
+
session_id: sessionId,
|
|
21630
|
+
leader_thread_id: leaderThreadId,
|
|
21631
|
+
updated_at: nowIso,
|
|
21632
|
+
threads: {
|
|
21633
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
21634
|
+
[childThreadId]: { thread_id: childThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
21635
|
+
},
|
|
21636
|
+
},
|
|
21637
|
+
},
|
|
21638
|
+
});
|
|
21639
|
+
|
|
21640
|
+
const result = await dispatchCodexNativeHook(
|
|
21641
|
+
{
|
|
21642
|
+
hook_event_name: "PreToolUse",
|
|
21643
|
+
cwd,
|
|
21644
|
+
session_id: sessionId,
|
|
21645
|
+
thread_id: childThreadId,
|
|
21646
|
+
agent_role: "executor",
|
|
21647
|
+
tool_name: "apply_patch",
|
|
21648
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
21649
|
+
},
|
|
21650
|
+
{ cwd },
|
|
21651
|
+
);
|
|
21652
|
+
|
|
21653
|
+
assert.equal(result.outputJson, null);
|
|
21654
|
+
} finally {
|
|
21655
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
21656
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
21657
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
21658
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
21659
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21660
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
21661
|
+
await rm(cwd, { recursive: true, force: true });
|
|
21662
|
+
}
|
|
21663
|
+
});
|
|
21664
|
+
|
|
21665
|
+
it("blocks conductor writes when corrupt tracker state labels the leader as a subagent", async () => {
|
|
21666
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-corrupt-leader-subagent-"));
|
|
21667
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
21668
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
21669
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
21670
|
+
try {
|
|
21671
|
+
process.env.OMX_ROOT = cwd;
|
|
21672
|
+
delete process.env.OMX_STATE_ROOT;
|
|
21673
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21674
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
21675
|
+
const sessionId = "sess-conductor-corrupt-leader-subagent";
|
|
21676
|
+
const leaderThreadId = "thread-conductor-corrupt-leader";
|
|
21677
|
+
const nowIso = new Date().toISOString();
|
|
21678
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
21679
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
21680
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
21681
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
21682
|
+
active: true,
|
|
21683
|
+
mode: "ultragoal",
|
|
21684
|
+
current_phase: "executing",
|
|
21685
|
+
session_id: sessionId,
|
|
21686
|
+
});
|
|
21687
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
21688
|
+
schemaVersion: 1,
|
|
21689
|
+
sessions: {
|
|
21690
|
+
[sessionId]: {
|
|
21691
|
+
session_id: sessionId,
|
|
21692
|
+
leader_thread_id: leaderThreadId,
|
|
21693
|
+
updated_at: nowIso,
|
|
21694
|
+
threads: {
|
|
21695
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "subagent", mode: "executor", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
21696
|
+
},
|
|
21697
|
+
},
|
|
21698
|
+
},
|
|
21699
|
+
});
|
|
21700
|
+
|
|
21701
|
+
const result = await dispatchCodexNativeHook(
|
|
21702
|
+
{
|
|
21703
|
+
hook_event_name: "PreToolUse",
|
|
21704
|
+
cwd,
|
|
21705
|
+
session_id: sessionId,
|
|
21706
|
+
thread_id: leaderThreadId,
|
|
21707
|
+
agent_role: "executor",
|
|
21708
|
+
tool_name: "apply_patch",
|
|
21709
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
21710
|
+
},
|
|
21711
|
+
{ cwd },
|
|
21712
|
+
);
|
|
21713
|
+
|
|
21714
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
21715
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/);
|
|
21716
|
+
} finally {
|
|
21717
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
21718
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
21719
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
21720
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
21721
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21722
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
21723
|
+
await rm(cwd, { recursive: true, force: true });
|
|
21724
|
+
}
|
|
21725
|
+
});
|
|
21726
|
+
|
|
21727
|
+
it("blocks conductor writes when thread_spawn provenance is attached to the leader thread", async () => {
|
|
21728
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-conductor-thread-spawn-leader-"));
|
|
21729
|
+
const originalOmxRoot = process.env.OMX_ROOT;
|
|
21730
|
+
const originalOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
21731
|
+
const originalOmxTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
21732
|
+
try {
|
|
21733
|
+
process.env.OMX_ROOT = cwd;
|
|
21734
|
+
delete process.env.OMX_STATE_ROOT;
|
|
21735
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21736
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
21737
|
+
const sessionId = "sess-conductor-thread-spawn-leader";
|
|
21738
|
+
const leaderThreadId = "thread-conductor-thread-spawn-leader";
|
|
21739
|
+
const nowIso = new Date().toISOString();
|
|
21740
|
+
await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
|
|
21741
|
+
await writeJson(join(stateDir, "session.json"), { session_id: sessionId, native_session_id: leaderThreadId });
|
|
21742
|
+
await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "executing");
|
|
21743
|
+
await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
|
|
21744
|
+
active: true,
|
|
21745
|
+
mode: "ultragoal",
|
|
21746
|
+
current_phase: "executing",
|
|
21747
|
+
session_id: sessionId,
|
|
21748
|
+
});
|
|
21749
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
21750
|
+
schemaVersion: 1,
|
|
21751
|
+
sessions: {
|
|
21752
|
+
[sessionId]: {
|
|
21753
|
+
session_id: sessionId,
|
|
21754
|
+
leader_thread_id: leaderThreadId,
|
|
21755
|
+
updated_at: nowIso,
|
|
21756
|
+
threads: {
|
|
21757
|
+
[leaderThreadId]: { thread_id: leaderThreadId, kind: "leader", first_seen_at: nowIso, last_seen_at: nowIso, turn_count: 1 },
|
|
21758
|
+
},
|
|
21759
|
+
},
|
|
21760
|
+
},
|
|
21761
|
+
});
|
|
21762
|
+
|
|
21763
|
+
const result = await dispatchCodexNativeHook(
|
|
21764
|
+
{
|
|
21765
|
+
hook_event_name: "PreToolUse",
|
|
21766
|
+
cwd,
|
|
21767
|
+
session_id: sessionId,
|
|
21768
|
+
thread_id: leaderThreadId,
|
|
21769
|
+
agent_role: "executor",
|
|
21770
|
+
source: {
|
|
21771
|
+
subagent: {
|
|
21772
|
+
thread_spawn: {
|
|
21773
|
+
parent_thread_id: leaderThreadId,
|
|
21774
|
+
},
|
|
21775
|
+
},
|
|
21776
|
+
},
|
|
21777
|
+
tool_name: "apply_patch",
|
|
21778
|
+
tool_input: { file_path: "configs/ws/kalshi_ws_auth_evidence.json" },
|
|
21779
|
+
},
|
|
21780
|
+
{ cwd },
|
|
21781
|
+
);
|
|
21782
|
+
|
|
21783
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
21784
|
+
assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active \(ultragoal phase: executing\)/);
|
|
21785
|
+
} finally {
|
|
21786
|
+
if (originalOmxRoot === undefined) delete process.env.OMX_ROOT;
|
|
21787
|
+
else process.env.OMX_ROOT = originalOmxRoot;
|
|
21788
|
+
if (originalOmxStateRoot === undefined) delete process.env.OMX_STATE_ROOT;
|
|
21789
|
+
else process.env.OMX_STATE_ROOT = originalOmxStateRoot;
|
|
21790
|
+
if (originalOmxTeamStateRoot === undefined) delete process.env.OMX_TEAM_STATE_ROOT;
|
|
21791
|
+
else process.env.OMX_TEAM_STATE_ROOT = originalOmxTeamStateRoot;
|
|
21792
|
+
await rm(cwd, { recursive: true, force: true });
|
|
21793
|
+
}
|
|
21794
|
+
});
|
|
21795
|
+
|
|
21456
21796
|
it("blocks Main-root ralph conductor source and planning artifact writes while allowing .omx workflow state writes", async () => {
|
|
21457
21797
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralph-conductor-write-"));
|
|
21458
21798
|
try {
|
|
@@ -3361,6 +3361,14 @@ function isActiveDeepInterviewPhase(state: Record<string, unknown> | null): bool
|
|
|
3361
3361
|
return true;
|
|
3362
3362
|
}
|
|
3363
3363
|
|
|
3364
|
+
function isInactiveCompletedDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
|
|
3365
|
+
if (!state || state.active !== false) return false;
|
|
3366
|
+
const mode = safeString(state.mode).trim();
|
|
3367
|
+
if (mode && mode !== "deep-interview") return false;
|
|
3368
|
+
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
3369
|
+
return phase === "complete" || phase === "completed";
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3364
3372
|
function isActiveRalplanPhase(state: Record<string, unknown> | null): boolean {
|
|
3365
3373
|
if (!state || state.active !== true) return false;
|
|
3366
3374
|
const mode = safeString(state.mode).trim();
|
|
@@ -6555,6 +6563,7 @@ async function readActiveDeepInterviewStateForPreToolUse(
|
|
|
6555
6563
|
));
|
|
6556
6564
|
if (hasActiveDeepInterviewSkill) return modeState;
|
|
6557
6565
|
}
|
|
6566
|
+
if (isInactiveCompletedDeepInterviewPhase(modeState)) return null;
|
|
6558
6567
|
|
|
6559
6568
|
const autopilotState = sessionId
|
|
6560
6569
|
? await readStopSessionPinnedState("autopilot-state.json", cwd, sessionId, stateDir)
|
|
@@ -6714,7 +6723,7 @@ async function buildRalplanPreToolUseBoundaryOutput(
|
|
|
6714
6723
|
resolvedSessionId?: string,
|
|
6715
6724
|
): Promise<Record<string, unknown> | null> {
|
|
6716
6725
|
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
6717
|
-
if (await
|
|
6726
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
6718
6727
|
const threadId = readPayloadThreadId(payload);
|
|
6719
6728
|
const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
6720
6729
|
if (!activeState) return null;
|
|
@@ -6782,7 +6791,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
|
6782
6791
|
resolvedSessionId?: string,
|
|
6783
6792
|
): Promise<Record<string, unknown> | null> {
|
|
6784
6793
|
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
6785
|
-
if (await
|
|
6794
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
6786
6795
|
const threadId = readPayloadThreadId(payload);
|
|
6787
6796
|
const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
6788
6797
|
if (!activeState) return null;
|
|
@@ -6950,12 +6959,20 @@ interface ActiveConductorState {
|
|
|
6950
6959
|
phase: string;
|
|
6951
6960
|
}
|
|
6952
6961
|
|
|
6953
|
-
async function
|
|
6962
|
+
async function hasTrustedTypedSubagentProvenanceForPreToolUse(
|
|
6954
6963
|
payload: CodexHookPayload,
|
|
6955
6964
|
cwd: string,
|
|
6956
6965
|
sessionId: string,
|
|
6957
6966
|
): Promise<boolean> {
|
|
6958
6967
|
if (hasTeamWorkerEnvironment()) return true;
|
|
6968
|
+
if (!isTypedAgentRolePayload(payload)) return false;
|
|
6969
|
+
const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
|
|
6970
|
+
const session = trackingState?.sessions?.[sessionId];
|
|
6971
|
+
if (!session) return false;
|
|
6972
|
+
|
|
6973
|
+
const payloadThreadId = readPayloadThreadId(payload);
|
|
6974
|
+
if (payloadThreadId && isTrustedSubagentThread(session, payloadThreadId)) return true;
|
|
6975
|
+
|
|
6959
6976
|
const source = safeObject(payload.source);
|
|
6960
6977
|
const subagent = safeObject(source.subagent);
|
|
6961
6978
|
const threadSpawn = safeObject(subagent.thread_spawn);
|
|
@@ -6966,11 +6983,9 @@ async function hasTrustedTypedSubagentThreadSpawnProvenanceForPreToolUse(
|
|
|
6966
6983
|
?? threadSpawn.leaderThreadId,
|
|
6967
6984
|
).trim();
|
|
6968
6985
|
if (!parentThreadId) return false;
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
if (!session) return false;
|
|
6973
|
-
return session.leader_thread_id === parentThreadId || parentThreadId in session.threads;
|
|
6986
|
+
const leaderThreadId = safeString(session.leader_thread_id).trim();
|
|
6987
|
+
if (payloadThreadId && leaderThreadId && payloadThreadId === leaderThreadId) return false;
|
|
6988
|
+
return leaderThreadId === parentThreadId || parentThreadId in session.threads;
|
|
6974
6989
|
}
|
|
6975
6990
|
|
|
6976
6991
|
function isActiveConductorModeState(state: Record<string, unknown> | null, mode: string, sessionId: string): boolean {
|
|
@@ -6999,7 +7014,7 @@ async function readActiveMainRootConductorStateForPreToolUse(
|
|
|
6999
7014
|
}
|
|
7000
7015
|
const threadId = readPayloadThreadId(payload);
|
|
7001
7016
|
if (!sessionId) return null;
|
|
7002
|
-
if (await
|
|
7017
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
7003
7018
|
|
|
7004
7019
|
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
7005
7020
|
if (!canonicalState) return null;
|
|
@@ -8031,6 +8046,54 @@ function modeStateHasExplicitMatchingCwd(state: Record<string, unknown>, cwd: st
|
|
|
8031
8046
|
}
|
|
8032
8047
|
}
|
|
8033
8048
|
|
|
8049
|
+
async function clearNativeStopSessionEntries(
|
|
8050
|
+
stateDir: string,
|
|
8051
|
+
payload: CodexHookPayload,
|
|
8052
|
+
canonicalSessionId?: string,
|
|
8053
|
+
): Promise<void> {
|
|
8054
|
+
const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
|
|
8055
|
+
const state = await readJsonIfExists(statePath);
|
|
8056
|
+
if (!state) return;
|
|
8057
|
+
|
|
8058
|
+
const sessions = safeObject(state.sessions);
|
|
8059
|
+
const keys = new Set(uniqueNonEmpty([
|
|
8060
|
+
readNativeStopSessionKey(payload, canonicalSessionId),
|
|
8061
|
+
canonicalSessionId,
|
|
8062
|
+
readPayloadSessionId(payload),
|
|
8063
|
+
readPayloadThreadId(payload),
|
|
8064
|
+
]));
|
|
8065
|
+
let changed = false;
|
|
8066
|
+
for (const key of keys) {
|
|
8067
|
+
if (Object.prototype.hasOwnProperty.call(sessions, key)) {
|
|
8068
|
+
delete sessions[key];
|
|
8069
|
+
changed = true;
|
|
8070
|
+
}
|
|
8071
|
+
}
|
|
8072
|
+
if (!changed) return;
|
|
8073
|
+
|
|
8074
|
+
await writeFile(statePath, JSON.stringify({ ...state, sessions }, null, 2));
|
|
8075
|
+
}
|
|
8076
|
+
|
|
8077
|
+
async function hasAuthoritativeInactiveSkillStopState(
|
|
8078
|
+
cwd: string,
|
|
8079
|
+
stateDir: string,
|
|
8080
|
+
skill: string,
|
|
8081
|
+
sessionId: string,
|
|
8082
|
+
threadId: string,
|
|
8083
|
+
): Promise<boolean> {
|
|
8084
|
+
const sessionModeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
|
|
8085
|
+
if (!sessionModeState || !isTerminalOrInactiveModeState(sessionModeState)) return false;
|
|
8086
|
+
|
|
8087
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
8088
|
+
if (canonicalState && !rootSkillStateHasNoActiveSkillForStopContext(canonicalState, skill, sessionId, threadId)) {
|
|
8089
|
+
return false;
|
|
8090
|
+
}
|
|
8091
|
+
|
|
8092
|
+
const rootModeState = await readJsonIfExists(join(stateDir, `${skill}-state.json`));
|
|
8093
|
+
if (!rootModeState) return true;
|
|
8094
|
+
if (!modeStateMatchesSkillStopContext(rootModeState, cwd, sessionId)) return true;
|
|
8095
|
+
return isTerminalOrInactiveModeState(rootModeState);
|
|
8096
|
+
}
|
|
8034
8097
|
async function readBlockingSkillForStop(
|
|
8035
8098
|
cwd: string,
|
|
8036
8099
|
stateDir: string,
|
|
@@ -8939,6 +9002,9 @@ async function buildStopHookOutput(
|
|
|
8939
9002
|
const suppressParentWorkflowStop = shouldSuppressParentWorkflowStopForSideConversation(payload);
|
|
8940
9003
|
if (canonicalSessionId) {
|
|
8941
9004
|
await reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, canonicalSessionId);
|
|
9005
|
+
if (await hasAuthoritativeInactiveSkillStopState(cwd, stateDir, "ralplan", canonicalSessionId, threadId)) {
|
|
9006
|
+
await clearNativeStopSessionEntries(stateDir, payload, canonicalSessionId);
|
|
9007
|
+
}
|
|
8942
9008
|
}
|
|
8943
9009
|
if (suppressParentWorkflowStop) {
|
|
8944
9010
|
return null;
|