oh-my-codex 0.18.3 → 0.18.4
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 +37 -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__/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/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +109 -12
- 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 +5 -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/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__/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__/reconcile.test.js +23 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/index.d.ts +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +24 -2
- 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/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 +83 -9
- package/dist/question/__tests__/policy.test.js.map +1 -1
- package/dist/question/autopilot-wait.d.ts +10 -0
- package/dist/question/autopilot-wait.d.ts.map +1 -0
- package/dist/question/autopilot-wait.js +134 -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 +19 -0
- package/dist/question/policy.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +331 -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 +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/ultragoal/__tests__/artifacts.test.js +126 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +126 -8
- 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/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/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/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/src/scripts/__tests__/codex-native-hook.test.ts +368 -0
- package/src/scripts/codex-native-hook.ts +50 -2
- package/src/scripts/notify-hook.ts +15 -0
- package/templates/AGENTS.md +3 -3
package/skills/plan/SKILL.md
CHANGED
|
@@ -30,7 +30,7 @@ Jumping into code without understanding requirements leads to rework, scope cree
|
|
|
30
30
|
- Auto-detect interview vs direct mode based on request specificity
|
|
31
31
|
- Ask one question at a time during interviews -- never batch multiple interview rounds into one question form
|
|
32
32
|
- Gather codebase facts via `explore` agent before asking the user about them
|
|
33
|
-
-
|
|
33
|
+
- `omx explore` is deprecated. Use normal repository inspection tools/subagents for simple read-only repository lookups during planning; use `omx sparkshell` only for explicit shell-native read-only evidence, and keep prompt-heavy or ambiguous planning work on the richer normal path.
|
|
34
34
|
- Plans must meet quality standards: 80%+ claims cite file/line, 90%+ criteria are testable
|
|
35
35
|
- Implementation step count must be right-sized to task scope; avoid defaulting to exactly five steps when the work is clearly smaller or larger
|
|
36
36
|
- Consensus mode outputs the final plan by default; add `--interactive` to enable execution handoff
|
|
@@ -80,8 +80,8 @@ Jumping into code without understanding requirements leads to rework, scope cree
|
|
|
80
80
|
- **Request changes** — return to step 1 with user feedback incorporated
|
|
81
81
|
- **Skip review** — go directly to final approval (step 7)
|
|
82
82
|
If NOT running with `--interactive`, automatically proceed to review (step 3).
|
|
83
|
-
3. **Architect** reviews for architectural soundness
|
|
84
|
-
4. **Critic** evaluates against quality criteria
|
|
83
|
+
3. **Architect** reviews for architectural soundness as a dedicated subsequent `Architect` subagent with the full task, current plan text/path, RALPLAN-DR summary, and relevant artifact context. Architect review **MUST** include: strongest steelman counterargument (antithesis) against the favored option, at least one meaningful tradeoff tension, and (when possible) a synthesis path. In deliberate mode, Architect should explicitly flag principle violations. **Wait for this step to complete before proceeding to step 4.** Do NOT run steps 3 and 4 in parallel. Do NOT substitute a default/improvised subagent prompt for the role-specific `Architect` prompt.
|
|
84
|
+
4. **Critic** evaluates against quality criteria as a dedicated subsequent `Critic` subagent with the full task, current plan text/path, RALPLAN-DR summary, artifact context, and the completed `Architect` result. Critic **MUST** verify principle-option consistency, fair alternative exploration, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. Critic **MUST** explicitly reject shallow alternatives, driver contradictions, vague risks, or weak verification. In deliberate mode, Critic **MUST** reject missing/weak pre-mortem or missing/weak expanded test plan. Run only after step 3 is complete. Do NOT let the `Architect` response self-approve the Critic gate.
|
|
85
85
|
5. **Re-review loop** (max 5 iterations): If Critic rejects or iterates, execute this closed loop:
|
|
86
86
|
a. Collect all feedback from Architect + Critic
|
|
87
87
|
b. Pass feedback to Planner to produce a revised plan
|
|
@@ -142,9 +142,9 @@ Plans are saved to `.omx/plans/`. Drafts go to `.omx/drafts/`.
|
|
|
142
142
|
- Use the `explore` agent (LOW tier, bounded quick pass) to gather codebase facts before asking the user
|
|
143
143
|
- Use `ask_codex` with `agent_role: "planner"` for planning validation on large-scope plans
|
|
144
144
|
- Use `ask_codex` with `agent_role: "analyst"` for requirements analysis
|
|
145
|
-
- Use `ask_codex` with `agent_role: "critic"` for
|
|
145
|
+
- Use `ask_codex` with `agent_role: "critic"` for standalone review mode. In consensus mode, use the dedicated sequential role-specific `Architect` and `Critic` subagents described in steps 3-4 instead of a single critic-only review call.
|
|
146
146
|
- If optional MCP compatibility tools or Codex consultation are unavailable, fall back to equivalent OMX prompt/native agents -- never block on external tools
|
|
147
|
-
- **CRITICAL — Consensus mode agent calls MUST be sequential, never parallel.** Always await the Architect result before issuing the Critic call.
|
|
147
|
+
- **CRITICAL — Consensus mode agent calls MUST be sequential, never parallel.** Always await the subsequent role-specific `Architect` result before issuing the subsequent role-specific `Critic` call.
|
|
148
148
|
- In consensus mode, default to RALPLAN-DR short mode; enable deliberate mode on `--deliberate` or explicit high-risk signals (auth/security, migrations, destructive changes, production incidents, compliance/PII, public API breakage)
|
|
149
149
|
- In consensus mode with `--interactive`: use `AskUserQuestion` / the structured question UI for the user feedback step (step 2) and the final approval step (step 7) -- never ask for approval in plain text when a structured surface is available. Without `--interactive`, auto-proceed through planning steps without pausing. Output the final plan without execution.
|
|
150
150
|
- In consensus mode with `--interactive`, on user approval **MUST** invoke the selected follow-up lane from step 9 (`$ultragoal`, `$team`, `$autoresearch-goal`, `$performance-goal`, or explicit `$ralph` fallback) -- never implement directly in the planning agent
|
package/skills/ralph/SKILL.md
CHANGED
|
@@ -50,7 +50,7 @@ Complex tasks often fail silently: partial implementations get declared "done",
|
|
|
50
50
|
- unknowns/open questions
|
|
51
51
|
- likely codebase touchpoints
|
|
52
52
|
- If an existing relevant snapshot is available, reuse it and record the path in Ralph state.
|
|
53
|
-
- If request ambiguity is high, gather brownfield facts first.
|
|
53
|
+
- If request ambiguity is high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick <task>` to close critical gaps.
|
|
54
54
|
- Do not begin Ralph execution work (delegation, implementation, or verification loops) until snapshot grounding exists. If forced to proceed quickly, note explicit risk tradeoffs.
|
|
55
55
|
1. **Review progress**: Check TODO list and any prior iteration state
|
|
56
56
|
2. **Continue from where you left off**: Pick up incomplete tasks
|
package/skills/ralplan/SKILL.md
CHANGED
|
@@ -20,7 +20,7 @@ $ralplan "task description"
|
|
|
20
20
|
|
|
21
21
|
## Ontology-heavy review
|
|
22
22
|
|
|
23
|
-
For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, Scholastic may be cited as an available advisory ontology reviewer/persona. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself remains the Planner → Architect → Critic consensus workflow and the durable gate remains Architect→Critic only.
|
|
23
|
+
For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, subagent `Scholastic` may be cited as an available advisory ontology reviewer/persona. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself remains the Planner → Architect → Critic consensus workflow and the durable gate remains Architect→Critic only.
|
|
24
24
|
|
|
25
25
|
## Usage with interactive mode
|
|
26
26
|
|
|
@@ -49,8 +49,8 @@ The consensus workflow:
|
|
|
49
49
|
- If only one viable option remains, explicit invalidation rationale for alternatives
|
|
50
50
|
- Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability)
|
|
51
51
|
2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the structured question UI (`omx question` in attached tmux; native structured input outside tmux when available) to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review.
|
|
52
|
-
3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. In deliberate mode, Architect should explicitly flag principle violations.
|
|
53
|
-
4. **Critic** evaluates against quality criteria — run only after step 3 completes. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
|
|
52
|
+
3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. Launch this as a subsequent `Architect` subagent (`agent_type: "architect"`) and pass the full task statement, context snapshot, PRD/test-spec paths, and relevant prior findings; do not use a default subagent with only a short improvised reviewer prompt. In deliberate mode, Architect should explicitly flag principle violations.
|
|
53
|
+
4. **Critic** evaluates against quality criteria — run only after step 3 completes. Launch this as a subsequent `Critic` subagent (`agent_type: "critic"`) with the full task statement, context snapshot, PRD/test-spec paths, and the completed Architect review; do not ask the Architect subagent to perform the Critic gate and do not substitute a default subagent fantasy prompt for the packaged Critic role. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
|
|
54
54
|
5. **Re-review loop** (max 5 iterations): Any non-`APPROVE` Critic verdict (`ITERATE` or `REJECT`) MUST run the same full closed loop:
|
|
55
55
|
a. Collect Architect and Critic feedback
|
|
56
56
|
b. Revise the plan with Planner
|
|
@@ -62,7 +62,7 @@ The consensus workflow:
|
|
|
62
62
|
7. *(--interactive only)* User chooses: Approve (`$ultragoal` durable goal execution, `$team`, explicit `$ralph` fallback, or a specialized goal-mode follow-up), Request changes, or Reject
|
|
63
63
|
8. *(--interactive only)* On approval: invoke `$ultragoal` for default durable sequential execution, `$team` for parallel team execution, the selected specialized goal-mode follow-up (`$autoresearch-goal` or `$performance-goal`), or `$ralph` only when the user explicitly selects that fallback with the approved plan and matching success/evaluator context -- never implement directly. Preserve the explicit available-agent-types roster, reasoning-by-lane guidance, role/staffing allocation guidance, launch hints, and verification-path guidance from the approved plan for Ultragoal/team paths and any explicit Ralph fallback.
|
|
64
64
|
|
|
65
|
-
> **Important:** Steps 3 and 4 MUST run sequentially. Do NOT issue both agent calls in the same parallel batch. Always await the Architect result before invoking Critic.
|
|
65
|
+
> **Important:** Steps 3 and 4 MUST run sequentially as role-specific subagents. Do NOT issue both agent calls in the same parallel batch. Always await the subsequent `Architect` result before invoking the subsequent `Critic`; only a completed, role-specific `Critic` approval can satisfy the durable gate.
|
|
66
66
|
|
|
67
67
|
## Durable Consensus Handoff Contract
|
|
68
68
|
|
|
@@ -102,7 +102,7 @@ Before consensus planning or execution handoff, ensure a grounded context snapsh
|
|
|
102
102
|
- constraints
|
|
103
103
|
- unknowns/open questions
|
|
104
104
|
- likely codebase touchpoints
|
|
105
|
-
4. If ambiguity remains high, gather brownfield facts first.
|
|
105
|
+
4. If ambiguity remains high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick <task>` before continuing.
|
|
106
106
|
5. If the plan depends on official docs, version-aware framework guidance, best practices, or external dependency behavior, use `$best-practice-research` as the bounded evidence wrapper and auto-delegate `researcher` for the official/upstream lookup before finalizing the planning handoff so execution does not start from repo-local recall alone.
|
|
107
107
|
6. If a prior `$autoresearch` or `$autoresearch-goal` run exists, treat its approved artifact as evidence for the plan. Do not include Autoresearch as a final architecture or runtime component unless the user explicitly requested ongoing research automation; otherwise synthesize the evidence into the `$ralplan` ADR, risks, and verification steps.
|
|
108
108
|
|
|
@@ -66,6 +66,26 @@ async function writeJson(path: string, value: unknown): Promise<void> {
|
|
|
66
66
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
async function setTeamPaneIds(
|
|
70
|
+
cwd: string,
|
|
71
|
+
teamName: string,
|
|
72
|
+
paneIds: { leaderPaneId: string; workerPaneIds: Record<string, string> },
|
|
73
|
+
): Promise<void> {
|
|
74
|
+
for (const fileName of ["config.json", "manifest.v2.json"]) {
|
|
75
|
+
const filePath = join(cwd, ".omx", "state", "team", teamName, fileName);
|
|
76
|
+
const parsed = JSON.parse(await readFile(filePath, "utf-8")) as {
|
|
77
|
+
leader_pane_id?: string | null;
|
|
78
|
+
workers?: Array<{ name?: string; pane_id?: string | null }>;
|
|
79
|
+
};
|
|
80
|
+
parsed.leader_pane_id = paneIds.leaderPaneId;
|
|
81
|
+
parsed.workers = (parsed.workers ?? []).map((worker) => ({
|
|
82
|
+
...worker,
|
|
83
|
+
pane_id: worker.name ? paneIds.workerPaneIds[worker.name] ?? worker.pane_id ?? null : worker.pane_id ?? null,
|
|
84
|
+
}));
|
|
85
|
+
await writeJson(filePath, parsed);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
69
89
|
async function withIsolatedHome<T>(prefix: string, run: (homeDir: string) => Promise<T>): Promise<T> {
|
|
70
90
|
const homeDir = await mkdtemp(join(tmpdir(), `omx-native-hook-home-${prefix}-`));
|
|
71
91
|
const previousHome = process.env.HOME;
|
|
@@ -243,6 +263,7 @@ const DEFAULT_AUTO_NUDGE_RESPONSE =
|
|
|
243
263
|
|
|
244
264
|
const TEAM_ENV_KEYS = [
|
|
245
265
|
"OMX_TEAM_WORKER",
|
|
266
|
+
"OMX_TEAM_INTERNAL_WORKER",
|
|
246
267
|
"OMX_TEAM_STATE_ROOT",
|
|
247
268
|
"OMX_TEAM_LEADER_CWD",
|
|
248
269
|
"OMX_SESSION_ID",
|
|
@@ -2362,6 +2383,38 @@ standardMaxRounds = 15
|
|
|
2362
2383
|
}
|
|
2363
2384
|
});
|
|
2364
2385
|
|
|
2386
|
+
it("does not repeat ultragoal Stop recovery after a safe completed-aggregate microgoal blocker is recorded", async () => {
|
|
2387
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-aggregate-blocked-stop-"));
|
|
2388
|
+
try {
|
|
2389
|
+
await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
|
|
2390
|
+
version: 1,
|
|
2391
|
+
codexGoalMode: "aggregate",
|
|
2392
|
+
activeGoalId: "G001-demo",
|
|
2393
|
+
goals: [{
|
|
2394
|
+
id: "G001-demo",
|
|
2395
|
+
status: "in_progress",
|
|
2396
|
+
objective: "Demo goal",
|
|
2397
|
+
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",
|
|
2398
|
+
}],
|
|
2399
|
+
});
|
|
2400
|
+
|
|
2401
|
+
const result = await dispatchCodexNativeHook({
|
|
2402
|
+
hook_event_name: "Stop",
|
|
2403
|
+
cwd,
|
|
2404
|
+
session_id: "sess-ultragoal-aggregate-blocked-stop",
|
|
2405
|
+
thread_id: "thread-ultragoal-aggregate-blocked-stop",
|
|
2406
|
+
stop_hook_active: true,
|
|
2407
|
+
last_assistant_message: "Goal complete.",
|
|
2408
|
+
}, { cwd });
|
|
2409
|
+
|
|
2410
|
+
assert.notEqual(result.outputJson?.decision, "block");
|
|
2411
|
+
assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
|
|
2412
|
+
assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
|
|
2413
|
+
} finally {
|
|
2414
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
|
|
2365
2418
|
|
|
2366
2419
|
it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
|
|
2367
2420
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
|
|
@@ -3852,6 +3905,198 @@ export async function onHookEvent(event) {
|
|
|
3852
3905
|
}
|
|
3853
3906
|
});
|
|
3854
3907
|
|
|
3908
|
+
it("skips prompt-submit HUD reconciliation for confirmed team worker panes", async () => {
|
|
3909
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-skip-"));
|
|
3910
|
+
try {
|
|
3911
|
+
const teamName = "hud-worker-skip";
|
|
3912
|
+
await initTeamState(teamName, "skip worker HUD reconcile", "executor", 1, cwd);
|
|
3913
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3914
|
+
leaderPaneId: "%42",
|
|
3915
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3916
|
+
});
|
|
3917
|
+
process.env.TMUX = "1";
|
|
3918
|
+
process.env.TMUX_PANE = "%10";
|
|
3919
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
3920
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
3921
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3922
|
+
|
|
3923
|
+
let reconcileCalls = 0;
|
|
3924
|
+
const result = await dispatchCodexNativeHook(
|
|
3925
|
+
{
|
|
3926
|
+
hook_event_name: "UserPromptSubmit",
|
|
3927
|
+
cwd,
|
|
3928
|
+
session_id: "sess-hud-team-worker",
|
|
3929
|
+
prompt: "$ralplan prepare plan",
|
|
3930
|
+
},
|
|
3931
|
+
{
|
|
3932
|
+
cwd,
|
|
3933
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
3934
|
+
reconcileCalls += 1;
|
|
3935
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
3936
|
+
},
|
|
3937
|
+
},
|
|
3938
|
+
);
|
|
3939
|
+
|
|
3940
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
3941
|
+
assert.equal(reconcileCalls, 0);
|
|
3942
|
+
} finally {
|
|
3943
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3944
|
+
}
|
|
3945
|
+
});
|
|
3946
|
+
|
|
3947
|
+
it("preserves prompt-submit HUD reconciliation for team leader panes", async () => {
|
|
3948
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-leader-preserve-"));
|
|
3949
|
+
try {
|
|
3950
|
+
const teamName = "hud-leader-keep";
|
|
3951
|
+
await initTeamState(teamName, "preserve leader HUD reconcile", "executor", 1, cwd);
|
|
3952
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3953
|
+
leaderPaneId: "%42",
|
|
3954
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3955
|
+
});
|
|
3956
|
+
process.env.TMUX = "1";
|
|
3957
|
+
process.env.TMUX_PANE = "%42";
|
|
3958
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3959
|
+
|
|
3960
|
+
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
3961
|
+
const result = await dispatchCodexNativeHook(
|
|
3962
|
+
{
|
|
3963
|
+
hook_event_name: "UserPromptSubmit",
|
|
3964
|
+
cwd,
|
|
3965
|
+
session_id: "sess-hud-team-leader",
|
|
3966
|
+
prompt: "$ralplan prepare plan",
|
|
3967
|
+
},
|
|
3968
|
+
{
|
|
3969
|
+
cwd,
|
|
3970
|
+
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
3971
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
3972
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
3973
|
+
},
|
|
3974
|
+
},
|
|
3975
|
+
);
|
|
3976
|
+
|
|
3977
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
3978
|
+
assert.deepEqual(reconcileCall, { cwd, sessionId: "sess-hud-team-leader" });
|
|
3979
|
+
} finally {
|
|
3980
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3981
|
+
}
|
|
3982
|
+
});
|
|
3983
|
+
|
|
3984
|
+
it("preserves prompt-submit HUD reconciliation when worker pane detection is ambiguous", async () => {
|
|
3985
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-ambiguous-"));
|
|
3986
|
+
try {
|
|
3987
|
+
const teamName = "hud-worker-ambiguous";
|
|
3988
|
+
await initTeamState(teamName, "fail closed for ambiguous worker HUD reconcile", "executor", 1, cwd);
|
|
3989
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
3990
|
+
leaderPaneId: "%42",
|
|
3991
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
3992
|
+
});
|
|
3993
|
+
process.env.TMUX = "1";
|
|
3994
|
+
process.env.TMUX_PANE = "%99";
|
|
3995
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
3996
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
3997
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
3998
|
+
|
|
3999
|
+
let reconcileCalls = 0;
|
|
4000
|
+
const result = await dispatchCodexNativeHook(
|
|
4001
|
+
{
|
|
4002
|
+
hook_event_name: "UserPromptSubmit",
|
|
4003
|
+
cwd,
|
|
4004
|
+
session_id: "sess-hud-team-worker-ambiguous",
|
|
4005
|
+
prompt: "$ralplan prepare plan",
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
cwd,
|
|
4009
|
+
reconcileHudForPromptSubmitFn: async () => {
|
|
4010
|
+
reconcileCalls += 1;
|
|
4011
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
4012
|
+
},
|
|
4013
|
+
},
|
|
4014
|
+
);
|
|
4015
|
+
|
|
4016
|
+
assert.equal(result.omxEventName, "keyword-detector");
|
|
4017
|
+
assert.equal(reconcileCalls, 1);
|
|
4018
|
+
} finally {
|
|
4019
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4020
|
+
}
|
|
4021
|
+
});
|
|
4022
|
+
|
|
4023
|
+
it("preserves prompt-submit HUD reconciliation for native subagents even with worker pane env", async () => {
|
|
4024
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-subagent-worker-preserve-"));
|
|
4025
|
+
try {
|
|
4026
|
+
const teamName = "hud-subagent-keep";
|
|
4027
|
+
await initTeamState(teamName, "preserve subagent HUD reconcile", "executor", 1, cwd);
|
|
4028
|
+
await setTeamPaneIds(cwd, teamName, {
|
|
4029
|
+
leaderPaneId: "%42",
|
|
4030
|
+
workerPaneIds: { "worker-1": "%10" },
|
|
4031
|
+
});
|
|
4032
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
4033
|
+
const canonicalSessionId = "sess-subagent-hud-parent";
|
|
4034
|
+
const leaderNativeSessionId = "native-subagent-hud-parent";
|
|
4035
|
+
const childNativeSessionId = "native-subagent-hud-child";
|
|
4036
|
+
const nowIso = new Date().toISOString();
|
|
4037
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
4038
|
+
session_id: canonicalSessionId,
|
|
4039
|
+
native_session_id: leaderNativeSessionId,
|
|
4040
|
+
});
|
|
4041
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
4042
|
+
schemaVersion: 1,
|
|
4043
|
+
sessions: {
|
|
4044
|
+
[canonicalSessionId]: {
|
|
4045
|
+
session_id: canonicalSessionId,
|
|
4046
|
+
leader_thread_id: leaderNativeSessionId,
|
|
4047
|
+
updated_at: nowIso,
|
|
4048
|
+
threads: {
|
|
4049
|
+
[leaderNativeSessionId]: {
|
|
4050
|
+
thread_id: leaderNativeSessionId,
|
|
4051
|
+
kind: "leader",
|
|
4052
|
+
first_seen_at: nowIso,
|
|
4053
|
+
last_seen_at: nowIso,
|
|
4054
|
+
turn_count: 1,
|
|
4055
|
+
},
|
|
4056
|
+
[childNativeSessionId]: {
|
|
4057
|
+
thread_id: childNativeSessionId,
|
|
4058
|
+
kind: "subagent",
|
|
4059
|
+
first_seen_at: nowIso,
|
|
4060
|
+
last_seen_at: nowIso,
|
|
4061
|
+
turn_count: 1,
|
|
4062
|
+
mode: "verifier",
|
|
4063
|
+
},
|
|
4064
|
+
},
|
|
4065
|
+
},
|
|
4066
|
+
},
|
|
4067
|
+
});
|
|
4068
|
+
process.env.TMUX = "1";
|
|
4069
|
+
process.env.TMUX_PANE = "%10";
|
|
4070
|
+
process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
|
|
4071
|
+
process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
|
|
4072
|
+
process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
|
|
4073
|
+
|
|
4074
|
+
let reconcileCall: { cwd: string; sessionId?: string } | null = null;
|
|
4075
|
+
const result = await dispatchCodexNativeHook(
|
|
4076
|
+
{
|
|
4077
|
+
hook_event_name: "UserPromptSubmit",
|
|
4078
|
+
cwd,
|
|
4079
|
+
session_id: childNativeSessionId,
|
|
4080
|
+
thread_id: childNativeSessionId,
|
|
4081
|
+
turn_id: "turn-subagent-hud-child",
|
|
4082
|
+
prompt: "Review the worker patch literally; do not activate $ralplan.",
|
|
4083
|
+
},
|
|
4084
|
+
{
|
|
4085
|
+
cwd,
|
|
4086
|
+
reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
|
|
4087
|
+
reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
|
|
4088
|
+
return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
|
|
4089
|
+
},
|
|
4090
|
+
},
|
|
4091
|
+
);
|
|
4092
|
+
|
|
4093
|
+
assert.equal(result.outputJson, null);
|
|
4094
|
+
assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId });
|
|
4095
|
+
} finally {
|
|
4096
|
+
await rm(cwd, { recursive: true, force: true });
|
|
4097
|
+
}
|
|
4098
|
+
});
|
|
4099
|
+
|
|
3855
4100
|
it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => {
|
|
3856
4101
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-"));
|
|
3857
4102
|
const originalTmux = process.env.TMUX;
|
|
@@ -9264,6 +9509,70 @@ exit 0
|
|
|
9264
9509
|
}
|
|
9265
9510
|
});
|
|
9266
9511
|
|
|
9512
|
+
it("does not report ralplan subagent waiting when notify-fallback already recorded completion", async () => {
|
|
9513
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-complete-"));
|
|
9514
|
+
try {
|
|
9515
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
9516
|
+
const now = new Date().toISOString();
|
|
9517
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent-complete"), { recursive: true });
|
|
9518
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent-complete" });
|
|
9519
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "skill-active-state.json"), {
|
|
9520
|
+
active: true,
|
|
9521
|
+
skill: "ralplan",
|
|
9522
|
+
phase: "planning",
|
|
9523
|
+
});
|
|
9524
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "ralplan-state.json"), {
|
|
9525
|
+
active: true,
|
|
9526
|
+
current_phase: "planning",
|
|
9527
|
+
});
|
|
9528
|
+
await writeJson(join(stateDir, "subagent-tracking.json"), {
|
|
9529
|
+
schemaVersion: 1,
|
|
9530
|
+
sessions: {
|
|
9531
|
+
"sess-stop-skill-subagent-complete": {
|
|
9532
|
+
session_id: "sess-stop-skill-subagent-complete",
|
|
9533
|
+
leader_thread_id: "leader-1",
|
|
9534
|
+
updated_at: now,
|
|
9535
|
+
threads: {
|
|
9536
|
+
"leader-1": {
|
|
9537
|
+
thread_id: "leader-1",
|
|
9538
|
+
kind: "leader",
|
|
9539
|
+
first_seen_at: now,
|
|
9540
|
+
last_seen_at: now,
|
|
9541
|
+
turn_count: 1,
|
|
9542
|
+
},
|
|
9543
|
+
"sub-1": {
|
|
9544
|
+
thread_id: "sub-1",
|
|
9545
|
+
kind: "subagent",
|
|
9546
|
+
first_seen_at: now,
|
|
9547
|
+
last_seen_at: now,
|
|
9548
|
+
completed_at: now,
|
|
9549
|
+
last_completed_turn_id: "turn-complete-1",
|
|
9550
|
+
completion_source: "notify-fallback-watcher",
|
|
9551
|
+
turn_count: 2,
|
|
9552
|
+
},
|
|
9553
|
+
},
|
|
9554
|
+
},
|
|
9555
|
+
},
|
|
9556
|
+
});
|
|
9557
|
+
|
|
9558
|
+
const result = await dispatchCodexNativeHook(
|
|
9559
|
+
{
|
|
9560
|
+
hook_event_name: "Stop",
|
|
9561
|
+
cwd,
|
|
9562
|
+
session_id: "sess-stop-skill-subagent-complete",
|
|
9563
|
+
},
|
|
9564
|
+
{ cwd },
|
|
9565
|
+
);
|
|
9566
|
+
|
|
9567
|
+
assert.equal(result.omxEventName, "stop");
|
|
9568
|
+
assert.equal(result.outputJson?.decision, "block");
|
|
9569
|
+
assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/);
|
|
9570
|
+
assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
|
|
9571
|
+
} finally {
|
|
9572
|
+
await rm(cwd, { recursive: true, force: true });
|
|
9573
|
+
}
|
|
9574
|
+
});
|
|
9575
|
+
|
|
9267
9576
|
it("does not block on stale root ralplan skill when the explicit session-scoped canonical skill state is absent", async () => {
|
|
9268
9577
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-"));
|
|
9269
9578
|
try {
|
|
@@ -13833,3 +14142,62 @@ describe("codex native hook triage integration", () => {
|
|
|
13833
14142
|
}
|
|
13834
14143
|
});
|
|
13835
14144
|
});
|
|
14145
|
+
|
|
14146
|
+
describe('native Stop autopilot deep-interview wait', () => {
|
|
14147
|
+
it('does not force continued execution while autopilot is waiting on a deep-interview omx question', async () => {
|
|
14148
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-native-hook-autopilot-question-wait-'));
|
|
14149
|
+
try {
|
|
14150
|
+
const sessionId = 'sess-autopilot-wait';
|
|
14151
|
+
const sessionDir = join(cwd, '.omx', 'state', 'sessions', sessionId);
|
|
14152
|
+
await writeJson(join(cwd, '.omx', 'state', 'session.json'), { session_id: sessionId });
|
|
14153
|
+
await writeJson(join(sessionDir, 'autopilot-state.json'), {
|
|
14154
|
+
mode: 'autopilot',
|
|
14155
|
+
active: true,
|
|
14156
|
+
current_phase: 'waiting-for-user',
|
|
14157
|
+
run_outcome: 'blocked_on_user',
|
|
14158
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14159
|
+
session_id: sessionId,
|
|
14160
|
+
state: {
|
|
14161
|
+
deep_interview_question: {
|
|
14162
|
+
status: 'waiting_for_user',
|
|
14163
|
+
source: 'omx-question',
|
|
14164
|
+
obligation_id: 'obligation-stop-1',
|
|
14165
|
+
previous_phase: 'deep-interview',
|
|
14166
|
+
},
|
|
14167
|
+
},
|
|
14168
|
+
});
|
|
14169
|
+
await writeJson(join(sessionDir, 'deep-interview-state.json'), {
|
|
14170
|
+
mode: 'deep-interview',
|
|
14171
|
+
active: false,
|
|
14172
|
+
current_phase: 'intent-first',
|
|
14173
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14174
|
+
run_outcome: 'blocked_on_user',
|
|
14175
|
+
session_id: sessionId,
|
|
14176
|
+
question_enforcement: {
|
|
14177
|
+
obligation_id: 'obligation-stop-1',
|
|
14178
|
+
source: 'omx-question',
|
|
14179
|
+
status: 'pending',
|
|
14180
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
14181
|
+
requested_at: '2026-04-19T00:00:00.000Z',
|
|
14182
|
+
},
|
|
14183
|
+
});
|
|
14184
|
+
await writeJson(join(sessionDir, 'skill-active-state.json'), {
|
|
14185
|
+
active: true,
|
|
14186
|
+
skill: 'autopilot',
|
|
14187
|
+
phase: 'deep-interview',
|
|
14188
|
+
session_id: sessionId,
|
|
14189
|
+
active_skills: [{ skill: 'autopilot', phase: 'deep-interview', active: true, session_id: sessionId }],
|
|
14190
|
+
});
|
|
14191
|
+
|
|
14192
|
+
const result = await dispatchCodexNativeHook({
|
|
14193
|
+
hook_event_name: 'Stop',
|
|
14194
|
+
session_id: sessionId,
|
|
14195
|
+
thread_id: 'thread-autopilot-wait',
|
|
14196
|
+
}, { cwd });
|
|
14197
|
+
|
|
14198
|
+
assert.equal(result.outputJson, null);
|
|
14199
|
+
} finally {
|
|
14200
|
+
await rm(cwd, { recursive: true, force: true });
|
|
14201
|
+
}
|
|
14202
|
+
});
|
|
14203
|
+
});
|
|
@@ -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.
|