oh-my-codex 0.18.1 → 0.18.3
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 +4 -2
- package/dist/agents/__tests__/definitions.test.js +23 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +20 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +40 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +1 -0
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +4 -0
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
- package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
- package/dist/auth/__tests__/config-sessions.test.js +48 -0
- package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
- package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
- package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
- package/dist/auth/__tests__/quota-rotation.test.js +33 -0
- package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
- package/dist/auth/__tests__/redact.test.d.ts +2 -0
- package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
- package/dist/auth/__tests__/redact.test.js +20 -0
- package/dist/auth/__tests__/redact.test.js.map +1 -0
- package/dist/auth/__tests__/storage.test.d.ts +2 -0
- package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
- package/dist/auth/__tests__/storage.test.js +108 -0
- package/dist/auth/__tests__/storage.test.js.map +1 -0
- package/dist/auth/config.d.ts +9 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +77 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/hotswap.d.ts +36 -0
- package/dist/auth/hotswap.d.ts.map +1 -0
- package/dist/auth/hotswap.js +159 -0
- package/dist/auth/hotswap.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/paths.d.ts +12 -0
- package/dist/auth/paths.d.ts.map +1 -0
- package/dist/auth/paths.js +78 -0
- package/dist/auth/paths.js.map +1 -0
- package/dist/auth/quota-detector.d.ts +10 -0
- package/dist/auth/quota-detector.d.ts.map +1 -0
- package/dist/auth/quota-detector.js +40 -0
- package/dist/auth/quota-detector.js.map +1 -0
- package/dist/auth/redact.d.ts +2 -0
- package/dist/auth/redact.d.ts.map +1 -0
- package/dist/auth/redact.js +26 -0
- package/dist/auth/redact.js.map +1 -0
- package/dist/auth/rotation.d.ts +9 -0
- package/dist/auth/rotation.d.ts.map +1 -0
- package/dist/auth/rotation.js +26 -0
- package/dist/auth/rotation.js.map +1 -0
- package/dist/auth/sessions.d.ts +15 -0
- package/dist/auth/sessions.d.ts.map +1 -0
- package/dist/auth/sessions.js +62 -0
- package/dist/auth/sessions.js.map +1 -0
- package/dist/auth/storage.d.ts +27 -0
- package/dist/auth/storage.d.ts.map +1 -0
- package/dist/auth/storage.js +111 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/catalog/__tests__/generator.test.js +4 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.d.ts +2 -0
- package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
- package/dist/cli/__tests__/auth.test.js +168 -0
- package/dist/cli/__tests__/auth.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +112 -5
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +20 -0
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +171 -21
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +51 -3
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +2 -2
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +47 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/auth.d.ts +4 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +89 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +190 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +12 -0
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +27 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +245 -47
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +11 -3
- package/dist/cli/setup.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +3 -3
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
- package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
- package/dist/config/__tests__/deep-interview.test.js +239 -0
- package/dist/config/__tests__/deep-interview.test.js.map +1 -0
- package/dist/config/__tests__/generator-idempotent.test.js +123 -0
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +1 -0
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +2 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/deep-interview.d.ts +22 -0
- package/dist/config/deep-interview.d.ts.map +1 -0
- package/dist/config/deep-interview.js +151 -0
- package/dist/config/deep-interview.js.map +1 -0
- package/dist/config/generator.d.ts +19 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +198 -29
- package/dist/config/generator.js.map +1 -1
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +3 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +45 -2
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +2 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +17 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explore-routing.test.js +1 -0
- package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +471 -15
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
- package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
- package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
- package/dist/hooks/deep-interview-config-instruction.js +47 -0
- package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
- package/dist/hooks/explore-routing.d.ts.map +1 -1
- package/dist/hooks/explore-routing.js +1 -0
- package/dist/hooks/explore-routing.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +6 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +80 -14
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +11 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +22 -0
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +213 -17
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +84 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +51 -1
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +171 -23
- package/dist/hud/__tests__/tmux.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 +8 -3
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +14 -3
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +26 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts +2 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +62 -1
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +17 -3
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +96 -10
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +22 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +63 -1
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +410 -4
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +29 -2
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +41 -6
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +43 -10
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/deep-interview.d.ts +2 -0
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/ui.d.ts +12 -0
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +83 -46
- package/dist/question/ui.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +200 -10
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +23 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -0
- package/dist/ralplan/consensus-gate.js +212 -0
- package/dist/ralplan/consensus-gate.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +25 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +144 -8
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1034 -28
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
- package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
- package/dist/scripts/__tests__/run-test-files.test.js +57 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +2 -2
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +238 -36
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +188 -4
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/run-test-files.js +13 -0
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
- package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
- package/dist/state/__tests__/planning-gate.test.js +219 -0
- package/dist/state/__tests__/planning-gate.test.js.map +1 -0
- package/dist/state/__tests__/workflow-transition.test.js +6 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/workflow-transition.d.ts +24 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +70 -0
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +4 -3
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +36 -44
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +144 -18
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +10 -20
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +22 -6
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +50 -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 +28 -2
- 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 +16 -4
- package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +10 -0
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +24 -5
- package/prompts/prometheus-strict-metis.md +274 -0
- package/prompts/prometheus-strict-momus.md +82 -0
- package/prompts/prometheus-strict-oracle.md +107 -0
- package/prompts/researcher.md +22 -3
- package/prompts/scholastic.md +11 -0
- package/skills/autopilot/SKILL.md +16 -4
- package/skills/autoresearch/SKILL.md +4 -0
- package/skills/autoresearch-goal/SKILL.md +1 -1
- package/skills/best-practice-research/SKILL.md +1 -1
- package/skills/deep-interview/SKILL.md +10 -0
- package/skills/pipeline/SKILL.md +1 -1
- package/skills/plan/SKILL.md +1 -1
- package/skills/prometheus-strict/README.md +35 -0
- package/skills/prometheus-strict/SKILL.md +219 -0
- package/skills/ralplan/SKILL.md +24 -5
- package/src/scripts/__tests__/codex-native-hook.test.ts +1307 -61
- package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
- package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
- package/src/scripts/__tests__/run-test-files.test.ts +67 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +2 -2
- package/src/scripts/codex-native-hook.ts +260 -31
- package/src/scripts/notify-dispatcher.ts +202 -4
- package/src/scripts/run-test-files.ts +13 -0
- package/templates/catalog-manifest.json +27 -0
|
@@ -3,8 +3,9 @@ import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
|
|
|
3
3
|
import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promises";
|
|
4
4
|
import { extname, join, relative, resolve } from "path";
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
|
-
import {
|
|
6
|
+
import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
7
7
|
import {
|
|
8
|
+
SKILL_ACTIVE_STATE_FILE,
|
|
8
9
|
extractSessionIdFromInitializedStatePath,
|
|
9
10
|
getSkillActiveStatePathsForStateDir,
|
|
10
11
|
listActiveSkills,
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
} from "../state/skill-active.js";
|
|
15
16
|
import {
|
|
16
17
|
readSubagentSessionSummary,
|
|
18
|
+
readSubagentTrackingState,
|
|
17
19
|
recordSubagentTurnForSession,
|
|
18
20
|
} from "../subagents/tracker.js";
|
|
19
21
|
import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
|
|
@@ -42,6 +44,7 @@ import {
|
|
|
42
44
|
recordSkillActivation,
|
|
43
45
|
type SkillActiveState,
|
|
44
46
|
} from "../hooks/keyword-detector.js";
|
|
47
|
+
import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
|
|
45
48
|
import {
|
|
46
49
|
detectNativeStopStallPattern,
|
|
47
50
|
loadAutoNudgeConfig,
|
|
@@ -297,18 +300,32 @@ async function isNativeSubagentHook(
|
|
|
297
300
|
nativeSessionId: string,
|
|
298
301
|
threadId: string,
|
|
299
302
|
): Promise<boolean> {
|
|
300
|
-
const sessionId = canonicalSessionId.trim();
|
|
301
|
-
if (!sessionId) return false;
|
|
302
|
-
|
|
303
|
-
const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
304
|
-
if (!summary) return false;
|
|
305
|
-
|
|
306
303
|
const candidateIds = [nativeSessionId, threadId]
|
|
307
304
|
.map((value) => value.trim())
|
|
308
305
|
.filter(Boolean);
|
|
309
306
|
if (candidateIds.length === 0) return false;
|
|
310
307
|
|
|
311
|
-
|
|
308
|
+
const sessionId = canonicalSessionId.trim();
|
|
309
|
+
if (sessionId) {
|
|
310
|
+
const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
311
|
+
if (summary && candidateIds.some((id) => summary.allSubagentThreadIds.includes(id))) {
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Native Codex resume can report the child native session as the canonical
|
|
317
|
+
// session id before OMX reconciles it back to the owning session. In that
|
|
318
|
+
// window the per-session summary lookup above misses the child and a
|
|
319
|
+
// subagent UserPromptSubmit can accidentally activate workflow keywords from
|
|
320
|
+
// quoted review context. Fall back to the global tracking index so any known
|
|
321
|
+
// subagent thread is treated as subagent-scoped, regardless of the current
|
|
322
|
+
// hook payload's session-id mapping.
|
|
323
|
+
const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
|
|
324
|
+
if (!trackingState) return false;
|
|
325
|
+
|
|
326
|
+
return Object.values(trackingState.sessions).some((session) => (
|
|
327
|
+
candidateIds.some((id) => session.threads[id]?.kind === "subagent")
|
|
328
|
+
));
|
|
312
329
|
}
|
|
313
330
|
|
|
314
331
|
function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
|
|
@@ -1683,6 +1700,17 @@ function buildSkillStateCliInstruction(mode: string, statePath: string): string
|
|
|
1683
1700
|
return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
|
|
1684
1701
|
}
|
|
1685
1702
|
|
|
1703
|
+
function buildAutopilotPromptActivationNote(skillState?: SkillActiveState | null): string | null {
|
|
1704
|
+
if (skillState?.initialized_mode !== "autopilot") return null;
|
|
1705
|
+
return [
|
|
1706
|
+
"Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal (+ $team if needed) -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).",
|
|
1707
|
+
"Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
|
|
1708
|
+
"The ralplan phase is not complete until Planner output has been reviewed sequentially by Architect and then Critic; do not hand off to Ultragoal or implementation until the ralplan state/artifact records both ralplan_architect_review and ralplan_critic_review with approval or an explicit blocker.",
|
|
1709
|
+
"Do not silently fall back to ordinary $plan/ralplan-only handling; keep autopilot-state.json, skill-active-state.json, HUD/statusline, and Codex goal-mode handoff guidance visible while the workflow is active.",
|
|
1710
|
+
"When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
|
|
1711
|
+
].join(" ");
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1686
1714
|
function buildAdditionalContextMessage(
|
|
1687
1715
|
prompt: string,
|
|
1688
1716
|
skillState?: SkillActiveState | null,
|
|
@@ -1693,7 +1721,24 @@ function buildAdditionalContextMessage(
|
|
|
1693
1721
|
const promptPriorityMessage = buildPromptPriorityMessage(prompt);
|
|
1694
1722
|
const matches = detectKeywords(prompt);
|
|
1695
1723
|
const match = detectPrimaryKeyword(prompt);
|
|
1696
|
-
if (!match)
|
|
1724
|
+
if (!match) {
|
|
1725
|
+
const continuedSkill = safeString(skillState?.skill).trim();
|
|
1726
|
+
if (!continuedSkill) return promptPriorityMessage;
|
|
1727
|
+
const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
|
|
1728
|
+
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
1729
|
+
: null;
|
|
1730
|
+
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1731
|
+
return [
|
|
1732
|
+
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
|
|
1733
|
+
promptPriorityMessage,
|
|
1734
|
+
skillState?.initialized_mode && skillState.initialized_state_path
|
|
1735
|
+
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
1736
|
+
: null,
|
|
1737
|
+
deepInterviewPromptActivationNote,
|
|
1738
|
+
deepInterviewConfigPromptActivationNote,
|
|
1739
|
+
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1740
|
+
].filter(Boolean).join(" ");
|
|
1741
|
+
}
|
|
1697
1742
|
const detectedKeywordMessage = matches.length > 1
|
|
1698
1743
|
? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
|
|
1699
1744
|
: `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
|
|
@@ -1710,12 +1755,14 @@ function buildAdditionalContextMessage(
|
|
|
1710
1755
|
const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
|
|
1711
1756
|
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
1712
1757
|
: null;
|
|
1758
|
+
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1713
1759
|
const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
|
|
1714
1760
|
? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
|
|
1715
1761
|
: null;
|
|
1716
1762
|
const ultragoalPromptActivationNote = match.skill === "ultragoal"
|
|
1717
1763
|
? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
|
|
1718
1764
|
: null;
|
|
1765
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
|
|
1719
1766
|
const combinedTransitionMessage = (() => {
|
|
1720
1767
|
if (!skillState?.transition_message) return null;
|
|
1721
1768
|
if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
|
|
@@ -1743,6 +1790,8 @@ function buildAdditionalContextMessage(
|
|
|
1743
1790
|
: null,
|
|
1744
1791
|
promptPriorityMessage,
|
|
1745
1792
|
ultragoalPromptActivationNote,
|
|
1793
|
+
autopilotPromptActivationNote,
|
|
1794
|
+
deepInterviewConfigPromptActivationNote,
|
|
1746
1795
|
skillState.initialized_mode && skillState.initialized_state_path
|
|
1747
1796
|
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
1748
1797
|
: null,
|
|
@@ -1767,8 +1816,10 @@ function buildAdditionalContextMessage(
|
|
|
1767
1816
|
promptPriorityMessage,
|
|
1768
1817
|
initializedStateMessage,
|
|
1769
1818
|
deepInterviewPromptActivationNote,
|
|
1819
|
+
deepInterviewConfigPromptActivationNote,
|
|
1770
1820
|
ultraworkPromptActivationNote,
|
|
1771
1821
|
ultragoalPromptActivationNote,
|
|
1822
|
+
autopilotPromptActivationNote,
|
|
1772
1823
|
buildTeamRuntimeInstruction(cwd, payload),
|
|
1773
1824
|
buildTeamHelpInstruction(cwd, payload),
|
|
1774
1825
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
@@ -1785,14 +1836,16 @@ function buildAdditionalContextMessage(
|
|
|
1785
1836
|
promptPriorityMessage,
|
|
1786
1837
|
buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
|
|
1787
1838
|
deepInterviewPromptActivationNote,
|
|
1839
|
+
deepInterviewConfigPromptActivationNote,
|
|
1788
1840
|
ultraworkPromptActivationNote,
|
|
1789
1841
|
ultragoalPromptActivationNote,
|
|
1842
|
+
autopilotPromptActivationNote,
|
|
1790
1843
|
ralphPromptActivationNote,
|
|
1791
1844
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1792
1845
|
].join(" ");
|
|
1793
1846
|
}
|
|
1794
1847
|
|
|
1795
|
-
return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
|
|
1848
|
+
return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, autopilotPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
|
|
1796
1849
|
}
|
|
1797
1850
|
|
|
1798
1851
|
function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
|
|
@@ -2041,6 +2094,7 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
|
|
|
2041
2094
|
`If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
|
|
2042
2095
|
`If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
|
|
2043
2096
|
`Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
|
|
2097
|
+
`If get_goal itself is unavailable with a Codex DB/schema/context error such as "no such table: thread_goals", record an auditable safe-recovery blocker instead: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<unavailable get_goal error JSON or path>' --evidence '<get_goal unavailable due to Codex DB/schema/context error; safe recovery requires a working Codex goal context>'.`,
|
|
2044
2098
|
"Then continue only from a Codex goal context with no active/completed conflicting goal in the same repo/worktree and create the intended goal there.",
|
|
2045
2099
|
].join(" "),
|
|
2046
2100
|
};
|
|
@@ -2127,36 +2181,60 @@ async function buildGoalWorkflowReconciliationStopOutput(
|
|
|
2127
2181
|
};
|
|
2128
2182
|
}
|
|
2129
2183
|
|
|
2184
|
+
interface TeamModeStateForStop {
|
|
2185
|
+
state: Record<string, unknown>;
|
|
2186
|
+
scope: "session" | "root";
|
|
2187
|
+
}
|
|
2188
|
+
|
|
2189
|
+
function teamStateMatchesThreadForStop(
|
|
2190
|
+
state: Record<string, unknown>,
|
|
2191
|
+
threadId?: string,
|
|
2192
|
+
options: { requireOwnerThread?: boolean } = {},
|
|
2193
|
+
): boolean {
|
|
2194
|
+
const normalizedThreadId = safeString(threadId).trim();
|
|
2195
|
+
if (!normalizedThreadId) return true;
|
|
2196
|
+
|
|
2197
|
+
const ownerThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
|
|
2198
|
+
if (!ownerThreadId) return options.requireOwnerThread !== true;
|
|
2199
|
+
return ownerThreadId === normalizedThreadId;
|
|
2200
|
+
}
|
|
2201
|
+
|
|
2130
2202
|
async function readTeamModeStateForStop(
|
|
2131
2203
|
cwd: string,
|
|
2132
2204
|
stateDir: string,
|
|
2133
2205
|
sessionId?: string,
|
|
2134
|
-
|
|
2206
|
+
threadId?: string,
|
|
2207
|
+
): Promise<TeamModeStateForStop | null> {
|
|
2135
2208
|
const normalizedSessionId = safeString(sessionId).trim();
|
|
2136
|
-
if (!normalizedSessionId)
|
|
2137
|
-
return await readModeState("team", cwd);
|
|
2138
|
-
}
|
|
2209
|
+
if (!normalizedSessionId) return null;
|
|
2139
2210
|
|
|
2140
2211
|
const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
|
|
2141
|
-
if (scopedState)
|
|
2212
|
+
if (scopedState) {
|
|
2213
|
+
return teamStateMatchesThreadForStop(scopedState, threadId)
|
|
2214
|
+
? { state: scopedState, scope: "session" }
|
|
2215
|
+
: null;
|
|
2216
|
+
}
|
|
2142
2217
|
|
|
2143
2218
|
const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
|
|
2144
2219
|
if (rootState?.active !== true) return null;
|
|
2145
2220
|
|
|
2221
|
+
const teamName = safeString(rootState.team_name).trim();
|
|
2222
|
+
if (!teamName) return null;
|
|
2223
|
+
|
|
2146
2224
|
const ownerSessionId = safeString(rootState.session_id).trim();
|
|
2147
|
-
if (ownerSessionId
|
|
2148
|
-
|
|
2149
|
-
}
|
|
2225
|
+
if (!ownerSessionId || ownerSessionId !== normalizedSessionId) return null;
|
|
2226
|
+
if (!teamStateMatchesThreadForStop(rootState, threadId, { requireOwnerThread: true })) return null;
|
|
2150
2227
|
|
|
2151
|
-
return rootState;
|
|
2228
|
+
return { state: rootState, scope: "root" };
|
|
2152
2229
|
}
|
|
2153
2230
|
|
|
2154
|
-
async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Record<string, unknown> | null> {
|
|
2231
|
+
async function buildTeamStopOutput(cwd: string, sessionId?: string, threadId?: string): Promise<Record<string, unknown> | null> {
|
|
2155
2232
|
if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
|
|
2156
2233
|
return null;
|
|
2157
2234
|
}
|
|
2158
|
-
const
|
|
2159
|
-
if (
|
|
2235
|
+
const teamStateForStop = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId, threadId);
|
|
2236
|
+
if (!teamStateForStop || teamStateForStop.state.active !== true) return null;
|
|
2237
|
+
const teamState = teamStateForStop.state;
|
|
2160
2238
|
const teamName = safeString(teamState.team_name).trim();
|
|
2161
2239
|
if (teamName) {
|
|
2162
2240
|
const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
|
|
@@ -2165,7 +2243,9 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
|
|
|
2165
2243
|
}
|
|
2166
2244
|
}
|
|
2167
2245
|
const coarsePhase = teamState.current_phase;
|
|
2168
|
-
const
|
|
2246
|
+
const canonicalPhaseState = teamName ? await readTeamPhase(teamName, cwd) : null;
|
|
2247
|
+
if (teamStateForStop.scope === "root" && !canonicalPhaseState) return null;
|
|
2248
|
+
const canonicalPhase = canonicalPhaseState?.current_phase ?? coarsePhase;
|
|
2169
2249
|
if (!isNonTerminalPhase(canonicalPhase)) return null;
|
|
2170
2250
|
return buildTeamStopOutputForPhase(teamName, formatPhase(canonicalPhase));
|
|
2171
2251
|
}
|
|
@@ -2272,6 +2352,151 @@ async function readStopSessionPinnedState(
|
|
|
2272
2352
|
return readJsonIfExists(statePath);
|
|
2273
2353
|
}
|
|
2274
2354
|
|
|
2355
|
+
const DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES = [
|
|
2356
|
+
".omx/context",
|
|
2357
|
+
".omx/interviews",
|
|
2358
|
+
".omx/specs",
|
|
2359
|
+
".omx/state",
|
|
2360
|
+
] as const;
|
|
2361
|
+
|
|
2362
|
+
const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = new Set([
|
|
2363
|
+
"Write",
|
|
2364
|
+
"Edit",
|
|
2365
|
+
"MultiEdit",
|
|
2366
|
+
"apply_patch",
|
|
2367
|
+
"ApplyPatch",
|
|
2368
|
+
]);
|
|
2369
|
+
|
|
2370
|
+
function isActiveDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
|
|
2371
|
+
if (!state || state.active !== true) return false;
|
|
2372
|
+
const mode = safeString(state.mode).trim();
|
|
2373
|
+
if (mode && mode !== "deep-interview") return false;
|
|
2374
|
+
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
2375
|
+
if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing")) return false;
|
|
2376
|
+
return true;
|
|
2377
|
+
}
|
|
2378
|
+
|
|
2379
|
+
function isAllowedDeepInterviewArtifactPath(cwd: string, rawPath: string): boolean {
|
|
2380
|
+
const trimmed = rawPath.trim().replace(/^['"]|['"]$/g, "");
|
|
2381
|
+
if (!trimmed || trimmed.includes("\0")) return false;
|
|
2382
|
+
let relativePath: string;
|
|
2383
|
+
try {
|
|
2384
|
+
const absolute = resolve(cwd, trimmed);
|
|
2385
|
+
relativePath = relative(cwd, absolute).replace(/\\/g, "/");
|
|
2386
|
+
} catch {
|
|
2387
|
+
return false;
|
|
2388
|
+
}
|
|
2389
|
+
if (!relativePath || relativePath.startsWith("..") || relativePath.startsWith("/")) return false;
|
|
2390
|
+
return DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES.some((prefix) => (
|
|
2391
|
+
relativePath === prefix || relativePath.startsWith(`${prefix}/`)
|
|
2392
|
+
));
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
function readPreToolUseCommand(payload: CodexHookPayload): string {
|
|
2396
|
+
const toolInput = safeObject(payload.tool_input);
|
|
2397
|
+
return safeString(toolInput.command).trim();
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
function readPreToolUsePathCandidates(payload: CodexHookPayload): string[] {
|
|
2401
|
+
const input = safeObject(payload.tool_input);
|
|
2402
|
+
const candidates = [
|
|
2403
|
+
input.file_path,
|
|
2404
|
+
input.filePath,
|
|
2405
|
+
input.path,
|
|
2406
|
+
input.target_path,
|
|
2407
|
+
input.targetPath,
|
|
2408
|
+
];
|
|
2409
|
+
return candidates.map((candidate) => safeString(candidate).trim()).filter(Boolean);
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
function commandHasDeepInterviewWriteIntent(command: string): boolean {
|
|
2413
|
+
return /\bapply_patch\b/.test(command)
|
|
2414
|
+
|| /(?:^|[;&|]\s*)(?:cat|printf|echo)\b[\s\S]{0,240}>\s*[^\s&|;]+/.test(command)
|
|
2415
|
+
|| /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
|
|
2416
|
+
|| /\bsed\s+(?:[^\n;&|]*\s)?-i(?:\b|['"])/.test(command)
|
|
2417
|
+
|| /\b(?:python3?|node|perl|ruby)\b[\s\S]{0,260}\b(?:writeFileSync|writeFile|write_text|open\([^)]*["']w|File\.write|Path\()/.test(command)
|
|
2418
|
+
|| /\b(?:git\s+(?:checkout|switch|restore|reset|apply|am|merge|rebase)|npm\s+(?:install|i|ci)|pnpm\s+(?:install|i)|yarn\s+(?:install|add))\b/.test(command);
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
function extractDeepInterviewCommandWriteTargets(command: string): string[] {
|
|
2422
|
+
const targets: string[] = [];
|
|
2423
|
+
for (const match of command.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
|
|
2424
|
+
const candidate = safeString(match[2]).trim();
|
|
2425
|
+
if (candidate) targets.push(candidate);
|
|
2426
|
+
}
|
|
2427
|
+
for (const match of command.matchAll(/\btee\s+(?:-a\s+)?(["']?)([^\s&|;<>]+)\1/g)) {
|
|
2428
|
+
const candidate = safeString(match[2]).trim();
|
|
2429
|
+
if (candidate) targets.push(candidate);
|
|
2430
|
+
}
|
|
2431
|
+
return targets;
|
|
2432
|
+
}
|
|
2433
|
+
|
|
2434
|
+
function isAllowedDeepInterviewBashWrite(cwd: string, command: string): boolean {
|
|
2435
|
+
if (!commandHasDeepInterviewWriteIntent(command)) return true;
|
|
2436
|
+
if (/\bomx\s+(?:state\s+(?:write|read|clear)|question)\b/.test(command)) return true;
|
|
2437
|
+
const targets = extractDeepInterviewCommandWriteTargets(command);
|
|
2438
|
+
return targets.length > 0 && targets.every((target) => isAllowedDeepInterviewArtifactPath(cwd, target));
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2441
|
+
async function readActiveDeepInterviewStateForPreToolUse(
|
|
2442
|
+
cwd: string,
|
|
2443
|
+
stateDir: string,
|
|
2444
|
+
sessionId: string,
|
|
2445
|
+
threadId: string,
|
|
2446
|
+
): Promise<Record<string, unknown> | null> {
|
|
2447
|
+
const modeState = sessionId
|
|
2448
|
+
? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
|
|
2449
|
+
: await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
|
|
2450
|
+
if (!isActiveDeepInterviewPhase(modeState) || !modeState) return null;
|
|
2451
|
+
if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) return null;
|
|
2452
|
+
|
|
2453
|
+
const canonicalState = sessionId
|
|
2454
|
+
? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
|
|
2455
|
+
: await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
|
|
2456
|
+
if (!canonicalState) return modeState;
|
|
2457
|
+
const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
2458
|
+
entry.skill === "deep-interview"
|
|
2459
|
+
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
2460
|
+
));
|
|
2461
|
+
return hasActiveDeepInterviewSkill ? modeState : null;
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
2465
|
+
payload: CodexHookPayload,
|
|
2466
|
+
cwd: string,
|
|
2467
|
+
stateDir: string,
|
|
2468
|
+
): Promise<Record<string, unknown> | null> {
|
|
2469
|
+
const sessionId = readPayloadSessionId(payload);
|
|
2470
|
+
const threadId = readPayloadThreadId(payload);
|
|
2471
|
+
const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
2472
|
+
if (!activeState) return null;
|
|
2473
|
+
|
|
2474
|
+
const toolName = safeString(payload.tool_name).trim();
|
|
2475
|
+
const command = readPreToolUseCommand(payload);
|
|
2476
|
+
const pathCandidates = readPreToolUsePathCandidates(payload);
|
|
2477
|
+
let blocked = false;
|
|
2478
|
+
|
|
2479
|
+
if (toolName === "Bash") {
|
|
2480
|
+
blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
|
|
2481
|
+
} else if (DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
|
|
2482
|
+
blocked = pathCandidates.length === 0
|
|
2483
|
+
|| !pathCandidates.every((candidate) => isAllowedDeepInterviewArtifactPath(cwd, candidate));
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
if (!blocked) return null;
|
|
2487
|
+
|
|
2488
|
+
const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
|
|
2489
|
+
return {
|
|
2490
|
+
decision: "block",
|
|
2491
|
+
reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
|
|
2492
|
+
hookSpecificOutput: {
|
|
2493
|
+
hookEventName: "PreToolUse",
|
|
2494
|
+
additionalContext:
|
|
2495
|
+
"Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under `.omx/context/`, `.omx/interviews/`, `.omx/specs/`, or required `.omx/state/` files. To implement, first ask for or process an explicit transition such as `$ralplan`, `$autopilot`, `$ralph`, `$team`, or `$ultragoal`.",
|
|
2496
|
+
},
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2275
2500
|
function matchesSkillStopContext(
|
|
2276
2501
|
entry: { session_id?: string; thread_id?: string },
|
|
2277
2502
|
state: { session_id?: string; thread_id?: string },
|
|
@@ -2909,6 +3134,7 @@ async function returnPersistentStopBlock(
|
|
|
2909
3134
|
async function findCanonicalActiveTeamForSession(
|
|
2910
3135
|
cwd: string,
|
|
2911
3136
|
sessionId: string,
|
|
3137
|
+
threadId?: string,
|
|
2912
3138
|
): Promise<{ teamName: string; phase: string } | null> {
|
|
2913
3139
|
if (!sessionId.trim()) return null;
|
|
2914
3140
|
const teamsRoot = join(resolveCanonicalTeamStateRoot(cwd), "team");
|
|
@@ -2927,6 +3153,7 @@ async function findCanonicalActiveTeamForSession(
|
|
|
2927
3153
|
if (!manifest || !phaseState) continue;
|
|
2928
3154
|
const ownerSessionId = (manifest.leader?.session_id ?? "").trim();
|
|
2929
3155
|
if (ownerSessionId && ownerSessionId !== sessionId.trim()) continue;
|
|
3156
|
+
if (!teamStateMatchesThreadForStop(manifest.leader as unknown as Record<string, unknown>, threadId)) continue;
|
|
2930
3157
|
if (!isNonTerminalPhase(phaseState.current_phase)) continue;
|
|
2931
3158
|
|
|
2932
3159
|
return {
|
|
@@ -2942,12 +3169,13 @@ async function resolveActiveTeamNameForStop(
|
|
|
2942
3169
|
cwd: string,
|
|
2943
3170
|
stateDir: string,
|
|
2944
3171
|
sessionId: string,
|
|
3172
|
+
threadId?: string,
|
|
2945
3173
|
): Promise<string> {
|
|
2946
|
-
const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
|
|
2947
|
-
const directTeamName = safeString(directState?.team_name).trim();
|
|
2948
|
-
if (directState?.active === true && directTeamName) return directTeamName;
|
|
3174
|
+
const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId, threadId);
|
|
3175
|
+
const directTeamName = safeString(directState?.state.team_name).trim();
|
|
3176
|
+
if (directState?.state.active === true && directTeamName) return directTeamName;
|
|
2949
3177
|
|
|
2950
|
-
const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
|
|
3178
|
+
const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId, threadId);
|
|
2951
3179
|
return canonicalTeam?.teamName ?? "";
|
|
2952
3180
|
}
|
|
2953
3181
|
|
|
@@ -2959,7 +3187,7 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
|
|
|
2959
3187
|
): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
|
|
2960
3188
|
if (!sessionId) return { matched: false, output: null };
|
|
2961
3189
|
|
|
2962
|
-
const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
|
|
3190
|
+
const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId, readPayloadThreadId(payload));
|
|
2963
3191
|
if (!teamName) return { matched: false, output: null };
|
|
2964
3192
|
|
|
2965
3193
|
const explicitReleaseReadinessContext =
|
|
@@ -3288,7 +3516,7 @@ async function buildStopHookOutput(
|
|
|
3288
3516
|
);
|
|
3289
3517
|
if (releaseReadinessFinalizeResult.matched) return releaseReadinessFinalizeResult.output;
|
|
3290
3518
|
|
|
3291
|
-
const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
|
|
3519
|
+
const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId, threadId);
|
|
3292
3520
|
if (teamOutput) {
|
|
3293
3521
|
return await returnPersistentStopBlock(
|
|
3294
3522
|
payload,
|
|
@@ -3320,7 +3548,7 @@ async function buildStopHookOutput(
|
|
|
3320
3548
|
|
|
3321
3549
|
const canonicalTeam = await readCanonicalTerminalRunStateForStop(cwd, canonicalSessionId, "team")
|
|
3322
3550
|
? null
|
|
3323
|
-
: await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
|
|
3551
|
+
: await findCanonicalActiveTeamForSession(cwd, canonicalSessionId, threadId);
|
|
3324
3552
|
if (canonicalTeam) {
|
|
3325
3553
|
const canonicalTeamOutput = buildTeamStopOutputForPhase(
|
|
3326
3554
|
canonicalTeam.teamName,
|
|
@@ -3710,7 +3938,8 @@ export async function dispatchCodexNativeHook(
|
|
|
3710
3938
|
};
|
|
3711
3939
|
}
|
|
3712
3940
|
} else if (hookEventName === "PreToolUse") {
|
|
3713
|
-
outputJson =
|
|
3941
|
+
outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir)
|
|
3942
|
+
?? buildNativePreToolUseOutput(payload);
|
|
3714
3943
|
} else if (hookEventName === "PostToolUse") {
|
|
3715
3944
|
if (detectMcpTransportFailure(payload)) {
|
|
3716
3945
|
await markTeamTransportFailure(cwd, payload);
|