oh-my-codex 0.16.3 → 0.17.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/README.md +3 -3
- package/dist/catalog/__tests__/generator.test.js +2 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +27 -0
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +175 -7
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +147 -12
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-serve.test.js +4 -0
- package/dist/cli/__tests__/mcp-serve.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +47 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +10 -5
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +299 -27
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +85 -3
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +1 -1
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +108 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +91 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +54 -8
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +8 -4
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +9 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -0
- package/dist/cli/codex-feature-probe.js +28 -0
- package/dist/cli/codex-feature-probe.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +214 -23
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +17 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +152 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +8 -8
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/mcp-serve.d.ts.map +1 -1
- package/dist/cli/mcp-serve.js +4 -0
- package/dist/cli/mcp-serve.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +23 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +203 -1
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +21 -0
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +4 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +7 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -3
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +140 -51
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +70 -5
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts +2 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +12 -3
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
- package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
- package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
- package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +143 -9
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +85 -9
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +116 -11
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
- package/dist/config/codex-feature-flags.d.ts +21 -0
- package/dist/config/codex-feature-flags.d.ts.map +1 -0
- package/dist/config/codex-feature-flags.js +56 -0
- package/dist/config/codex-feature-flags.js.map +1 -0
- package/dist/config/codex-hooks.d.ts +14 -13
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +108 -8
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +15 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +233 -129
- package/dist/config/generator.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts +3 -1
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
- package/dist/config/omx-first-party-mcp.js +9 -2
- package/dist/config/omx-first-party-mcp.js.map +1 -1
- package/dist/hooks/__tests__/design-skill.test.d.ts +2 -0
- package/dist/hooks/__tests__/design-skill.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/design-skill.test.js +55 -0
- package/dist/hooks/__tests__/design-skill.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +92 -2
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +125 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +265 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +41 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +2 -2
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +12 -6
- 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 +2 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +47 -2
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +164 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +4 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +3 -0
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts +2 -0
- package/dist/mcp/__tests__/hermes-bridge.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js +374 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -0
- package/dist/mcp/__tests__/state-paths.test.js +155 -11
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +166 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +2 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts +81 -0
- package/dist/mcp/hermes-bridge.d.ts.map +1 -0
- package/dist/mcp/hermes-bridge.js +400 -0
- package/dist/mcp/hermes-bridge.js.map +1 -0
- package/dist/mcp/hermes-server.d.ts +269 -0
- package/dist/mcp/hermes-server.d.ts.map +1 -0
- package/dist/mcp/hermes-server.js +121 -0
- package/dist/mcp/hermes-server.js.map +1 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +64 -11
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +22 -0
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +88 -27
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +5 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
- package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
- package/dist/planning/__tests__/artifacts.test.js +533 -4
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/__tests__/context-pack-status.test.js +524 -0
- package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
- package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
- package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
- package/dist/planning/__tests__/markdown-structure.test.js +459 -0
- package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
- package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +1 -1
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +227 -28
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/planning/context-pack-status.d.ts +25 -0
- package/dist/planning/context-pack-status.d.ts.map +1 -1
- package/dist/planning/context-pack-status.js +272 -31
- package/dist/planning/context-pack-status.js.map +1 -1
- package/dist/planning/markdown-structure.d.ts +20 -0
- package/dist/planning/markdown-structure.d.ts.map +1 -0
- package/dist/planning/markdown-structure.js +137 -0
- package/dist/planning/markdown-structure.js.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
- package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
- package/dist/ralph/__tests__/completion-audit.test.js +121 -0
- package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
- package/dist/ralph/completion-audit.d.ts +8 -0
- package/dist/ralph/completion-audit.d.ts.map +1 -0
- package/dist/ralph/completion-audit.js +99 -0
- package/dist/ralph/completion-audit.js.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +407 -15
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +177 -71
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +4 -2
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +30 -1
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +91 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +3 -1
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +102 -27
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +2 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +21 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +9 -3
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +7 -0
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +25 -8
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +22 -15
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.js +3 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js +39 -0
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +5 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +497 -2
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +1 -1
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/approved-execution.d.ts.map +1 -1
- package/dist/team/approved-execution.js +3 -0
- package/dist/team/approved-execution.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +43 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +4 -0
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +2 -6
- package/dist/team/state.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +245 -1
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +52 -2
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +301 -15
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +31 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts +6 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/wiki/lifecycle.js +4 -4
- package/dist/wiki/lifecycle.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/.mcp.json +13 -5
- package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
- package/plugins/oh-my-codex/skills/design/SKILL.md +180 -0
- package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
- package/plugins/oh-my-codex/skills/skill/SKILL.md +2 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +175 -64
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
- package/skills/analyze/SKILL.md +0 -2
- package/skills/ask-claude/SKILL.md +5 -3
- package/skills/ask-gemini/SKILL.md +5 -3
- package/skills/autopilot/SKILL.md +2 -2
- package/skills/code-review/SKILL.md +1 -3
- package/skills/deep-interview/SKILL.md +5 -7
- package/skills/design/SKILL.md +180 -0
- package/skills/doctor/SKILL.md +2 -2
- package/skills/ecomode/SKILL.md +105 -1
- package/skills/frontend-ui-ux/SKILL.md +6 -24
- package/skills/git-master/SKILL.md +2 -4
- package/skills/omx-setup/SKILL.md +3 -3
- package/skills/pipeline/SKILL.md +3 -3
- package/skills/plan/SKILL.md +3 -6
- package/skills/ralph/SKILL.md +9 -10
- package/skills/skill/SKILL.md +2 -1
- package/skills/swarm/SKILL.md +5 -3
- package/skills/tdd/SKILL.md +95 -1
- package/skills/ultragoal/SKILL.md +36 -3
- package/skills/ultraqa/SKILL.md +175 -64
- package/skills/ultrawork/SKILL.md +8 -8
- package/skills/visual-ralph/SKILL.md +2 -2
- package/skills/web-clone/SKILL.md +348 -1
- package/skills/wiki/SKILL.md +13 -13
- package/src/scripts/__tests__/codex-native-hook.test.ts +437 -14
- package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
- package/src/scripts/codex-native-hook.ts +205 -61
- package/src/scripts/codex-native-pre-post.ts +4 -1
- package/src/scripts/notify-dispatcher.ts +40 -1
- package/src/scripts/notify-hook/tmux-injection.ts +110 -3
- package/src/scripts/notify-hook.ts +3 -1
- package/templates/catalog-manifest.json +9 -2
|
@@ -6,10 +6,10 @@ import { pathToFileURL } from "url";
|
|
|
6
6
|
import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
7
7
|
import {
|
|
8
8
|
extractSessionIdFromInitializedStatePath,
|
|
9
|
-
|
|
9
|
+
getSkillActiveStatePathsForStateDir,
|
|
10
10
|
listActiveSkills,
|
|
11
11
|
readSkillActiveState,
|
|
12
|
-
|
|
12
|
+
readVisibleSkillActiveStateForStateDir,
|
|
13
13
|
type SkillActiveStateLike,
|
|
14
14
|
} from "../state/skill-active.js";
|
|
15
15
|
import {
|
|
@@ -33,9 +33,9 @@ import {
|
|
|
33
33
|
writeTeamLeaderAttention,
|
|
34
34
|
writeTeamPhase,
|
|
35
35
|
} from "../team/state.js";
|
|
36
|
-
import { omxNotepadPath,
|
|
36
|
+
import { omxNotepadPath, resolveProjectMemoryPath } from "../utils/paths.js";
|
|
37
37
|
import { findGitLayout } from "../utils/git-layout.js";
|
|
38
|
-
import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
|
|
38
|
+
import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
|
|
39
39
|
import {
|
|
40
40
|
detectKeywords,
|
|
41
41
|
detectPrimaryKeyword,
|
|
@@ -76,6 +76,7 @@ import {
|
|
|
76
76
|
} from "../wiki/lifecycle.js";
|
|
77
77
|
import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
|
|
78
78
|
import { readRunState } from "../runtime/run-state.js";
|
|
79
|
+
import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
|
|
79
80
|
import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
|
|
80
81
|
import { triagePrompt } from "../hooks/triage-heuristic.js";
|
|
81
82
|
import { readTriageConfig } from "../hooks/triage-config.js";
|
|
@@ -156,6 +157,12 @@ function safeObject(value: unknown): Record<string, unknown> {
|
|
|
156
157
|
return value && typeof value === "object" ? value as Record<string, unknown> : {};
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
function safeContextSnippet(value: unknown, maxLength = 300): string {
|
|
161
|
+
const text = safeString(value).replace(/\s+/g, " ").trim();
|
|
162
|
+
if (text.length <= maxLength) return text;
|
|
163
|
+
return `${text.slice(0, maxLength - 1).trimEnd()}…`;
|
|
164
|
+
}
|
|
165
|
+
|
|
159
166
|
interface NativeSubagentSessionStartMetadata {
|
|
160
167
|
parentThreadId: string;
|
|
161
168
|
agentNickname?: string;
|
|
@@ -485,6 +492,12 @@ interface ActiveRalphStopState {
|
|
|
485
492
|
path: string;
|
|
486
493
|
}
|
|
487
494
|
|
|
495
|
+
interface RalphCompletionAuditBlockState {
|
|
496
|
+
state: Record<string, unknown>;
|
|
497
|
+
path: string;
|
|
498
|
+
reason: string;
|
|
499
|
+
}
|
|
500
|
+
|
|
488
501
|
interface RalphStopOwnershipContext {
|
|
489
502
|
sessionId: string;
|
|
490
503
|
payloadSessionId: string;
|
|
@@ -559,8 +572,8 @@ async function readCanonicalTerminalRunStateForStop(
|
|
|
559
572
|
return shouldHonorCanonicalTerminalRunState(runRecord, mode) ? runRecord : null;
|
|
560
573
|
}
|
|
561
574
|
|
|
562
|
-
async function isVisibleRalphActiveForSession(
|
|
563
|
-
const canonicalState = await
|
|
575
|
+
async function isVisibleRalphActiveForSession(stateDir: string, sessionId: string): Promise<boolean> {
|
|
576
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
564
577
|
if (!canonicalState) return false;
|
|
565
578
|
return listActiveSkills(canonicalState).some((entry) => (
|
|
566
579
|
entry.skill === "ralph"
|
|
@@ -568,8 +581,8 @@ async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): P
|
|
|
568
581
|
));
|
|
569
582
|
}
|
|
570
583
|
|
|
571
|
-
async function hasConsistentRalphSkillActivation(
|
|
572
|
-
const canonicalState = await
|
|
584
|
+
async function hasConsistentRalphSkillActivation(stateDir: string, sessionId: string): Promise<boolean> {
|
|
585
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
573
586
|
if (!canonicalState) return true;
|
|
574
587
|
|
|
575
588
|
const initializedMode = safeString(canonicalState.initialized_mode).trim();
|
|
@@ -581,7 +594,74 @@ async function hasConsistentRalphSkillActivation(cwd: string, sessionId: string)
|
|
|
581
594
|
return true;
|
|
582
595
|
}
|
|
583
596
|
|
|
597
|
+
|
|
598
|
+
async function readRalphCompletionAuditBlockState(
|
|
599
|
+
cwd: string,
|
|
600
|
+
stateDir: string,
|
|
601
|
+
preferredSessionId?: string,
|
|
602
|
+
ownerContext?: {
|
|
603
|
+
payloadSessionId?: string;
|
|
604
|
+
threadId?: string;
|
|
605
|
+
tmuxPaneId?: string;
|
|
606
|
+
},
|
|
607
|
+
): Promise<RalphCompletionAuditBlockState | null> {
|
|
608
|
+
const [rawSessionInfo, usableSessionInfo] = await Promise.all([
|
|
609
|
+
readSessionState(cwd),
|
|
610
|
+
readUsableSessionState(cwd),
|
|
611
|
+
]);
|
|
612
|
+
const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
|
|
613
|
+
const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
|
|
614
|
+
const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
|
|
615
|
+
? safeString(rawSessionInfo.session_id).trim()
|
|
616
|
+
: "";
|
|
617
|
+
const sessionCandidates = [...new Set([
|
|
618
|
+
safeString(preferredSessionId).trim(),
|
|
619
|
+
currentOmxSessionId,
|
|
620
|
+
].filter(Boolean))];
|
|
621
|
+
|
|
622
|
+
const evaluateCandidate = (state: Record<string, unknown> | null, path: string, sessionId: string): RalphCompletionAuditBlockState | null => {
|
|
623
|
+
if (!state || state.mode && safeString(state.mode) !== "ralph") return null;
|
|
624
|
+
if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase)) return null;
|
|
625
|
+
if (activeRalphStateMatchesStopOwner(state, {
|
|
626
|
+
sessionId,
|
|
627
|
+
payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
|
|
628
|
+
threadId: safeString(ownerContext?.threadId).trim(),
|
|
629
|
+
currentNativeSessionId,
|
|
630
|
+
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
631
|
+
}) !== true) return null;
|
|
632
|
+
const audit = evaluateRalphCompletionAuditEvidence(state, cwd);
|
|
633
|
+
return audit.complete ? null : { state, path, reason: audit.reason };
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
for (const sessionId of sessionCandidates) {
|
|
637
|
+
if (staleCurrentSessionId && sessionId === staleCurrentSessionId) continue;
|
|
638
|
+
const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
|
|
639
|
+
const result = evaluateCandidate(await readJsonIfExists(sessionScopedPath), sessionScopedPath, sessionId);
|
|
640
|
+
if (result) return result;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
if (sessionCandidates.length > 0) return null;
|
|
644
|
+
|
|
645
|
+
const directPath = join(stateDir, "ralph-state.json");
|
|
646
|
+
return evaluateCandidate(await readJsonIfExists(directPath), directPath, "");
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
async function reopenRalphCompletionAuditBlock(block: RalphCompletionAuditBlockState): Promise<void> {
|
|
650
|
+
const nowIso = new Date().toISOString();
|
|
651
|
+
const next: Record<string, unknown> = {
|
|
652
|
+
...block.state,
|
|
653
|
+
active: true,
|
|
654
|
+
current_phase: "verifying",
|
|
655
|
+
completion_audit_gate: "blocked",
|
|
656
|
+
completion_audit_missing_reason: block.reason,
|
|
657
|
+
completion_audit_blocked_at: nowIso,
|
|
658
|
+
};
|
|
659
|
+
delete next.completed_at;
|
|
660
|
+
await writeFile(block.path, JSON.stringify(next, null, 2));
|
|
661
|
+
}
|
|
662
|
+
|
|
584
663
|
async function readActiveRalphState(
|
|
664
|
+
cwd: string,
|
|
585
665
|
stateDir: string,
|
|
586
666
|
preferredSessionId?: string,
|
|
587
667
|
ownerContext?: {
|
|
@@ -590,7 +670,6 @@ async function readActiveRalphState(
|
|
|
590
670
|
tmuxPaneId?: string;
|
|
591
671
|
},
|
|
592
672
|
): Promise<ActiveRalphStopState | null> {
|
|
593
|
-
const cwd = resolve(stateDir, "..", "..");
|
|
594
673
|
const [rawSessionInfo, usableSessionInfo] = await Promise.all([
|
|
595
674
|
readSessionState(cwd),
|
|
596
675
|
readUsableSessionState(cwd),
|
|
@@ -620,7 +699,7 @@ async function readActiveRalphState(
|
|
|
620
699
|
if (
|
|
621
700
|
sessionScoped?.active === true
|
|
622
701
|
&& isRalphStartingPhase(sessionScoped)
|
|
623
|
-
&& !(await isVisibleRalphActiveForSession(
|
|
702
|
+
&& !(await isVisibleRalphActiveForSession(stateDir, sessionId))
|
|
624
703
|
) {
|
|
625
704
|
continue;
|
|
626
705
|
}
|
|
@@ -634,7 +713,7 @@ async function readActiveRalphState(
|
|
|
634
713
|
currentNativeSessionId,
|
|
635
714
|
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
636
715
|
})
|
|
637
|
-
&& await hasConsistentRalphSkillActivation(
|
|
716
|
+
&& await hasConsistentRalphSkillActivation(stateDir, sessionId)
|
|
638
717
|
) {
|
|
639
718
|
return { state: sessionScoped, path: sessionScopedPath };
|
|
640
719
|
}
|
|
@@ -1049,28 +1128,31 @@ async function buildSessionStartContext(
|
|
|
1049
1128
|
sections.push(["[Active OMX modes]", ...modeSummaries].join("\n"));
|
|
1050
1129
|
}
|
|
1051
1130
|
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1131
|
+
const projectMemoryPath = resolveProjectMemoryPath(cwd);
|
|
1132
|
+
const projectMemory = projectMemoryPath ? await readJsonIfExists(projectMemoryPath) : null;
|
|
1133
|
+
if (projectMemory && projectMemoryPath) {
|
|
1054
1134
|
const directives = Array.isArray(projectMemory.directives) ? projectMemory.directives : [];
|
|
1055
1135
|
const notes = Array.isArray(projectMemory.notes) ? projectMemory.notes : [];
|
|
1056
|
-
const techStack =
|
|
1057
|
-
const conventions =
|
|
1058
|
-
const build =
|
|
1136
|
+
const techStack = safeContextSnippet(projectMemory.techStack);
|
|
1137
|
+
const conventions = safeContextSnippet(projectMemory.conventions);
|
|
1138
|
+
const build = safeContextSnippet(projectMemory.build);
|
|
1059
1139
|
const summary: string[] = [];
|
|
1140
|
+
const relativeMemoryPath = relative(cwd, projectMemoryPath).replace(/\\/g, "/");
|
|
1141
|
+
summary.push(`- source: ${relativeMemoryPath === "project-memory.json" ? "project-memory.json" : ".omx/project-memory.json"}`);
|
|
1060
1142
|
if (techStack) summary.push(`- stack: ${techStack}`);
|
|
1061
1143
|
if (conventions) summary.push(`- conventions: ${conventions}`);
|
|
1062
1144
|
if (build) summary.push(`- build: ${build}`);
|
|
1063
1145
|
if (directives.length > 0) {
|
|
1064
1146
|
const firstDirective = directives[0] as Record<string, unknown>;
|
|
1065
|
-
const directive =
|
|
1147
|
+
const directive = safeContextSnippet(firstDirective.directive);
|
|
1066
1148
|
if (directive) summary.push(`- directive: ${directive}`);
|
|
1067
1149
|
}
|
|
1068
1150
|
if (notes.length > 0) {
|
|
1069
1151
|
const firstNote = notes[0] as Record<string, unknown>;
|
|
1070
|
-
const note =
|
|
1152
|
+
const note = safeContextSnippet(firstNote.content);
|
|
1071
1153
|
if (note) summary.push(`- note: ${note}`);
|
|
1072
1154
|
}
|
|
1073
|
-
if (summary.length >
|
|
1155
|
+
if (summary.length > 1) {
|
|
1074
1156
|
sections.push(["[Project memory]", ...summary].join("\n"));
|
|
1075
1157
|
}
|
|
1076
1158
|
}
|
|
@@ -1313,6 +1395,10 @@ function buildNativeOutsideTmuxTeamPromptBlockState(
|
|
|
1313
1395
|
};
|
|
1314
1396
|
}
|
|
1315
1397
|
|
|
1398
|
+
function buildSkillStateCliInstruction(mode: string, statePath: string): string {
|
|
1399
|
+
return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1316
1402
|
function buildAdditionalContextMessage(
|
|
1317
1403
|
prompt: string,
|
|
1318
1404
|
skillState?: SkillActiveState | null,
|
|
@@ -1374,7 +1460,7 @@ function buildAdditionalContextMessage(
|
|
|
1374
1460
|
promptPriorityMessage,
|
|
1375
1461
|
ultragoalPromptActivationNote,
|
|
1376
1462
|
skillState.initialized_mode && skillState.initialized_state_path
|
|
1377
|
-
?
|
|
1463
|
+
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
1378
1464
|
: null,
|
|
1379
1465
|
teamDetected
|
|
1380
1466
|
? buildTeamRuntimeInstruction(cwd, payload)
|
|
@@ -1386,7 +1472,7 @@ function buildAdditionalContextMessage(
|
|
|
1386
1472
|
|
|
1387
1473
|
if (teamDetected) {
|
|
1388
1474
|
const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
|
|
1389
|
-
?
|
|
1475
|
+
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
1390
1476
|
: null;
|
|
1391
1477
|
return [
|
|
1392
1478
|
detectedKeywordMessage,
|
|
@@ -1413,7 +1499,7 @@ function buildAdditionalContextMessage(
|
|
|
1413
1499
|
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
1414
1500
|
: null,
|
|
1415
1501
|
promptPriorityMessage,
|
|
1416
|
-
|
|
1502
|
+
buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
|
|
1417
1503
|
deepInterviewPromptActivationNote,
|
|
1418
1504
|
ultraworkPromptActivationNote,
|
|
1419
1505
|
ultragoalPromptActivationNote,
|
|
@@ -1480,7 +1566,7 @@ async function resolveTeamWorkerStopDecision(
|
|
|
1480
1566
|
const blockWorkerStop = (
|
|
1481
1567
|
reasonCode: string,
|
|
1482
1568
|
detail: string,
|
|
1483
|
-
stateDirForDecision =
|
|
1569
|
+
stateDirForDecision = getBaseStateDir(cwd),
|
|
1484
1570
|
): TeamWorkerStopDecision => ({
|
|
1485
1571
|
kind: "blocked",
|
|
1486
1572
|
stateDir: stateDirForDecision,
|
|
@@ -1618,20 +1704,33 @@ async function buildModeBasedStopOutput(
|
|
|
1618
1704
|
};
|
|
1619
1705
|
}
|
|
1620
1706
|
|
|
1621
|
-
function looksLikeGoalCompletionPrompt(text: string): boolean {
|
|
1622
|
-
return /\
|
|
1623
|
-
|| /\
|
|
1624
|
-
|| /\
|
|
1707
|
+
export function looksLikeGoalCompletionPrompt(text: string): boolean {
|
|
1708
|
+
return /\bupdate_goal\s*\(/i.test(text)
|
|
1709
|
+
|| /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text)
|
|
1710
|
+
|| /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b/i.test(text)
|
|
1711
|
+
|| /\b(?:ultragoal|performance[-\s]goal|autoresearch[-\s]goal)\b.{0,80}\b(?:complete|checkpoint|finish|close|mark)\b/i.test(text)
|
|
1712
|
+
|| /(?:^|[.!?]\s+)(?:the\s+)?goal\s+(?:is\s+|now\s+|has\s+been\s+)?(?:complete|completed|finished|closed)(?:\s*(?:[.!?]|$)|\s*[:;]\s*\S|\s*[—–-]\s*\S)/i.test(text);
|
|
1625
1713
|
}
|
|
1626
1714
|
|
|
1627
|
-
async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string } | null> {
|
|
1715
|
+
async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
|
|
1628
1716
|
const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
|
|
1717
|
+
const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
|
|
1718
|
+
const aggregateProductComplete = safeString(aggregateCompletion.status) === "complete";
|
|
1629
1719
|
const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
|
|
1630
|
-
const activeUltragoal =
|
|
1720
|
+
const activeUltragoal = aggregateProductComplete
|
|
1721
|
+
? undefined
|
|
1722
|
+
: ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
|
|
1631
1723
|
if (activeUltragoal) {
|
|
1724
|
+
const goalId = safeString(activeUltragoal.id) || "<goal-id>";
|
|
1632
1725
|
return {
|
|
1633
1726
|
workflow: "ultragoal",
|
|
1634
|
-
command: `omx ultragoal checkpoint --goal-id ${
|
|
1727
|
+
command: `omx ultragoal checkpoint --goal-id ${goalId} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
|
|
1728
|
+
remediation: [
|
|
1729
|
+
`If get_goal returns a completed task-scoped objective for the same aggregate ultragoal plan, checkpoint ${goalId} with evidence naming ${goalId} plus .omx/ultragoal/goals.json or ledger.jsonl and pass final quality-gate JSON; OMX will reconcile the completed planned scope without mutating Codex goal state.`,
|
|
1730
|
+
`If get_goal instead returns a different completed legacy objective and complete checkpointing fails, do not repeat --status complete in this thread.`,
|
|
1731
|
+
`Record the non-terminal blocker with: omx ultragoal checkpoint --goal-id ${goalId} --status blocked --codex-goal-json '<different completed get_goal JSON or path>' --evidence '<completed legacy Codex goal blocks create_goal in this thread>'.`,
|
|
1732
|
+
"Then continue this ultragoal from a fresh Codex thread in the same repo/worktree and create the intended goal there.",
|
|
1733
|
+
].join(" "),
|
|
1635
1734
|
};
|
|
1636
1735
|
}
|
|
1637
1736
|
|
|
@@ -1675,7 +1774,8 @@ async function buildGoalWorkflowReconciliationPromptWarning(cwd: string, prompt:
|
|
|
1675
1774
|
`OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
|
|
1676
1775
|
"Call get_goal, pass the resulting JSON or a path with --codex-goal-json, and do not rely on hooks or shell commands to mutate Codex-owned goal state.",
|
|
1677
1776
|
`Required command shape: ${requirement.command}.`,
|
|
1678
|
-
|
|
1777
|
+
requirement.remediation,
|
|
1778
|
+
].filter(Boolean).join(" ");
|
|
1679
1779
|
}
|
|
1680
1780
|
|
|
1681
1781
|
async function buildGoalWorkflowReconciliationStopOutput(
|
|
@@ -1687,7 +1787,11 @@ async function buildGoalWorkflowReconciliationStopOutput(
|
|
|
1687
1787
|
const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
|
|
1688
1788
|
if (!requirement) return null;
|
|
1689
1789
|
const systemMessage =
|
|
1690
|
-
|
|
1790
|
+
[
|
|
1791
|
+
`OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}.`,
|
|
1792
|
+
requirement.remediation,
|
|
1793
|
+
"Hooks must not mutate Codex goal state.",
|
|
1794
|
+
].filter(Boolean).join(" ");
|
|
1691
1795
|
return {
|
|
1692
1796
|
decision: "block",
|
|
1693
1797
|
reason: systemMessage,
|
|
@@ -1698,6 +1802,7 @@ async function buildGoalWorkflowReconciliationStopOutput(
|
|
|
1698
1802
|
|
|
1699
1803
|
async function readTeamModeStateForStop(
|
|
1700
1804
|
cwd: string,
|
|
1805
|
+
stateDir: string,
|
|
1701
1806
|
sessionId?: string,
|
|
1702
1807
|
): Promise<Record<string, unknown> | null> {
|
|
1703
1808
|
const normalizedSessionId = safeString(sessionId).trim();
|
|
@@ -1705,10 +1810,10 @@ async function readTeamModeStateForStop(
|
|
|
1705
1810
|
return await readModeState("team", cwd);
|
|
1706
1811
|
}
|
|
1707
1812
|
|
|
1708
|
-
const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
|
|
1813
|
+
const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
|
|
1709
1814
|
if (scopedState) return scopedState;
|
|
1710
1815
|
|
|
1711
|
-
const rootState = await readJsonIfExists(join(
|
|
1816
|
+
const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
|
|
1712
1817
|
if (rootState?.active !== true) return null;
|
|
1713
1818
|
|
|
1714
1819
|
const ownerSessionId = safeString(rootState.session_id).trim();
|
|
@@ -1723,7 +1828,7 @@ async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Rec
|
|
|
1723
1828
|
if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
|
|
1724
1829
|
return null;
|
|
1725
1830
|
}
|
|
1726
|
-
const teamState = await readTeamModeStateForStop(cwd, sessionId);
|
|
1831
|
+
const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
|
|
1727
1832
|
if (teamState?.active !== true) return null;
|
|
1728
1833
|
const teamName = safeString(teamState.team_name).trim();
|
|
1729
1834
|
if (teamName) {
|
|
@@ -1781,12 +1886,13 @@ function hasReleaseReadinessMode(payload: CodexHookPayload): boolean {
|
|
|
1781
1886
|
|
|
1782
1887
|
async function hasReleaseReadinessStopMarker(
|
|
1783
1888
|
cwd: string,
|
|
1889
|
+
stateDir: string,
|
|
1784
1890
|
sessionId: string,
|
|
1785
1891
|
teamName: string,
|
|
1786
1892
|
): Promise<boolean> {
|
|
1787
1893
|
if (!sessionId) return false;
|
|
1788
1894
|
|
|
1789
|
-
const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
|
|
1895
|
+
const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId, stateDir);
|
|
1790
1896
|
if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
|
|
1791
1897
|
return false;
|
|
1792
1898
|
}
|
|
@@ -1831,8 +1937,11 @@ async function readStopSessionPinnedState(
|
|
|
1831
1937
|
fileName: string,
|
|
1832
1938
|
cwd: string,
|
|
1833
1939
|
sessionId: string,
|
|
1940
|
+
stateDir?: string,
|
|
1834
1941
|
): Promise<Record<string, unknown> | null> {
|
|
1835
|
-
const statePath =
|
|
1942
|
+
const statePath = stateDir && sessionId
|
|
1943
|
+
? join(stateDir, "sessions", sessionId, fileName)
|
|
1944
|
+
: getStateFilePath(fileName, cwd, sessionId || undefined);
|
|
1836
1945
|
return readJsonIfExists(statePath);
|
|
1837
1946
|
}
|
|
1838
1947
|
|
|
@@ -1883,11 +1992,12 @@ function modeStateMatchesSkillStopContext(
|
|
|
1883
1992
|
|
|
1884
1993
|
async function readBlockingSkillForStop(
|
|
1885
1994
|
cwd: string,
|
|
1995
|
+
stateDir: string,
|
|
1886
1996
|
sessionId: string,
|
|
1887
1997
|
threadId: string,
|
|
1888
1998
|
requiredSkill?: string,
|
|
1889
1999
|
): Promise<{ skill: string; phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string } | null> {
|
|
1890
|
-
const canonicalState = await
|
|
2000
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
1891
2001
|
const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
|
|
1892
2002
|
const candidateSkills = requiredSkill
|
|
1893
2003
|
? [requiredSkill]
|
|
@@ -1897,7 +2007,7 @@ async function readBlockingSkillForStop(
|
|
|
1897
2007
|
const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
|
|
1898
2008
|
if (terminalRunState) continue;
|
|
1899
2009
|
|
|
1900
|
-
const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
|
|
2010
|
+
const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
|
|
1901
2011
|
if (!modeState || modeState.active !== true) continue;
|
|
1902
2012
|
if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) continue;
|
|
1903
2013
|
|
|
@@ -1957,11 +2067,12 @@ function isTerminalOrInactiveModeState(state: Record<string, unknown> | null): b
|
|
|
1957
2067
|
|
|
1958
2068
|
async function readSessionScopedModeStateForRootSkill(
|
|
1959
2069
|
cwd: string,
|
|
2070
|
+
stateDir: string,
|
|
1960
2071
|
skill: string,
|
|
1961
2072
|
sessionIds: string[],
|
|
1962
2073
|
): Promise<Record<string, unknown> | null> {
|
|
1963
2074
|
for (const sessionId of sessionIds) {
|
|
1964
|
-
const state = await
|
|
2075
|
+
const state = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
|
|
1965
2076
|
if (state) return state;
|
|
1966
2077
|
}
|
|
1967
2078
|
return null;
|
|
@@ -1969,9 +2080,10 @@ async function readSessionScopedModeStateForRootSkill(
|
|
|
1969
2080
|
|
|
1970
2081
|
async function reconcileStaleRootSkillActiveStateForStop(
|
|
1971
2082
|
cwd: string,
|
|
2083
|
+
stateDir: string,
|
|
1972
2084
|
sessionId: string,
|
|
1973
2085
|
): Promise<void> {
|
|
1974
|
-
const { rootPath } =
|
|
2086
|
+
const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
|
|
1975
2087
|
const rootState = await readSkillActiveState(rootPath);
|
|
1976
2088
|
if (!rootState?.active) return;
|
|
1977
2089
|
|
|
@@ -1997,7 +2109,7 @@ async function reconcileStaleRootSkillActiveStateForStop(
|
|
|
1997
2109
|
initializedSessionId,
|
|
1998
2110
|
safeString(rootState.session_id),
|
|
1999
2111
|
]);
|
|
2000
|
-
const modeState = await readSessionScopedModeStateForRootSkill(cwd, skill, candidateSessionIds);
|
|
2112
|
+
const modeState = await readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, candidateSessionIds);
|
|
2001
2113
|
if (isTerminalOrInactiveModeState(modeState)) {
|
|
2002
2114
|
changed = true;
|
|
2003
2115
|
continue;
|
|
@@ -2076,12 +2188,13 @@ function buildRalplanContinuationStatus(
|
|
|
2076
2188
|
|
|
2077
2189
|
async function readStopAutoNudgePhase(
|
|
2078
2190
|
cwd: string,
|
|
2191
|
+
stateDir: string,
|
|
2079
2192
|
sessionId: string,
|
|
2080
2193
|
threadId: string,
|
|
2081
2194
|
): Promise<string> {
|
|
2082
2195
|
const normalizedSessionId = sessionId.trim();
|
|
2083
2196
|
if (normalizedSessionId) {
|
|
2084
|
-
const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
|
|
2197
|
+
const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
|
|
2085
2198
|
if (
|
|
2086
2199
|
scopedModeState?.active === true
|
|
2087
2200
|
&& safeString(scopedModeState.current_phase).trim().toLowerCase() === "intent-first"
|
|
@@ -2089,7 +2202,7 @@ async function readStopAutoNudgePhase(
|
|
|
2089
2202
|
return "planning";
|
|
2090
2203
|
}
|
|
2091
2204
|
} else {
|
|
2092
|
-
const rootModeState = await readJsonIfExists(join(
|
|
2205
|
+
const rootModeState = await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
|
|
2093
2206
|
if (
|
|
2094
2207
|
rootModeState?.active === true
|
|
2095
2208
|
&& safeString(rootModeState.current_phase).trim().toLowerCase() === "intent-first"
|
|
@@ -2100,7 +2213,7 @@ async function readStopAutoNudgePhase(
|
|
|
2100
2213
|
|
|
2101
2214
|
if (!normalizedSessionId) return "";
|
|
2102
2215
|
|
|
2103
|
-
const canonicalState = await
|
|
2216
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, normalizedSessionId);
|
|
2104
2217
|
const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
|
|
2105
2218
|
const deepInterview = visibleEntries.find((entry) => (
|
|
2106
2219
|
entry.skill === "deep-interview"
|
|
@@ -2108,7 +2221,7 @@ async function readStopAutoNudgePhase(
|
|
|
2108
2221
|
));
|
|
2109
2222
|
if (!deepInterview) return "";
|
|
2110
2223
|
|
|
2111
|
-
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
|
|
2224
|
+
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
|
|
2112
2225
|
if (!modeState || modeState.active !== true) return "";
|
|
2113
2226
|
|
|
2114
2227
|
const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
|
|
@@ -2117,11 +2230,12 @@ async function readStopAutoNudgePhase(
|
|
|
2117
2230
|
|
|
2118
2231
|
async function buildDeepInterviewQuestionStopOutput(
|
|
2119
2232
|
cwd: string,
|
|
2233
|
+
stateDir: string,
|
|
2120
2234
|
sessionId: string,
|
|
2121
2235
|
threadId: string,
|
|
2122
2236
|
): Promise<{ output: Record<string, unknown>; obligationId: string } | null> {
|
|
2123
2237
|
await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
|
|
2124
|
-
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
|
|
2238
|
+
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
|
|
2125
2239
|
if (!modeState) return null;
|
|
2126
2240
|
|
|
2127
2241
|
const questionEnforcement = safeObject(modeState.question_enforcement);
|
|
@@ -2133,7 +2247,7 @@ async function buildDeepInterviewQuestionStopOutput(
|
|
|
2133
2247
|
return null;
|
|
2134
2248
|
}
|
|
2135
2249
|
|
|
2136
|
-
const canonicalState = await
|
|
2250
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
2137
2251
|
if (canonicalState) {
|
|
2138
2252
|
const blocker = listActiveSkills(canonicalState).find((entry) => (
|
|
2139
2253
|
entry.skill === "deep-interview"
|
|
@@ -2336,9 +2450,10 @@ async function findCanonicalActiveTeamForSession(
|
|
|
2336
2450
|
|
|
2337
2451
|
async function resolveActiveTeamNameForStop(
|
|
2338
2452
|
cwd: string,
|
|
2453
|
+
stateDir: string,
|
|
2339
2454
|
sessionId: string,
|
|
2340
2455
|
): Promise<string> {
|
|
2341
|
-
const directState = await readTeamModeStateForStop(cwd, sessionId);
|
|
2456
|
+
const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
|
|
2342
2457
|
const directTeamName = safeString(directState?.team_name).trim();
|
|
2343
2458
|
if (directState?.active === true && directTeamName) return directTeamName;
|
|
2344
2459
|
|
|
@@ -2354,12 +2469,12 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
|
|
|
2354
2469
|
): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
|
|
2355
2470
|
if (!sessionId) return { matched: false, output: null };
|
|
2356
2471
|
|
|
2357
|
-
const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
|
|
2472
|
+
const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
|
|
2358
2473
|
if (!teamName) return { matched: false, output: null };
|
|
2359
2474
|
|
|
2360
2475
|
const explicitReleaseReadinessContext =
|
|
2361
2476
|
hasReleaseReadinessMode(payload)
|
|
2362
|
-
|| await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
|
|
2477
|
+
|| await hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName);
|
|
2363
2478
|
if (!explicitReleaseReadinessContext) {
|
|
2364
2479
|
return { matched: false, output: null };
|
|
2365
2480
|
}
|
|
@@ -2397,10 +2512,11 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(
|
|
|
2397
2512
|
|
|
2398
2513
|
async function buildSkillStopOutput(
|
|
2399
2514
|
cwd: string,
|
|
2515
|
+
stateDir: string,
|
|
2400
2516
|
sessionId: string,
|
|
2401
2517
|
threadId: string,
|
|
2402
2518
|
): Promise<Record<string, unknown> | null> {
|
|
2403
|
-
const blocker = await readBlockingSkillForStop(cwd, sessionId, threadId);
|
|
2519
|
+
const blocker = await readBlockingSkillForStop(cwd, stateDir, sessionId, threadId);
|
|
2404
2520
|
if (!blocker) return null;
|
|
2405
2521
|
|
|
2406
2522
|
const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
@@ -2547,17 +2663,41 @@ async function buildStopHookOutput(
|
|
|
2547
2663
|
const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
|
|
2548
2664
|
const threadId = readPayloadThreadId(payload);
|
|
2549
2665
|
if (canonicalSessionId) {
|
|
2550
|
-
await reconcileStaleRootSkillActiveStateForStop(cwd, canonicalSessionId);
|
|
2666
|
+
await reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, canonicalSessionId);
|
|
2551
2667
|
}
|
|
2552
2668
|
const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
|
|
2553
2669
|
if (execFollowupOutput) return execFollowupOutput;
|
|
2670
|
+
const ralphOwnerContext = {
|
|
2671
|
+
payloadSessionId: sessionId,
|
|
2672
|
+
threadId,
|
|
2673
|
+
tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
|
|
2674
|
+
};
|
|
2675
|
+
const ralphCompletionAuditBlock = options.skipRalphStopBlock === true
|
|
2676
|
+
? null
|
|
2677
|
+
: await readRalphCompletionAuditBlockState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
|
|
2678
|
+
if (ralphCompletionAuditBlock) {
|
|
2679
|
+
await reopenRalphCompletionAuditBlock(ralphCompletionAuditBlock);
|
|
2680
|
+
const blockingPath = formatStopStatePath(cwd, ralphCompletionAuditBlock.path);
|
|
2681
|
+
const systemMessage =
|
|
2682
|
+
`OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}); continue verification, record a prompt-to-artifact checklist plus verification evidence, and do not report complete yet.`;
|
|
2683
|
+
return await returnPersistentStopBlock(
|
|
2684
|
+
payload,
|
|
2685
|
+
stateDir,
|
|
2686
|
+
"ralph-completion-audit-stop",
|
|
2687
|
+
`${blockingPath}|${ralphCompletionAuditBlock.reason}`,
|
|
2688
|
+
{
|
|
2689
|
+
decision: "block",
|
|
2690
|
+
reason: systemMessage,
|
|
2691
|
+
stopReason: `ralph_completion_audit_${ralphCompletionAuditBlock.reason}`,
|
|
2692
|
+
systemMessage,
|
|
2693
|
+
},
|
|
2694
|
+
canonicalSessionId,
|
|
2695
|
+
{ allowRepeatDuringStopHook: true },
|
|
2696
|
+
);
|
|
2697
|
+
}
|
|
2554
2698
|
const ralphState = options.skipRalphStopBlock === true
|
|
2555
2699
|
? null
|
|
2556
|
-
: await readActiveRalphState(stateDir, canonicalSessionId,
|
|
2557
|
-
payloadSessionId: sessionId,
|
|
2558
|
-
threadId,
|
|
2559
|
-
tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
|
|
2560
|
-
});
|
|
2700
|
+
: await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
|
|
2561
2701
|
if (!ralphState) {
|
|
2562
2702
|
const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
|
|
2563
2703
|
if (autoresearchState) {
|
|
@@ -2667,6 +2807,7 @@ async function buildStopHookOutput(
|
|
|
2667
2807
|
if (canonicalSessionId) {
|
|
2668
2808
|
const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(
|
|
2669
2809
|
cwd,
|
|
2810
|
+
stateDir,
|
|
2670
2811
|
canonicalSessionId,
|
|
2671
2812
|
threadId,
|
|
2672
2813
|
);
|
|
@@ -2700,7 +2841,7 @@ async function buildStopHookOutput(
|
|
|
2700
2841
|
if (repeatedCanonicalTeamOutput) return repeatedCanonicalTeamOutput;
|
|
2701
2842
|
}
|
|
2702
2843
|
|
|
2703
|
-
const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
|
|
2844
|
+
const skillOutput = await buildSkillStopOutput(cwd, stateDir, canonicalSessionId, threadId);
|
|
2704
2845
|
if (skillOutput) {
|
|
2705
2846
|
return await returnPersistentStopBlock(
|
|
2706
2847
|
payload,
|
|
@@ -2730,7 +2871,7 @@ async function buildStopHookOutput(
|
|
|
2730
2871
|
);
|
|
2731
2872
|
}
|
|
2732
2873
|
const autoNudgeConfig = await loadAutoNudgeConfig();
|
|
2733
|
-
const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
|
|
2874
|
+
const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
|
|
2734
2875
|
|
|
2735
2876
|
if (
|
|
2736
2877
|
autoNudgeConfig.enabled
|
|
@@ -2816,7 +2957,9 @@ export async function dispatchCodexNativeHook(
|
|
|
2816
2957
|
): Promise<NativeHookDispatchResult> {
|
|
2817
2958
|
const hookEventName = readHookEventName(payload);
|
|
2818
2959
|
const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
|
|
2819
|
-
|
|
2960
|
+
// Native hooks must use the same authoritative runtime state root as HUD/MCP
|
|
2961
|
+
// when boxed/team roots are active; do not bypass it with cwd/.omx/state.
|
|
2962
|
+
const stateDir = getBaseStateDir(cwd);
|
|
2820
2963
|
await mkdir(stateDir, { recursive: true });
|
|
2821
2964
|
|
|
2822
2965
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
@@ -2918,6 +3061,7 @@ export async function dispatchCodexNativeHook(
|
|
|
2918
3061
|
turnId || undefined,
|
|
2919
3062
|
) ?? await recordSkillActivation({
|
|
2920
3063
|
stateDir,
|
|
3064
|
+
sourceCwd: cwd,
|
|
2921
3065
|
text: prompt,
|
|
2922
3066
|
sessionId: sessionIdForState,
|
|
2923
3067
|
threadId,
|
|
@@ -185,7 +185,10 @@ function resolveOmxParityTarget(toolName: string): { command: OmxParityCommand;
|
|
|
185
185
|
if (!match) return null;
|
|
186
186
|
|
|
187
187
|
const [, server, tool] = match;
|
|
188
|
-
if (server === "state")
|
|
188
|
+
if (server === "state") {
|
|
189
|
+
const stateTool = tool.replace(/^state_/, "").replace(/_/g, "-");
|
|
190
|
+
return { command: "state", tool: stateTool };
|
|
191
|
+
}
|
|
189
192
|
if (server === "trace") return { command: "trace", tool };
|
|
190
193
|
if (server === "code_intel") return { command: "code-intel", tool };
|
|
191
194
|
if (server === "memory" && tool.startsWith("notepad_")) {
|