oh-my-codex 0.18.3 → 0.18.5
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 +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +61 -3
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +8 -7
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +63 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +81 -0
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +56 -17
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +2 -2
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +60 -1
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +111 -14
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts +1 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +6 -0
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +11 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/question.d.ts.map +1 -1
- package/dist/cli/question.js +31 -1
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +18 -54
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +18 -4
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +5 -5
- package/dist/config/generator.d.ts +8 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +48 -4
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +9 -9
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +11 -2
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -0
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
- package/dist/hooks/__tests__/explore-routing.test.js +10 -13
- package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +13 -15
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +33 -0
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +60 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/explore-routing.d.ts.map +1 -1
- package/dist/hooks/explore-routing.js +8 -14
- package/dist/hooks/explore-routing.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +15 -10
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +125 -1
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +32 -8
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +160 -4
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +120 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +32 -1
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/constants.d.ts +2 -2
- package/dist/hud/constants.js +2 -2
- package/dist/hud/index.d.ts +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +27 -3
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +15 -0
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +42 -20
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +18 -0
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +1 -0
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +8 -0
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +80 -7
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/policy.test.js +115 -9
- package/dist/question/__tests__/policy.test.js.map +1 -1
- package/dist/question/autopilot-wait.d.ts +11 -0
- package/dist/question/autopilot-wait.d.ts.map +1 -0
- package/dist/question/autopilot-wait.js +140 -0
- package/dist/question/autopilot-wait.js.map +1 -0
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js +4 -0
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/policy.d.ts +1 -0
- package/dist/question/policy.d.ts.map +1 -1
- package/dist/question/policy.js +21 -0
- package/dist/question/policy.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +335 -3
- 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 +45 -3
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook.js +13 -0
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/subagents/__tests__/tracker.test.js +69 -0
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +5 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +16 -0
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +3 -3
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +9 -4
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +208 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +5 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +11 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +157 -12
- 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/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +8 -4
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +4 -4
- package/plugins/oh-my-codex/skills/plan/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +10 -2
- package/prompts/executor.md +1 -1
- package/prompts/explore-harness.md +2 -2
- package/prompts/explore.md +1 -1
- package/prompts/planner.md +1 -1
- package/prompts/sisyphus-lite.md +1 -1
- package/skills/autopilot/SKILL.md +2 -2
- package/skills/code-review/SKILL.md +8 -4
- package/skills/deep-interview/SKILL.md +1 -1
- package/skills/omx-setup/SKILL.md +4 -4
- package/skills/plan/SKILL.md +5 -5
- package/skills/ralph/SKILL.md +1 -1
- package/skills/ralplan/SKILL.md +5 -5
- package/skills/ultragoal/SKILL.md +10 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +372 -3
- package/src/scripts/codex-native-hook.ts +50 -2
- package/src/scripts/notify-hook.ts +15 -0
- package/templates/AGENTS.md +3 -3
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
import { writeSessionStart } from "../../hooks/session.js";
|
|
25
25
|
import { resetTriageConfigCache } from "../../hooks/triage-config.js";
|
|
26
26
|
import { executeStateOperation } from "../../state/operations.js";
|
|
27
|
+
import { HUD_TMUX_HEIGHT_LINES } from "../../hud/constants.js";
|
|
27
28
|
import { OMX_TMUX_HUD_OWNER_ENV } from "../../hud/reconcile.js";
|
|
28
29
|
import { OMX_TMUX_HUD_LEADER_PANE_ENV } from "../../hud/tmux.js";
|
|
29
30
|
import { readAllState } from "../../hud/state.js";
|
|
@@ -66,6 +67,26 @@ async function writeJson(path: string, value: unknown): Promise<void> {
|
|
|
66
67
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
async function setTeamPaneIds(
|
|
71
|
+
cwd: string,
|
|
72
|
+
teamName: string,
|
|
73
|
+
paneIds: { leaderPaneId: string; workerPaneIds: Record<string, string> },
|
|
74
|
+
): Promise<void> {
|
|
75
|
+
for (const fileName of ["config.json", "manifest.v2.json"]) {
|
|
76
|
+
const filePath = join(cwd, ".omx", "state", "team", teamName, fileName);
|
|
77
|
+
const parsed = JSON.parse(await readFile(filePath, "utf-8")) as {
|
|
78
|
+
leader_pane_id?: string | null;
|
|
79
|
+
workers?: Array<{ name?: string; pane_id?: string | null }>;
|
|
80
|
+
};
|
|
81
|
+
parsed.leader_pane_id = paneIds.leaderPaneId;
|
|
82
|
+
parsed.workers = (parsed.workers ?? []).map((worker) => ({
|
|
83
|
+
...worker,
|
|
84
|
+
pane_id: worker.name ? paneIds.workerPaneIds[worker.name] ?? worker.pane_id ?? null : worker.pane_id ?? null,
|
|
85
|
+
}));
|
|
86
|
+
await writeJson(filePath, parsed);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
69
90
|
async function withIsolatedHome<T>(prefix: string, run: (homeDir: string) => Promise<T>): Promise<T> {
|
|
70
91
|
const homeDir = await mkdtemp(join(tmpdir(), `omx-native-hook-home-${prefix}-`));
|
|
71
92
|
const previousHome = process.env.HOME;
|
|
@@ -243,6 +264,7 @@ const DEFAULT_AUTO_NUDGE_RESPONSE =
|
|
|
243
264
|
|
|
244
265
|
const TEAM_ENV_KEYS = [
|
|
245
266
|
"OMX_TEAM_WORKER",
|
|
267
|
+
"OMX_TEAM_INTERNAL_WORKER",
|
|
246
268
|
"OMX_TEAM_STATE_ROOT",
|
|
247
269
|
"OMX_TEAM_LEADER_CWD",
|
|
248
270
|
"OMX_SESSION_ID",
|
|
@@ -2362,6 +2384,38 @@ standardMaxRounds = 15
|
|
|
2362
2384
|
}
|
|
2363
2385
|
});
|
|
2364
2386
|
|
|
2387
|
+
it("does not repeat ultragoal Stop recovery after a safe completed-aggregate microgoal blocker is recorded", async () => {
|
|
2388
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-aggregate-blocked-stop-"));
|
|
2389
|
+
try {
|
|
2390
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
2391
|
+
version: 1,
|
|
2392
|
+
codexGoalMode: "aggregate",
|
|
2393
|
+
activeGoalId: "G001-demo",
|
|
2394
|
+
goals: [{
|
|
2395
|
+
id: "G001-demo",
|
|
2396
|
+
status: "in_progress",
|
|
2397
|
+
objective: "Demo goal",
|
|
2398
|
+
failureReason: "aggregate Codex goal already complete and unreconcilable while repo-native .omx/ultragoal/goals.json still has an in-progress microgoal; stop the recovery loop",
|
|
2399
|
+
}],
|
|
2400
|
+
});
|
|
2401
|
+
|
|
2402
|
+
const result = await dispatchCodexNativeHook({
|
|
2403
|
+
hook_event_name: "Stop",
|
|
2404
|
+
cwd,
|
|
2405
|
+
session_id: "sess-ultragoal-aggregate-blocked-stop",
|
|
2406
|
+
thread_id: "thread-ultragoal-aggregate-blocked-stop",
|
|
2407
|
+
stop_hook_active: true,
|
|
2408
|
+
last_assistant_message: "Goal complete.",
|
|
2409
|
+
}, { cwd });
|
|
2410
|
+
|
|
2411
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2412
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
2413
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
2414
|
+
} finally {
|
|
2415
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2416
|
+
}
|
|
2417
|
+
});
|
|
2418
|
+
|
|
2365
2419
|
|
|
2366
2420
|
it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
|
|
2367
2421
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
|
|
@@ -3852,6 +3906,198 @@ export async function onHookEvent(event) {
|
|
|
3852
3906
|
}
|
|
3853
3907
|
});
|
|
3854
3908
|
|
|
3909
|
+
it("skips prompt-submit HUD reconciliation for confirmed team worker panes", async () => {
|
|
3910
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-skip-"));
|
|
3911
|
+
try {
|
|
3912
|
+
const teamName = "hud-worker-skip";
|
|
3913
|
+
await initTeamState(teamName, "skip worker HUD reconcile", "executor", 1, cwd);
|
|
3914
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3915
|
+
leaderPaneId: "%42",
|
|
3916
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3917
|
+
});
|
|
3918
|
+
process.env.TMUX = "1";
|
|
3919
|
+
process.env.TMUX_PANE = "%10";
|
|
3920
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
3921
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
3922
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3923
|
+
|
|
3924
|
+
let reconcileCalls = 0;
|
|
3925
|
+
const result = await dispatchCodexNativeHook(
|
|
3926
|
+
{
|
|
3927
|
+
hook_event_name: "UserPromptSubmit",
|
|
3928
|
+
cwd,
|
|
3929
|
+
session_id: "sess-hud-team-worker",
|
|
3930
|
+
prompt: "$ralplan prepare plan",
|
|
3931
|
+
},
|
|
3932
|
+
{
|
|
3933
|
+
cwd,
|
|
3934
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
3935
|
+
reconcileCalls += 1;
|
|
3936
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
3937
|
+
},
|
|
3938
|
+
},
|
|
3939
|
+
);
|
|
3940
|
+
|
|
3941
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
3942
|
+
assert.equal(reconcileCalls, 0);
|
|
3943
|
+
} finally {
|
|
3944
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3945
|
+
}
|
|
3946
|
+
});
|
|
3947
|
+
|
|
3948
|
+
it("preserves prompt-submit HUD reconciliation for team leader panes", async () => {
|
|
3949
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-leader-preserve-"));
|
|
3950
|
+
try {
|
|
3951
|
+
const teamName = "hud-leader-keep";
|
|
3952
|
+
await initTeamState(teamName, "preserve leader HUD reconcile", "executor", 1, cwd);
|
|
3953
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3954
|
+
leaderPaneId: "%42",
|
|
3955
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3956
|
+
});
|
|
3957
|
+
process.env.TMUX = "1";
|
|
3958
|
+
process.env.TMUX_PANE = "%42";
|
|
3959
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3960
|
+
|
|
3961
|
+
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
3962
|
+
const result = await dispatchCodexNativeHook(
|
|
3963
|
+
{
|
|
3964
|
+
hook_event_name: "UserPromptSubmit",
|
|
3965
|
+
cwd,
|
|
3966
|
+
session_id: "sess-hud-team-leader",
|
|
3967
|
+
prompt: "$ralplan prepare plan",
|
|
3968
|
+
},
|
|
3969
|
+
{
|
|
3970
|
+
cwd,
|
|
3971
|
+
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
3972
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
3973
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
3974
|
+
},
|
|
3975
|
+
},
|
|
3976
|
+
);
|
|
3977
|
+
|
|
3978
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
3979
|
+
assert.deepEqual(reconcileCall, { cwd, sessionId: "sess-hud-team-leader" });
|
|
3980
|
+
} finally {
|
|
3981
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3982
|
+
}
|
|
3983
|
+
});
|
|
3984
|
+
|
|
3985
|
+
it("preserves prompt-submit HUD reconciliation when worker pane detection is ambiguous", async () => {
|
|
3986
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-ambiguous-"));
|
|
3987
|
+
try {
|
|
3988
|
+
const teamName = "hud-worker-ambiguous";
|
|
3989
|
+
await initTeamState(teamName, "fail closed for ambiguous worker HUD reconcile", "executor", 1, cwd);
|
|
3990
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3991
|
+
leaderPaneId: "%42",
|
|
3992
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3993
|
+
});
|
|
3994
|
+
process.env.TMUX = "1";
|
|
3995
|
+
process.env.TMUX_PANE = "%99";
|
|
3996
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
3997
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
3998
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3999
|
+
|
|
4000
|
+
let reconcileCalls = 0;
|
|
4001
|
+
const result = await dispatchCodexNativeHook(
|
|
4002
|
+
{
|
|
4003
|
+
hook_event_name: "UserPromptSubmit",
|
|
4004
|
+
cwd,
|
|
4005
|
+
session_id: "sess-hud-team-worker-ambiguous",
|
|
4006
|
+
prompt: "$ralplan prepare plan",
|
|
4007
|
+
},
|
|
4008
|
+
{
|
|
4009
|
+
cwd,
|
|
4010
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
4011
|
+
reconcileCalls += 1;
|
|
4012
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
4013
|
+
},
|
|
4014
|
+
},
|
|
4015
|
+
);
|
|
4016
|
+
|
|
4017
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
4018
|
+
assert.equal(reconcileCalls, 1);
|
|
4019
|
+
} finally {
|
|
4020
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4021
|
+
}
|
|
4022
|
+
});
|
|
4023
|
+
|
|
4024
|
+
it("preserves prompt-submit HUD reconciliation for native subagents even with worker pane env", async () => {
|
|
4025
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-subagent-worker-preserve-"));
|
|
4026
|
+
try {
|
|
4027
|
+
const teamName = "hud-subagent-keep";
|
|
4028
|
+
await initTeamState(teamName, "preserve subagent HUD reconcile", "executor", 1, cwd);
|
|
4029
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
4030
|
+
leaderPaneId: "%42",
|
|
4031
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
4032
|
+
});
|
|
4033
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
4034
|
+
const canonicalSessionId = "sess-subagent-hud-parent";
|
|
4035
|
+
const leaderNativeSessionId = "native-subagent-hud-parent";
|
|
4036
|
+
const childNativeSessionId = "native-subagent-hud-child";
|
|
4037
|
+
const nowIso = new Date().toISOString();
|
|
4038
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
4039
|
+
session_id: canonicalSessionId,
|
|
4040
|
+
native_session_id: leaderNativeSessionId,
|
|
4041
|
+
});
|
|
4042
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
4043
|
+
schemaVersion: 1,
|
|
4044
|
+
sessions: {
|
|
4045
|
+
[canonicalSessionId]: {
|
|
4046
|
+
session_id: canonicalSessionId,
|
|
4047
|
+
leader_thread_id: leaderNativeSessionId,
|
|
4048
|
+
updated_at: nowIso,
|
|
4049
|
+
threads: {
|
|
4050
|
+
[leaderNativeSessionId]: {
|
|
4051
|
+
thread_id: leaderNativeSessionId,
|
|
4052
|
+
kind: "leader",
|
|
4053
|
+
first_seen_at: nowIso,
|
|
4054
|
+
last_seen_at: nowIso,
|
|
4055
|
+
turn_count: 1,
|
|
4056
|
+
},
|
|
4057
|
+
[childNativeSessionId]: {
|
|
4058
|
+
thread_id: childNativeSessionId,
|
|
4059
|
+
kind: "subagent",
|
|
4060
|
+
first_seen_at: nowIso,
|
|
4061
|
+
last_seen_at: nowIso,
|
|
4062
|
+
turn_count: 1,
|
|
4063
|
+
mode: "verifier",
|
|
4064
|
+
},
|
|
4065
|
+
},
|
|
4066
|
+
},
|
|
4067
|
+
},
|
|
4068
|
+
});
|
|
4069
|
+
process.env.TMUX = "1";
|
|
4070
|
+
process.env.TMUX_PANE = "%10";
|
|
4071
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
4072
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
4073
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
4074
|
+
|
|
4075
|
+
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
4076
|
+
const result = await dispatchCodexNativeHook(
|
|
4077
|
+
{
|
|
4078
|
+
hook_event_name: "UserPromptSubmit",
|
|
4079
|
+
cwd,
|
|
4080
|
+
session_id: childNativeSessionId,
|
|
4081
|
+
thread_id: childNativeSessionId,
|
|
4082
|
+
turn_id: "turn-subagent-hud-child",
|
|
4083
|
+
prompt: "Review the worker patch literally; do not activate $ralplan.",
|
|
4084
|
+
},
|
|
4085
|
+
{
|
|
4086
|
+
cwd,
|
|
4087
|
+
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
4088
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
4089
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
4090
|
+
},
|
|
4091
|
+
},
|
|
4092
|
+
);
|
|
4093
|
+
|
|
4094
|
+
assert.equal(result.outputJson, null);
|
|
4095
|
+
assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId });
|
|
4096
|
+
} finally {
|
|
4097
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4098
|
+
}
|
|
4099
|
+
});
|
|
4100
|
+
|
|
3855
4101
|
it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => {
|
|
3856
4102
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-"));
|
|
3857
4103
|
const originalTmux = process.env.TMUX;
|
|
@@ -3908,8 +4154,8 @@ esac
|
|
|
3908
4154
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
3909
4155
|
const tmuxCalls = await readFile(tmuxLog, "utf-8");
|
|
3910
4156
|
assert.match(tmuxCalls, /list-panes -t %1 -F/);
|
|
3911
|
-
assert.match(tmuxCalls,
|
|
3912
|
-
assert.match(tmuxCalls,
|
|
4157
|
+
assert.match(tmuxCalls, new RegExp(`split-window -v -l ${HUD_TMUX_HEIGHT_LINES} -d -t %1 -c`));
|
|
4158
|
+
assert.match(tmuxCalls, new RegExp(`resize-pane -t %9 -y ${HUD_TMUX_HEIGHT_LINES}`));
|
|
3913
4159
|
assert.match(tmuxCalls, /dist\/cli\/omx\.js' hud --watch --preset=focused/);
|
|
3914
4160
|
assert.doesNotMatch(tmuxCalls, /\/tmp\/codex-host-binary' hud --watch/);
|
|
3915
4161
|
} finally {
|
|
@@ -3990,7 +4236,7 @@ esac
|
|
|
3990
4236
|
assert.equal(result.omxEventName, "keyword-detector");
|
|
3991
4237
|
const tmuxCalls = await readFile(tmuxLog, "utf-8");
|
|
3992
4238
|
assert.match(tmuxCalls, /list-panes -t %1 -F/);
|
|
3993
|
-
assert.match(tmuxCalls,
|
|
4239
|
+
assert.match(tmuxCalls, new RegExp(`resize-pane -t %2 -y ${HUD_TMUX_HEIGHT_LINES}`));
|
|
3994
4240
|
assert.doesNotMatch(tmuxCalls, /split-window/);
|
|
3995
4241
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
|
|
3996
4242
|
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
|
|
@@ -9264,6 +9510,70 @@ exit 0
|
|
|
9264
9510
|
}
|
|
9265
9511
|
});
|
|
9266
9512
|
|
|
9513
|
+
it("does not report ralplan subagent waiting when notify-fallback already recorded completion", async () => {
|
|
9514
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-complete-"));
|
|
9515
|
+
try {
|
|
9516
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
9517
|
+
const now = new Date().toISOString();
|
|
9518
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent-complete"), { recursive: true });
|
|
9519
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent-complete" });
|
|
9520
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "skill-active-state.json"), {
|
|
9521
|
+
active: true,
|
|
9522
|
+
skill: "ralplan",
|
|
9523
|
+
phase: "planning",
|
|
9524
|
+
});
|
|
9525
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "ralplan-state.json"), {
|
|
9526
|
+
active: true,
|
|
9527
|
+
current_phase: "planning",
|
|
9528
|
+
});
|
|
9529
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
9530
|
+
schemaVersion: 1,
|
|
9531
|
+
sessions: {
|
|
9532
|
+
"sess-stop-skill-subagent-complete": {
|
|
9533
|
+
session_id: "sess-stop-skill-subagent-complete",
|
|
9534
|
+
leader_thread_id: "leader-1",
|
|
9535
|
+
updated_at: now,
|
|
9536
|
+
threads: {
|
|
9537
|
+
"leader-1": {
|
|
9538
|
+
thread_id: "leader-1",
|
|
9539
|
+
kind: "leader",
|
|
9540
|
+
first_seen_at: now,
|
|
9541
|
+
last_seen_at: now,
|
|
9542
|
+
turn_count: 1,
|
|
9543
|
+
},
|
|
9544
|
+
"sub-1": {
|
|
9545
|
+
thread_id: "sub-1",
|
|
9546
|
+
kind: "subagent",
|
|
9547
|
+
first_seen_at: now,
|
|
9548
|
+
last_seen_at: now,
|
|
9549
|
+
completed_at: now,
|
|
9550
|
+
last_completed_turn_id: "turn-complete-1",
|
|
9551
|
+
completion_source: "notify-fallback-watcher",
|
|
9552
|
+
turn_count: 2,
|
|
9553
|
+
},
|
|
9554
|
+
},
|
|
9555
|
+
},
|
|
9556
|
+
},
|
|
9557
|
+
});
|
|
9558
|
+
|
|
9559
|
+
const result = await dispatchCodexNativeHook(
|
|
9560
|
+
{
|
|
9561
|
+
hook_event_name: "Stop",
|
|
9562
|
+
cwd,
|
|
9563
|
+
session_id: "sess-stop-skill-subagent-complete",
|
|
9564
|
+
},
|
|
9565
|
+
{ cwd },
|
|
9566
|
+
);
|
|
9567
|
+
|
|
9568
|
+
assert.equal(result.omxEventName, "stop");
|
|
9569
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
9570
|
+
assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/);
|
|
9571
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
9572
|
+
} finally {
|
|
9573
|
+
await rm(cwd, { recursive: true, force: true });
|
|
9574
|
+
}
|
|
9575
|
+
});
|
|
9576
|
+
|
|
9267
9577
|
it("does not block on stale root ralplan skill when the explicit session-scoped canonical skill state is absent", async () => {
|
|
9268
9578
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-"));
|
|
9269
9579
|
try {
|
|
@@ -13833,3 +14143,62 @@ describe("codex native hook triage integration", () => {
|
|
|
13833
14143
|
}
|
|
13834
14144
|
});
|
|
13835
14145
|
});
|
|
14146
|
+
|
|
14147
|
+
describe('native Stop autopilot deep-interview wait', () => {
|
|
14148
|
+
it('does not force continued execution while autopilot is waiting on a deep-interview omx question', async () => {
|
|
14149
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-native-hook-autopilot-question-wait-'));
|
|
14150
|
+
try {
|
|
14151
|
+
const sessionId = 'sess-autopilot-wait';
|
|
14152
|
+
const sessionDir = join(cwd, '.omx', 'state', 'sessions', sessionId);
|
|
14153
|
+
await writeJson(join(cwd, '.omx', 'state', 'session.json'), { session_id: sessionId });
|
|
14154
|
+
await writeJson(join(sessionDir, 'autopilot-state.json'), {
|
|
14155
|
+
mode: 'autopilot',
|
|
14156
|
+
active: true,
|
|
14157
|
+
current_phase: 'waiting-for-user',
|
|
14158
|
+
run_outcome: 'blocked_on_user',
|
|
14159
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14160
|
+
session_id: sessionId,
|
|
14161
|
+
state: {
|
|
14162
|
+
deep_interview_question: {
|
|
14163
|
+
status: 'waiting_for_user',
|
|
14164
|
+
source: 'omx-question',
|
|
14165
|
+
obligation_id: 'obligation-stop-1',
|
|
14166
|
+
previous_phase: 'deep-interview',
|
|
14167
|
+
},
|
|
14168
|
+
},
|
|
14169
|
+
});
|
|
14170
|
+
await writeJson(join(sessionDir, 'deep-interview-state.json'), {
|
|
14171
|
+
mode: 'deep-interview',
|
|
14172
|
+
active: false,
|
|
14173
|
+
current_phase: 'intent-first',
|
|
14174
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14175
|
+
run_outcome: 'blocked_on_user',
|
|
14176
|
+
session_id: sessionId,
|
|
14177
|
+
question_enforcement: {
|
|
14178
|
+
obligation_id: 'obligation-stop-1',
|
|
14179
|
+
source: 'omx-question',
|
|
14180
|
+
status: 'pending',
|
|
14181
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14182
|
+
requested_at: '2026-04-19T00:00:00.000Z',
|
|
14183
|
+
},
|
|
14184
|
+
});
|
|
14185
|
+
await writeJson(join(sessionDir, 'skill-active-state.json'), {
|
|
14186
|
+
active: true,
|
|
14187
|
+
skill: 'autopilot',
|
|
14188
|
+
phase: 'deep-interview',
|
|
14189
|
+
session_id: sessionId,
|
|
14190
|
+
active_skills: [{ skill: 'autopilot', phase: 'deep-interview', active: true, session_id: sessionId }],
|
|
14191
|
+
});
|
|
14192
|
+
|
|
14193
|
+
const result = await dispatchCodexNativeHook({
|
|
14194
|
+
hook_event_name: 'Stop',
|
|
14195
|
+
session_id: sessionId,
|
|
14196
|
+
thread_id: 'thread-autopilot-wait',
|
|
14197
|
+
}, { cwd });
|
|
14198
|
+
|
|
14199
|
+
assert.equal(result.outputJson, null);
|
|
14200
|
+
} finally {
|
|
14201
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14202
|
+
}
|
|
14203
|
+
});
|
|
14204
|
+
});
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
import {
|
|
31
31
|
appendTeamEvent,
|
|
32
32
|
readTeamLeaderAttention,
|
|
33
|
+
readTeamConfig,
|
|
33
34
|
readTeamManifestV2,
|
|
34
35
|
readTeamPhase,
|
|
35
36
|
writeTeamLeaderAttention,
|
|
@@ -100,6 +101,7 @@ import {
|
|
|
100
101
|
isPendingDeepInterviewQuestionEnforcement,
|
|
101
102
|
reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords,
|
|
102
103
|
} from "../question/deep-interview.js";
|
|
104
|
+
import { readAutopilotDeepInterviewQuestionWaitState } from "../question/autopilot-wait.js";
|
|
103
105
|
import {
|
|
104
106
|
buildDocumentRefreshAdvisoryOutput,
|
|
105
107
|
evaluateFinalHandoffDocumentRefresh,
|
|
@@ -1873,6 +1875,27 @@ async function resolveTeamStateDirForWorkerContext(
|
|
|
1873
1875
|
return null;
|
|
1874
1876
|
}
|
|
1875
1877
|
|
|
1878
|
+
async function isConfirmedTeamWorkerPromptSubmitPane(cwd: string): Promise<boolean> {
|
|
1879
|
+
const workerContext =
|
|
1880
|
+
parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER))
|
|
1881
|
+
|| parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
|
|
1882
|
+
if (!workerContext) return false;
|
|
1883
|
+
|
|
1884
|
+
const currentPaneId = safeString(process.env.TMUX_PANE).trim();
|
|
1885
|
+
if (!currentPaneId) return false;
|
|
1886
|
+
|
|
1887
|
+
const config = await readTeamConfig(workerContext.teamName, cwd).catch(() => null);
|
|
1888
|
+
if (!config) return false;
|
|
1889
|
+
|
|
1890
|
+
const leaderPaneId = safeString(config.leader_pane_id).trim();
|
|
1891
|
+
if (leaderPaneId && leaderPaneId === currentPaneId) return false;
|
|
1892
|
+
|
|
1893
|
+
const workerPaneId = safeString(
|
|
1894
|
+
config.workers.find((worker) => worker.name === workerContext.workerName)?.pane_id,
|
|
1895
|
+
).trim();
|
|
1896
|
+
return workerPaneId !== "" && workerPaneId === currentPaneId;
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1876
1899
|
|
|
1877
1900
|
type TeamWorkerStopDecision =
|
|
1878
1901
|
| {
|
|
@@ -2033,6 +2056,9 @@ async function buildModeBasedStopOutput(
|
|
|
2033
2056
|
if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, mode)) {
|
|
2034
2057
|
return null;
|
|
2035
2058
|
}
|
|
2059
|
+
if (mode === "autopilot" && await readAutopilotDeepInterviewQuestionWaitState(cwd, sessionId)) {
|
|
2060
|
+
return null;
|
|
2061
|
+
}
|
|
2036
2062
|
const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
|
|
2037
2063
|
if (!state || !shouldContinueRun(state)) return null;
|
|
2038
2064
|
const phase = formatPhase(state.current_phase);
|
|
@@ -2077,6 +2103,18 @@ function reportsBlockedPerformanceGoalObjectiveMismatch(state: unknown): boolean
|
|
|
2077
2103
|
return /objective mismatch/i.test(evidence);
|
|
2078
2104
|
}
|
|
2079
2105
|
|
|
2106
|
+
function reportsBlockedUltragoalCompletedAggregateMicrogoalLoop(goal: Record<string, unknown>): boolean {
|
|
2107
|
+
const evidence = [
|
|
2108
|
+
safeString(goal.failureReason),
|
|
2109
|
+
safeString(goal.blockedReason),
|
|
2110
|
+
safeString(goal.evidence),
|
|
2111
|
+
].join(" ");
|
|
2112
|
+
return /aggregate codex goal/i.test(evidence)
|
|
2113
|
+
&& /\bcomplete(?:d)?\b/i.test(evidence)
|
|
2114
|
+
&& /microgoal/i.test(evidence)
|
|
2115
|
+
&& /\b(?:unreconcilable|mismatch|loop|already complete|already completed|blocks?)\b/i.test(evidence);
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2080
2118
|
async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
|
|
2081
2119
|
const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
|
|
2082
2120
|
const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
|
|
@@ -2085,6 +2123,9 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
|
|
|
2085
2123
|
const activeUltragoal = aggregateProductComplete
|
|
2086
2124
|
? undefined
|
|
2087
2125
|
: ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
|
|
2126
|
+
if (activeUltragoal && reportsBlockedUltragoalCompletedAggregateMicrogoalLoop(activeUltragoal)) {
|
|
2127
|
+
return null;
|
|
2128
|
+
}
|
|
2088
2129
|
if (activeUltragoal) {
|
|
2089
2130
|
const goalId = safeString(activeUltragoal.id) || "<goal-id>";
|
|
2090
2131
|
return {
|
|
@@ -2847,6 +2888,9 @@ async function buildDeepInterviewQuestionStopOutput(
|
|
|
2847
2888
|
threadId: string,
|
|
2848
2889
|
): Promise<{ output: Record<string, unknown>; obligationId: string } | null> {
|
|
2849
2890
|
await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
|
|
2891
|
+
if (await readAutopilotDeepInterviewQuestionWaitState(cwd, sessionId)) {
|
|
2892
|
+
return null;
|
|
2893
|
+
}
|
|
2850
2894
|
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
|
|
2851
2895
|
if (!modeState) return null;
|
|
2852
2896
|
|
|
@@ -3878,8 +3922,12 @@ export async function dispatchCodexNativeHook(
|
|
|
3878
3922
|
triageAdditionalContext = null;
|
|
3879
3923
|
}
|
|
3880
3924
|
}
|
|
3881
|
-
const
|
|
3882
|
-
|
|
3925
|
+
const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
|
|
3926
|
+
&& await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
|
|
3927
|
+
if (!skipHudReconcileForTeamWorkerPane) {
|
|
3928
|
+
const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
|
|
3929
|
+
await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
|
|
3930
|
+
}
|
|
3883
3931
|
}
|
|
3884
3932
|
|
|
3885
3933
|
if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
|
|
@@ -272,6 +272,14 @@ function isTurnCompletePayload(payload: Record<string, unknown>): boolean {
|
|
|
272
272
|
return type === '' || type === 'agent-turn-complete' || type === 'turn-complete';
|
|
273
273
|
}
|
|
274
274
|
|
|
275
|
+
function isNotifyFallbackTaskCompletePayload(payload: Record<string, unknown>): boolean {
|
|
276
|
+
const source = safeString(payload.source || '').trim();
|
|
277
|
+
if (source !== 'notify-fallback-watcher') return false;
|
|
278
|
+
return normalizeInputMessages(payload).some((message) => (
|
|
279
|
+
message.includes('[notify-fallback] synthesized from rollout task_complete')
|
|
280
|
+
));
|
|
281
|
+
}
|
|
282
|
+
|
|
275
283
|
async function main() {
|
|
276
284
|
const rawPayload = process.argv[process.argv.length - 1];
|
|
277
285
|
if (!rawPayload || rawPayload.startsWith('-')) {
|
|
@@ -294,6 +302,7 @@ async function main() {
|
|
|
294
302
|
const inputMessages = normalizeInputMessages(payload);
|
|
295
303
|
const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
|
|
296
304
|
const isTurnComplete = isTurnCompletePayload(payload);
|
|
305
|
+
const isNotifyFallbackTaskComplete = isNotifyFallbackTaskCompletePayload(payload);
|
|
297
306
|
|
|
298
307
|
// Team worker detection via environment variable
|
|
299
308
|
const teamWorkerEnv = process.env.OMX_TEAM_INTERNAL_WORKER || process.env.OMX_TEAM_WORKER; // e.g., "fix-ts/worker-1"
|
|
@@ -358,6 +367,12 @@ async function main() {
|
|
|
358
367
|
...(turnId ? { turnId } : {}),
|
|
359
368
|
timestamp: new Date().toISOString(),
|
|
360
369
|
mode: safeString(payload.mode || ''),
|
|
370
|
+
...(isNotifyFallbackTaskComplete
|
|
371
|
+
? {
|
|
372
|
+
completed: true,
|
|
373
|
+
completionSource: 'notify-fallback-watcher',
|
|
374
|
+
}
|
|
375
|
+
: {}),
|
|
361
376
|
});
|
|
362
377
|
}
|
|
363
378
|
} catch {
|
package/templates/AGENTS.md
CHANGED
|
@@ -274,10 +274,10 @@ Verification loop: define the claim and success criteria, run the smallest valid
|
|
|
274
274
|
Mode selection: use `$deep-interview` for unclear intent/boundaries; `$ralplan` for consensus on architecture, tradeoffs, or tests; `$team` for approved multi-lane work; `$ralph` for persistent single-owner completion/verification loops; otherwise execute directly in solo mode. Switch modes only when evidence shows the current lane is mismatched or blocked.
|
|
275
275
|
|
|
276
276
|
Command routing:
|
|
277
|
-
-
|
|
278
|
-
-
|
|
277
|
+
- `omx explore` is deprecated and MUST NOT be recommended as the default surface for simple read-only repository lookup tasks. Use normal Codex repository inspection tools/subagents for file, symbol, pattern, relationship, and implementation discovery.
|
|
278
|
+
- `USE_OMX_EXPLORE_CMD` is compatibility-only for legacy callers; it does not make `omx explore` preferred for new work.
|
|
279
279
|
|
|
280
|
-
Use `omx
|
|
280
|
+
Use `omx sparkshell` for explicit shell-native read-only commands, bounded verification, repo-wide listing/search, or explicit `omx sparkshell --tmux-pane` summaries. Treat sparkshell as explicit opt-in. When to use what: keep ambiguous, implementation-heavy, edit-heavy, diagnostics, tests, MCP/web, and complex shell work on the normal path; if `omx sparkshell` is incomplete, retry narrower or gracefully fall back to the normal path.
|
|
281
281
|
|
|
282
282
|
Leader vs worker:
|
|
283
283
|
- The leader chooses the mode, keeps the brief current, delegates bounded work, and owns verification plus stop/escalate calls.
|