oh-my-codex 0.11.13 → 0.12.1
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 +34 -17
- package/crates/omx-runtime/src/main.rs +6 -2
- package/dist/agents/native-config.js +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +74 -2
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +37 -30
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js +3 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +276 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +95 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +49 -9
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +9 -0
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +136 -11
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +10 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -0
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +2 -1
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +2 -1
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +10 -5
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/index.d.ts +21 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +298 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/omx.js +2 -0
- package/dist/cli/omx.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +41 -7
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +16 -557
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +34 -9
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +79 -2
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +2 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +11 -0
- package/dist/config/codex-hooks.d.ts.map +1 -0
- package/dist/config/codex-hooks.js +50 -0
- package/dist/config/codex-hooks.js.map +1 -0
- package/dist/config/generator.d.ts +5 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +24 -14
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +20 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +132 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +292 -4
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +86 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +40 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +54 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js +31 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +51 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +136 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +120 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +145 -20
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +116 -0
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +86 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js +1 -0
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/runtime.test.js +49 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
- package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
- package/dist/hooks/extensibility/runtime.js +10 -0
- package/dist/hooks/extensibility/runtime.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +1 -1
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +2 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +76 -4
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +12 -8
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +5 -1
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +10 -6
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +6 -1
- package/dist/hud/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +0 -3
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +27 -1
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +0 -5
- package/dist/mcp/__tests__/server-lifecycle.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 +0 -1
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts +20 -0
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +6 -5
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js +24 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
- package/dist/notifications/__tests__/reply-listener.test.js +20 -1
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/tmux.test.js +41 -0
- package/dist/notifications/__tests__/tmux.test.js.map +1 -1
- package/dist/notifications/idle-cooldown.d.ts +13 -0
- package/dist/notifications/idle-cooldown.d.ts.map +1 -1
- package/dist/notifications/idle-cooldown.js +50 -16
- package/dist/notifications/idle-cooldown.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +2 -0
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +4 -0
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts +2 -0
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +1050 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +22 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -0
- package/dist/scripts/codex-native-hook.js +792 -0
- package/dist/scripts/codex-native-hook.js.map +1 -0
- package/dist/scripts/codex-native-pre-post.d.ts +26 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -0
- package/dist/scripts/codex-native-pre-post.js +118 -0
- package/dist/scripts/codex-native-pre-post.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +322 -21
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +5 -6
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/log.d.ts +2 -2
- package/dist/scripts/notify-hook/log.d.ts.map +1 -1
- package/dist/scripts/notify-hook/log.js +10 -2
- package/dist/scripts/notify-hook/log.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +2 -0
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/orchestration-intent.d.ts +18 -0
- package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -0
- package/dist/scripts/notify-hook/orchestration-intent.js +72 -0
- package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -0
- package/dist/scripts/notify-hook/process-runner.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +7 -0
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +15 -6
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +125 -6
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +3 -2
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +165 -37
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +33 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +68 -5
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/utils.d.ts +1 -1
- package/dist/scripts/notify-hook/utils.d.ts.map +1 -1
- package/dist/scripts/notify-hook/utils.js.map +1 -1
- package/dist/scripts/notify-hook.js +55 -32
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +344 -18
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts +2 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts.map +1 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +671 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -0
- package/dist/team/__tests__/mcp-comm.test.js +5 -0
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +543 -15
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +133 -8
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +4 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +160 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +19 -1
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +95 -23
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/contracts.d.ts +11 -1
- package/dist/team/contracts.d.ts.map +1 -1
- package/dist/team/contracts.js +29 -0
- package/dist/team/contracts.js.map +1 -1
- package/dist/team/delivery-log.d.ts +14 -0
- package/dist/team/delivery-log.d.ts.map +1 -0
- package/dist/team/delivery-log.js +35 -0
- package/dist/team/delivery-log.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +2 -2
- package/dist/team/idle-nudge.js +2 -2
- package/dist/team/mcp-comm.d.ts +4 -0
- package/dist/team/mcp-comm.d.ts.map +1 -1
- package/dist/team/mcp-comm.js +84 -1
- package/dist/team/mcp-comm.js.map +1 -1
- package/dist/team/pane-status.d.ts +149 -0
- package/dist/team/pane-status.d.ts.map +1 -0
- package/dist/team/pane-status.js +558 -0
- package/dist/team/pane-status.js.map +1 -0
- package/dist/team/reminder-intents.d.ts +11 -0
- package/dist/team/reminder-intents.d.ts.map +1 -0
- package/dist/team/reminder-intents.js +40 -0
- package/dist/team/reminder-intents.js.map +1 -0
- package/dist/team/runtime-cli.d.ts +1 -1
- package/dist/team/runtime-cli.js +2 -2
- package/dist/team/runtime-cli.js.map +1 -1
- package/dist/team/runtime.d.ts +2 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +409 -191
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +6 -5
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts +4 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +59 -18
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/mailbox.d.ts.map +1 -1
- package/dist/team/state/mailbox.js +45 -2
- package/dist/team/state/mailbox.js.map +1 -1
- package/dist/team/state/monitor.d.ts +2 -1
- package/dist/team/state/monitor.d.ts.map +1 -1
- package/dist/team/state/monitor.js +30 -1
- package/dist/team/state/monitor.js.map +1 -1
- package/dist/team/state/types.d.ts +5 -2
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +30 -3
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +170 -2
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +19 -3
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +4 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +33 -6
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +63 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/__tests__/platform-command.test.js +50 -4
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/paths.d.ts +12 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +44 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +13 -5
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js +10 -1
- package/dist/utils/sleep.js.map +1 -1
- package/package.json +1 -1
- package/prompts/analyst.md +2 -2
- package/prompts/api-reviewer.md +2 -2
- package/prompts/architect.md +2 -2
- package/prompts/build-fixer.md +2 -2
- package/prompts/code-reviewer.md +2 -2
- package/prompts/code-simplifier.md +1 -1
- package/prompts/critic.md +2 -2
- package/prompts/debugger.md +3 -2
- package/prompts/dependency-expert.md +2 -2
- package/prompts/designer.md +2 -2
- package/prompts/executor.md +3 -2
- package/prompts/explore.md +2 -2
- package/prompts/git-master.md +2 -2
- package/prompts/information-architect.md +15 -102
- package/prompts/performance-reviewer.md +2 -2
- package/prompts/planner.md +3 -2
- package/prompts/product-analyst.md +2 -2
- package/prompts/product-manager.md +2 -2
- package/prompts/qa-tester.md +2 -2
- package/prompts/quality-reviewer.md +2 -2
- package/prompts/quality-strategist.md +2 -2
- package/prompts/researcher.md +2 -2
- package/prompts/security-reviewer.md +2 -2
- package/prompts/sisyphus-lite.md +2 -2
- package/prompts/style-reviewer.md +2 -2
- package/prompts/team-executor.md +2 -2
- package/prompts/test-engineer.md +2 -2
- package/prompts/ux-researcher.md +2 -2
- package/prompts/verifier.md +3 -2
- package/prompts/vision.md +2 -2
- package/prompts/writer.md +2 -2
- package/skills/team/SKILL.md +18 -33
- package/src/scripts/__tests__/codex-native-hook.test.ts +1346 -0
- package/src/scripts/codex-native-hook.ts +983 -0
- package/src/scripts/codex-native-pre-post.ts +161 -0
- package/src/scripts/notify-fallback-watcher.ts +378 -29
- package/src/scripts/notify-hook/auto-nudge.ts +5 -10
- package/src/scripts/notify-hook/log.ts +18 -4
- package/src/scripts/notify-hook/managed-tmux.ts +1 -0
- package/src/scripts/notify-hook/orchestration-intent.ts +82 -0
- package/src/scripts/notify-hook/process-runner.ts +4 -4
- package/src/scripts/notify-hook/ralph-session-resume.ts +9 -0
- package/src/scripts/notify-hook/team-dispatch.ts +134 -6
- package/src/scripts/notify-hook/team-leader-nudge.ts +183 -37
- package/src/scripts/notify-hook/team-tmux-guard.ts +35 -43
- package/src/scripts/notify-hook/team-worker.ts +73 -4
- package/src/scripts/notify-hook/utils.ts +1 -1
- package/src/scripts/notify-hook.ts +64 -32
- package/templates/AGENTS.md +21 -11
- package/README.de.md +0 -263
- package/README.el.md +0 -223
- package/README.es.md +0 -263
- package/README.fr.md +0 -263
- package/README.it.md +0 -263
- package/README.ja.md +0 -264
- package/README.ko.md +0 -264
- package/README.pl.md +0 -216
- package/README.pt.md +0 -263
- package/README.ru.md +0 -263
- package/README.tr.md +0 -263
- package/README.vi.md +0 -223
- package/README.zh-TW.md +0 -293
- package/README.zh.md +0 -264
- package/dist/mcp/__tests__/team-server-cleanup.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-cleanup.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-cleanup.test.js +0 -219
- package/dist/mcp/__tests__/team-server-cleanup.test.js.map +0 -1
- package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-runtime-deps.test.js +0 -13
- package/dist/mcp/__tests__/team-server-runtime-deps.test.js.map +0 -1
- package/dist/mcp/__tests__/team-server-wait.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-wait.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-wait.test.js +0 -155
- package/dist/mcp/__tests__/team-server-wait.test.js.map +0 -1
- package/dist/mcp/team-server.d.ts +0 -24
- package/dist/mcp/team-server.d.ts.map +0 -1
- package/dist/mcp/team-server.js +0 -482
- package/dist/mcp/team-server.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
|
-
import { appendFile, mkdir, open, readFile, readdir, rename, stat, unlink, writeFile } from 'fs/promises';
|
|
4
|
+
import { appendFile, mkdir, open, readFile, readdir, rename, rm, stat, unlink, writeFile } from 'fs/promises';
|
|
5
5
|
import { spawnSync } from 'child_process';
|
|
6
6
|
import { dirname, join, resolve } from 'path';
|
|
7
7
|
import { homedir } from 'os';
|
|
@@ -25,6 +25,10 @@ import {
|
|
|
25
25
|
import { DEFAULT_MARKER } from './tmux-hook-engine.js';
|
|
26
26
|
import { isTerminalPhase } from './notify-hook/utils.js';
|
|
27
27
|
import { isSessionStale, readSessionState } from '../hooks/session.js';
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_SUBAGENT_ACTIVE_WINDOW_MS,
|
|
30
|
+
readSubagentSessionSummary,
|
|
31
|
+
} from '../subagents/tracker.js';
|
|
28
32
|
|
|
29
33
|
function argValue(name: string, fallback = ''): string {
|
|
30
34
|
const idx = process.argv.indexOf(name);
|
|
@@ -82,6 +86,10 @@ const authorityOnly = process.argv.includes('--authority-only');
|
|
|
82
86
|
// ack budget so leaderless team dispatch + stale-alert recovery do not feel
|
|
83
87
|
// laggy between native notify-hook turns.
|
|
84
88
|
const pollMs = Math.max(50, asNumber(argValue('--poll-ms', '250'), 250));
|
|
89
|
+
const idleMaxPollMs = Math.max(
|
|
90
|
+
pollMs,
|
|
91
|
+
asNumber(argValue('--idle-max-poll-ms', process.env.OMX_NOTIFY_FALLBACK_IDLE_MAX_POLL_MS || '1000'), 1000),
|
|
92
|
+
);
|
|
85
93
|
const parentPid = Math.trunc(asNumber(argValue('--parent-pid', String(process.ppid || 0)), process.ppid || 0));
|
|
86
94
|
const startedAt = Date.now();
|
|
87
95
|
const fileWindowMs = runOnce ? 15000 : 30000;
|
|
@@ -102,6 +110,13 @@ const stateDir = join(omxDir, 'state');
|
|
|
102
110
|
const statePath = join(stateDir, 'notify-fallback-state.json');
|
|
103
111
|
const pidFilePath = resolve(argValue('--pid-file', join(stateDir, 'notify-fallback.pid')));
|
|
104
112
|
const logPath = join(logsDir, `notify-fallback-${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
113
|
+
const logRotatePath = `${logPath}.1`;
|
|
114
|
+
const logLockPath = `${logPath}.lock`;
|
|
115
|
+
const defaultMaxLogBytes = 10 * 1024 * 1024;
|
|
116
|
+
const maxLogBytes = Math.max(
|
|
117
|
+
0,
|
|
118
|
+
asNumber(argValue('--log-max-bytes', process.env.OMX_NOTIFY_FALLBACK_LOG_MAX_BYTES || String(defaultMaxLogBytes)), defaultMaxLogBytes),
|
|
119
|
+
);
|
|
105
120
|
const ralphSteerTimestampPath = join(stateDir, 'ralph-last-steer-at');
|
|
106
121
|
const ralphSteerLockPath = join(stateDir, 'ralph-continue-steer.lock');
|
|
107
122
|
const watcherOwnerToken = `${process.pid}-${startedAt}-${Math.random().toString(36).slice(2, 10)}`;
|
|
@@ -109,6 +124,7 @@ const RALPH_CONTINUE_TEXT = 'Ralph loop active continue';
|
|
|
109
124
|
const RALPH_CONTINUE_CADENCE_MS = 60_000;
|
|
110
125
|
const RALPH_STEER_LOCK_STALE_MS = 30_000;
|
|
111
126
|
const RALPH_TERMINAL_PHASES = new Set(['complete', 'failed', 'cancelled']);
|
|
127
|
+
const QUIET_ONCE_EVENT_TYPES = new Set(['watcher_start', 'watcher_once_complete']);
|
|
112
128
|
|
|
113
129
|
interface WatcherFileMeta {
|
|
114
130
|
threadId: string;
|
|
@@ -131,6 +147,8 @@ interface RalphContinueSteerState {
|
|
|
131
147
|
pane_id: string;
|
|
132
148
|
pane_current_command: string;
|
|
133
149
|
current_phase: string;
|
|
150
|
+
subagent_session_id: string;
|
|
151
|
+
active_subagent_thread_ids: string[];
|
|
134
152
|
shared_timestamp_path: string;
|
|
135
153
|
shared_last_sent_at: string;
|
|
136
154
|
singleton_lock_path: string;
|
|
@@ -174,6 +192,15 @@ interface ParentGuardState {
|
|
|
174
192
|
pane_count?: number;
|
|
175
193
|
}
|
|
176
194
|
|
|
195
|
+
interface AuthorityBackoffState {
|
|
196
|
+
active: boolean;
|
|
197
|
+
reason: string;
|
|
198
|
+
primary_pid: number | null;
|
|
199
|
+
primary_last_tick_at: string;
|
|
200
|
+
freshness_ms: number | null;
|
|
201
|
+
threshold_ms: number | null;
|
|
202
|
+
}
|
|
203
|
+
|
|
177
204
|
interface ActiveTeamResult {
|
|
178
205
|
active: boolean;
|
|
179
206
|
reason: string;
|
|
@@ -196,6 +223,22 @@ interface FallbackAutoNudgeState {
|
|
|
196
223
|
last_nudged_at: string;
|
|
197
224
|
}
|
|
198
225
|
|
|
226
|
+
interface AdaptivePollState {
|
|
227
|
+
enabled: boolean;
|
|
228
|
+
base_ms: number;
|
|
229
|
+
max_ms: number;
|
|
230
|
+
current_ms: number;
|
|
231
|
+
idle_streak: number;
|
|
232
|
+
last_tick_at: string | null;
|
|
233
|
+
last_activity_at: string | null;
|
|
234
|
+
last_activity_reason: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
interface CycleActivitySummary {
|
|
238
|
+
active: boolean;
|
|
239
|
+
reason: string;
|
|
240
|
+
}
|
|
241
|
+
|
|
199
242
|
const fileState = new Map<string, WatcherFileMeta>();
|
|
200
243
|
const seenTurnKeys = new Set<string>();
|
|
201
244
|
let stopping = false;
|
|
@@ -231,6 +274,8 @@ let lastRalphContinueSteer: RalphContinueSteerState = {
|
|
|
231
274
|
pane_id: '',
|
|
232
275
|
pane_current_command: '',
|
|
233
276
|
current_phase: '',
|
|
277
|
+
subagent_session_id: '',
|
|
278
|
+
active_subagent_thread_ids: [],
|
|
234
279
|
shared_timestamp_path: ralphSteerTimestampPath,
|
|
235
280
|
shared_last_sent_at: '',
|
|
236
281
|
singleton_lock_path: ralphSteerLockPath,
|
|
@@ -240,6 +285,14 @@ let lastParentGuard: ParentGuardState = {
|
|
|
240
285
|
state_path: '',
|
|
241
286
|
current_phase: '',
|
|
242
287
|
};
|
|
288
|
+
let lastAuthorityBackoff: AuthorityBackoffState = {
|
|
289
|
+
active: false,
|
|
290
|
+
reason: '',
|
|
291
|
+
primary_pid: null,
|
|
292
|
+
primary_last_tick_at: '',
|
|
293
|
+
freshness_ms: null,
|
|
294
|
+
threshold_ms: null,
|
|
295
|
+
};
|
|
243
296
|
const AUTO_NUDGE_STALL_MS = Math.max(
|
|
244
297
|
pollMs,
|
|
245
298
|
asNumber(process.env.OMX_NOTIFY_FALLBACK_AUTO_NUDGE_STALL_MS || '5000', 5000),
|
|
@@ -256,14 +309,108 @@ let lastFallbackAutoNudge: FallbackAutoNudgeState = {
|
|
|
256
309
|
last_nudged_signature: '',
|
|
257
310
|
last_nudged_at: '',
|
|
258
311
|
};
|
|
259
|
-
|
|
260
|
-
|
|
312
|
+
let adaptivePollState: AdaptivePollState = {
|
|
313
|
+
enabled: true,
|
|
314
|
+
base_ms: pollMs,
|
|
315
|
+
max_ms: idleMaxPollMs,
|
|
316
|
+
current_ms: pollMs,
|
|
317
|
+
idle_streak: 0,
|
|
318
|
+
last_tick_at: null,
|
|
319
|
+
last_activity_at: null,
|
|
320
|
+
last_activity_reason: 'init',
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
function shouldSuppressEventLog(event: Record<string, unknown>): boolean {
|
|
324
|
+
const eventType = safeString(event.type).trim();
|
|
325
|
+
return runOnce && QUIET_ONCE_EVENT_TYPES.has(eventType);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function acquireLogLock(timeoutMs = 1000): Promise<boolean> {
|
|
329
|
+
const deadline = Date.now() + timeoutMs;
|
|
330
|
+
while (Date.now() < deadline) {
|
|
331
|
+
try {
|
|
332
|
+
await mkdir(logLockPath, { recursive: false });
|
|
333
|
+
return true;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
if ((error as NodeJS.ErrnoException | null)?.code !== 'EEXIST') return false;
|
|
336
|
+
const lockStat = await stat(logLockPath).catch(() => null);
|
|
337
|
+
if (lockStat && Date.now() - lockStat.mtimeMs > 5000) {
|
|
338
|
+
await rm(logLockPath, { recursive: true, force: true }).catch(() => {});
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
await sleep(10);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async function releaseLogLock(): Promise<void> {
|
|
348
|
+
await rm(logLockPath, { recursive: true, force: true }).catch(() => {});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function rotateLogIfNeeded(nextEntryBytes: number): Promise<void> {
|
|
352
|
+
if (maxLogBytes <= 0) return;
|
|
353
|
+
const currentStat = await stat(logPath).catch(() => null);
|
|
354
|
+
if (!currentStat || currentStat.size + nextEntryBytes <= maxLogBytes) return;
|
|
355
|
+
await unlink(logRotatePath).catch(() => {});
|
|
356
|
+
await rename(logPath, logRotatePath).catch(() => {});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function eventLog(event: Record<string, unknown>): Promise<void> {
|
|
360
|
+
if (shouldSuppressEventLog(event)) return;
|
|
361
|
+
const line = `${JSON.stringify({ timestamp: new Date().toISOString(), ...event })}\n`;
|
|
362
|
+
await mkdir(dirname(logPath), { recursive: true }).catch(() => {});
|
|
363
|
+
const locked = await acquireLogLock();
|
|
364
|
+
if (!locked) return;
|
|
365
|
+
try {
|
|
366
|
+
await rotateLogIfNeeded(Buffer.byteLength(line));
|
|
367
|
+
await appendFile(logPath, line);
|
|
368
|
+
} catch {
|
|
369
|
+
// best effort only
|
|
370
|
+
} finally {
|
|
371
|
+
await releaseLogLock();
|
|
372
|
+
}
|
|
261
373
|
}
|
|
262
374
|
|
|
263
375
|
function shouldLogLeaderNudgeTick(reason: string): boolean {
|
|
264
376
|
return reason === 'leader_nudge_checked' || reason === 'leader_nudge_failed';
|
|
265
377
|
}
|
|
266
378
|
|
|
379
|
+
function nextIdlePollMs(currentMs: number): number {
|
|
380
|
+
return Math.min(idleMaxPollMs, Math.max(pollMs, currentMs * 2));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function updateAdaptivePollState(summary: CycleActivitySummary): number {
|
|
384
|
+
const nowIso = new Date().toISOString();
|
|
385
|
+
if (summary.active) {
|
|
386
|
+
adaptivePollState = {
|
|
387
|
+
...adaptivePollState,
|
|
388
|
+
enabled: true,
|
|
389
|
+
base_ms: pollMs,
|
|
390
|
+
max_ms: idleMaxPollMs,
|
|
391
|
+
current_ms: pollMs,
|
|
392
|
+
idle_streak: 0,
|
|
393
|
+
last_tick_at: nowIso,
|
|
394
|
+
last_activity_at: nowIso,
|
|
395
|
+
last_activity_reason: summary.reason,
|
|
396
|
+
};
|
|
397
|
+
return adaptivePollState.current_ms;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const nextMs = nextIdlePollMs(adaptivePollState.current_ms);
|
|
401
|
+
adaptivePollState = {
|
|
402
|
+
...adaptivePollState,
|
|
403
|
+
enabled: true,
|
|
404
|
+
base_ms: pollMs,
|
|
405
|
+
max_ms: idleMaxPollMs,
|
|
406
|
+
current_ms: nextMs,
|
|
407
|
+
idle_streak: adaptivePollState.idle_streak + 1,
|
|
408
|
+
last_tick_at: nowIso,
|
|
409
|
+
last_activity_reason: summary.reason,
|
|
410
|
+
};
|
|
411
|
+
return adaptivePollState.current_ms;
|
|
412
|
+
}
|
|
413
|
+
|
|
267
414
|
function shouldLogDispatchDrainTick(result: unknown): boolean {
|
|
268
415
|
if (!result || typeof result !== 'object') return false;
|
|
269
416
|
const record = result as Record<string, unknown>;
|
|
@@ -291,6 +438,10 @@ function normalizeRalphContinueSteerState(raw: Record<string, unknown> | null |
|
|
|
291
438
|
pane_id: safeString(raw.pane_id),
|
|
292
439
|
pane_current_command: safeString(raw.pane_current_command),
|
|
293
440
|
current_phase: safeString(raw.current_phase),
|
|
441
|
+
subagent_session_id: safeString(raw.subagent_session_id),
|
|
442
|
+
active_subagent_thread_ids: Array.isArray(raw.active_subagent_thread_ids)
|
|
443
|
+
? raw.active_subagent_thread_ids.map((value) => safeString(value).trim()).filter(Boolean)
|
|
444
|
+
: [],
|
|
294
445
|
shared_timestamp_path: safeString(raw.shared_timestamp_path) || ralphSteerTimestampPath,
|
|
295
446
|
shared_last_sent_at: safeString(raw.shared_last_sent_at),
|
|
296
447
|
singleton_lock_path: safeString(raw.singleton_lock_path) || ralphSteerLockPath,
|
|
@@ -328,6 +479,19 @@ async function loadPersistedWatcherState(): Promise<void> {
|
|
|
328
479
|
last_nudged_at: safeString(persistedAutoNudge.last_nudged_at),
|
|
329
480
|
};
|
|
330
481
|
}
|
|
482
|
+
const persistedAdaptivePoll = persisted?.adaptive_poll as Record<string, unknown> | null | undefined;
|
|
483
|
+
if (persistedAdaptivePoll && typeof persistedAdaptivePoll === 'object') {
|
|
484
|
+
adaptivePollState = {
|
|
485
|
+
enabled: persistedAdaptivePoll.enabled !== false,
|
|
486
|
+
base_ms: pollMs,
|
|
487
|
+
max_ms: idleMaxPollMs,
|
|
488
|
+
current_ms: Math.min(idleMaxPollMs, Math.max(pollMs, asNumber(persistedAdaptivePoll.current_ms as string | number | undefined, pollMs))),
|
|
489
|
+
idle_streak: Math.max(0, Math.trunc(asNumber(persistedAdaptivePoll.idle_streak as string | number | undefined, 0))),
|
|
490
|
+
last_tick_at: safeString(persistedAdaptivePoll.last_tick_at) || null,
|
|
491
|
+
last_activity_at: safeString(persistedAdaptivePoll.last_activity_at) || null,
|
|
492
|
+
last_activity_reason: safeString(persistedAdaptivePoll.last_activity_reason) || 'init',
|
|
493
|
+
};
|
|
494
|
+
}
|
|
331
495
|
}
|
|
332
496
|
|
|
333
497
|
interface ActiveModeResult {
|
|
@@ -534,7 +698,8 @@ async function withRalphSteerLock<T>(task: () => Promise<T>): Promise<T | null>
|
|
|
534
698
|
if (code !== 'EEXIST') throw error;
|
|
535
699
|
const existing = await readRalphSteerLock(ralphSteerLockPath);
|
|
536
700
|
const lockAgeMs = parseIsoMillis(existing?.acquired_at) ?? 0;
|
|
537
|
-
const stale =
|
|
701
|
+
const stale = existing !== null
|
|
702
|
+
&& (!isPidAlive(existing.pid) || (lockAgeMs > 0 && Date.now() - lockAgeMs > RALPH_STEER_LOCK_STALE_MS));
|
|
538
703
|
if (stale) {
|
|
539
704
|
await unlink(ralphSteerLockPath).catch(() => {});
|
|
540
705
|
continue;
|
|
@@ -560,29 +725,51 @@ interface RalphProgressGateResult {
|
|
|
560
725
|
allow: boolean;
|
|
561
726
|
reason: string;
|
|
562
727
|
progress_at: string;
|
|
563
|
-
|
|
728
|
+
subagent_session_id?: string;
|
|
729
|
+
active_subagent_thread_ids?: string[];
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
async function readRalphProgressGate(
|
|
733
|
+
activeRalphState: Record<string, unknown> | null,
|
|
734
|
+
now: number,
|
|
735
|
+
): Promise<RalphProgressGateResult> {
|
|
736
|
+
const subagentSessionId = safeString(activeRalphState?.owner_codex_session_id).trim();
|
|
737
|
+
if (subagentSessionId) {
|
|
738
|
+
const summary = await readSubagentSessionSummary(cwd, subagentSessionId, {
|
|
739
|
+
now: new Date(now),
|
|
740
|
+
activeWindowMs: DEFAULT_SUBAGENT_ACTIVE_WINDOW_MS,
|
|
741
|
+
});
|
|
742
|
+
if ((summary?.activeSubagentThreadIds.length ?? 0) > 0) {
|
|
743
|
+
return {
|
|
744
|
+
allow: false,
|
|
745
|
+
reason: 'subagents_active',
|
|
746
|
+
progress_at: '',
|
|
747
|
+
subagent_session_id: subagentSessionId,
|
|
748
|
+
active_subagent_thread_ids: summary?.activeSubagentThreadIds ?? [],
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
}
|
|
564
752
|
|
|
565
|
-
async function readRalphProgressGate(now: number): Promise<RalphProgressGateResult> {
|
|
566
753
|
const hudState = await readJsonObject(join(stateDir, 'hud-state.json'));
|
|
567
754
|
if (!hudState || typeof hudState !== 'object') {
|
|
568
|
-
return { allow: false, reason: 'progress_missing', progress_at: '' };
|
|
755
|
+
return { allow: false, reason: 'progress_missing', progress_at: '', subagent_session_id: subagentSessionId };
|
|
569
756
|
}
|
|
570
757
|
|
|
571
758
|
const progressAt = safeString(hudState.last_progress_at).trim();
|
|
572
759
|
if (!progressAt) {
|
|
573
|
-
return { allow: false, reason: 'progress_missing', progress_at: '' };
|
|
760
|
+
return { allow: false, reason: 'progress_missing', progress_at: '', subagent_session_id: subagentSessionId };
|
|
574
761
|
}
|
|
575
762
|
|
|
576
763
|
const progressMs = parseIsoMillis(progressAt);
|
|
577
764
|
if (progressMs === null) {
|
|
578
|
-
return { allow: false, reason: 'progress_invalid', progress_at: progressAt };
|
|
765
|
+
return { allow: false, reason: 'progress_invalid', progress_at: progressAt, subagent_session_id: subagentSessionId };
|
|
579
766
|
}
|
|
580
767
|
|
|
581
768
|
if (now - progressMs < RALPH_CONTINUE_CADENCE_MS) {
|
|
582
|
-
return { allow: false, reason: 'progress_fresh', progress_at: progressAt };
|
|
769
|
+
return { allow: false, reason: 'progress_fresh', progress_at: progressAt, subagent_session_id: subagentSessionId };
|
|
583
770
|
}
|
|
584
771
|
|
|
585
|
-
return { allow: true, reason: 'progress_stale', progress_at: progressAt };
|
|
772
|
+
return { allow: true, reason: 'progress_stale', progress_at: progressAt, subagent_session_id: subagentSessionId };
|
|
586
773
|
}
|
|
587
774
|
|
|
588
775
|
function shouldSkipRalphContinue(now: number, candidateIso: string, startupIso: string): { skip: boolean; reason: string; anchorMs: number; anchorIso: string } {
|
|
@@ -625,6 +812,108 @@ async function readPidFileRecord(path: string): Promise<PidFileRecord | null> {
|
|
|
625
812
|
}
|
|
626
813
|
}
|
|
627
814
|
|
|
815
|
+
function createAuthorityBackoffState(
|
|
816
|
+
reason: string,
|
|
817
|
+
overrides: Partial<AuthorityBackoffState> = {},
|
|
818
|
+
): AuthorityBackoffState {
|
|
819
|
+
return {
|
|
820
|
+
active: false,
|
|
821
|
+
reason,
|
|
822
|
+
primary_pid: null,
|
|
823
|
+
primary_last_tick_at: '',
|
|
824
|
+
freshness_ms: null,
|
|
825
|
+
threshold_ms: null,
|
|
826
|
+
...overrides,
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function latestWatcherTickIso(state: Record<string, unknown> | null): string {
|
|
831
|
+
if (!state || typeof state !== 'object') return '';
|
|
832
|
+
const candidates = [
|
|
833
|
+
safeString((state.dispatch_drain as Record<string, unknown> | undefined)?.last_tick_at),
|
|
834
|
+
safeString((state.leader_nudge as Record<string, unknown> | undefined)?.last_tick_at),
|
|
835
|
+
safeString((state.fallback_auto_nudge as Record<string, unknown> | undefined)?.last_tick_at),
|
|
836
|
+
safeString((state.ralph_continue_steer as Record<string, unknown> | undefined)?.last_state_check_at),
|
|
837
|
+
]
|
|
838
|
+
.map((value) => value.trim())
|
|
839
|
+
.filter(Boolean);
|
|
840
|
+
let latestIso = '';
|
|
841
|
+
let latestMs = -1;
|
|
842
|
+
for (const candidate of candidates) {
|
|
843
|
+
const parsed = parseIsoMillis(candidate);
|
|
844
|
+
if (parsed === null || parsed <= latestMs) continue;
|
|
845
|
+
latestMs = parsed;
|
|
846
|
+
latestIso = candidate;
|
|
847
|
+
}
|
|
848
|
+
return latestIso;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
async function resolveAuthorityPrimaryWatcherHealth(now = Date.now()): Promise<AuthorityBackoffState> {
|
|
852
|
+
if (!authorityOnly) return createAuthorityBackoffState('not_authority');
|
|
853
|
+
|
|
854
|
+
const existingRecord = await readPidFileRecord(pidFilePath).catch(() => null);
|
|
855
|
+
if (!existingRecord) return createAuthorityBackoffState('pid_missing');
|
|
856
|
+
if (existingRecord.cwd && resolve(existingRecord.cwd) !== cwd) return createAuthorityBackoffState('cwd_mismatch');
|
|
857
|
+
if (!isPidAlive(existingRecord.pid)) {
|
|
858
|
+
return createAuthorityBackoffState('pid_stale', {
|
|
859
|
+
primary_pid: existingRecord.pid,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
const persistedState = await readJsonObject(statePath);
|
|
864
|
+
if (!persistedState) {
|
|
865
|
+
return createAuthorityBackoffState('state_missing', {
|
|
866
|
+
primary_pid: existingRecord.pid,
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
const persistedPid = Math.trunc(asNumber(persistedState.pid as string | number | undefined, 0));
|
|
871
|
+
if (persistedPid > 0 && persistedPid !== existingRecord.pid) {
|
|
872
|
+
return createAuthorityBackoffState('state_pid_mismatch', {
|
|
873
|
+
primary_pid: existingRecord.pid,
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const lastTickAt = latestWatcherTickIso(persistedState);
|
|
878
|
+
if (!lastTickAt) {
|
|
879
|
+
return createAuthorityBackoffState('tick_missing', {
|
|
880
|
+
primary_pid: existingRecord.pid,
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
const lastTickMs = parseIsoMillis(lastTickAt);
|
|
885
|
+
const primaryPollMs = Math.max(50, asNumber(persistedState.poll_ms as string | number | undefined, 250));
|
|
886
|
+
const thresholdMs = Math.max(1_000, primaryPollMs * 4);
|
|
887
|
+
if (lastTickMs === null) {
|
|
888
|
+
return createAuthorityBackoffState('tick_invalid', {
|
|
889
|
+
primary_pid: existingRecord.pid,
|
|
890
|
+
primary_last_tick_at: lastTickAt,
|
|
891
|
+
threshold_ms: thresholdMs,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const freshnessMs = now - lastTickMs;
|
|
896
|
+
if (freshnessMs > thresholdMs) {
|
|
897
|
+
return {
|
|
898
|
+
active: false,
|
|
899
|
+
reason: 'tick_stale',
|
|
900
|
+
primary_pid: existingRecord.pid,
|
|
901
|
+
primary_last_tick_at: lastTickAt,
|
|
902
|
+
freshness_ms: freshnessMs,
|
|
903
|
+
threshold_ms: thresholdMs,
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
return {
|
|
908
|
+
active: true,
|
|
909
|
+
reason: 'primary_watcher_healthy',
|
|
910
|
+
primary_pid: existingRecord.pid,
|
|
911
|
+
primary_last_tick_at: lastTickAt,
|
|
912
|
+
freshness_ms: freshnessMs,
|
|
913
|
+
threshold_ms: thresholdMs,
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
|
|
628
917
|
async function writePidFileRecord(): Promise<void> {
|
|
629
918
|
const nextRecord: PidFileRecord = {
|
|
630
919
|
pid: process.pid,
|
|
@@ -653,6 +942,8 @@ async function runRalphContinueSteerTick(): Promise<void> {
|
|
|
653
942
|
state_path: activeRalph.path,
|
|
654
943
|
pane_id: activePaneId,
|
|
655
944
|
pane_current_command: '',
|
|
945
|
+
subagent_session_id: safeString(activeRalph.state?.owner_codex_session_id).trim(),
|
|
946
|
+
active_subagent_thread_ids: [],
|
|
656
947
|
shared_timestamp_path: ralphSteerTimestampPath,
|
|
657
948
|
singleton_lock_path: ralphSteerLockPath,
|
|
658
949
|
};
|
|
@@ -686,9 +977,11 @@ async function runRalphContinueSteerTick(): Promise<void> {
|
|
|
686
977
|
return { sent: false, skipped: true };
|
|
687
978
|
}
|
|
688
979
|
|
|
689
|
-
const progressGate = await readRalphProgressGate(Date.now());
|
|
980
|
+
const progressGate = await readRalphProgressGate(activeRalph.state, Date.now());
|
|
690
981
|
if (!progressGate.allow) {
|
|
691
982
|
lastRalphContinueSteer.last_reason = progressGate.reason;
|
|
983
|
+
lastRalphContinueSteer.subagent_session_id = progressGate.subagent_session_id ?? lastRalphContinueSteer.subagent_session_id;
|
|
984
|
+
lastRalphContinueSteer.active_subagent_thread_ids = progressGate.active_subagent_thread_ids ?? [];
|
|
692
985
|
return { sent: false, skipped: true };
|
|
693
986
|
}
|
|
694
987
|
|
|
@@ -811,6 +1104,8 @@ async function writeState(extra: Record<string, unknown> = {}): Promise<void> {
|
|
|
811
1104
|
notify_script: notifyScript,
|
|
812
1105
|
authority_only: authorityOnly,
|
|
813
1106
|
poll_ms: pollMs,
|
|
1107
|
+
effective_poll_ms: adaptivePollState.current_ms,
|
|
1108
|
+
idle_max_poll_ms: idleMaxPollMs,
|
|
814
1109
|
pid_file: runOnce ? null : pidFilePath,
|
|
815
1110
|
max_lifetime_ms: maxLifetimeMs,
|
|
816
1111
|
tracked_files: fileState.size,
|
|
@@ -837,11 +1132,27 @@ async function writeState(extra: Record<string, unknown> = {}): Promise<void> {
|
|
|
837
1132
|
enabled: true,
|
|
838
1133
|
stall_ms: AUTO_NUDGE_STALL_MS,
|
|
839
1134
|
},
|
|
1135
|
+
authority_backoff: lastAuthorityBackoff,
|
|
1136
|
+
adaptive_poll: {
|
|
1137
|
+
...adaptivePollState,
|
|
1138
|
+
enabled: true,
|
|
1139
|
+
base_ms: pollMs,
|
|
1140
|
+
max_ms: idleMaxPollMs,
|
|
1141
|
+
},
|
|
840
1142
|
...extra,
|
|
841
1143
|
};
|
|
842
1144
|
await writeFile(statePath, JSON.stringify(state, null, 2)).catch(() => {});
|
|
843
1145
|
}
|
|
844
1146
|
|
|
1147
|
+
async function writeAuthorityBackoffState(): Promise<void> {
|
|
1148
|
+
await mkdir(stateDir, { recursive: true }).catch(() => {});
|
|
1149
|
+
const existing = await readJsonObject(statePath);
|
|
1150
|
+
const state = existing && typeof existing === 'object'
|
|
1151
|
+
? { ...existing, authority_backoff: lastAuthorityBackoff }
|
|
1152
|
+
: { authority_backoff: lastAuthorityBackoff };
|
|
1153
|
+
await writeFile(statePath, JSON.stringify(state, null, 2)).catch(() => {});
|
|
1154
|
+
}
|
|
1155
|
+
|
|
845
1156
|
async function readJsonObject(path: string): Promise<Record<string, unknown> | null> {
|
|
846
1157
|
return readFile(path, 'utf-8')
|
|
847
1158
|
.then((content) => JSON.parse(content) as Record<string, unknown>)
|
|
@@ -1195,7 +1506,8 @@ function splitBufferedLines(partial: string, delta: string): { lines: string[];
|
|
|
1195
1506
|
};
|
|
1196
1507
|
}
|
|
1197
1508
|
|
|
1198
|
-
async function pollFiles(): Promise<
|
|
1509
|
+
async function pollFiles(): Promise<number> {
|
|
1510
|
+
let processedCount = 0;
|
|
1199
1511
|
for (const [path, meta] of fileState.entries()) {
|
|
1200
1512
|
const currentSize = (await stat(path).catch(() => ({ size: 0 }))).size || 0;
|
|
1201
1513
|
if (currentSize <= meta.offset) continue;
|
|
@@ -1209,11 +1521,13 @@ async function pollFiles(): Promise<void> {
|
|
|
1209
1521
|
for (const line of lines) {
|
|
1210
1522
|
if (!line.trim()) continue;
|
|
1211
1523
|
await processLine(meta, line, path);
|
|
1524
|
+
processedCount += 1;
|
|
1212
1525
|
}
|
|
1213
1526
|
}
|
|
1527
|
+
return processedCount;
|
|
1214
1528
|
}
|
|
1215
1529
|
|
|
1216
|
-
async function runLeaderNudgeTick(): Promise<
|
|
1530
|
+
async function runLeaderNudgeTick(): Promise<boolean> {
|
|
1217
1531
|
const startedIso = new Date().toISOString();
|
|
1218
1532
|
const leaderOnly = safeString(process.env.OMX_TEAM_WORKER || '').trim() === '';
|
|
1219
1533
|
const staleThresholdMs = resolveLeaderStalenessThresholdMs();
|
|
@@ -1228,7 +1542,7 @@ async function runLeaderNudgeTick(): Promise<void> {
|
|
|
1228
1542
|
last_tick_at: startedIso,
|
|
1229
1543
|
last_error: 'worker_context',
|
|
1230
1544
|
};
|
|
1231
|
-
return;
|
|
1545
|
+
return false;
|
|
1232
1546
|
}
|
|
1233
1547
|
|
|
1234
1548
|
try {
|
|
@@ -1239,6 +1553,7 @@ async function runLeaderNudgeTick(): Promise<void> {
|
|
|
1239
1553
|
logsDir,
|
|
1240
1554
|
preComputedLeaderStale,
|
|
1241
1555
|
allowFreshMailboxNudges: false,
|
|
1556
|
+
source: 'notify_fallback_watcher',
|
|
1242
1557
|
});
|
|
1243
1558
|
leaderNudgeRuns += 1;
|
|
1244
1559
|
lastLeaderNudge = {
|
|
@@ -1260,6 +1575,7 @@ async function runLeaderNudgeTick(): Promise<void> {
|
|
|
1260
1575
|
reason,
|
|
1261
1576
|
});
|
|
1262
1577
|
}
|
|
1578
|
+
return preComputedLeaderStale;
|
|
1263
1579
|
} catch (err) {
|
|
1264
1580
|
leaderNudgeRuns += 1;
|
|
1265
1581
|
lastLeaderNudge = {
|
|
@@ -1278,10 +1594,11 @@ async function runLeaderNudgeTick(): Promise<void> {
|
|
|
1278
1594
|
reason: 'leader_nudge_failed',
|
|
1279
1595
|
error: lastLeaderNudge.last_error,
|
|
1280
1596
|
});
|
|
1597
|
+
return true;
|
|
1281
1598
|
}
|
|
1282
1599
|
}
|
|
1283
1600
|
|
|
1284
|
-
async function runDispatchDrainTick(): Promise<
|
|
1601
|
+
async function runDispatchDrainTick(): Promise<boolean> {
|
|
1285
1602
|
const startedIso = new Date().toISOString();
|
|
1286
1603
|
try {
|
|
1287
1604
|
const result = await drainPendingTeamDispatch({ cwd, stateDir, logsDir, maxPerTick: dispatchTickMax } as any);
|
|
@@ -1301,6 +1618,7 @@ async function runDispatchDrainTick(): Promise<void> {
|
|
|
1301
1618
|
...(result && typeof result === 'object' ? result as Record<string, unknown> : {}),
|
|
1302
1619
|
});
|
|
1303
1620
|
}
|
|
1621
|
+
return shouldLogDispatchDrainTick(result);
|
|
1304
1622
|
} catch (err) {
|
|
1305
1623
|
dispatchDrainRuns += 1;
|
|
1306
1624
|
lastDispatchDrain = {
|
|
@@ -1317,6 +1635,7 @@ async function runDispatchDrainTick(): Promise<void> {
|
|
|
1317
1635
|
reason: 'dispatch_drain_failed',
|
|
1318
1636
|
error: lastDispatchDrain.last_error,
|
|
1319
1637
|
});
|
|
1638
|
+
return true;
|
|
1320
1639
|
}
|
|
1321
1640
|
}
|
|
1322
1641
|
|
|
@@ -1328,34 +1647,62 @@ async function shouldSuppressInteractiveFallbackTicks(): Promise<boolean> {
|
|
|
1328
1647
|
return deepInterviewStateActive || deepInterviewInputLockActive;
|
|
1329
1648
|
}
|
|
1330
1649
|
|
|
1331
|
-
async function pumpTeamControlPlaneTick(): Promise<
|
|
1332
|
-
await runDispatchDrainTick();
|
|
1333
|
-
if (await shouldSuppressInteractiveFallbackTicks())
|
|
1334
|
-
|
|
1650
|
+
async function pumpTeamControlPlaneTick(): Promise<CycleActivitySummary> {
|
|
1651
|
+
const dispatchActive = await runDispatchDrainTick();
|
|
1652
|
+
if (await shouldSuppressInteractiveFallbackTicks()) {
|
|
1653
|
+
return { active: dispatchActive, reason: dispatchActive ? 'dispatch_drain' : 'deep_interview_locked' };
|
|
1654
|
+
}
|
|
1655
|
+
const leaderActive = await runLeaderNudgeTick();
|
|
1335
1656
|
await runFallbackAutoNudgeTick();
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1657
|
+
const autoNudgeActive = lastFallbackAutoNudge.last_reason === 'sent';
|
|
1658
|
+
if (dispatchActive) return { active: true, reason: 'dispatch_drain' };
|
|
1659
|
+
if (leaderActive) return { active: true, reason: 'leader_nudge' };
|
|
1660
|
+
if (autoNudgeActive) return { active: true, reason: 'fallback_auto_nudge' };
|
|
1661
|
+
return { active: false, reason: lastFallbackAutoNudge.last_reason || 'control_plane_idle' };
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
async function runWatcherCycle(): Promise<number> {
|
|
1666
|
+
let processedRolloutCount = 0;
|
|
1667
|
+
if (authorityOnly) {
|
|
1668
|
+
const authorityBackoff = await resolveAuthorityPrimaryWatcherHealth();
|
|
1669
|
+
lastAuthorityBackoff = authorityBackoff;
|
|
1670
|
+
if (authorityBackoff.active) {
|
|
1671
|
+
await writeAuthorityBackoffState();
|
|
1672
|
+
return processedRolloutCount;
|
|
1673
|
+
}
|
|
1674
|
+
} else {
|
|
1675
|
+
lastAuthorityBackoff = createAuthorityBackoffState('');
|
|
1676
|
+
}
|
|
1340
1677
|
if (!authorityOnly) {
|
|
1341
1678
|
await ensureTrackedFiles();
|
|
1342
|
-
await pollFiles();
|
|
1679
|
+
processedRolloutCount = await pollFiles();
|
|
1343
1680
|
}
|
|
1344
|
-
await pumpTeamControlPlaneTick();
|
|
1681
|
+
const controlPlaneSummary = await pumpTeamControlPlaneTick();
|
|
1345
1682
|
if (!authorityOnly && !(await shouldSuppressInteractiveFallbackTicks())) {
|
|
1346
1683
|
await runRalphWatcherBehaviorTick();
|
|
1347
1684
|
}
|
|
1348
|
-
|
|
1685
|
+
const ralphActive = lastRalphContinueSteer.last_reason === 'sent';
|
|
1686
|
+
const summary: CycleActivitySummary = processedRolloutCount > 0
|
|
1687
|
+
? { active: true, reason: 'rollout_event' }
|
|
1688
|
+
: controlPlaneSummary.active
|
|
1689
|
+
? controlPlaneSummary
|
|
1690
|
+
: ralphActive
|
|
1691
|
+
? { active: true, reason: 'ralph_continue_steer' }
|
|
1692
|
+
: { active: false, reason: controlPlaneSummary.reason || lastRalphContinueSteer.last_reason || 'idle' };
|
|
1693
|
+
const nextDelayMs = updateAdaptivePollState(summary);
|
|
1694
|
+
await writeState({ last_cycle_activity: summary.reason });
|
|
1695
|
+
return nextDelayMs;
|
|
1349
1696
|
}
|
|
1350
1697
|
|
|
1351
1698
|
async function tick(): Promise<void> {
|
|
1352
1699
|
if (stopping) return;
|
|
1353
1700
|
if (await enforceLifecycleGuards()) return;
|
|
1354
|
-
await runWatcherCycle();
|
|
1701
|
+
const nextDelayMs = await runWatcherCycle();
|
|
1355
1702
|
if (await enforceLifecycleGuards()) return;
|
|
1356
1703
|
setTimeout(() => {
|
|
1357
1704
|
void tick();
|
|
1358
|
-
},
|
|
1705
|
+
}, nextDelayMs);
|
|
1359
1706
|
}
|
|
1360
1707
|
|
|
1361
1708
|
function shutdown(signal: string): void {
|
|
@@ -1379,6 +1726,8 @@ async function main(): Promise<void> {
|
|
|
1379
1726
|
notify_script: notifyScript,
|
|
1380
1727
|
authority_only: authorityOnly,
|
|
1381
1728
|
poll_ms: pollMs,
|
|
1729
|
+
effective_poll_ms: adaptivePollState.current_ms,
|
|
1730
|
+
idle_max_poll_ms: idleMaxPollMs,
|
|
1382
1731
|
once: runOnce,
|
|
1383
1732
|
parent_pid: parentPid,
|
|
1384
1733
|
pid_file: runOnce ? null : pidFilePath,
|
|
@@ -12,6 +12,7 @@ import { readJsonIfExists, getScopedStateDirsForCurrentSession, readdir } from '
|
|
|
12
12
|
import { runProcess } from './process-runner.js';
|
|
13
13
|
import { logTmuxHookEvent } from './log.js';
|
|
14
14
|
import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
|
|
15
|
+
import { stripOrchestrationIntentTags } from './orchestration-intent.js';
|
|
15
16
|
import { buildCapturePaneArgv, DEFAULT_MARKER, tmuxHookExplicitlyDisablesInjection } from '../tmux-hook-engine.js';
|
|
16
17
|
import {
|
|
17
18
|
isManagedOmxSession,
|
|
@@ -72,18 +73,12 @@ function buildBlockedAutoApprovalMatcher(blockedInputs) {
|
|
|
72
73
|
export function isBlockedAutoApprovalInput(text, blockedInputs = DEEP_INTERVIEW_BLOCKED_APPROVAL_INPUTS) {
|
|
73
74
|
const normalized = normalizeBlockedAutoApprovalInput(text);
|
|
74
75
|
if (!normalized) return false;
|
|
75
|
-
const
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
const blockedPrefixes = normalizedBlockedInputs.filter((entry) => DEEP_INTERVIEW_BLOCKED_APPROVAL_PREFIXES.has(entry));
|
|
79
|
-
if (blockedPrefixes.some((prefix) => normalized.startsWith(`${prefix} `))) return true;
|
|
76
|
+
const { exactMatches, prefixedMatches, blockedTokenSet } = buildBlockedAutoApprovalMatcher(blockedInputs);
|
|
77
|
+
if (exactMatches.has(normalized)) return true;
|
|
78
|
+
if (prefixedMatches.some((prefix) => normalized.startsWith(`${prefix} `))) return true;
|
|
80
79
|
|
|
81
80
|
const tokens = normalized.split(/\s+/).filter(Boolean);
|
|
82
81
|
if (tokens.length === 0) return false;
|
|
83
|
-
|
|
84
|
-
const blockedTokenSet = new Set(
|
|
85
|
-
normalizedBlockedInputs.flatMap((entry) => entry.split(/\s+/).filter(Boolean)),
|
|
86
|
-
);
|
|
87
82
|
return tokens.every((token) => blockedTokenSet.has(token));
|
|
88
83
|
}
|
|
89
84
|
|
|
@@ -288,7 +283,7 @@ const SEMANTIC_STALL_PROMPT_PATTERNS = [
|
|
|
288
283
|
];
|
|
289
284
|
|
|
290
285
|
function normalizeStallDetectionText(text) {
|
|
291
|
-
return safeString(text)
|
|
286
|
+
return stripOrchestrationIntentTags(safeString(text))
|
|
292
287
|
.replace(/\r\n?/g, '\n')
|
|
293
288
|
.split('\n')
|
|
294
289
|
.filter((line) => !line.includes(DEFAULT_MARKER))
|