oh-my-codex 0.13.2 → 0.14.0
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 +5 -5
- package/Cargo.toml +1 -1
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts +2 -0
- package/dist/autoresearch/__tests__/skill-validation.test.d.ts.map +1 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js +91 -0
- package/dist/autoresearch/__tests__/skill-validation.test.js.map +1 -0
- package/dist/autoresearch/skill-validation.d.ts +13 -0
- package/dist/autoresearch/skill-validation.d.ts.map +1 -0
- package/dist/autoresearch/skill-validation.js +165 -0
- package/dist/autoresearch/skill-validation.js.map +1 -0
- package/dist/catalog/__tests__/schema.test.js +6 -0
- package/dist/catalog/__tests__/schema.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +236 -273
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js +64 -653
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +7 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +2 -1
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.d.ts +2 -0
- package/dist/cli/__tests__/question.test.d.ts.map +1 -0
- package/dist/cli/__tests__/question.test.js +113 -0
- package/dist/cli/__tests__/question.test.js.map +1 -0
- package/dist/cli/__tests__/session-search-help.test.js +1 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts +24 -7
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +189 -130
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts +3 -2
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +29 -305
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +43 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +8 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/question.d.ts +3 -0
- package/dist/cli/question.d.ts.map +1 -0
- package/dist/cli/question.js +182 -0
- package/dist/cli/question.js.map +1 -0
- package/dist/hooks/__tests__/analyze-routing-contract.test.js +22 -13
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +2 -2
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +22 -5
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +2 -2
- package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +308 -17
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +570 -2
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +717 -16
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +25 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +894 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +34 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +132 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +22 -4
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js +4 -2
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts +1 -0
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.d.ts.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js +4 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +28 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js +5 -4
- package/dist/hooks/__tests__/prompt-orchestration-boundary.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-team-routing.test.js +2 -2
- package/dist/hooks/__tests__/prompt-team-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/triage-config.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-config.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-config.test.js +211 -0
- package/dist/hooks/__tests__/triage-config.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-heuristic.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js +230 -0
- package/dist/hooks/__tests__/triage-heuristic.test.js.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts +2 -0
- package/dist/hooks/__tests__/triage-state.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/triage-state.test.js +426 -0
- package/dist/hooks/__tests__/triage-state.test.js.map +1 -0
- package/dist/hooks/keyword-detector.d.ts +26 -7
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +97 -26
- 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 +16 -9
- 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 +28 -1
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/triage-config.d.ts +33 -0
- package/dist/hooks/triage-config.d.ts.map +1 -0
- package/dist/hooks/triage-config.js +87 -0
- package/dist/hooks/triage-config.js.map +1 -0
- package/dist/hooks/triage-heuristic.d.ts +20 -0
- package/dist/hooks/triage-heuristic.d.ts.map +1 -0
- package/dist/hooks/triage-heuristic.js +210 -0
- package/dist/hooks/triage-heuristic.js.map +1 -0
- package/dist/hooks/triage-state.d.ts +63 -0
- package/dist/hooks/triage-state.d.ts.map +1 -0
- package/dist/hooks/triage-state.js +138 -0
- package/dist/hooks/triage-state.js.map +1 -0
- package/dist/hud/__tests__/reconcile.test.js +20 -0
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts +1 -0
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +2 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +1 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/state-server.d.ts +8 -0
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +4 -0
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/modes/__tests__/base-ralph-contract.test.js +15 -0
- package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -1
- package/dist/modes/base.d.ts +1 -0
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +22 -6
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/index.test.js +78 -0
- package/dist/notifications/__tests__/index.test.js.map +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +39 -22
- package/dist/notifications/index.js.map +1 -1
- package/dist/openclaw/index.d.ts +5 -3
- package/dist/openclaw/index.d.ts.map +1 -1
- package/dist/openclaw/index.js +5 -3
- package/dist/openclaw/index.js.map +1 -1
- package/dist/question/__tests__/client.test.d.ts +2 -0
- package/dist/question/__tests__/client.test.d.ts.map +1 -0
- package/dist/question/__tests__/client.test.js +70 -0
- package/dist/question/__tests__/client.test.js.map +1 -0
- package/dist/question/__tests__/deep-interview.test.d.ts +2 -0
- package/dist/question/__tests__/deep-interview.test.d.ts.map +1 -0
- package/dist/question/__tests__/deep-interview.test.js +108 -0
- package/dist/question/__tests__/deep-interview.test.js.map +1 -0
- package/dist/question/__tests__/policy.test.d.ts +2 -0
- package/dist/question/__tests__/policy.test.d.ts.map +1 -0
- package/dist/question/__tests__/policy.test.js +107 -0
- package/dist/question/__tests__/policy.test.js.map +1 -0
- package/dist/question/__tests__/renderer.test.d.ts +2 -0
- package/dist/question/__tests__/renderer.test.d.ts.map +1 -0
- package/dist/question/__tests__/renderer.test.js +88 -0
- package/dist/question/__tests__/renderer.test.js.map +1 -0
- package/dist/question/__tests__/state.test.d.ts +2 -0
- package/dist/question/__tests__/state.test.d.ts.map +1 -0
- package/dist/question/__tests__/state.test.js +55 -0
- package/dist/question/__tests__/state.test.js.map +1 -0
- package/dist/question/__tests__/types.test.d.ts +2 -0
- package/dist/question/__tests__/types.test.d.ts.map +1 -0
- package/dist/question/__tests__/types.test.js +44 -0
- package/dist/question/__tests__/types.test.js.map +1 -0
- package/dist/question/__tests__/ui.test.d.ts +2 -0
- package/dist/question/__tests__/ui.test.d.ts.map +1 -0
- package/dist/question/__tests__/ui.test.js +169 -0
- package/dist/question/__tests__/ui.test.js.map +1 -0
- package/dist/question/client.d.ts +54 -0
- package/dist/question/client.d.ts.map +1 -0
- package/dist/question/client.js +77 -0
- package/dist/question/client.js.map +1 -0
- package/dist/question/deep-interview.d.ts +27 -0
- package/dist/question/deep-interview.d.ts.map +1 -0
- package/dist/question/deep-interview.js +101 -0
- package/dist/question/deep-interview.js.map +1 -0
- package/dist/question/policy.d.ts +18 -0
- package/dist/question/policy.d.ts.map +1 -0
- package/dist/question/policy.js +77 -0
- package/dist/question/policy.js.map +1 -0
- package/dist/question/renderer.d.ts +18 -0
- package/dist/question/renderer.d.ts.map +1 -0
- package/dist/question/renderer.js +128 -0
- package/dist/question/renderer.js.map +1 -0
- package/dist/question/state.d.ts +19 -0
- package/dist/question/state.d.ts.map +1 -0
- package/dist/question/state.js +108 -0
- package/dist/question/state.js.map +1 -0
- package/dist/question/types.d.ts +66 -0
- package/dist/question/types.d.ts.map +1 -0
- package/dist/question/types.js +82 -0
- package/dist/question/types.js.map +1 -0
- package/dist/question/ui.d.ts +38 -0
- package/dist/question/ui.d.ts.map +1 -0
- package/dist/question/ui.js +321 -0
- package/dist/question/ui.js.map +1 -0
- package/dist/ralph/contract.d.ts +1 -1
- package/dist/ralph/contract.d.ts.map +1 -1
- package/dist/ralph/contract.js +4 -1
- package/dist/ralph/contract.js.map +1 -1
- package/dist/ralplan/runtime.js +1 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/run-loop.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-loop.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-loop.test.js +35 -0
- package/dist/runtime/__tests__/run-loop.test.js.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts +2 -0
- package/dist/runtime/__tests__/run-outcome.test.d.ts.map +1 -0
- package/dist/runtime/__tests__/run-outcome.test.js +64 -0
- package/dist/runtime/__tests__/run-outcome.test.js.map +1 -0
- package/dist/runtime/run-loop.d.ts +41 -0
- package/dist/runtime/run-loop.d.ts.map +1 -0
- package/dist/runtime/run-loop.js +46 -0
- package/dist/runtime/run-loop.js.map +1 -0
- package/dist/runtime/run-outcome.d.ts +28 -0
- package/dist/runtime/run-outcome.d.ts.map +1 -0
- package/dist/runtime/run-outcome.js +136 -0
- package/dist/runtime/run-outcome.js.map +1 -0
- package/dist/runtime/run-state.d.ts +36 -0
- package/dist/runtime/run-state.d.ts.map +1 -0
- package/dist/runtime/run-state.js +110 -0
- package/dist/runtime/run-state.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +1128 -85
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +2 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +199 -11
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +81 -2
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +27 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +83 -20
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +64 -38
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook.js +15 -5
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/sync-prompt-guidance-fragments.js +5 -0
- package/dist/scripts/sync-prompt-guidance-fragments.js.map +1 -1
- package/dist/state/__tests__/operations-ralph-phase.test.js +21 -0
- package/dist/state/__tests__/operations-ralph-phase.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +11 -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 +15 -0
- package/dist/state/operations.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +14 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +3 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/followup-planner.test.js +15 -0
- package/dist/team/__tests__/followup-planner.test.js.map +1 -1
- package/dist/team/__tests__/role-router.test.js +41 -0
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/followup-planner.d.ts.map +1 -1
- package/dist/team/followup-planner.js +31 -9
- package/dist/team/followup-planner.js.map +1 -1
- package/dist/team/role-router.d.ts.map +1 -1
- package/dist/team/role-router.js +73 -0
- package/dist/team/role-router.js.map +1 -1
- package/package.json +3 -2
- package/prompts/dependency-expert.md +3 -0
- package/prompts/executor.md +5 -0
- package/prompts/explore.md +2 -0
- package/prompts/planner.md +5 -0
- package/prompts/product-analyst.md +8 -8
- package/prompts/researcher.md +78 -30
- package/prompts/verifier.md +4 -0
- package/skills/autoresearch/SKILL.md +68 -0
- package/skills/deep-interview/SKILL.md +10 -9
- package/skills/help/SKILL.md +3 -1
- package/skills/ralplan/SKILL.md +1 -0
- package/skills/team/SKILL.md +1 -0
- package/skills/ultrawork/SKILL.md +1 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +1495 -188
- package/src/scripts/codex-native-hook.ts +235 -19
- package/src/scripts/notify-fallback-watcher.ts +92 -2
- package/src/scripts/notify-hook/auto-nudge.ts +89 -20
- package/src/scripts/notify-hook/managed-tmux.ts +70 -31
- package/src/scripts/notify-hook/ralph-session-resume.ts +1 -1
- package/src/scripts/notify-hook.ts +23 -5
- package/src/scripts/sync-prompt-guidance-fragments.ts +4 -0
- package/templates/AGENTS.md +48 -37
- package/templates/catalog-manifest.json +7 -0
|
@@ -21,6 +21,8 @@ import { logTmuxHookEvent } from './log.js';
|
|
|
21
21
|
import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
|
|
22
22
|
import { stripOrchestrationIntentTags } from './orchestration-intent.js';
|
|
23
23
|
import { buildCapturePaneArgv, DEFAULT_MARKER, tmuxHookExplicitlyDisablesInjection } from '../tmux-hook-engine.js';
|
|
24
|
+
import { readAutoresearchCompletionStatus } from '../../autoresearch/skill-validation.js';
|
|
25
|
+
import { persistDeepInterviewModeState } from '../../hooks/keyword-detector.js';
|
|
24
26
|
import {
|
|
25
27
|
isManagedOmxSession,
|
|
26
28
|
resolveManagedCurrentPane,
|
|
@@ -36,6 +38,7 @@ export const DEEP_INTERVIEW_INPUT_LOCK_MESSAGE = 'Deep interview is active; auto
|
|
|
36
38
|
export const DEFAULT_AUTO_NUDGE_RESPONSE = 'continue with the current task only if it is already authorized';
|
|
37
39
|
const DEEP_INTERVIEW_ERROR_PATTERNS = [' error', ' failed', ' failure', ' exception', 'unable to continue', 'cannot continue', 'could not continue'];
|
|
38
40
|
const DEEP_INTERVIEW_ABORT_PATTERNS = ['aborted', 'cancelled', 'canceled'];
|
|
41
|
+
const DEEP_INTERVIEW_SUCCESS_PATTERNS = ['interview completed', 'interview complete', 'interview finished', 'final summary ready'];
|
|
39
42
|
const DEEP_INTERVIEW_ABORT_INPUTS = new Set(['abort', 'cancel', 'stop']);
|
|
40
43
|
const DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES = new Set(['next i should']);
|
|
41
44
|
const SKILL_PHASES = new Set(['planning', 'executing', 'reviewing', 'completing']);
|
|
@@ -99,6 +102,10 @@ function hasAnySubstring(text, patterns) {
|
|
|
99
102
|
return patterns.some((pattern) => lower.includes(pattern));
|
|
100
103
|
}
|
|
101
104
|
|
|
105
|
+
function looksLikeDeepInterviewSuccess(text) {
|
|
106
|
+
return hasAnySubstring(text, DEEP_INTERVIEW_SUCCESS_PATTERNS);
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
export function isDeepInterviewAutoApprovalLocked(skillState) {
|
|
103
110
|
return Boolean(
|
|
104
111
|
skillState
|
|
@@ -188,6 +195,78 @@ async function persistSkillActiveState(stateDir, sessionId, state) {
|
|
|
188
195
|
await writeScopedJson(stateDir, SKILL_ACTIVE_STATE_FILE, sessionId, state).catch(() => {});
|
|
189
196
|
}
|
|
190
197
|
|
|
198
|
+
function cloneSkillActiveState(state) {
|
|
199
|
+
if (!state || typeof state !== 'object') return null;
|
|
200
|
+
return {
|
|
201
|
+
...state,
|
|
202
|
+
input_lock: state.input_lock
|
|
203
|
+
? {
|
|
204
|
+
...state.input_lock,
|
|
205
|
+
blocked_inputs: Array.isArray(state.input_lock.blocked_inputs)
|
|
206
|
+
? [...state.input_lock.blocked_inputs]
|
|
207
|
+
: state.input_lock.blocked_inputs,
|
|
208
|
+
}
|
|
209
|
+
: state.input_lock,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function syncSkillStateFromTurn(stateDir, payload) {
|
|
214
|
+
const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
|
|
215
|
+
const latestUserInput = latestUserInputFromPayload(payload);
|
|
216
|
+
const invocationSessionId = resolveInvocationSessionId(payload);
|
|
217
|
+
let skillState = await loadSkillActiveState(stateDir, invocationSessionId);
|
|
218
|
+
let releaseReason = null;
|
|
219
|
+
|
|
220
|
+
if (!skillState) {
|
|
221
|
+
return { invocationSessionId, skillState: null, releaseReason };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const previousSkillState = cloneSkillActiveState(skillState);
|
|
225
|
+
const previousPhase = normalizeSkillPhase(skillState.phase);
|
|
226
|
+
const inferredPhase = inferSkillPhaseFromText(lastMessage, previousPhase);
|
|
227
|
+
const explicitDeepInterviewSuccess = skillState.skill === 'deep-interview' && looksLikeDeepInterviewSuccess(lastMessage);
|
|
228
|
+
const nextPhase = skillState.skill === 'deep-interview'
|
|
229
|
+
&& inferredPhase === 'completing'
|
|
230
|
+
&& previousPhase !== 'completing'
|
|
231
|
+
&& !explicitDeepInterviewSuccess
|
|
232
|
+
? previousPhase
|
|
233
|
+
: inferredPhase;
|
|
234
|
+
skillState.phase = nextPhase;
|
|
235
|
+
skillState.active = nextPhase !== 'completing';
|
|
236
|
+
|
|
237
|
+
if (skillState.skill === 'autoresearch') {
|
|
238
|
+
const completion = await readAutoresearchCompletionStatus(payload.cwd || process.cwd(), invocationSessionId);
|
|
239
|
+
skillState.validation_mode = completion.validationMode;
|
|
240
|
+
skillState.autoresearch_completion_reason = completion.reason;
|
|
241
|
+
skillState.completion_artifact_path = completion.artifactPath;
|
|
242
|
+
if (completion.complete) {
|
|
243
|
+
skillState.phase = 'completing';
|
|
244
|
+
skillState.active = false;
|
|
245
|
+
} else if (inferredPhase === 'completing') {
|
|
246
|
+
skillState.phase = previousPhase === 'completing' ? 'reviewing' : previousPhase;
|
|
247
|
+
skillState.active = true;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const nowIso = new Date().toISOString();
|
|
252
|
+
skillState.updated_at = nowIso;
|
|
253
|
+
releaseReason = inferDeepInterviewReleaseReason({ skillState, latestUserInput, lastMessage });
|
|
254
|
+
if (releaseReason && isDeepInterviewAutoApprovalLocked(skillState)) {
|
|
255
|
+
releaseDeepInterviewInputLock(skillState, releaseReason, nowIso);
|
|
256
|
+
}
|
|
257
|
+
await persistSkillActiveState(stateDir, invocationSessionId, skillState);
|
|
258
|
+
|
|
259
|
+
if (skillState.skill === 'deep-interview' || previousSkillState?.skill === 'deep-interview') {
|
|
260
|
+
await persistDeepInterviewModeState(stateDir, skillState, nowIso, previousSkillState, {
|
|
261
|
+
sessionId: invocationSessionId,
|
|
262
|
+
threadId: safeString(payload?.['thread-id'] || payload?.thread_id || ''),
|
|
263
|
+
turnId: safeString(payload?.['turn-id'] || payload?.turn_id || ''),
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return { invocationSessionId, skillState, releaseReason };
|
|
268
|
+
}
|
|
269
|
+
|
|
191
270
|
|
|
192
271
|
export async function isDeepInterviewStateActive(stateDir, sessionId) {
|
|
193
272
|
const modeState = await readScopedJsonIfExists(stateDir, 'deep-interview-state.json', sessionId, null);
|
|
@@ -479,8 +558,10 @@ export async function resolveNudgePaneTarget(stateDir: any, cwd = '', payload: a
|
|
|
479
558
|
if (!anchoredPane) continue;
|
|
480
559
|
const managedPane = await resolveManagedPaneFromAnchor(anchoredPane, cwd, payload, { allowTeamWorker });
|
|
481
560
|
if (managedPane) return managedPane;
|
|
482
|
-
|
|
483
|
-
|
|
561
|
+
if (allowTeamWorker) {
|
|
562
|
+
const verdict = await verifyManagedPaneTarget(anchoredPane, cwd, payload, { allowTeamWorker });
|
|
563
|
+
if (verdict.ok) return anchoredPane;
|
|
564
|
+
}
|
|
484
565
|
} catch {
|
|
485
566
|
// skip malformed state
|
|
486
567
|
}
|
|
@@ -516,21 +597,9 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
516
597
|
}
|
|
517
598
|
|
|
518
599
|
const lastMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
|
|
519
|
-
const
|
|
520
|
-
const invocationSessionId = resolveInvocationSessionId(payload);
|
|
521
|
-
let skillState = await loadSkillActiveState(stateDir, invocationSessionId);
|
|
522
|
-
let releaseReason = null;
|
|
600
|
+
const { invocationSessionId, skillState, releaseReason } = await syncSkillStateFromTurn(stateDir, payload);
|
|
523
601
|
|
|
524
602
|
try {
|
|
525
|
-
if (skillState) {
|
|
526
|
-
const inferredPhase = inferSkillPhaseFromText(lastMessage, skillState.phase);
|
|
527
|
-
skillState.phase = inferredPhase;
|
|
528
|
-
skillState.active = inferredPhase !== 'completing';
|
|
529
|
-
skillState.updated_at = new Date().toISOString();
|
|
530
|
-
releaseReason = inferDeepInterviewReleaseReason({ skillState, latestUserInput, lastMessage });
|
|
531
|
-
await persistSkillActiveState(stateDir, invocationSessionId, skillState);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
603
|
const nudgeStatePath = await getScopedStatePath(stateDir, 'auto-nudge-state.json', invocationSessionId);
|
|
535
604
|
let nudgeState = await readScopedJsonIfExists(stateDir, 'auto-nudge-state.json', invocationSessionId, null);
|
|
536
605
|
if (!nudgeState || typeof nudgeState !== 'object') {
|
|
@@ -618,8 +687,10 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
618
687
|
return;
|
|
619
688
|
}
|
|
620
689
|
|
|
621
|
-
const
|
|
622
|
-
|
|
690
|
+
const blockedAutoApproval = isDeepInterviewAutoApprovalLocked(skillState)
|
|
691
|
+
&& !releaseReason
|
|
692
|
+
&& isBlockedAutoApprovalInput(effectiveResponse, skillState.input_lock?.blocked_inputs);
|
|
693
|
+
if (blockedAutoApproval) {
|
|
623
694
|
const blockedMessage = skillState.input_lock?.message || DEEP_INTERVIEW_INPUT_LOCK_MESSAGE;
|
|
624
695
|
await logTmuxHookEvent(logsDir, {
|
|
625
696
|
timestamp: new Date().toISOString(),
|
|
@@ -628,9 +699,7 @@ export async function maybeAutoNudge({ cwd, stateDir, logsDir, payload }) {
|
|
|
628
699
|
response: effectiveResponse,
|
|
629
700
|
source,
|
|
630
701
|
blocked_by: 'deep-interview-lock',
|
|
631
|
-
block_kind:
|
|
632
|
-
? 'blocked-auto-approval'
|
|
633
|
-
: 'input-lock-active',
|
|
702
|
+
block_kind: 'blocked-auto-approval',
|
|
634
703
|
message: blockedMessage,
|
|
635
704
|
suppressed: true,
|
|
636
705
|
}).catch(() => {});
|
|
@@ -248,7 +248,7 @@ export async function verifyManagedPaneTarget(paneId: string, cwd: string, paylo
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
|
|
251
|
-
async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string }> {
|
|
251
|
+
async function readManagedPaneCommandState(paneTarget: string): Promise<{ currentCommand: string; startCommand: string; lookupFailed: boolean }> {
|
|
252
252
|
try {
|
|
253
253
|
const [currentResult, startResult] = await Promise.all([
|
|
254
254
|
runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_current_command}'], 2000),
|
|
@@ -257,9 +257,10 @@ async function readManagedPaneCommandState(paneTarget: string): Promise<{ curren
|
|
|
257
257
|
return {
|
|
258
258
|
currentCommand: safeString(currentResult.stdout).trim().toLowerCase(),
|
|
259
259
|
startCommand: safeString(startResult.stdout).trim().toLowerCase(),
|
|
260
|
+
lookupFailed: false,
|
|
260
261
|
};
|
|
261
262
|
} catch {
|
|
262
|
-
return { currentCommand: '', startCommand: '' };
|
|
263
|
+
return { currentCommand: '', startCommand: '', lookupFailed: true };
|
|
263
264
|
}
|
|
264
265
|
}
|
|
265
266
|
|
|
@@ -268,6 +269,59 @@ function paneLooksLikeManagedAgent({ currentCommand, startCommand }: { currentCo
|
|
|
268
269
|
if (startCommand.includes('codex')) return true;
|
|
269
270
|
return currentCommand === 'codex' || currentCommand === 'node' || currentCommand === 'npx';
|
|
270
271
|
}
|
|
272
|
+
|
|
273
|
+
function paneLooksLikeRetainableManagedAnchor({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
|
|
274
|
+
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
|
|
275
|
+
if (currentCommand === 'codex') return true;
|
|
276
|
+
if ((currentCommand === 'node' || currentCommand === 'npx') && startCommand.includes('codex')) return true;
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function paneLooksLikeDetachedManagedWrapperFallback({ currentCommand, startCommand }: { currentCommand: string; startCommand: string }): boolean {
|
|
281
|
+
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) return false;
|
|
282
|
+
return currentCommand === 'node' || currentCommand === 'npx';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
interface ManagedSessionPaneRow {
|
|
286
|
+
paneId: string;
|
|
287
|
+
active: boolean;
|
|
288
|
+
currentCommand: string;
|
|
289
|
+
startCommand: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function parseManagedSessionPaneRows(stdout: string): ManagedSessionPaneRow[] {
|
|
293
|
+
return safeString(stdout)
|
|
294
|
+
.trim()
|
|
295
|
+
.split('\n')
|
|
296
|
+
.filter(Boolean)
|
|
297
|
+
.map((line) => {
|
|
298
|
+
const [paneId = '', activeRaw = '0', rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
|
|
299
|
+
return {
|
|
300
|
+
paneId: safeString(paneId).trim(),
|
|
301
|
+
active: safeString(activeRaw).trim() === '1',
|
|
302
|
+
currentCommand: safeString(rawCurrentCommand).trim().toLowerCase(),
|
|
303
|
+
startCommand: safeString(rawStartCommand).trim().toLowerCase(),
|
|
304
|
+
};
|
|
305
|
+
})
|
|
306
|
+
.filter((row) => row.paneId !== '');
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function selectManagedSessionPane(
|
|
310
|
+
rows: ManagedSessionPaneRow[],
|
|
311
|
+
{ allowWrapperFallback = false }: { allowWrapperFallback?: boolean } = {},
|
|
312
|
+
): string {
|
|
313
|
+
const nonHudRows = rows.filter((row) => !/\bomx\b.*\bhud\b.*--watch/i.test(row.startCommand));
|
|
314
|
+
const canonicalRows = nonHudRows.filter((row) => paneLooksLikeRetainableManagedAnchor(row));
|
|
315
|
+
const activeCanonical = canonicalRows.find((row) => row.active);
|
|
316
|
+
if (activeCanonical) return activeCanonical.paneId;
|
|
317
|
+
if (canonicalRows[0]?.paneId) return canonicalRows[0].paneId;
|
|
318
|
+
if (!allowWrapperFallback) return '';
|
|
319
|
+
|
|
320
|
+
const wrapperFallbackRows = nonHudRows.filter((row) => paneLooksLikeDetachedManagedWrapperFallback(row));
|
|
321
|
+
const activeWrapperFallback = wrapperFallbackRows.find((row) => row.active);
|
|
322
|
+
if (activeWrapperFallback) return activeWrapperFallback.paneId;
|
|
323
|
+
return wrapperFallbackRows[0]?.paneId || '';
|
|
324
|
+
}
|
|
271
325
|
export async function resolveManagedCurrentPane(cwd: string, payload: any, { allowTeamWorker = false } = {}): Promise<string> {
|
|
272
326
|
const paneTarget = safeString(process.env.TMUX_PANE || '').trim();
|
|
273
327
|
if (!paneTarget) return '';
|
|
@@ -286,22 +340,10 @@ export async function resolveManagedSessionPane(cwd: string, payload: any): Prom
|
|
|
286
340
|
try {
|
|
287
341
|
const panesResult = await runProcess(
|
|
288
342
|
'tmux',
|
|
289
|
-
['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
343
|
+
['list-panes', '-s', '-t', expectedSession, '-F', '#{pane_id}\t#{pane_active}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
290
344
|
2000,
|
|
291
345
|
);
|
|
292
|
-
|
|
293
|
-
.trim()
|
|
294
|
-
.split('\n')
|
|
295
|
-
.filter(Boolean);
|
|
296
|
-
for (const line of panes) {
|
|
297
|
-
const [candidatePaneId, rawCurrentCommand = '', rawStartCommand = ''] = line.split('\t');
|
|
298
|
-
const startCommand = safeString(rawStartCommand).toLowerCase();
|
|
299
|
-
const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
|
|
300
|
-
if (!candidatePaneId) continue;
|
|
301
|
-
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
302
|
-
if (startCommand.includes('codex')) return candidatePaneId;
|
|
303
|
-
if (currentCommand === 'codex') return candidatePaneId;
|
|
304
|
-
}
|
|
346
|
+
return selectManagedSessionPane(parseManagedSessionPaneRows(panesResult.stdout));
|
|
305
347
|
} catch {
|
|
306
348
|
// best effort only
|
|
307
349
|
}
|
|
@@ -315,29 +357,26 @@ export async function resolveManagedPaneFromAnchor(anchorPane: string, cwd: stri
|
|
|
315
357
|
const verdict = await verifyManagedPaneTarget(paneTarget, cwd, payload, { allowTeamWorker });
|
|
316
358
|
if (!verdict.ok) return '';
|
|
317
359
|
|
|
360
|
+
const commandState = await readManagedPaneCommandState(paneTarget);
|
|
361
|
+
if (commandState.lookupFailed) return paneTarget;
|
|
362
|
+
if (paneLooksLikeRetainableManagedAnchor(commandState)) return paneTarget;
|
|
363
|
+
|
|
318
364
|
try {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
if (!sessionName) return paneTarget;
|
|
365
|
+
const sessionName = safeString(verdict.paneSessionName || verdict.managedContext?.expectedTmuxSessionName).trim();
|
|
366
|
+
if (!sessionName) return '';
|
|
322
367
|
|
|
323
368
|
const panesResult = await runProcess(
|
|
324
369
|
'tmux',
|
|
325
|
-
['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
370
|
+
['list-panes', '-s', '-t', sessionName, '-F', '#{pane_id}\t#{pane_active}\t#{pane_current_command}\t#{pane_start_command}'],
|
|
326
371
|
2000,
|
|
327
372
|
);
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const currentCommand = safeString(rawCurrentCommand).trim().toLowerCase();
|
|
333
|
-
if (!candidatePaneId) continue;
|
|
334
|
-
if (/\bomx\b.*\bhud\b.*--watch/i.test(startCommand)) continue;
|
|
335
|
-
if (startCommand.includes('codex')) return candidatePaneId;
|
|
336
|
-
if (currentCommand === 'codex') return candidatePaneId;
|
|
337
|
-
}
|
|
373
|
+
const selectedPane = selectManagedSessionPane(parseManagedSessionPaneRows(panesResult.stdout), {
|
|
374
|
+
allowWrapperFallback: paneLooksLikeDetachedManagedWrapperFallback(commandState),
|
|
375
|
+
});
|
|
376
|
+
if (selectedPane) return selectedPane;
|
|
338
377
|
} catch {
|
|
339
378
|
// best effort only
|
|
340
379
|
}
|
|
341
380
|
|
|
342
|
-
return
|
|
381
|
+
return '';
|
|
343
382
|
}
|
|
@@ -7,7 +7,7 @@ import { resolveCodexPane } from '../tmux-hook-engine.js';
|
|
|
7
7
|
import { safeString } from './utils.js';
|
|
8
8
|
|
|
9
9
|
const SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
10
|
-
const RALPH_TERMINAL_PHASES = new Set(['complete', 'failed', 'cancelled']);
|
|
10
|
+
const RALPH_TERMINAL_PHASES = new Set(['blocked_on_user', 'complete', 'failed', 'cancelled']);
|
|
11
11
|
const RALPH_RESUME_LOCK_STALE_MS = 10_000;
|
|
12
12
|
const RALPH_RESUME_LOCK_TIMEOUT_MS = 5_000;
|
|
13
13
|
const RALPH_RESUME_LOCK_RETRY_MS = 25;
|
|
@@ -40,7 +40,13 @@ import {
|
|
|
40
40
|
import { isLeaderStale, resolveLeaderStalenessThresholdMs, maybeNudgeTeamLeader } from './notify-hook/team-leader-nudge.js';
|
|
41
41
|
import { drainPendingTeamDispatch } from './notify-hook/team-dispatch.js';
|
|
42
42
|
import { handleTmuxInjection } from './notify-hook/tmux-injection.js';
|
|
43
|
-
import {
|
|
43
|
+
import {
|
|
44
|
+
maybeAutoNudge,
|
|
45
|
+
resolveNudgePaneTarget,
|
|
46
|
+
isDeepInterviewStateActive,
|
|
47
|
+
isDeepInterviewInputLockActive,
|
|
48
|
+
syncSkillStateFromTurn,
|
|
49
|
+
} from './notify-hook/auto-nudge.js';
|
|
44
50
|
import { isManagedOmxSession } from './notify-hook/managed-tmux.js';
|
|
45
51
|
import { logNotifyHookEvent } from './notify-hook/log.js';
|
|
46
52
|
import { reconcileRalphSessionResume } from './notify-hook/ralph-session-resume.js';
|
|
@@ -238,14 +244,19 @@ async function main() {
|
|
|
238
244
|
}
|
|
239
245
|
|
|
240
246
|
// 1. Log the turn
|
|
247
|
+
const normalizedInputMessages = normalizeInputMessages(payload);
|
|
248
|
+
const latestInputPreview = safeString(
|
|
249
|
+
normalizedInputMessages.length > 0
|
|
250
|
+
? normalizedInputMessages[normalizedInputMessages.length - 1]
|
|
251
|
+
: '',
|
|
252
|
+
).slice(0, 200);
|
|
241
253
|
const logEntry = {
|
|
242
254
|
timestamp: new Date().toISOString(),
|
|
243
255
|
type: payload.type || 'agent-turn-complete',
|
|
244
256
|
thread_id: payload['thread-id'] || payload.thread_id,
|
|
245
257
|
turn_id: payload['turn-id'] || payload.turn_id,
|
|
246
|
-
input_preview:
|
|
247
|
-
|
|
248
|
-
.join('; '),
|
|
258
|
+
input_preview: latestInputPreview,
|
|
259
|
+
input_message_count: normalizedInputMessages.length,
|
|
249
260
|
output_preview: (payload['last-assistant-message'] || payload.last_assistant_message || '')
|
|
250
261
|
.slice(0, 200),
|
|
251
262
|
};
|
|
@@ -473,7 +484,14 @@ async function main() {
|
|
|
473
484
|
// Non-fatal: keyword detector module may not be built yet
|
|
474
485
|
}
|
|
475
486
|
|
|
487
|
+
try {
|
|
488
|
+
await syncSkillStateFromTurn(stateDir, payload);
|
|
489
|
+
} catch {
|
|
490
|
+
// Non-fatal: lifecycle sync should not block the hook
|
|
491
|
+
}
|
|
492
|
+
|
|
476
493
|
const deepInterviewStateActive = await isDeepInterviewStateActive(stateDir, getEffectiveSessionId());
|
|
494
|
+
const deepInterviewInputLockActive = await isDeepInterviewInputLockActive(stateDir, getEffectiveSessionId());
|
|
477
495
|
|
|
478
496
|
// 4.55. Notify leader when individual worker transitions to idle (worker session only)
|
|
479
497
|
if (isTeamWorker && parsedTeamWorker && !deepInterviewStateActive) {
|
|
@@ -642,7 +660,7 @@ async function main() {
|
|
|
642
660
|
|
|
643
661
|
// 9. Auto-nudge: detect Codex stall patterns and automatically send a continuation prompt.
|
|
644
662
|
// Works for both leader and worker contexts.
|
|
645
|
-
if (!deepInterviewStateActive) {
|
|
663
|
+
if (!deepInterviewStateActive || deepInterviewInputLockActive) {
|
|
646
664
|
try {
|
|
647
665
|
await maybeAutoNudge({ cwd, stateDir, logsDir, payload });
|
|
648
666
|
} catch {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
2
3
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
4
|
|
|
4
5
|
async function read(path: string): Promise<string> { return await readFile(path, 'utf-8'); }
|
|
@@ -12,6 +13,7 @@ function replaceBetween(text: string, startMarker: string, endMarker: string, re
|
|
|
12
13
|
|
|
13
14
|
async function main(): Promise<void> {
|
|
14
15
|
const op = (await read('docs/prompt-guidance-fragments/core-operating-principles.md')).trim();
|
|
16
|
+
const sr = (await read('docs/prompt-guidance-fragments/leader-specialist-routing.md')).trim();
|
|
15
17
|
const vs = (await read('docs/prompt-guidance-fragments/core-verification-and-sequencing.md')).trim();
|
|
16
18
|
const exC = (await read('docs/prompt-guidance-fragments/executor-constraints.md')).trim();
|
|
17
19
|
const exO = (await read('docs/prompt-guidance-fragments/executor-output.md')).trim();
|
|
@@ -22,8 +24,10 @@ async function main(): Promise<void> {
|
|
|
22
24
|
const vfI = (await read('docs/prompt-guidance-fragments/verifier-investigation.md')).trim();
|
|
23
25
|
|
|
24
26
|
for (const file of ['AGENTS.md', 'templates/AGENTS.md']) {
|
|
27
|
+
if (!existsSync(file)) continue;
|
|
25
28
|
let text = await read(file);
|
|
26
29
|
text = replaceBetween(text, '<!-- OMX:GUIDANCE:OPERATING:START -->', '<!-- OMX:GUIDANCE:OPERATING:END -->', op);
|
|
30
|
+
text = replaceBetween(text, '<!-- OMX:GUIDANCE:SPECIALIST-ROUTING:START -->', '<!-- OMX:GUIDANCE:SPECIALIST-ROUTING:END -->', sr);
|
|
27
31
|
text = replaceBetween(text, '<!-- OMX:GUIDANCE:VERIFYSEQ:START -->', '<!-- OMX:GUIDANCE:VERIFYSEQ:END -->', vs);
|
|
28
32
|
await writeFile(file, text);
|
|
29
33
|
}
|
package/templates/AGENTS.md
CHANGED
|
@@ -10,6 +10,7 @@ USE CODEX NATIVE SUBAGENTS FOR INDEPENDENT PARALLEL SUBTASKS WHEN THAT IMPROVES
|
|
|
10
10
|
You are running with oh-my-codex (OMX), a coordination layer for Codex CLI.
|
|
11
11
|
This AGENTS.md is the top-level operating contract for the workspace.
|
|
12
12
|
Role prompts under `prompts/*.md` are narrower execution surfaces. They must follow this file, not override it.
|
|
13
|
+
When OMX is installed, load the installed prompt/skill/agent surfaces from `~/.codex/prompts`, `~/.codex/skills`, and `~/.codex/agents` (or the project-local `./.codex/...` equivalents when project scope is active).
|
|
13
14
|
|
|
14
15
|
<guidance_schema_contract>
|
|
15
16
|
Canonical guidance schema for this template is defined in `docs/guidance-schema.md`.
|
|
@@ -38,6 +39,11 @@ Keep runtime marker contracts stable and non-destructive when overlays are appli
|
|
|
38
39
|
<!-- OMX:GUIDANCE:OPERATING:START -->
|
|
39
40
|
- Default to quality-first, intent-deepening responses; think one more step before replying or asking for clarification, and use as much detail as needed for a strong result without empty verbosity.
|
|
40
41
|
- Proceed automatically on clear, low-risk, reversible next steps; ask only for irreversible, side-effectful, or materially branching actions.
|
|
42
|
+
- AUTO-CONTINUE for clear, already-requested, low-risk, reversible, local edit-test-verify work; keep inspecting, editing, testing, and verifying without permission handoff.
|
|
43
|
+
- ASK only for destructive, irreversible, credential-gated, external-production, or materially scope-changing actions, or when missing authority blocks progress.
|
|
44
|
+
- On AUTO-CONTINUE branches, do not use permission-handoff phrasing; state the next action or evidence-backed result.
|
|
45
|
+
- Keep going unless blocked; finish the current safe branch before asking for confirmation or handoff.
|
|
46
|
+
- Ask only when blocked by missing information, missing authority, or an irreversible/destructive branch.
|
|
41
47
|
- Do not ask or instruct humans to perform ordinary non-destructive, reversible actions; execute those safe reversible OMX/runtime operations and ordinary commands yourself.
|
|
42
48
|
- Treat OMX runtime manipulation, state transitions, and ordinary command execution as agent responsibilities when they are safe and reversible.
|
|
43
49
|
- Treat newer user task updates as local overrides for the active task while preserving earlier non-conflicting instructions.
|
|
@@ -176,12 +182,25 @@ Rules:
|
|
|
176
182
|
<model_routing>
|
|
177
183
|
Match role to task shape:
|
|
178
184
|
- Low complexity: `explore`, `style-reviewer`, `writer`
|
|
185
|
+
- Research/discovery: `explore` for repo lookup, `researcher` for official docs/reference gathering, `dependency-expert` for SDK/API/package evaluation
|
|
179
186
|
- Standard: `executor`, `debugger`, `test-engineer`
|
|
180
187
|
- High complexity: `architect`, `executor`, `critic`
|
|
181
188
|
|
|
182
189
|
For Codex native child agents, model routing defaults to inheritance/current repo defaults unless the caller has a concrete reason to override it.
|
|
183
190
|
</model_routing>
|
|
184
191
|
|
|
192
|
+
<specialist_routing>
|
|
193
|
+
Leader/workflow routing contract:
|
|
194
|
+
<!-- OMX:GUIDANCE:SPECIALIST-ROUTING:START -->
|
|
195
|
+
- Route to `explore` for repo-local file / symbol / pattern / relationship lookup, current implementation discovery, or mapping how this repo currently uses a dependency. `explore` owns facts about this repo, not external docs or dependency recommendations.
|
|
196
|
+
- Route to `researcher` when the main need is official docs, external API behavior, version-aware framework guidance, release-note history, or citation-backed reference gathering. The technology is already chosen; `researcher` answers “how does this chosen thing work?” and is not the default dependency-comparison role.
|
|
197
|
+
- Route to `dependency-expert` when the main need is package / SDK selection or a comparative dependency decision: whether / which package, SDK, or framework to adopt, upgrade, replace, or migrate; candidate comparison; maintenance, license, security, or risk evaluation across options.
|
|
198
|
+
- Use mixed routing deliberately: `explore` -> `researcher` for current local usage plus official-doc confirmation; `explore` -> `dependency-expert` for current dependency usage plus upgrade / replacement / migration evaluation; `researcher` -> `explore` when docs are clear but repo usage or impact still needs confirmation; `dependency-expert` -> `explore` when a dependency decision is clear but the local migration surface still needs mapping.
|
|
199
|
+
- Specialists should report boundary crossings upward instead of silently absorbing adjacent work.
|
|
200
|
+
- When external evidence materially affects the answer, do not keep the leader in the main lane on recall alone; route to the relevant specialist first, then return to planning or execution.
|
|
201
|
+
<!-- OMX:GUIDANCE:SPECIALIST-ROUTING:END -->
|
|
202
|
+
</specialist_routing>
|
|
203
|
+
|
|
185
204
|
---
|
|
186
205
|
|
|
187
206
|
<agent_catalog>
|
|
@@ -193,50 +212,43 @@ Key roles:
|
|
|
193
212
|
- `executor` — implementation and refactoring
|
|
194
213
|
- `verifier` — completion evidence and validation
|
|
195
214
|
|
|
215
|
+
Research/discovery specialists:
|
|
216
|
+
- `explore` — first-stop repository lookup and symbol/file mapping
|
|
217
|
+
- `researcher` — official docs, references, and external fact gathering
|
|
218
|
+
- `dependency-expert` — SDK/API/package evaluation before adopting or changing dependencies
|
|
219
|
+
|
|
196
220
|
Specialists remain available through the role catalog and native child-agent surfaces when the task clearly benefits from them.
|
|
197
221
|
</agent_catalog>
|
|
198
222
|
|
|
199
223
|
---
|
|
200
224
|
|
|
201
225
|
<keyword_detection>
|
|
202
|
-
|
|
203
|
-
Do not ask for confirmation.
|
|
226
|
+
Keyword routing is implemented primarily by native `UserPromptSubmit` hooks and the generated keyword registry. Treat hook-injected routing context as authoritative for the current turn, then load the named `SKILL.md` or prompt file as instructed.
|
|
204
227
|
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
Fallback behavior when hook context is unavailable:
|
|
229
|
+
- Explicit `$name` invocations run left-to-right and override implicit keywords.
|
|
230
|
+
- Bare skill names do not activate skills by themselves; skill-name activation requires explicit `$skill` invocation. Natural-language routing phrases may still map to a workflow when they are not just the bare skill name. Examples: `analyze` / `investigate` → `$analyze` for read-only deep analysis with ranked synthesis, explicit confidence, and concrete file references; `deep interview`, `interview`, `don't assume`, or `ouroboros` → `$deep-interview`; `ralplan` / `consensus plan` → `$ralplan`; `cancel`, `stop`, or `abort` → `$cancel`.
|
|
231
|
+
- Keep the detailed keyword list in `src/hooks/keyword-registry.ts`; do not duplicate that table here.
|
|
207
232
|
|
|
208
|
-
| Keyword(s) | Skill | Action |
|
|
209
|
-
|-------------|-------|--------|
|
|
210
233
|
Runtime availability gate:
|
|
211
234
|
- Treat `autopilot`, `ralph`, `ultrawork`, `ultraqa`, `team`/`swarm`, and `ecomode` as **OMX runtime workflows**, not generic prompt aliases.
|
|
212
|
-
- Auto-activate
|
|
235
|
+
- Auto-activate runtime workflows only when the current session is actually running under OMX CLI/runtime (for example, launched via `omx`, with OMX session overlay/runtime state available, or when the user explicitly asks to run `omx ...` in the shell).
|
|
213
236
|
- In Codex App or plain Codex sessions without OMX runtime, do **not** treat those keywords alone as activation. Explain that they require OMX CLI runtime support, and continue with the nearest App-safe surface (`deep-interview`, `ralplan`, `plan`, or native subagents) unless the user explicitly wants you to launch OMX from the shell.
|
|
237
|
+
- When deep-interview is active in OMX CLI/runtime, ask interview rounds via `omx question`; do not substitute `request_user_input` or ad hoc plain-text questioning, and respect Stop-hook blocking while a deep-interview question obligation is pending.
|
|
238
|
+
|
|
239
|
+
<triage_routing>
|
|
240
|
+
## Triage: advisory prompt-routing context
|
|
241
|
+
|
|
242
|
+
The keyword detector is the first and deterministic routing surface. Triage runs only when no keyword matches.
|
|
214
243
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
| "interview", "deep interview", "gather requirements", "interview me", "don't assume", "ouroboros" | `$deep-interview` | Read `~/.codex/skills/deep-interview/SKILL.md`, run Ouroboros-inspired Socratic ambiguity-gated interview workflow |
|
|
224
|
-
| "ralplan", "consensus plan" | `$ralplan` | Read `~/.codex/skills/ralplan/SKILL.md`, start consensus planning with RALPLAN-DR structured deliberation (short by default, `--deliberate` for high-risk) |
|
|
225
|
-
| "team", "swarm", "coordinated team", "coordinated swarm" | `$team` | Runtime-only: read `~/.codex/skills/team/SKILL.md`, start tmux-based team orchestration only inside OMX CLI/runtime (swarm compatibility alias) |
|
|
226
|
-
| "ecomode", "eco", "budget" | `$ecomode` | Runtime-only: read `~/.codex/skills/ultrawork/SKILL.md`, execute cost-aware parallel workflow only inside OMX CLI/runtime (ecomode compatibility alias) |
|
|
227
|
-
| "cancel", "stop", "abort" | `$cancel` | Read `~/.codex/skills/cancel/SKILL.md`, cancel active modes |
|
|
228
|
-
| "tdd", "test first" | `$tdd` | Read `~/.codex/prompts/test-engineer.md`, run test-first workflow (tdd compatibility alias) |
|
|
229
|
-
| "fix build", "type errors" | `$build-fix` | Read `~/.codex/prompts/build-fixer.md`, fix build errors with minimal diff (build-fix compatibility alias) |
|
|
230
|
-
| "review code", "code review", "code-review" | `$code-review` | Read `~/.codex/skills/code-review/SKILL.md`, run code review |
|
|
231
|
-
| "security review" | `$security-review` | Read `~/.codex/skills/security-review/SKILL.md`, run security audit |
|
|
232
|
-
| "web-clone", "clone site", "clone website", "copy webpage" | `$web-clone` | Read `~/.codex/skills/web-clone/SKILL.md`, start website cloning pipeline |
|
|
233
|
-
|
|
234
|
-
Detection rules:
|
|
235
|
-
- Keywords are case-insensitive and match anywhere in the user message.
|
|
236
|
-
- Explicit `$name` invocations run left-to-right and override non-explicit keyword resolution.
|
|
237
|
-
- If multiple non-explicit keywords match, use the most specific match.
|
|
238
|
-
- Runtime-only keywords must pass the runtime availability gate before activation.
|
|
239
|
-
- The rest of the user message becomes the task description.
|
|
244
|
+
When active, triage emits **advisory prompt-routing context** — a developer-context string that the model may follow. It does not activate a skill or workflow by itself. It is a best-effort hint, not a guarantee.
|
|
245
|
+
|
|
246
|
+
Note: `explore`, `executor`, and `designer` are agent role-prompt files under `prompts/`, not workflow skills.
|
|
247
|
+
|
|
248
|
+
Explicit keywords remain the deterministic control surface when you want explicit, guaranteed routing — use them whenever exact behavior matters.
|
|
249
|
+
|
|
250
|
+
To opt out per prompt with phrases such as `no workflow`, `just chat`, or `plain answer` — the triage layer will suppress context injection for that prompt.
|
|
251
|
+
</triage_routing>
|
|
240
252
|
|
|
241
253
|
Ralph / Ralplan execution gate:
|
|
242
254
|
- Enforce **ralplan-first** when ralph is active and planning is not complete.
|
|
@@ -374,6 +386,8 @@ Do not cancel while recoverable work remains.
|
|
|
374
386
|
---
|
|
375
387
|
|
|
376
388
|
<state_management>
|
|
389
|
+
Hooks own normal skill-active and workflow-state persistence under `.omx/state/`.
|
|
390
|
+
|
|
377
391
|
OMX persists runtime state under `.omx/`:
|
|
378
392
|
- `.omx/state/` — mode state
|
|
379
393
|
- `.omx/notepad.md` — session notes
|
|
@@ -383,11 +397,8 @@ OMX persists runtime state under `.omx/`:
|
|
|
383
397
|
|
|
384
398
|
Available MCP groups include state/memory tools, code-intel tools, and trace tools.
|
|
385
399
|
|
|
386
|
-
|
|
387
|
-
-
|
|
388
|
-
- Update state on phase or iteration change.
|
|
389
|
-
- Mark inactive with `completed_at` on completion.
|
|
390
|
-
- Clear state on cancel/abort cleanup.
|
|
400
|
+
Agents may use OMX state/MCP tools for explicit lifecycle transitions, recovery, checkpointing, cancellation cleanup, or compaction resilience.
|
|
401
|
+
Do not manually duplicate hook-owned activation state unless recovering from missing or stale state.
|
|
391
402
|
</state_management>
|
|
392
403
|
|
|
393
404
|
---
|