oh-my-codex 0.18.7 → 0.18.9
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 +12 -12
- package/Cargo.toml +1 -1
- package/README.md +5 -5
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/native-config.test.js +42 -1
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +8 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +1 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +17 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.js +3 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
- package/dist/autopilot/fsm.js +2 -2
- package/dist/autopilot/fsm.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +4 -2
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +98 -6
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +28 -8
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +26 -9
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +50 -1
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +65 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/update.test.js +323 -18
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +21 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +143 -28
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +22 -3
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +312 -26
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +5 -9
- package/dist/cli/version.js.map +1 -1
- package/dist/compat/__tests__/doctor-contract.test.js +12 -1
- package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +12 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +30 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +423 -3
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +258 -12
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +1 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +435 -32
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +42 -0
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +642 -15
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +160 -4
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +180 -21
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +324 -28
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +3 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +42 -19
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +128 -19
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +65 -80
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +24 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +136 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/question/__tests__/renderer.test.js +566 -1
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/renderer.d.ts +9 -1
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +246 -70
- package/dist/question/renderer.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +837 -101
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- 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 +107 -39
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +75 -11
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +3 -16
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +25 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +7 -39
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -14
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +195 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +142 -12
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/utils/__tests__/platform-command.test.js +16 -1
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/__tests__/version.test.d.ts +2 -0
- package/dist/utils/__tests__/version.test.d.ts.map +1 -0
- package/dist/utils/__tests__/version.test.js +51 -0
- package/dist/utils/__tests__/version.test.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +16 -4
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts +9 -0
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +15 -0
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/utils/version.d.ts +7 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +67 -0
- package/dist/utils/version.js.map +1 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js +89 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +16 -2
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/package.json +11 -10
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +51 -11
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/skills/autopilot/SKILL.md +3 -1
- package/skills/code-review/SKILL.md +7 -7
- package/skills/deep-interview/SKILL.md +51 -11
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ultraqa/SKILL.md +9 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +946 -98
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +123 -34
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +91 -4
- package/src/scripts/prepare-build.js +83 -0
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
- package/src/scripts/postinstall-bootstrap.js +0 -23
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
type SkillActiveState,
|
|
49
49
|
} from "../hooks/keyword-detector.js";
|
|
50
50
|
import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
|
|
51
|
+
import { readTeamModeConfig } from "../config/team-mode.js";
|
|
51
52
|
import {
|
|
52
53
|
detectNativeStopStallPattern,
|
|
53
54
|
loadAutoNudgeConfig,
|
|
@@ -82,6 +83,7 @@ import {
|
|
|
82
83
|
onSessionStart as buildWikiSessionStartContext,
|
|
83
84
|
} from "../wiki/lifecycle.js";
|
|
84
85
|
import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
|
|
86
|
+
import { normalizeAutopilotPhase } from "../autopilot/fsm.js";
|
|
85
87
|
import { readRunState } from "../runtime/run-state.js";
|
|
86
88
|
import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
|
|
87
89
|
import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
|
|
@@ -189,6 +191,25 @@ function resolveHudReconcileSessionId(
|
|
|
189
191
|
return canonicalSessionId || sessionIdForState || undefined;
|
|
190
192
|
}
|
|
191
193
|
|
|
194
|
+
function resolveHudReconcileSessionIds(
|
|
195
|
+
currentSessionState: SessionState | null,
|
|
196
|
+
canonicalSessionId: string | null,
|
|
197
|
+
sessionIdForState: string | null,
|
|
198
|
+
nativeSessionId: string | null,
|
|
199
|
+
): string[] {
|
|
200
|
+
const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
|
|
201
|
+
return uniqueNonEmpty([
|
|
202
|
+
resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState),
|
|
203
|
+
canonicalSessionId ?? undefined,
|
|
204
|
+
sessionIdForState ?? undefined,
|
|
205
|
+
nativeSessionId ?? undefined,
|
|
206
|
+
safeString(currentSessionState?.session_id),
|
|
207
|
+
safeString(currentSessionState?.native_session_id),
|
|
208
|
+
OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId) ? ownerOmxSessionId : undefined,
|
|
209
|
+
safeString(currentSessionState?.owner_codex_session_id),
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
212
|
+
|
|
192
213
|
function safeContextSnippet(value: unknown, maxLength = 300): string {
|
|
193
214
|
const text = safeString(value).replace(/\s+/g, " ").trim();
|
|
194
215
|
if (text.length <= maxLength) return text;
|
|
@@ -1730,6 +1751,7 @@ function buildNativeOutsideTmuxTeamPromptBlockState(
|
|
|
1730
1751
|
threadId?: string,
|
|
1731
1752
|
turnId?: string,
|
|
1732
1753
|
): SkillActiveState | null {
|
|
1754
|
+
if (!readTeamModeConfig(cwd).enabled) return null;
|
|
1733
1755
|
const match = detectPrimaryKeyword(prompt);
|
|
1734
1756
|
if (match?.skill !== "team") return null;
|
|
1735
1757
|
|
|
@@ -1765,11 +1787,14 @@ function buildSkillStateCliInstruction(mode: string, statePath: string): string
|
|
|
1765
1787
|
|
|
1766
1788
|
function buildAutopilotPromptActivationNote(
|
|
1767
1789
|
skillState?: SkillActiveState | null,
|
|
1768
|
-
options: { markedQuestionAnswer?: boolean } = {},
|
|
1790
|
+
options: { markedQuestionAnswer?: boolean; cwd?: string } = {},
|
|
1769
1791
|
): string | null {
|
|
1770
1792
|
if (skillState?.initialized_mode !== "autopilot") return null;
|
|
1793
|
+
const teamHandoff = readTeamModeConfig(options.cwd).enabled
|
|
1794
|
+
? " (+ $team if needed)"
|
|
1795
|
+
: "";
|
|
1771
1796
|
return [
|
|
1772
|
-
|
|
1797
|
+
`Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
|
|
1773
1798
|
"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.",
|
|
1774
1799
|
"Deep-interview is a structured question chain, not a one-question gate: after an omx question answer, re-score ambiguity against the active threshold, treat max_rounds as a cap, and crystallize once ambiguity is at or below threshold and readiness gates pass.",
|
|
1775
1800
|
options.markedQuestionAnswer
|
|
@@ -1782,6 +1807,12 @@ function buildAutopilotPromptActivationNote(
|
|
|
1782
1807
|
].filter(Boolean).join(" ");
|
|
1783
1808
|
}
|
|
1784
1809
|
|
|
1810
|
+
function formatExecutionHandoffList(cwd: string): string {
|
|
1811
|
+
return readTeamModeConfig(cwd).enabled
|
|
1812
|
+
? "`$ultragoal`, `$team`, or `$ralph`"
|
|
1813
|
+
: "`$ultragoal` or `$ralph`";
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1785
1816
|
function buildAdditionalContextMessage(
|
|
1786
1817
|
prompt: string,
|
|
1787
1818
|
skillState?: SkillActiveState | null,
|
|
@@ -1790,8 +1821,9 @@ function buildAdditionalContextMessage(
|
|
|
1790
1821
|
): string | null {
|
|
1791
1822
|
if (!prompt) return null;
|
|
1792
1823
|
const promptPriorityMessage = buildPromptPriorityMessage(prompt);
|
|
1793
|
-
const
|
|
1794
|
-
const
|
|
1824
|
+
const teamMode = readTeamModeConfig(cwd);
|
|
1825
|
+
const matches = detectKeywords(prompt).filter((entry) => teamMode.enabled || entry.skill !== "team");
|
|
1826
|
+
const match = matches[0] ?? null;
|
|
1795
1827
|
if (!match) {
|
|
1796
1828
|
const continuedSkill = safeString(skillState?.skill).trim();
|
|
1797
1829
|
if (!continuedSkill) return promptPriorityMessage;
|
|
@@ -1800,7 +1832,7 @@ function buildAdditionalContextMessage(
|
|
|
1800
1832
|
: null;
|
|
1801
1833
|
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1802
1834
|
const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
|
|
1803
|
-
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer });
|
|
1835
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd });
|
|
1804
1836
|
return [
|
|
1805
1837
|
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
|
|
1806
1838
|
promptPriorityMessage,
|
|
@@ -1826,7 +1858,7 @@ function buildAdditionalContextMessage(
|
|
|
1826
1858
|
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
1827
1859
|
: null;
|
|
1828
1860
|
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1829
|
-
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true });
|
|
1861
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd });
|
|
1830
1862
|
return [
|
|
1831
1863
|
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
|
|
1832
1864
|
promptPriorityMessage,
|
|
@@ -1859,7 +1891,7 @@ function buildAdditionalContextMessage(
|
|
|
1859
1891
|
const ultragoalPromptActivationNote = match.skill === "ultragoal"
|
|
1860
1892
|
? "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."
|
|
1861
1893
|
: null;
|
|
1862
|
-
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
|
|
1894
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { cwd });
|
|
1863
1895
|
const combinedTransitionMessage = (() => {
|
|
1864
1896
|
if (!skillState?.transition_message) return null;
|
|
1865
1897
|
if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
|
|
@@ -2464,8 +2496,11 @@ function readPayloadTurnId(payload: CodexHookPayload): string {
|
|
|
2464
2496
|
async function resolveInternalSessionIdForPayload(
|
|
2465
2497
|
cwd: string,
|
|
2466
2498
|
payloadSessionId: string,
|
|
2499
|
+
stateDir?: string,
|
|
2467
2500
|
): Promise<string> {
|
|
2468
|
-
const currentSession =
|
|
2501
|
+
const currentSession = stateDir
|
|
2502
|
+
? await readUsableSessionStateFromStateDir(cwd, stateDir)
|
|
2503
|
+
: await readUsableSessionState(cwd);
|
|
2469
2504
|
const canonicalSessionId = safeString(currentSession?.session_id).trim();
|
|
2470
2505
|
if (!canonicalSessionId) return payloadSessionId;
|
|
2471
2506
|
|
|
@@ -2476,6 +2511,22 @@ async function resolveInternalSessionIdForPayload(
|
|
|
2476
2511
|
return payloadSessionId;
|
|
2477
2512
|
}
|
|
2478
2513
|
|
|
2514
|
+
async function readUsableSessionStateFromStateDir(
|
|
2515
|
+
cwd: string,
|
|
2516
|
+
stateDir: string,
|
|
2517
|
+
): Promise<SessionState | null> {
|
|
2518
|
+
const sessionPath = join(stateDir, "session.json");
|
|
2519
|
+
if (!existsSync(sessionPath)) return null;
|
|
2520
|
+
|
|
2521
|
+
try {
|
|
2522
|
+
const content = await readFile(sessionPath, "utf-8");
|
|
2523
|
+
const state = JSON.parse(content) as SessionState;
|
|
2524
|
+
return isSessionStateUsable(state, cwd) ? state : null;
|
|
2525
|
+
} catch {
|
|
2526
|
+
return null;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2479
2530
|
async function readStopSessionPinnedState(
|
|
2480
2531
|
fileName: string,
|
|
2481
2532
|
cwd: string,
|
|
@@ -2514,7 +2565,8 @@ const PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES = new Set([
|
|
|
2514
2565
|
const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES;
|
|
2515
2566
|
|
|
2516
2567
|
const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
|
|
2517
|
-
|
|
2568
|
+
// Autopilot is intentionally excluded: it supervises planning phases such as
|
|
2569
|
+
// ralplan/replan and is not by itself an execution authorization.
|
|
2518
2570
|
"autoresearch",
|
|
2519
2571
|
"ralph",
|
|
2520
2572
|
"team",
|
|
@@ -2541,12 +2593,12 @@ function isActiveRalplanPhase(state: Record<string, unknown> | null): boolean {
|
|
|
2541
2593
|
return true;
|
|
2542
2594
|
}
|
|
2543
2595
|
|
|
2544
|
-
function
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
return phase === "ralplan";
|
|
2596
|
+
function isAutopilotRalplanLikePhase(phase: string): boolean {
|
|
2597
|
+
return normalizeAutopilotPhase(phase) === "ralplan";
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
function canAutopilotSkillMirrorSupplyRalplanPhase(phase: string): boolean {
|
|
2601
|
+
return phase === "" || normalizeAutopilotPhase(phase) === "ralplan";
|
|
2550
2602
|
}
|
|
2551
2603
|
|
|
2552
2604
|
function hasExplicitExecutionHandoffSkill(
|
|
@@ -2649,7 +2701,7 @@ async function readActiveDeepInterviewStateForPreToolUse(
|
|
|
2649
2701
|
const canonicalState = sessionId
|
|
2650
2702
|
? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
|
|
2651
2703
|
: await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
|
|
2652
|
-
if (!canonicalState) return
|
|
2704
|
+
if (!canonicalState) return null;
|
|
2653
2705
|
const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
2654
2706
|
entry.skill === "deep-interview"
|
|
2655
2707
|
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
@@ -2671,7 +2723,7 @@ async function readActiveRalplanStateForPreToolUse(
|
|
|
2671
2723
|
: await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
|
|
2672
2724
|
if (isActiveRalplanPhase(modeState) && modeState && modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) {
|
|
2673
2725
|
if (hasExplicitExecutionHandoffSkill(canonicalState, sessionId, threadId)) return null;
|
|
2674
|
-
if (!canonicalState) return
|
|
2726
|
+
if (!canonicalState) return null;
|
|
2675
2727
|
const hasActiveRalplanSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
2676
2728
|
entry.skill === "ralplan"
|
|
2677
2729
|
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
@@ -2682,15 +2734,26 @@ async function readActiveRalplanStateForPreToolUse(
|
|
|
2682
2734
|
const autopilotState = sessionId
|
|
2683
2735
|
? await readStopSessionPinnedState("autopilot-state.json", cwd, sessionId, stateDir)
|
|
2684
2736
|
: await readJsonIfExists(join(stateDir, "autopilot-state.json"));
|
|
2685
|
-
if (!
|
|
2737
|
+
if (!autopilotState || autopilotState.active !== true) return null;
|
|
2738
|
+
const autopilotMode = safeString(autopilotState.mode).trim();
|
|
2739
|
+
if (autopilotMode && autopilotMode !== "autopilot") return null;
|
|
2686
2740
|
if (!modeStateMatchesSkillStopContext(autopilotState, cwd, sessionId)) return null;
|
|
2687
2741
|
const terminalAutopilotRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, "autopilot");
|
|
2688
2742
|
if (terminalAutopilotRunState) return null;
|
|
2689
|
-
if (!canonicalState) return
|
|
2743
|
+
if (!canonicalState) return null;
|
|
2690
2744
|
const hasActiveAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
2691
2745
|
entry.skill === "autopilot"
|
|
2692
2746
|
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
2693
2747
|
));
|
|
2748
|
+
if (!hasActiveAutopilotSkill) return null;
|
|
2749
|
+
const autopilotStatePhase = safeString(autopilotState.current_phase ?? autopilotState.currentPhase).trim().toLowerCase();
|
|
2750
|
+
if (!canAutopilotSkillMirrorSupplyRalplanPhase(autopilotStatePhase)) return null;
|
|
2751
|
+
const hasRalplanScopedAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
2752
|
+
entry.skill === "autopilot"
|
|
2753
|
+
&& isAutopilotRalplanLikePhase(safeString(entry.phase).trim().toLowerCase())
|
|
2754
|
+
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
2755
|
+
));
|
|
2756
|
+
if (!isAutopilotRalplanLikePhase(autopilotStatePhase) && !hasRalplanScopedAutopilotSkill) return null;
|
|
2694
2757
|
return hasActiveAutopilotSkill ? autopilotState : null;
|
|
2695
2758
|
}
|
|
2696
2759
|
|
|
@@ -2705,8 +2768,9 @@ async function buildRalplanPreToolUseBoundaryOutput(
|
|
|
2705
2768
|
payload: CodexHookPayload,
|
|
2706
2769
|
cwd: string,
|
|
2707
2770
|
stateDir: string,
|
|
2771
|
+
resolvedSessionId?: string,
|
|
2708
2772
|
): Promise<Record<string, unknown> | null> {
|
|
2709
|
-
const sessionId = readPayloadSessionId(payload);
|
|
2773
|
+
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
2710
2774
|
const threadId = readPayloadThreadId(payload);
|
|
2711
2775
|
const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
2712
2776
|
if (!activeState) return null;
|
|
@@ -2726,13 +2790,21 @@ async function buildRalplanPreToolUseBoundaryOutput(
|
|
|
2726
2790
|
if (!blocked) return null;
|
|
2727
2791
|
|
|
2728
2792
|
const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
|
|
2793
|
+
const activeMode = safeString(activeState.mode).trim().toLowerCase();
|
|
2794
|
+
const planningModeLabel = activeMode === "autopilot" ? "Autopilot planning" : "Ralplan";
|
|
2795
|
+
const planningModeDescription = activeMode === "autopilot"
|
|
2796
|
+
? "Autopilot is supervising a planning phase"
|
|
2797
|
+
: "Ralplan is consensus-planning mode";
|
|
2729
2798
|
return {
|
|
2730
2799
|
decision: "block",
|
|
2731
|
-
reason:
|
|
2800
|
+
reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
|
|
2732
2801
|
hookSpecificOutput: {
|
|
2733
2802
|
hookEventName: "PreToolUse",
|
|
2734
2803
|
additionalContext:
|
|
2735
|
-
|
|
2804
|
+
`${planningModeDescription}. `
|
|
2805
|
+
+ "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, or required `.omx/state/` files. "
|
|
2806
|
+
+ "Do not edit implementation files or run implementation-focused writes from planning phases. "
|
|
2807
|
+
+ `To execute, first process an explicit handoff such as ${formatExecutionHandoffList(cwd)}, which must emit terminal planning state before implementation begins.`,
|
|
2736
2808
|
},
|
|
2737
2809
|
};
|
|
2738
2810
|
}
|
|
@@ -2741,8 +2813,9 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
|
2741
2813
|
payload: CodexHookPayload,
|
|
2742
2814
|
cwd: string,
|
|
2743
2815
|
stateDir: string,
|
|
2816
|
+
resolvedSessionId?: string,
|
|
2744
2817
|
): Promise<Record<string, unknown> | null> {
|
|
2745
|
-
const sessionId = readPayloadSessionId(payload);
|
|
2818
|
+
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
2746
2819
|
const threadId = readPayloadThreadId(payload);
|
|
2747
2820
|
const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
2748
2821
|
if (!activeState) return null;
|
|
@@ -2768,7 +2841,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
|
2768
2841
|
hookSpecificOutput: {
|
|
2769
2842
|
hookEventName: "PreToolUse",
|
|
2770
2843
|
additionalContext:
|
|
2771
|
-
|
|
2844
|
+
`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\`, ${formatExecutionHandoffList(cwd)}.`,
|
|
2772
2845
|
},
|
|
2773
2846
|
};
|
|
2774
2847
|
}
|
|
@@ -3027,6 +3100,7 @@ async function reconcileStaleRootSkillActiveStateForStop(
|
|
|
3027
3100
|
function buildRalplanContinuationStatus(
|
|
3028
3101
|
blocker: { phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string },
|
|
3029
3102
|
activeSubagentCount: number,
|
|
3103
|
+
cwd: string,
|
|
3030
3104
|
): { reason: string; systemMessage: string; stopReasonSuffix: string } {
|
|
3031
3105
|
const phase = blocker.phase || "planning";
|
|
3032
3106
|
const artifact = blocker.latestPlanPath
|
|
@@ -3062,7 +3136,7 @@ function buildRalplanContinuationStatus(
|
|
|
3062
3136
|
}
|
|
3063
3137
|
|
|
3064
3138
|
const completeHint = blocker.planningComplete
|
|
3065
|
-
?
|
|
3139
|
+
? ` The planning artifacts are present; if consensus is approved, emit terminal ralplan complete/approved handoff state and stop planning. Implementation must wait for an explicit ${formatExecutionHandoffList(cwd).replaceAll("`", "")} handoff.`
|
|
3066
3140
|
: "";
|
|
3067
3141
|
|
|
3068
3142
|
return {
|
|
@@ -3282,6 +3356,11 @@ async function maybeBuildOrdinaryStopNoProgressOutput(
|
|
|
3282
3356
|
stateDir: string,
|
|
3283
3357
|
canonicalSessionId?: string,
|
|
3284
3358
|
): Promise<Record<string, unknown> | null> {
|
|
3359
|
+
const lastAssistantMessage = safeString(
|
|
3360
|
+
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
3361
|
+
).trim();
|
|
3362
|
+
if (!lastAssistantMessage) return null;
|
|
3363
|
+
|
|
3285
3364
|
const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
|
|
3286
3365
|
const state = await readJsonIfExists(statePath) ?? {};
|
|
3287
3366
|
const sessions = safeObject(state.sessions);
|
|
@@ -3313,9 +3392,6 @@ async function maybeBuildOrdinaryStopNoProgressOutput(
|
|
|
3313
3392
|
await mkdir(stateDir, { recursive: true });
|
|
3314
3393
|
await writeFile(statePath, JSON.stringify({ ...state, sessions }, null, 2));
|
|
3315
3394
|
|
|
3316
|
-
const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
|
|
3317
|
-
if (!stopHookActive) return null;
|
|
3318
|
-
|
|
3319
3395
|
const maxRepeats = parseBoundedPositiveInteger(
|
|
3320
3396
|
process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS,
|
|
3321
3397
|
ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS,
|
|
@@ -3520,7 +3596,7 @@ async function buildSkillStopOutput(
|
|
|
3520
3596
|
const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
|
|
3521
3597
|
|
|
3522
3598
|
if (blocker.skill === "ralplan") {
|
|
3523
|
-
const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
|
|
3599
|
+
const status = buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd);
|
|
3524
3600
|
return {
|
|
3525
3601
|
decision: "block",
|
|
3526
3602
|
reason: status.reason,
|
|
@@ -3978,7 +4054,9 @@ export async function dispatchCodexNativeHook(
|
|
|
3978
4054
|
// Native hooks must use the same authoritative runtime state root as HUD/MCP
|
|
3979
4055
|
// when boxed/team roots are active; do not bypass it with cwd/.omx/state.
|
|
3980
4056
|
const stateDir = getBaseStateDir(cwd);
|
|
3981
|
-
|
|
4057
|
+
if (hookEventName !== "Stop") {
|
|
4058
|
+
await mkdir(stateDir, { recursive: true });
|
|
4059
|
+
}
|
|
3982
4060
|
|
|
3983
4061
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
3984
4062
|
let skillState: SkillActiveState | null = null;
|
|
@@ -4179,16 +4257,23 @@ export async function dispatchCodexNativeHook(
|
|
|
4179
4257
|
triageAdditionalContext = null;
|
|
4180
4258
|
}
|
|
4181
4259
|
}
|
|
4260
|
+
const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
|
|
4182
4261
|
const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
|
|
4183
4262
|
&& await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
|
|
4184
|
-
if (!skipHudReconcileForTeamWorkerPane) {
|
|
4263
|
+
if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
|
|
4185
4264
|
const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
|
|
4186
4265
|
const hudSessionId = resolveHudReconcileSessionId(
|
|
4187
4266
|
currentSessionState,
|
|
4188
4267
|
canonicalSessionId,
|
|
4189
4268
|
sessionIdForState,
|
|
4190
4269
|
);
|
|
4191
|
-
|
|
4270
|
+
const hudSessionIds = resolveHudReconcileSessionIds(
|
|
4271
|
+
currentSessionState,
|
|
4272
|
+
canonicalSessionId,
|
|
4273
|
+
sessionIdForState,
|
|
4274
|
+
nativeSessionId,
|
|
4275
|
+
);
|
|
4276
|
+
await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId, sessionIds: hudSessionIds }).catch(() => {});
|
|
4192
4277
|
}
|
|
4193
4278
|
}
|
|
4194
4279
|
|
|
@@ -4248,8 +4333,12 @@ export async function dispatchCodexNativeHook(
|
|
|
4248
4333
|
};
|
|
4249
4334
|
}
|
|
4250
4335
|
} else if (hookEventName === "PreToolUse") {
|
|
4251
|
-
|
|
4252
|
-
|
|
4336
|
+
const payloadSessionId = readPayloadSessionId(payload);
|
|
4337
|
+
const preToolUseSessionId = payloadSessionId
|
|
4338
|
+
? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
|
|
4339
|
+
: "";
|
|
4340
|
+
outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
4341
|
+
?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
4253
4342
|
?? buildNativePreToolUseOutput(payload);
|
|
4254
4343
|
} else if (hookEventName === "PostToolUse") {
|
|
4255
4344
|
if (detectMcpTransportFailure(payload)) {
|
|
@@ -89,10 +89,8 @@ OMX_TEAM_WORKER_LAUNCH_ARGS="${OMX_TEAM_WORKER_LAUNCH_ARGS:--c model_reasoning_e
|
|
|
89
89
|
TEAM_STARTED=0
|
|
90
90
|
cleanup() {
|
|
91
91
|
if ((TEAM_STARTED == 1)); then
|
|
92
|
-
echo "[cleanup]
|
|
93
|
-
omx team
|
|
94
|
-
echo "[cleanup] cleaning state for team: $TEAM_NAME"
|
|
95
|
-
omx team api cleanup --input "{\"team_name\":\"$TEAM_NAME\"}" --json >/dev/null 2>&1 || true
|
|
92
|
+
echo "[cleanup] force-cleaning demo team: $TEAM_NAME"
|
|
93
|
+
omx team api cleanup --input "{\"team_name\":\"$TEAM_NAME\",\"force\":true,\"confirm_issues\":true}" --json >/dev/null 2>&1 || true
|
|
96
94
|
fi
|
|
97
95
|
}
|
|
98
96
|
trap cleanup EXIT
|
|
@@ -106,7 +104,13 @@ echo "OMX_TEAM_WORKER_CLI_MAP=$OMX_TEAM_WORKER_CLI_MAP"
|
|
|
106
104
|
echo "OMX_TEAM_WORKER_LAUNCH_ARGS=$OMX_TEAM_WORKER_LAUNCH_ARGS"
|
|
107
105
|
|
|
108
106
|
echo "[1/8] start team (${WORKER_COUNT} mixed workers)"
|
|
109
|
-
omx team "${WORKER_COUNT}:executor" "$TEAM_TASK"
|
|
107
|
+
START_OUTPUT="$(omx team "${WORKER_COUNT}:executor" "$TEAM_TASK")"
|
|
108
|
+
echo "$START_OUTPUT"
|
|
109
|
+
ACTUAL_TEAM_NAME="$(echo "$START_OUTPUT" | sed -nE 's/^Team started: ([^[:space:]]+)$/\1/p' | head -n 1)"
|
|
110
|
+
if [[ -n "$ACTUAL_TEAM_NAME" && "$ACTUAL_TEAM_NAME" != "$TEAM_NAME" ]]; then
|
|
111
|
+
echo "TEAM_NAME_RESOLVED=$ACTUAL_TEAM_NAME"
|
|
112
|
+
TEAM_NAME="$ACTUAL_TEAM_NAME"
|
|
113
|
+
fi
|
|
110
114
|
TEAM_STARTED=1
|
|
111
115
|
|
|
112
116
|
echo "[2/8] status"
|
|
@@ -174,8 +178,7 @@ SUMMARY_JSON="$(omx team api get-summary --input "$SUMMARY_INPUT" --json)"
|
|
|
174
178
|
echo "$SUMMARY_JSON" | jq -e '.schema_version == "1.0" and .operation == "get-summary" and .ok == true' >/dev/null
|
|
175
179
|
|
|
176
180
|
echo "[8/8] shutdown + cleanup"
|
|
177
|
-
omx team
|
|
178
|
-
omx team api cleanup --input "{\"team_name\":\"$TEAM_NAME\"}" --json >/dev/null
|
|
181
|
+
omx team api cleanup --input "{\"team_name\":\"$TEAM_NAME\",\"force\":true,\"confirm_issues\":true}" --json >/dev/null
|
|
179
182
|
TEAM_STARTED=0
|
|
180
183
|
|
|
181
184
|
echo "E2E demo complete."
|
|
@@ -9,7 +9,7 @@ if (build.status !== 0) {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const test = spawnSync('node', [
|
|
12
|
-
'
|
|
12
|
+
'dist/scripts/run-test-files.js',
|
|
13
13
|
'dist/autoresearch/__tests__/contracts.test.js',
|
|
14
14
|
'dist/autoresearch/__tests__/runtime.test.js',
|
|
15
15
|
'dist/cli/__tests__/autoresearch.test.js',
|
|
@@ -269,7 +269,9 @@ export async function syncSkillStateFromTurn(stateDir, payload) {
|
|
|
269
269
|
|
|
270
270
|
|
|
271
271
|
export async function isDeepInterviewStateActive(stateDir, sessionId) {
|
|
272
|
-
const modeState =
|
|
272
|
+
const modeState = typeof sessionId === 'string' && sessionId.trim()
|
|
273
|
+
? await readScopedJsonIfExists(stateDir, 'deep-interview-state.json', sessionId, null)
|
|
274
|
+
: await readJsonIfExists(join(stateDir, 'deep-interview-state.json'), null);
|
|
273
275
|
return Boolean(modeState && modeState.active === true);
|
|
274
276
|
}
|
|
275
277
|
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'fs';
|
|
2
2
|
import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'fs/promises';
|
|
3
|
-
import { dirname, join
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
4
|
import { captureTmuxPaneFromEnv } from '../../state/mode-state-context.js';
|
|
5
|
-
import { isSessionStateUsable } from '../../hooks/session.js';
|
|
6
5
|
import { resolveCodexPane } from '../tmux-hook-engine.js';
|
|
7
6
|
import { safeString } from './utils.js';
|
|
8
7
|
|
|
@@ -222,15 +221,10 @@ function readSessionIdFromEnvironment(env: NodeJS.ProcessEnv = process.env): str
|
|
|
222
221
|
|
|
223
222
|
async function readCurrentOmxSessionId(stateDir: string, env: NodeJS.ProcessEnv = process.env): Promise<string> {
|
|
224
223
|
const envSessionId = readSessionIdFromEnvironment(env);
|
|
225
|
-
if (envSessionId)
|
|
226
|
-
const envScopedDir = join(stateDir, 'sessions', envSessionId);
|
|
227
|
-
if (existsSync(envScopedDir)) return envSessionId;
|
|
228
|
-
}
|
|
224
|
+
if (envSessionId) return envSessionId;
|
|
229
225
|
|
|
230
|
-
const cwd = resolve(stateDir, '..', '..');
|
|
231
226
|
const session = await readJson(join(stateDir, 'session.json'));
|
|
232
227
|
if (!session || typeof session !== 'object') return '';
|
|
233
|
-
if (!isSessionStateUsable(session as any, cwd)) return '';
|
|
234
228
|
const sessionId = safeString(session?.session_id).trim();
|
|
235
229
|
return SESSION_ID_PATTERN.test(sessionId) ? sessionId : '';
|
|
236
230
|
}
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { mkdir, readFile, readdir, writeFile } from 'fs/promises';
|
|
6
|
-
import { dirname, join
|
|
7
|
-
import {
|
|
8
|
-
import { isSessionStateUsable } from '../../hooks/session.js';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
7
|
+
import { validateSessionId } from '../../mcp/state-paths.js';
|
|
9
8
|
import { asNumber, safeString } from './utils.js';
|
|
10
9
|
|
|
11
|
-
const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
12
10
|
|
|
13
11
|
export { readdir };
|
|
14
12
|
|
|
@@ -25,46 +23,90 @@ function isSafeStateFileName(fileName: string): boolean {
|
|
|
25
23
|
&& !fileName.includes('\\');
|
|
26
24
|
}
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
interface SessionMetadata {
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
nativeAliases: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function readSessionMetadataFromBaseStateDir(baseStateDir: string): Promise<SessionMetadata> {
|
|
32
|
+
const session = await readJsonIfExists(join(baseStateDir, 'session.json'), null);
|
|
33
|
+
let sessionId: string | undefined;
|
|
34
|
+
try {
|
|
35
|
+
sessionId = validateSessionId(session?.session_id);
|
|
36
|
+
} catch {
|
|
37
|
+
sessionId = undefined;
|
|
34
38
|
}
|
|
35
|
-
|
|
39
|
+
const nativeAliases = [
|
|
40
|
+
session?.native_session_id,
|
|
41
|
+
session?.codex_session_id,
|
|
42
|
+
session?.previous_native_session_id,
|
|
43
|
+
]
|
|
44
|
+
.map((value) => safeString(value).trim())
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
return { sessionId, nativeAliases: [...new Set(nativeAliases)] };
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
49
|
+
function readSessionIdFromEnvironment(): string | undefined {
|
|
50
|
+
for (const candidate of [process.env.OMX_SESSION_ID, process.env.CODEX_SESSION_ID, process.env.SESSION_ID]) {
|
|
51
|
+
if (typeof candidate !== 'string') continue;
|
|
52
|
+
const trimmed = candidate.trim();
|
|
53
|
+
if (!trimmed) continue;
|
|
54
|
+
try {
|
|
55
|
+
const sessionId = validateSessionId(trimmed);
|
|
56
|
+
if (sessionId) return sessionId;
|
|
57
|
+
} catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
43
60
|
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
44
63
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
return SESSION_ID_PATTERN.test(sessionId) ? sessionId : undefined;
|
|
64
|
+
function resolveCanonicalSessionId(candidate: string | undefined, metadata: SessionMetadata): string | undefined {
|
|
65
|
+
if (!candidate) return undefined;
|
|
66
|
+
return metadata.sessionId && metadata.nativeAliases.includes(candidate)
|
|
67
|
+
? metadata.sessionId
|
|
68
|
+
: candidate;
|
|
51
69
|
}
|
|
52
70
|
|
|
53
|
-
|
|
71
|
+
async function resolveBaseScopedStateDir(
|
|
54
72
|
baseStateDir: string,
|
|
55
73
|
explicitSessionId?: string,
|
|
56
74
|
): Promise<string> {
|
|
57
|
-
const normalizedExplicit =
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
const normalizedExplicit = typeof explicitSessionId === 'string' && explicitSessionId.trim()
|
|
76
|
+
? explicitSessionId.trim()
|
|
77
|
+
: undefined;
|
|
78
|
+
const validatedExplicit = validateSessionId(normalizedExplicit);
|
|
79
|
+
const metadata = await readSessionMetadataFromBaseStateDir(baseStateDir);
|
|
80
|
+
const sessionId = resolveCanonicalSessionId(validatedExplicit, metadata)
|
|
81
|
+
?? resolveCanonicalSessionId(readSessionIdFromEnvironment(), metadata)
|
|
82
|
+
?? metadata.sessionId;
|
|
83
|
+
return sessionId ? join(baseStateDir, 'sessions', sessionId) : baseStateDir;
|
|
84
|
+
}
|
|
61
85
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
86
|
+
async function resolveBaseScopedStateDirs(
|
|
87
|
+
baseStateDir: string,
|
|
88
|
+
explicitSessionId?: string,
|
|
89
|
+
options: { includeRootFallback?: boolean } = {},
|
|
90
|
+
): Promise<string[]> {
|
|
91
|
+
const scopedDir = await resolveBaseScopedStateDir(baseStateDir, explicitSessionId);
|
|
92
|
+
return options.includeRootFallback === true && scopedDir !== baseStateDir
|
|
93
|
+
? [scopedDir, baseStateDir]
|
|
94
|
+
: [scopedDir];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
export async function readCurrentSessionId(baseStateDir: string): Promise<string | undefined> {
|
|
101
|
+
const metadata = await readSessionMetadataFromBaseStateDir(baseStateDir);
|
|
102
|
+
return resolveCanonicalSessionId(readSessionIdFromEnvironment(), metadata) ?? metadata.sessionId;
|
|
103
|
+
}
|
|
66
104
|
|
|
67
|
-
|
|
105
|
+
export async function resolveScopedStateDir(
|
|
106
|
+
baseStateDir: string,
|
|
107
|
+
explicitSessionId?: string,
|
|
108
|
+
): Promise<string> {
|
|
109
|
+
return resolveBaseScopedStateDir(baseStateDir, explicitSessionId);
|
|
68
110
|
}
|
|
69
111
|
|
|
70
112
|
export async function getScopedStateDirsForCurrentSession(
|
|
@@ -72,11 +114,7 @@ export async function getScopedStateDirsForCurrentSession(
|
|
|
72
114
|
explicitSessionId?: string,
|
|
73
115
|
options: { includeRootFallback?: boolean } = {},
|
|
74
116
|
): Promise<string[]> {
|
|
75
|
-
|
|
76
|
-
if (scopedDir === baseStateDir || options.includeRootFallback !== true) {
|
|
77
|
-
return [scopedDir];
|
|
78
|
-
}
|
|
79
|
-
return [scopedDir, baseStateDir];
|
|
117
|
+
return resolveBaseScopedStateDirs(baseStateDir, explicitSessionId, options);
|
|
80
118
|
}
|
|
81
119
|
|
|
82
120
|
export async function getScopedStatePath(
|
|
@@ -25,6 +25,7 @@ import { writeTeamLeaderAttention } from '../../team/state.js';
|
|
|
25
25
|
import { readLatestTeamProgressEvidenceMs } from '../../team/progress-evidence.js';
|
|
26
26
|
import { validateSessionId } from '../../mcp/state-paths.js';
|
|
27
27
|
import { TEAM_NAME_SAFE_PATTERN } from '../../team/contracts.js';
|
|
28
|
+
import { isDeepInterviewStateActive } from './auto-nudge.js';
|
|
28
29
|
const LEADER_PANE_MISSING_NO_INJECTION_REASON = 'leader_pane_missing_no_injection';
|
|
29
30
|
const LEADER_PANE_SHELL_NO_INJECTION_REASON = 'leader_pane_shell_no_injection';
|
|
30
31
|
const LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON = 'pane_already_shows_same_classified_state';
|
|
@@ -571,6 +572,12 @@ export async function maybeNudgeTeamLeader({
|
|
|
571
572
|
|
|
572
573
|
const candidateTeamNames = new Set();
|
|
573
574
|
const currentSessionId = await resolveCurrentSessionId(stateDir);
|
|
575
|
+
const deepInterviewActive = currentSessionId
|
|
576
|
+
? await isDeepInterviewStateActive(stateDir, currentSessionId).catch(() => false)
|
|
577
|
+
: await isDeepInterviewStateActive(stateDir, undefined).catch(() => false);
|
|
578
|
+
if (deepInterviewActive) {
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
574
581
|
try {
|
|
575
582
|
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir);
|
|
576
583
|
const candidateStateDirs = [...new Set([...scopedDirs, stateDir])];
|