oh-my-codex 0.12.5 → 0.12.6
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 +17 -0
- package/dist/cli/__tests__/ask.test.js +26 -0
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +95 -8
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +58 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +169 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +31 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +11 -1
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +92 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +14 -1
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/cleanup.js +1 -1
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/constants.d.ts +1 -0
- package/dist/cli/constants.d.ts.map +1 -1
- package/dist/cli/constants.js +1 -0
- package/dist/cli/constants.js.map +1 -1
- package/dist/cli/explore.d.ts +1 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +45 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +103 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.d.ts +1 -1
- package/dist/cli/mcp-parity.d.ts.map +1 -1
- package/dist/cli/mcp-parity.js +24 -0
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +2 -2
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +54 -14
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +1 -0
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +1 -0
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/mcp-registry.test.js +61 -0
- package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
- package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
- package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +8 -0
- package/dist/config/generator.js.map +1 -1
- package/dist/config/mcp-registry.d.ts +2 -0
- package/dist/config/mcp-registry.d.ts.map +1 -1
- package/dist/config/mcp-registry.js +12 -0
- package/dist/config/mcp-registry.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +18 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +75 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +140 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +35 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +86 -2
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +193 -0
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +41 -1
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +0 -1
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +41 -0
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +42 -5
- 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 +3 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +26 -4
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +27 -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 +3 -1
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +65 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +31 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
- package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/wiki-server.test.js +30 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
- package/dist/mcp/bootstrap.d.ts +18 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +158 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +27 -22
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +181 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -0
- package/dist/mcp/wiki-server.js +235 -0
- package/dist/mcp/wiki-server.js.map +1 -0
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +22 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +10 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/formatter.test.js +11 -0
- package/dist/notifications/__tests__/formatter.test.js.map +1 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
- package/dist/notifications/__tests__/index.test.d.ts +2 -0
- package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/index.test.js +113 -0
- package/dist/notifications/__tests__/index.test.js.map +1 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
- package/dist/notifications/__tests__/reply-listener.test.js +155 -0
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
- package/dist/notifications/__tests__/session-registry.test.js +48 -1
- package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
- package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
- package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/session-status.test.js +159 -0
- package/dist/notifications/__tests__/session-status.test.js.map +1 -0
- package/dist/notifications/__tests__/tmux.test.js +58 -1
- package/dist/notifications/__tests__/tmux.test.js.map +1 -1
- package/dist/notifications/idle-cooldown.d.ts +11 -0
- package/dist/notifications/idle-cooldown.d.ts.map +1 -1
- package/dist/notifications/idle-cooldown.js +42 -8
- package/dist/notifications/idle-cooldown.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +41 -8
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
- package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
- package/dist/notifications/lifecycle-dedupe.js +112 -0
- package/dist/notifications/lifecycle-dedupe.js.map +1 -0
- package/dist/notifications/reply-listener.d.ts +2 -0
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +42 -6
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/session-registry.d.ts.map +1 -1
- package/dist/notifications/session-registry.js +7 -1
- package/dist/notifications/session-registry.js.map +1 -1
- package/dist/notifications/session-status.d.ts +23 -0
- package/dist/notifications/session-status.d.ts.map +1 -0
- package/dist/notifications/session-status.js +187 -0
- package/dist/notifications/session-status.js.map +1 -0
- package/dist/notifications/tmux.d.ts +10 -0
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +59 -5
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/notifications/types.d.ts +2 -0
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/openclaw/__tests__/index.test.js +84 -0
- package/dist/openclaw/__tests__/index.test.js.map +1 -1
- package/dist/openclaw/index.d.ts.map +1 -1
- package/dist/openclaw/index.js +7 -14
- package/dist/openclaw/index.js.map +1 -1
- package/dist/openclaw/types.d.ts +2 -2
- package/dist/openclaw/types.d.ts.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +270 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +134 -6
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/hook-derived-watcher.js +43 -1
- package/dist/scripts/hook-derived-watcher.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +27 -2
- 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 +17 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +5 -3
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +305 -167
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +79 -3
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +2 -1
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/run-provider-advisor.js +20 -2
- package/dist/scripts/run-provider-advisor.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +1 -8
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +12 -68
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +88 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +1 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +1 -0
- package/dist/state/skill-active.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +90 -0
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/hardening-e2e.test.js +17 -0
- package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +23 -0
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +210 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +10 -1
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/delivery-log.d.ts +1 -1
- package/dist/team/delivery-log.d.ts.map +1 -1
- package/dist/team/delivery-log.js.map +1 -1
- package/dist/team/leader-activity.d.ts +1 -0
- package/dist/team/leader-activity.d.ts.map +1 -1
- package/dist/team/leader-activity.js +4 -2
- package/dist/team/leader-activity.js.map +1 -1
- package/dist/team/progress-evidence.d.ts +2 -0
- package/dist/team/progress-evidence.d.ts.map +1 -0
- package/dist/team/progress-evidence.js +77 -0
- package/dist/team/progress-evidence.js.map +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +44 -0
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +57 -8
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts +6 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -2
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/agents-md.test.js +21 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
- package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
- package/dist/utils/__tests__/repo-deps.test.js +71 -0
- package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
- package/dist/utils/agents-md.d.ts +1 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +7 -3
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +20 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/repo-deps.d.ts +20 -0
- package/dist/utils/repo-deps.d.ts.map +1 -0
- package/dist/utils/repo-deps.js +78 -0
- package/dist/utils/repo-deps.js.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
- package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
- package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
- package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
- package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
- package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/escape-newline.test.js +45 -0
- package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
- package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
- package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/ingest.test.js +181 -0
- package/dist/wiki/__tests__/ingest.test.js.map +1 -0
- package/dist/wiki/__tests__/lint.test.d.ts +5 -0
- package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/lint.test.js +163 -0
- package/dist/wiki/__tests__/lint.test.js.map +1 -0
- package/dist/wiki/__tests__/query.test.d.ts +5 -0
- package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/query.test.js +141 -0
- package/dist/wiki/__tests__/query.test.js.map +1 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
- package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
- package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/session-hooks.test.js +36 -0
- package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
- package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
- package/dist/wiki/__tests__/storage.test.d.ts +5 -0
- package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/storage.test.js +278 -0
- package/dist/wiki/__tests__/storage.test.js.map +1 -0
- package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
- package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/wiki/__tests__/test-helpers.js +108 -0
- package/dist/wiki/__tests__/test-helpers.js.map +1 -0
- package/dist/wiki/index.d.ts +14 -0
- package/dist/wiki/index.d.ts.map +1 -0
- package/dist/wiki/index.js +17 -0
- package/dist/wiki/index.js.map +1 -0
- package/dist/wiki/ingest.d.ts +20 -0
- package/dist/wiki/ingest.d.ts.map +1 -0
- package/dist/wiki/ingest.js +115 -0
- package/dist/wiki/ingest.js.map +1 -0
- package/dist/wiki/lifecycle.d.ts +20 -0
- package/dist/wiki/lifecycle.d.ts.map +1 -0
- package/dist/wiki/lifecycle.js +212 -0
- package/dist/wiki/lifecycle.js.map +1 -0
- package/dist/wiki/lint.d.ts +25 -0
- package/dist/wiki/lint.d.ts.map +1 -0
- package/dist/wiki/lint.js +166 -0
- package/dist/wiki/lint.js.map +1 -0
- package/dist/wiki/query.d.ts +36 -0
- package/dist/wiki/query.d.ts.map +1 -0
- package/dist/wiki/query.js +138 -0
- package/dist/wiki/query.js.map +1 -0
- package/dist/wiki/storage.d.ts +33 -0
- package/dist/wiki/storage.d.ts.map +1 -0
- package/dist/wiki/storage.js +321 -0
- package/dist/wiki/storage.js.map +1 -0
- package/dist/wiki/types.d.ts +83 -0
- package/dist/wiki/types.d.ts.map +1 -0
- package/dist/wiki/types.js +15 -0
- package/dist/wiki/types.js.map +1 -0
- package/package.json +3 -1
- package/skills/configure-notifications/SKILL.md +1 -0
- package/skills/doctor/SKILL.md +11 -0
- package/skills/omx-setup/SKILL.md +1 -1
- package/skills/wiki/SKILL.md +57 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +378 -0
- package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
- package/src/scripts/codex-native-hook.ts +187 -7
- package/src/scripts/hook-derived-watcher.ts +43 -1
- package/src/scripts/notify-fallback-watcher.ts +26 -2
- package/src/scripts/notify-hook/auto-nudge.ts +19 -1
- package/src/scripts/notify-hook/managed-tmux.ts +8 -3
- package/src/scripts/notify-hook/team-dispatch.ts +326 -168
- package/src/scripts/notify-hook/team-leader-nudge.ts +82 -3
- package/src/scripts/notify-hook/tmux-injection.ts +2 -1
- package/src/scripts/run-provider-advisor.ts +20 -2
- package/src/scripts/smoke-packed-install.ts +16 -83
|
@@ -97,6 +97,7 @@ async function writeJsonAtomic(path, value) {
|
|
|
97
97
|
|
|
98
98
|
// Keep stale-timeout semantics aligned with src/team/state.ts LOCK_STALE_MS.
|
|
99
99
|
const DISPATCH_LOCK_STALE_MS = 5 * 60 * 1000;
|
|
100
|
+
const DISPATCH_REQUEST_LEASE_STALE_MS = 30 * 1000;
|
|
100
101
|
const DEFAULT_ISSUE_DISPATCH_COOLDOWN_MS = 15 * 60 * 1000;
|
|
101
102
|
const ISSUE_DISPATCH_COOLDOWN_ENV = 'OMX_TEAM_DISPATCH_ISSUE_COOLDOWN_MS';
|
|
102
103
|
const DEFAULT_DISPATCH_TRIGGER_COOLDOWN_MS = 30 * 1000;
|
|
@@ -183,6 +184,31 @@ function parseTriggerCooldownEntry(entry) {
|
|
|
183
184
|
};
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
function reserveDispatchCooldowns({
|
|
188
|
+
issueCooldownMs,
|
|
189
|
+
triggerCooldownMs,
|
|
190
|
+
issueCooldownByIssue,
|
|
191
|
+
triggerCooldownByKey,
|
|
192
|
+
issueKey,
|
|
193
|
+
triggerKey,
|
|
194
|
+
requestId,
|
|
195
|
+
reservedAt = Date.now(),
|
|
196
|
+
}) {
|
|
197
|
+
let mutated = false;
|
|
198
|
+
if (issueKey && issueCooldownMs > 0) {
|
|
199
|
+
issueCooldownByIssue[issueKey] = reservedAt;
|
|
200
|
+
mutated = true;
|
|
201
|
+
}
|
|
202
|
+
if (triggerKey && triggerCooldownMs > 0) {
|
|
203
|
+
triggerCooldownByKey[triggerKey] = {
|
|
204
|
+
at: reservedAt,
|
|
205
|
+
last_request_id: safeString(requestId).trim(),
|
|
206
|
+
};
|
|
207
|
+
mutated = true;
|
|
208
|
+
}
|
|
209
|
+
return mutated;
|
|
210
|
+
}
|
|
211
|
+
|
|
186
212
|
async function withLockDirectory(lockDir, timeoutError, fn) {
|
|
187
213
|
const ownerPath = join(lockDir, 'owner');
|
|
188
214
|
const ownerToken = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
|
|
@@ -245,6 +271,49 @@ async function withMailboxLock(teamDirPath, workerName, fn) {
|
|
|
245
271
|
);
|
|
246
272
|
}
|
|
247
273
|
|
|
274
|
+
function dispatchRequestLeaseDir(teamDirPath, requestId) {
|
|
275
|
+
return join(teamDirPath, 'dispatch', `.processing-${safeString(requestId).trim()}`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function tryAcquireDispatchRequestLease(teamDirPath, requestId) {
|
|
279
|
+
const lockDir = dispatchRequestLeaseDir(teamDirPath, requestId);
|
|
280
|
+
const ownerPath = join(lockDir, 'owner');
|
|
281
|
+
const ownerToken = `${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}`;
|
|
282
|
+
await mkdir(dirname(lockDir), { recursive: true });
|
|
283
|
+
|
|
284
|
+
while (true) {
|
|
285
|
+
try {
|
|
286
|
+
await mkdir(lockDir, { recursive: false });
|
|
287
|
+
await writeFile(ownerPath, ownerToken, 'utf8');
|
|
288
|
+
return { lockDir, ownerPath, ownerToken, requestId };
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
291
|
+
try {
|
|
292
|
+
const info = await stat(lockDir);
|
|
293
|
+
if (Date.now() - info.mtimeMs > DISPATCH_REQUEST_LEASE_STALE_MS) {
|
|
294
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
} catch {
|
|
298
|
+
// best effort
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function releaseDispatchRequestLease(lease) {
|
|
306
|
+
if (!lease?.lockDir || !lease?.ownerPath || !lease?.ownerToken) return;
|
|
307
|
+
try {
|
|
308
|
+
const currentOwner = await readFile(lease.ownerPath, 'utf8');
|
|
309
|
+
if (currentOwner.trim() === lease.ownerToken) {
|
|
310
|
+
await rm(lease.lockDir, { recursive: true, force: true });
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
313
|
+
// best effort
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
248
317
|
function resolveLeaderPaneId(config) {
|
|
249
318
|
return safeString(config?.leader_pane_id).trim();
|
|
250
319
|
}
|
|
@@ -336,6 +405,213 @@ async function appendLeaderNotificationDeferredEvent({
|
|
|
336
405
|
await appendFile(eventsPath, JSON.stringify(event) + '\n').catch(() => {});
|
|
337
406
|
}
|
|
338
407
|
|
|
408
|
+
async function finalizeClaimedDispatchRequest({
|
|
409
|
+
claim,
|
|
410
|
+
result,
|
|
411
|
+
teamName,
|
|
412
|
+
teamDirPath,
|
|
413
|
+
config,
|
|
414
|
+
cwd,
|
|
415
|
+
stateDir,
|
|
416
|
+
logsDir,
|
|
417
|
+
issueCooldownMs,
|
|
418
|
+
triggerCooldownMs,
|
|
419
|
+
}) {
|
|
420
|
+
const requestsPath = join(teamDirPath, 'dispatch', 'requests.json');
|
|
421
|
+
const issueKey = extractIssueKey(claim.request.trigger_message);
|
|
422
|
+
const triggerKey = normalizeTriggerKey(claim.request.trigger_message);
|
|
423
|
+
let summary = { processed: 0, skipped: 0, failed: 0 };
|
|
424
|
+
|
|
425
|
+
await withDispatchLock(teamDirPath, async () => {
|
|
426
|
+
const bridgeRequests = await readBridgeDispatchRequests(stateDir, teamName);
|
|
427
|
+
const usingLegacyRequests = bridgeRequests === null;
|
|
428
|
+
const requests = usingLegacyRequests ? await readJson(requestsPath, []) : bridgeRequests;
|
|
429
|
+
if (!Array.isArray(requests)) return;
|
|
430
|
+
|
|
431
|
+
const index = requests.findIndex((entry) => safeString(entry?.request_id).trim() === claim.request.request_id);
|
|
432
|
+
if (index < 0) return;
|
|
433
|
+
|
|
434
|
+
const request = requests[index];
|
|
435
|
+
if (!request || typeof request !== 'object' || shouldSkipRequest(request) || request.status !== 'pending') return;
|
|
436
|
+
|
|
437
|
+
const issueCooldownState = await readIssueCooldownState(teamDirPath);
|
|
438
|
+
const triggerCooldownState = await readTriggerCooldownState(teamDirPath);
|
|
439
|
+
const issueCooldownByIssue = issueCooldownState.by_issue || {};
|
|
440
|
+
const triggerCooldownByKey = triggerCooldownState.by_trigger || {};
|
|
441
|
+
const nowIso = new Date().toISOString();
|
|
442
|
+
let mutated = false;
|
|
443
|
+
|
|
444
|
+
mutated = reserveDispatchCooldowns({
|
|
445
|
+
issueCooldownMs,
|
|
446
|
+
triggerCooldownMs,
|
|
447
|
+
issueCooldownByIssue,
|
|
448
|
+
triggerCooldownByKey,
|
|
449
|
+
issueKey,
|
|
450
|
+
triggerKey,
|
|
451
|
+
requestId: request.request_id,
|
|
452
|
+
}) || mutated;
|
|
453
|
+
|
|
454
|
+
request.attempt_count = Number.isFinite(request.attempt_count) ? Math.max(0, request.attempt_count + 1) : 1;
|
|
455
|
+
request.updated_at = nowIso;
|
|
456
|
+
|
|
457
|
+
if (result.ok) {
|
|
458
|
+
const MAX_UNCONFIRMED_ATTEMPTS = 3;
|
|
459
|
+
if (result.reason === 'tmux_send_keys_unconfirmed' && request.attempt_count < MAX_UNCONFIRMED_ATTEMPTS) {
|
|
460
|
+
request.last_reason = result.reason;
|
|
461
|
+
summary.skipped += 1;
|
|
462
|
+
mutated = true;
|
|
463
|
+
await appendDispatchLog(logsDir, {
|
|
464
|
+
type: 'dispatch_unconfirmed_retry',
|
|
465
|
+
team: teamName,
|
|
466
|
+
request_id: request.request_id,
|
|
467
|
+
worker: request.to_worker,
|
|
468
|
+
attempt: request.attempt_count,
|
|
469
|
+
reason: result.reason,
|
|
470
|
+
...buildDispatchAttemptEvidence(result),
|
|
471
|
+
});
|
|
472
|
+
await appendDeliveryTelemetry(logsDir, {
|
|
473
|
+
event: 'dispatch_result',
|
|
474
|
+
team: teamName,
|
|
475
|
+
request_id: request.request_id,
|
|
476
|
+
message_id: request.message_id || null,
|
|
477
|
+
to_worker: request.to_worker,
|
|
478
|
+
transport: 'send-keys',
|
|
479
|
+
result: 'retry',
|
|
480
|
+
reason: result.reason,
|
|
481
|
+
});
|
|
482
|
+
await emitOperationalHookEvent(cwd, 'retry-needed', {
|
|
483
|
+
team: teamName,
|
|
484
|
+
worker: request.to_worker,
|
|
485
|
+
request_id: request.request_id,
|
|
486
|
+
attempt: request.attempt_count,
|
|
487
|
+
command: request.trigger_message,
|
|
488
|
+
reason: result.reason,
|
|
489
|
+
status: 'retry-needed',
|
|
490
|
+
});
|
|
491
|
+
} else if (result.reason === 'tmux_send_keys_unconfirmed') {
|
|
492
|
+
request.status = 'failed';
|
|
493
|
+
request.failed_at = nowIso;
|
|
494
|
+
request.last_reason = 'unconfirmed_after_max_retries';
|
|
495
|
+
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir);
|
|
496
|
+
summary.processed += 1;
|
|
497
|
+
summary.failed += 1;
|
|
498
|
+
mutated = true;
|
|
499
|
+
await appendDispatchLog(logsDir, {
|
|
500
|
+
type: 'dispatch_failed',
|
|
501
|
+
team: teamName,
|
|
502
|
+
request_id: request.request_id,
|
|
503
|
+
worker: request.to_worker,
|
|
504
|
+
message_id: request.message_id || null,
|
|
505
|
+
reason: request.last_reason,
|
|
506
|
+
...buildDispatchAttemptEvidence(result),
|
|
507
|
+
});
|
|
508
|
+
await appendDeliveryTelemetry(logsDir, {
|
|
509
|
+
event: 'dispatch_result',
|
|
510
|
+
team: teamName,
|
|
511
|
+
request_id: request.request_id,
|
|
512
|
+
message_id: request.message_id || null,
|
|
513
|
+
to_worker: request.to_worker,
|
|
514
|
+
transport: 'send-keys',
|
|
515
|
+
result: 'failed',
|
|
516
|
+
reason: request.last_reason,
|
|
517
|
+
});
|
|
518
|
+
await emitOperationalHookEvent(cwd, 'failed', {
|
|
519
|
+
team: teamName,
|
|
520
|
+
worker: request.to_worker,
|
|
521
|
+
request_id: request.request_id,
|
|
522
|
+
message_id: request.message_id || null,
|
|
523
|
+
command: request.trigger_message,
|
|
524
|
+
reason: request.last_reason,
|
|
525
|
+
error_summary: request.last_reason,
|
|
526
|
+
status: 'failed',
|
|
527
|
+
});
|
|
528
|
+
} else {
|
|
529
|
+
request.status = 'notified';
|
|
530
|
+
request.notified_at = nowIso;
|
|
531
|
+
request.last_reason = result.reason;
|
|
532
|
+
runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir);
|
|
533
|
+
if (request.kind === 'mailbox' && request.message_id) {
|
|
534
|
+
runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir);
|
|
535
|
+
if (usingLegacyRequests) {
|
|
536
|
+
await updateMailboxNotified(stateDir, teamName, request.to_worker, request.message_id).catch(() => {});
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
summary.processed += 1;
|
|
540
|
+
mutated = true;
|
|
541
|
+
await appendDispatchLog(logsDir, {
|
|
542
|
+
type: 'dispatch_notified',
|
|
543
|
+
team: teamName,
|
|
544
|
+
request_id: request.request_id,
|
|
545
|
+
worker: request.to_worker,
|
|
546
|
+
message_id: request.message_id || null,
|
|
547
|
+
reason: result.reason,
|
|
548
|
+
...buildDispatchAttemptEvidence(result),
|
|
549
|
+
});
|
|
550
|
+
await appendDeliveryTelemetry(logsDir, {
|
|
551
|
+
event: 'dispatch_result',
|
|
552
|
+
team: teamName,
|
|
553
|
+
request_id: request.request_id,
|
|
554
|
+
message_id: request.message_id || null,
|
|
555
|
+
to_worker: request.to_worker,
|
|
556
|
+
transport: 'send-keys',
|
|
557
|
+
result: 'notified',
|
|
558
|
+
reason: result.reason,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
} else {
|
|
562
|
+
request.status = 'failed';
|
|
563
|
+
request.failed_at = nowIso;
|
|
564
|
+
request.last_reason = result.reason;
|
|
565
|
+
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir);
|
|
566
|
+
summary.processed += 1;
|
|
567
|
+
summary.failed += 1;
|
|
568
|
+
mutated = true;
|
|
569
|
+
await appendDispatchLog(logsDir, {
|
|
570
|
+
type: 'dispatch_failed',
|
|
571
|
+
team: teamName,
|
|
572
|
+
request_id: request.request_id,
|
|
573
|
+
worker: request.to_worker,
|
|
574
|
+
message_id: request.message_id || null,
|
|
575
|
+
reason: result.reason,
|
|
576
|
+
...buildDispatchAttemptEvidence(result),
|
|
577
|
+
});
|
|
578
|
+
await appendDeliveryTelemetry(logsDir, {
|
|
579
|
+
event: 'dispatch_result',
|
|
580
|
+
team: teamName,
|
|
581
|
+
request_id: request.request_id,
|
|
582
|
+
message_id: request.message_id || null,
|
|
583
|
+
to_worker: request.to_worker,
|
|
584
|
+
transport: 'send-keys',
|
|
585
|
+
result: 'failed',
|
|
586
|
+
reason: result.reason,
|
|
587
|
+
});
|
|
588
|
+
await emitOperationalHookEvent(cwd, result.reason === LEADER_PANE_MISSING_DEFERRED_REASON ? 'handoff-needed' : 'failed', {
|
|
589
|
+
team: teamName,
|
|
590
|
+
worker: request.to_worker,
|
|
591
|
+
request_id: request.request_id,
|
|
592
|
+
message_id: request.message_id || null,
|
|
593
|
+
command: request.trigger_message,
|
|
594
|
+
reason: result.reason,
|
|
595
|
+
...(result.reason === LEADER_PANE_MISSING_DEFERRED_REASON
|
|
596
|
+
? { status: 'handoff-needed' }
|
|
597
|
+
: { status: 'failed', error_summary: result.reason }),
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (!mutated) return;
|
|
602
|
+
issueCooldownState.by_issue = issueCooldownByIssue;
|
|
603
|
+
await writeJsonAtomic(issueCooldownStatePath(teamDirPath), issueCooldownState);
|
|
604
|
+
triggerCooldownState.by_trigger = triggerCooldownByKey;
|
|
605
|
+
await writeJsonAtomic(triggerCooldownStatePath(teamDirPath), triggerCooldownState);
|
|
606
|
+
await writeJsonAtomic(requestsPath, requests);
|
|
607
|
+
if (!usingLegacyRequests) {
|
|
608
|
+
await writeBridgeDispatchCompat(stateDir, teamName, requests);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
return summary;
|
|
613
|
+
}
|
|
614
|
+
|
|
339
615
|
function resolveWorkerCliForRequest(request, config) {
|
|
340
616
|
const workers = Array.isArray(config?.workers) ? config.workers : [];
|
|
341
617
|
const idx = Number.isFinite(request?.worker_index) ? Number(request.worker_index) : null;
|
|
@@ -602,8 +878,8 @@ export async function drainPendingTeamDispatch({
|
|
|
602
878
|
const manifestPath = join(teamDirPath, 'manifest.v2.json');
|
|
603
879
|
const configPath = join(teamDirPath, 'config.json');
|
|
604
880
|
const requestsPath = join(teamDirPath, 'dispatch', 'requests.json');
|
|
605
|
-
|
|
606
881
|
const config = await readJson(existsSync(manifestPath) ? manifestPath : configPath, {});
|
|
882
|
+
const claims = [];
|
|
607
883
|
await withDispatchLock(teamDirPath, async () => {
|
|
608
884
|
const bridgeRequests = await readBridgeDispatchRequests(stateDir, teamName);
|
|
609
885
|
const usingLegacyRequests = bridgeRequests === null;
|
|
@@ -617,7 +893,7 @@ export async function drainPendingTeamDispatch({
|
|
|
617
893
|
|
|
618
894
|
let mutated = false;
|
|
619
895
|
for (const request of requests) {
|
|
620
|
-
if (processed >= maxPerTick) break;
|
|
896
|
+
if (processed + claims.length >= maxPerTick) break;
|
|
621
897
|
if (!request || typeof request !== 'object') continue;
|
|
622
898
|
if (shouldSkipRequest(request)) {
|
|
623
899
|
skipped += 1;
|
|
@@ -660,9 +936,6 @@ export async function drainPendingTeamDispatch({
|
|
|
660
936
|
result: 'deferred',
|
|
661
937
|
reason: LEADER_PANE_MISSING_DEFERRED_REASON,
|
|
662
938
|
});
|
|
663
|
-
// On the legacy fallback lane, requests.json still carries the queue
|
|
664
|
-
// state for this deferred request; this event stays a progress
|
|
665
|
-
// artifact for hook/watcher readers.
|
|
666
939
|
await appendLeaderNotificationDeferredEvent({
|
|
667
940
|
stateDir,
|
|
668
941
|
teamName,
|
|
@@ -697,170 +970,24 @@ export async function drainPendingTeamDispatch({
|
|
|
697
970
|
}
|
|
698
971
|
}
|
|
699
972
|
|
|
700
|
-
const
|
|
701
|
-
if (
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
}
|
|
705
|
-
if (triggerKey && triggerCooldownMs > 0) {
|
|
706
|
-
triggerCooldownByKey[triggerKey] = {
|
|
707
|
-
at: Date.now(),
|
|
708
|
-
last_request_id: safeString(request.request_id).trim(),
|
|
709
|
-
};
|
|
710
|
-
mutated = true;
|
|
711
|
-
}
|
|
712
|
-
const nowIso = new Date().toISOString();
|
|
713
|
-
request.attempt_count = Number.isFinite(request.attempt_count) ? Math.max(0, request.attempt_count + 1) : 1;
|
|
714
|
-
request.updated_at = nowIso;
|
|
715
|
-
|
|
716
|
-
if (result.ok) {
|
|
717
|
-
// Unconfirmed sends: trigger text was still visible after retry
|
|
718
|
-
// rounds. Leave as pending for the next tick to retry (up to 3
|
|
719
|
-
// total attempts) rather than marking notified. Fixes #391.
|
|
720
|
-
const MAX_UNCONFIRMED_ATTEMPTS = 3;
|
|
721
|
-
if (result.reason === 'tmux_send_keys_unconfirmed' && request.attempt_count < MAX_UNCONFIRMED_ATTEMPTS) {
|
|
722
|
-
request.last_reason = result.reason;
|
|
723
|
-
mutated = true;
|
|
724
|
-
skipped += 1;
|
|
725
|
-
await appendDispatchLog(logsDir, {
|
|
726
|
-
type: 'dispatch_unconfirmed_retry',
|
|
727
|
-
team: teamName,
|
|
728
|
-
request_id: request.request_id,
|
|
729
|
-
worker: request.to_worker,
|
|
730
|
-
attempt: request.attempt_count,
|
|
731
|
-
reason: result.reason,
|
|
732
|
-
...buildDispatchAttemptEvidence(result),
|
|
733
|
-
});
|
|
734
|
-
await appendDeliveryTelemetry(logsDir, {
|
|
735
|
-
event: 'dispatch_result',
|
|
736
|
-
team: teamName,
|
|
737
|
-
request_id: request.request_id,
|
|
738
|
-
message_id: request.message_id || null,
|
|
739
|
-
to_worker: request.to_worker,
|
|
740
|
-
transport: 'send-keys',
|
|
741
|
-
result: 'retry',
|
|
742
|
-
reason: result.reason,
|
|
743
|
-
});
|
|
744
|
-
await emitOperationalHookEvent(cwd, 'retry-needed', {
|
|
745
|
-
team: teamName,
|
|
746
|
-
worker: request.to_worker,
|
|
747
|
-
request_id: request.request_id,
|
|
748
|
-
attempt: request.attempt_count,
|
|
749
|
-
command: request.trigger_message,
|
|
750
|
-
reason: result.reason,
|
|
751
|
-
status: 'retry-needed',
|
|
752
|
-
});
|
|
753
|
-
continue;
|
|
754
|
-
}
|
|
755
|
-
if (result.reason === 'tmux_send_keys_unconfirmed') {
|
|
756
|
-
request.status = 'failed';
|
|
757
|
-
request.failed_at = nowIso;
|
|
758
|
-
request.last_reason = 'unconfirmed_after_max_retries';
|
|
759
|
-
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: 'unconfirmed_after_max_retries' }, stateDir);
|
|
760
|
-
processed += 1;
|
|
761
|
-
failed += 1;
|
|
762
|
-
mutated = true;
|
|
763
|
-
await appendDispatchLog(logsDir, {
|
|
764
|
-
type: 'dispatch_failed',
|
|
765
|
-
team: teamName,
|
|
766
|
-
request_id: request.request_id,
|
|
767
|
-
worker: request.to_worker,
|
|
768
|
-
message_id: request.message_id || null,
|
|
769
|
-
reason: request.last_reason,
|
|
770
|
-
...buildDispatchAttemptEvidence(result),
|
|
771
|
-
});
|
|
772
|
-
await appendDeliveryTelemetry(logsDir, {
|
|
773
|
-
event: 'dispatch_result',
|
|
774
|
-
team: teamName,
|
|
775
|
-
request_id: request.request_id,
|
|
776
|
-
message_id: request.message_id || null,
|
|
777
|
-
to_worker: request.to_worker,
|
|
778
|
-
transport: 'send-keys',
|
|
779
|
-
result: 'failed',
|
|
780
|
-
reason: request.last_reason,
|
|
781
|
-
});
|
|
782
|
-
await emitOperationalHookEvent(cwd, 'failed', {
|
|
783
|
-
team: teamName,
|
|
784
|
-
worker: request.to_worker,
|
|
785
|
-
request_id: request.request_id,
|
|
786
|
-
message_id: request.message_id || null,
|
|
787
|
-
command: request.trigger_message,
|
|
788
|
-
reason: request.last_reason,
|
|
789
|
-
error_summary: request.last_reason,
|
|
790
|
-
status: 'failed',
|
|
791
|
-
});
|
|
792
|
-
continue;
|
|
793
|
-
}
|
|
794
|
-
request.status = 'notified';
|
|
795
|
-
request.notified_at = nowIso;
|
|
796
|
-
request.last_reason = result.reason;
|
|
797
|
-
runtimeExec({ command: 'MarkNotified', request_id: request.request_id, channel: 'tmux' }, stateDir);
|
|
798
|
-
if (request.kind === 'mailbox' && request.message_id) {
|
|
799
|
-
runtimeExec({ command: 'MarkMailboxNotified', message_id: request.message_id }, stateDir);
|
|
800
|
-
if (usingLegacyRequests) {
|
|
801
|
-
await updateMailboxNotified(stateDir, teamName, request.to_worker, request.message_id).catch(() => {});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
processed += 1;
|
|
805
|
-
mutated = true;
|
|
806
|
-
await appendDispatchLog(logsDir, {
|
|
807
|
-
type: 'dispatch_notified',
|
|
808
|
-
team: teamName,
|
|
809
|
-
request_id: request.request_id,
|
|
810
|
-
worker: request.to_worker,
|
|
811
|
-
message_id: request.message_id || null,
|
|
812
|
-
reason: result.reason,
|
|
813
|
-
...buildDispatchAttemptEvidence(result),
|
|
814
|
-
});
|
|
815
|
-
await appendDeliveryTelemetry(logsDir, {
|
|
816
|
-
event: 'dispatch_result',
|
|
817
|
-
team: teamName,
|
|
818
|
-
request_id: request.request_id,
|
|
819
|
-
message_id: request.message_id || null,
|
|
820
|
-
to_worker: request.to_worker,
|
|
821
|
-
transport: 'send-keys',
|
|
822
|
-
result: 'notified',
|
|
823
|
-
reason: result.reason,
|
|
824
|
-
});
|
|
825
|
-
} else {
|
|
826
|
-
request.status = 'failed';
|
|
827
|
-
request.failed_at = nowIso;
|
|
828
|
-
request.last_reason = result.reason;
|
|
829
|
-
runtimeExec({ command: 'MarkFailed', request_id: request.request_id, reason: result.reason }, stateDir);
|
|
830
|
-
processed += 1;
|
|
831
|
-
failed += 1;
|
|
832
|
-
mutated = true;
|
|
833
|
-
await appendDispatchLog(logsDir, {
|
|
834
|
-
type: 'dispatch_failed',
|
|
835
|
-
team: teamName,
|
|
836
|
-
request_id: request.request_id,
|
|
837
|
-
worker: request.to_worker,
|
|
838
|
-
message_id: request.message_id || null,
|
|
839
|
-
reason: result.reason,
|
|
840
|
-
...buildDispatchAttemptEvidence(result),
|
|
841
|
-
});
|
|
842
|
-
await appendDeliveryTelemetry(logsDir, {
|
|
843
|
-
event: 'dispatch_result',
|
|
844
|
-
team: teamName,
|
|
845
|
-
request_id: request.request_id,
|
|
846
|
-
message_id: request.message_id || null,
|
|
847
|
-
to_worker: request.to_worker,
|
|
848
|
-
transport: 'send-keys',
|
|
849
|
-
result: 'failed',
|
|
850
|
-
reason: result.reason,
|
|
851
|
-
});
|
|
852
|
-
await emitOperationalHookEvent(cwd, result.reason === LEADER_PANE_MISSING_DEFERRED_REASON ? 'handoff-needed' : 'failed', {
|
|
853
|
-
team: teamName,
|
|
854
|
-
worker: request.to_worker,
|
|
855
|
-
request_id: request.request_id,
|
|
856
|
-
message_id: request.message_id || null,
|
|
857
|
-
command: request.trigger_message,
|
|
858
|
-
reason: result.reason,
|
|
859
|
-
...(result.reason === LEADER_PANE_MISSING_DEFERRED_REASON
|
|
860
|
-
? { status: 'handoff-needed' }
|
|
861
|
-
: { status: 'failed', error_summary: result.reason }),
|
|
862
|
-
});
|
|
973
|
+
const lease = await tryAcquireDispatchRequestLease(teamDirPath, request.request_id);
|
|
974
|
+
if (!lease) {
|
|
975
|
+
skipped += 1;
|
|
976
|
+
continue;
|
|
863
977
|
}
|
|
978
|
+
mutated = reserveDispatchCooldowns({
|
|
979
|
+
issueCooldownMs,
|
|
980
|
+
triggerCooldownMs,
|
|
981
|
+
issueCooldownByIssue,
|
|
982
|
+
triggerCooldownByKey,
|
|
983
|
+
issueKey,
|
|
984
|
+
triggerKey,
|
|
985
|
+
requestId: request.request_id,
|
|
986
|
+
}) || mutated;
|
|
987
|
+
claims.push({
|
|
988
|
+
request: { ...request },
|
|
989
|
+
lease,
|
|
990
|
+
});
|
|
864
991
|
}
|
|
865
992
|
|
|
866
993
|
if (mutated) {
|
|
@@ -874,6 +1001,37 @@ export async function drainPendingTeamDispatch({
|
|
|
874
1001
|
}
|
|
875
1002
|
}
|
|
876
1003
|
});
|
|
1004
|
+
|
|
1005
|
+
try {
|
|
1006
|
+
for (const claim of claims) {
|
|
1007
|
+
try {
|
|
1008
|
+
const result = await injector(claim.request, config, resolve(cwd), stateDir);
|
|
1009
|
+
const delta = await finalizeClaimedDispatchRequest({
|
|
1010
|
+
claim,
|
|
1011
|
+
result,
|
|
1012
|
+
teamName,
|
|
1013
|
+
teamDirPath,
|
|
1014
|
+
config,
|
|
1015
|
+
cwd,
|
|
1016
|
+
stateDir,
|
|
1017
|
+
logsDir,
|
|
1018
|
+
issueCooldownMs,
|
|
1019
|
+
triggerCooldownMs,
|
|
1020
|
+
});
|
|
1021
|
+
processed += delta.processed;
|
|
1022
|
+
skipped += delta.skipped;
|
|
1023
|
+
failed += delta.failed;
|
|
1024
|
+
} finally {
|
|
1025
|
+
claim.released = true;
|
|
1026
|
+
await releaseDispatchRequestLease(claim.lease);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
} finally {
|
|
1030
|
+
for (const claim of claims) {
|
|
1031
|
+
if (claim.released) continue;
|
|
1032
|
+
await releaseDispatchRequestLease(claim.lease);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
877
1035
|
}
|
|
878
1036
|
|
|
879
1037
|
return { processed, skipped, failed };
|
|
@@ -22,8 +22,10 @@ import { DEFAULT_MARKER } from '../tmux-hook-engine.js';
|
|
|
22
22
|
import { isLeaderRuntimeStale } from '../../team/leader-activity.js';
|
|
23
23
|
import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
|
|
24
24
|
import { writeTeamLeaderAttention } from '../../team/state.js';
|
|
25
|
+
import { readLatestTeamProgressEvidenceMs } from '../../team/progress-evidence.js';
|
|
25
26
|
const LEADER_PANE_MISSING_NO_INJECTION_REASON = 'leader_pane_missing_no_injection';
|
|
26
27
|
const LEADER_PANE_SHELL_NO_INJECTION_REASON = 'leader_pane_shell_no_injection';
|
|
28
|
+
const LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON = 'pane_already_shows_same_classified_state';
|
|
27
29
|
const LEADER_NOTIFICATION_DEFERRED_TYPE = 'leader_notification_deferred';
|
|
28
30
|
const ACK_WITHOUT_START_EVIDENCE_REASON = 'ack_without_start_evidence';
|
|
29
31
|
const ACK_LIKE_PATTERNS = [
|
|
@@ -438,6 +440,27 @@ function formatMailboxBodyForLeader(body, maxLength = 40) {
|
|
|
438
440
|
return `${normalized.slice(0, maxLength - 1)}…`;
|
|
439
441
|
}
|
|
440
442
|
|
|
443
|
+
function normalizeVisibleLeaderStateText(text) {
|
|
444
|
+
return safeString(text)
|
|
445
|
+
.toLowerCase()
|
|
446
|
+
.replace(/\[omx_tmux_inject\]/g, ' ')
|
|
447
|
+
.replace(/\[omx_intent:[^\]]+\]/g, ' ')
|
|
448
|
+
.replace(/said\s+"[^"]*"/g, 'said "<content>"')
|
|
449
|
+
.replace(/said\s+'[^']*'/g, 'said "<content>"')
|
|
450
|
+
.replace(/\b\d+[smhd](?:\s+\d+[smhd])*\b/g, '<duration>')
|
|
451
|
+
.replace(/\b\d+\b/g, '<n>')
|
|
452
|
+
.replace(/\s+/g, ' ')
|
|
453
|
+
.trim();
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function paneAlreadyShowsVisibleLeaderState(paneCapture, visibleText) {
|
|
457
|
+
const normalizedVisibleText = normalizeVisibleLeaderStateText(visibleText);
|
|
458
|
+
if (!normalizedVisibleText) return false;
|
|
459
|
+
const normalizedPaneCapture = normalizeVisibleLeaderStateText(paneCapture);
|
|
460
|
+
if (!normalizedPaneCapture) return false;
|
|
461
|
+
return normalizedPaneCapture.includes(normalizedVisibleText);
|
|
462
|
+
}
|
|
463
|
+
|
|
441
464
|
async function workerHasOwnedStartedTask(stateDir, teamName, workerName) {
|
|
442
465
|
const tasksDir = join(stateDir, 'team', teamName, 'tasks');
|
|
443
466
|
if (!existsSync(tasksDir)) return false;
|
|
@@ -674,11 +697,15 @@ export async function maybeNudgeTeamLeader({
|
|
|
674
697
|
const workerTurnProgress = hasWorkerTurnProgress(progressSnapshot.workerSnapshot, previousTurnCounts);
|
|
675
698
|
const hasTrackableTurnSignals = hasTrackableActiveWorkerTurns(progressSnapshot.workerSnapshot, previousTurnCounts);
|
|
676
699
|
const progressChanged = !previousSignature || previousSignature !== progressSnapshot.signature || workerTurnProgress;
|
|
700
|
+
const extraProgressEvidenceMs = await readLatestTeamProgressEvidenceMs(cwd, teamName).catch(() => Number.NaN);
|
|
677
701
|
const effectiveProgressAtMs = progressChanged || !Number.isFinite(previousProgressAtMs)
|
|
678
702
|
? nowMs
|
|
679
703
|
: previousProgressAtMs;
|
|
680
|
-
const
|
|
681
|
-
|
|
704
|
+
const latestProgressEvidenceMs = Number.isFinite(extraProgressEvidenceMs)
|
|
705
|
+
? Math.max(effectiveProgressAtMs, extraProgressEvidenceMs)
|
|
706
|
+
: effectiveProgressAtMs;
|
|
707
|
+
const effectiveProgressAtIso = new Date(latestProgressEvidenceMs).toISOString();
|
|
708
|
+
const stalledForMs = Math.max(0, nowMs - latestProgressEvidenceMs);
|
|
682
709
|
const stallThresholdMs = hasTrackableTurnSignals ? workerTurnStallThresholdMs : fallbackProgressStallThresholdMs;
|
|
683
710
|
const teamProgressStalled =
|
|
684
711
|
progressSnapshot.workRemaining
|
|
@@ -686,6 +713,9 @@ export async function maybeNudgeTeamLeader({
|
|
|
686
713
|
&& !allWorkersIdle
|
|
687
714
|
&& !progressChanged
|
|
688
715
|
&& stalledForMs >= stallThresholdMs;
|
|
716
|
+
const hasFreshProgressEvidence =
|
|
717
|
+
progressSnapshot.workRemaining
|
|
718
|
+
&& stalledForMs < stallThresholdMs;
|
|
689
719
|
const leaderActionState = classifyLeaderActionState({
|
|
690
720
|
allWorkersIdle,
|
|
691
721
|
workerPanesAlive: paneStatus.alive,
|
|
@@ -722,7 +752,7 @@ export async function maybeNudgeTeamLeader({
|
|
|
722
752
|
|
|
723
753
|
// Stale-leader follow-up is the only periodic visible nudge path.
|
|
724
754
|
// This keeps the leader pane quieter when the leader is not actually stale.
|
|
725
|
-
const stalePanesNudge = paneStatus.alive && leaderStale;
|
|
755
|
+
const stalePanesNudge = paneStatus.alive && leaderStale && !hasFreshProgressEvidence;
|
|
726
756
|
const previousStalledTeamNudge = prevReason === 'stuck_waiting_on_leader';
|
|
727
757
|
const stalledTeamNudge = teamProgressStalled && (dueByTime || !previousStalledTeamNudge);
|
|
728
758
|
const staleFollowupDue = stalePanesNudge && dueByTime;
|
|
@@ -923,6 +953,55 @@ export async function maybeNudgeTeamLeader({
|
|
|
923
953
|
continue;
|
|
924
954
|
}
|
|
925
955
|
|
|
956
|
+
if (paneAlreadyShowsVisibleLeaderState(paneGuard.paneCapture, capped)) {
|
|
957
|
+
nudgeState.last_nudged_by_team[teamName] = {
|
|
958
|
+
at: nowIso,
|
|
959
|
+
last_message_id: newestId || prevMsgId || '',
|
|
960
|
+
reason: nudgeReason,
|
|
961
|
+
orchestration_intent: orchestrationIntent,
|
|
962
|
+
};
|
|
963
|
+
if (shouldSendAllIdleNudge) {
|
|
964
|
+
nudgeState.last_idle_nudged_by_team[teamName] = {
|
|
965
|
+
at: nowIso,
|
|
966
|
+
worker_count: workerNames.length,
|
|
967
|
+
orchestration_intent: orchestrationIntent,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
await emitTeamNudgeEvent(cwd, teamName, nudgeReason, orchestrationIntent, nowIso);
|
|
972
|
+
|
|
973
|
+
try {
|
|
974
|
+
await logTmuxHookEvent(logsDir, {
|
|
975
|
+
timestamp: nowIso,
|
|
976
|
+
type: 'team_leader_nudge',
|
|
977
|
+
team: teamName,
|
|
978
|
+
tmux_target: tmuxTarget,
|
|
979
|
+
reason: nudgeReason,
|
|
980
|
+
orchestration_intent: orchestrationIntent,
|
|
981
|
+
pane_count: paneStatus.paneCount,
|
|
982
|
+
leader_stale: leaderStale,
|
|
983
|
+
message_count: messages.length,
|
|
984
|
+
stalled_for_ms: teamProgressStalled ? stalledForMs : undefined,
|
|
985
|
+
missing_signal_workers: progressSnapshot.missingSignalWorkers,
|
|
986
|
+
visible_injection_suppressed: true,
|
|
987
|
+
suppression_reason: LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON,
|
|
988
|
+
});
|
|
989
|
+
} catch { /* ignore */ }
|
|
990
|
+
await appendTeamDeliveryLog(logsDir, {
|
|
991
|
+
event: 'nudge_triggered',
|
|
992
|
+
source,
|
|
993
|
+
team: teamName,
|
|
994
|
+
to_worker: 'leader-fixed',
|
|
995
|
+
transport: 'send-keys',
|
|
996
|
+
result: 'suppressed',
|
|
997
|
+
reason: nudgeReason,
|
|
998
|
+
orchestration_intent: orchestrationIntent,
|
|
999
|
+
visible_injection_suppressed: true,
|
|
1000
|
+
suppression_reason: LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON,
|
|
1001
|
+
}).catch(() => {});
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
926
1005
|
try {
|
|
927
1006
|
const sendResult = await sendPaneInput({
|
|
928
1007
|
paneTarget: tmuxTarget,
|