oh-my-codex 0.18.13 → 0.18.15
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +41 -18
- package/dist/agents/__tests__/native-config.test.js +62 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +15 -2
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.js +7 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.d.ts +2 -0
- package/dist/autopilot/__tests__/planner-routing.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/planner-routing.test.js +74 -0
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +315 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/completion-gate.js +1 -1
- package/dist/autopilot/completion-gate.js.map +1 -1
- package/dist/autopilot/fsm.d.ts +2 -2
- package/dist/autopilot/fsm.d.ts.map +1 -1
- package/dist/autopilot/fsm.js +3 -0
- package/dist/autopilot/fsm.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts +21 -0
- package/dist/autopilot/planner-routing.d.ts.map +1 -0
- package/dist/autopilot/planner-routing.js +59 -0
- package/dist/autopilot/planner-routing.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +30 -0
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/goal.d.ts.map +1 -1
- package/dist/autoresearch/goal.js +10 -1
- package/dist/autoresearch/goal.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.js +33 -0
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +3 -0
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js +158 -0
- package/dist/cli/__tests__/doctor-artifact-ownership.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js +33 -0
- package/dist/cli/__tests__/doctor-explore-deprecated.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js +13 -0
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +132 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +5 -0
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +40 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/nested-help-routing.test.js +4 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +7 -1
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/performance-goal.test.js +22 -0
- package/dist/cli/__tests__/performance-goal.test.js.map +1 -1
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts +2 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.d.ts.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +64 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -0
- package/dist/cli/__tests__/resume.test.js +96 -0
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +162 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search.test.js +36 -0
- package/dist/cli/__tests__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +70 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +33 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +17 -3
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +37 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +81 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.d.ts +2 -0
- package/dist/cli/__tests__/url.test.d.ts.map +1 -0
- package/dist/cli/__tests__/url.test.js +82 -0
- package/dist/cli/__tests__/url.test.js.map +1 -0
- package/dist/cli/autoresearch-goal.d.ts.map +1 -1
- package/dist/cli/autoresearch-goal.js +2 -1
- package/dist/cli/autoresearch-goal.js.map +1 -1
- package/dist/cli/autoresearch-intake.d.ts.map +1 -1
- package/dist/cli/autoresearch-intake.js +2 -0
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/doctor.d.ts +39 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +460 -40
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +71 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/performance-goal.d.ts.map +1 -1
- package/dist/cli/performance-goal.js +4 -2
- package/dist/cli/performance-goal.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +88 -67
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.d.ts +2 -0
- package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -1
- package/dist/cli/project-runtime-codex-homes.js +112 -5
- package/dist/cli/project-runtime-codex-homes.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +15 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +5 -1
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +23 -4
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.d.ts +8 -0
- package/dist/cli/url.d.ts.map +1 -0
- package/dist/cli/url.js +69 -0
- package/dist/cli/url.js.map +1 -0
- package/dist/config/__tests__/codex-hooks.test.js +20 -4
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +21 -1
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +1 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/models.d.ts +5 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +23 -0
- package/dist/config/models.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +2 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +10 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +7 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +46 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +100 -0
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +75 -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 +225 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +75 -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 +563 -0
- 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 +187 -12
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +225 -0
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +75 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +4 -0
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +137 -2
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +107 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +17 -0
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +21 -7
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +9 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +43 -5
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/types.d.ts +6 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +24 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +53 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +2 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +32 -6
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +11 -5
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.js +48 -0
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +1 -0
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.js +9 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -1
- package/dist/notifications/http-client.js +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/performance-goal/artifacts.d.ts.map +1 -1
- package/dist/performance-goal/artifacts.js +10 -1
- package/dist/performance-goal/artifacts.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +112 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +23 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +133 -21
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1251 -13
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +601 -24
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +1 -0
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +279 -86
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +87 -8
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/run-compiled-ci.d.ts +2 -0
- package/dist/scripts/run-compiled-ci.d.ts.map +1 -0
- package/dist/scripts/run-compiled-ci.js +77 -0
- package/dist/scripts/run-compiled-ci.js.map +1 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +1 -0
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/session-history/search.d.ts.map +1 -1
- package/dist/session-history/search.js +24 -9
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/args.test.d.ts +2 -0
- package/dist/sidecar/__tests__/args.test.d.ts.map +1 -0
- package/dist/sidecar/__tests__/args.test.js +18 -0
- package/dist/sidecar/__tests__/args.test.js.map +1 -0
- package/dist/sidecar/index.d.ts.map +1 -1
- package/dist/sidecar/index.js +12 -4
- package/dist/sidecar/index.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +107 -3
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +57 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +22 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +57 -1
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +1468 -110
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +63 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +24 -1
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +512 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +42 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +16 -0
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +41 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +116 -30
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +9 -1
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/types.d.ts +2 -0
- 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 +3 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +20 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +38 -3
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +275 -36
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +26 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +388 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +3 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +23 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +275 -12
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/__tests__/url-reader.test.d.ts +2 -0
- package/dist/url-reader/__tests__/url-reader.test.d.ts.map +1 -0
- package/dist/url-reader/__tests__/url-reader.test.js +433 -0
- package/dist/url-reader/__tests__/url-reader.test.js.map +1 -0
- package/dist/url-reader/index.d.ts +4 -0
- package/dist/url-reader/index.d.ts.map +1 -0
- package/dist/url-reader/index.js +522 -0
- package/dist/url-reader/index.js.map +1 -0
- package/dist/url-reader/types.d.ts +41 -0
- package/dist/url-reader/types.d.ts.map +1 -0
- package/dist/url-reader/types.js +2 -0
- package/dist/url-reader/types.js.map +1 -0
- package/dist/utils/__tests__/agents-md.test.js +39 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +28 -0
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts +4 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +28 -0
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts +7 -3
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +16 -8
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/utils/platform-command.d.ts +7 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +12 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +4 -0
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/vscode/__tests__/index.test.d.ts +2 -0
- package/dist/vscode/__tests__/index.test.d.ts.map +1 -0
- package/dist/vscode/__tests__/index.test.js +236 -0
- package/dist/vscode/__tests__/index.test.js.map +1 -0
- package/dist/vscode/index.d.ts +76 -0
- package/dist/vscode/index.d.ts.map +1 -0
- package/dist/vscode/index.js +196 -0
- package/dist/vscode/index.js.map +1 -0
- package/package.json +3 -3
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +59 -8
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +9 -5
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +3 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +22 -4
- package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +61 -0
- package/skills/autopilot/SKILL.md +9 -5
- package/skills/autoresearch-goal/SKILL.md +3 -0
- package/skills/ecomode/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +61 -0
- package/skills/performance-goal/SKILL.md +3 -0
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/ultragoal/SKILL.md +22 -4
- package/skills/ultrawork/SKILL.md +2 -2
- package/skills/ultrawork/references/agent-tiers.md +61 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +1560 -181
- package/src/scripts/codex-native-hook.ts +663 -24
- package/src/scripts/notify-hook/team-leader-nudge.ts +298 -91
- package/src/scripts/notify-hook/team-tmux-guard.ts +92 -8
- package/src/scripts/notify-hook/tmux-injection.ts +1 -1
- package/src/scripts/run-compiled-ci.ts +85 -0
- package/src/scripts/sync-plugin-mirror.ts +1 -0
- package/templates/AGENTS.md +6 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Team leader nudge: remind the leader to check teammate/mailbox state.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { readFile, writeFile, mkdir, appendFile, readdir } from 'fs/promises';
|
|
6
|
+
import { readFile, writeFile, mkdir, appendFile, readdir, rename, unlink } from 'fs/promises';
|
|
7
7
|
import { existsSync } from 'fs';
|
|
8
8
|
import { join, resolve } from 'path';
|
|
9
9
|
import { readUsableSessionState } from '../../hooks/session.js';
|
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
import { DEFAULT_MARKER, paneHasActiveTask } from '../tmux-hook-engine.js';
|
|
22
22
|
import { isLeaderRuntimeStale } from '../../team/leader-activity.js';
|
|
23
23
|
import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
|
|
24
|
-
import { writeTeamLeaderAttention } from '../../team/state.js';
|
|
25
24
|
import { readLatestTeamProgressEvidenceMs } from '../../team/progress-evidence.js';
|
|
26
25
|
import { validateSessionId } from '../../mcp/state-paths.js';
|
|
27
26
|
import { TEAM_NAME_SAFE_PATTERN } from '../../team/contracts.js';
|
|
28
27
|
import { isDeepInterviewStateActive } from './auto-nudge.js';
|
|
29
28
|
const LEADER_PANE_MISSING_NO_INJECTION_REASON = 'leader_pane_missing_no_injection';
|
|
30
29
|
const LEADER_PANE_SHELL_NO_INJECTION_REASON = 'leader_pane_shell_no_injection';
|
|
30
|
+
const TEAM_SHUTDOWN_NO_INJECTION_REASON = 'team_state_gone_or_shutdown';
|
|
31
31
|
const LEADER_PANE_SAME_CLASSIFIED_STATE_SUPPRESSED_REASON = 'pane_already_shows_same_classified_state';
|
|
32
32
|
const LEADER_NOTIFICATION_DEFERRED_TYPE = 'leader_notification_deferred';
|
|
33
33
|
const ACK_WITHOUT_START_EVIDENCE_REASON = 'ack_without_start_evidence';
|
|
@@ -37,6 +37,109 @@ const ACK_LIKE_PATTERNS = [
|
|
|
37
37
|
/^(?:on it|will do|i(?:'|')ll do it|working on it)[.!]*$/i,
|
|
38
38
|
];
|
|
39
39
|
|
|
40
|
+
let atomicJsonWriteCounter = 0;
|
|
41
|
+
|
|
42
|
+
// Synchronous test-only callbacks let regression tests emulate filesystem races
|
|
43
|
+
// without shipping env-gated destructive fault injection in the notify hook.
|
|
44
|
+
const leaderNudgeTestHooks = {
|
|
45
|
+
beforeLeaderAttentionRename: null,
|
|
46
|
+
afterLeaderAttentionRename: null,
|
|
47
|
+
beforeGlobalNudgeStateRename: null,
|
|
48
|
+
afterGlobalNudgeStateRename: null,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export function setLeaderNudgeTestHooksForTests(hooks = {}) {
|
|
52
|
+
leaderNudgeTestHooks.beforeLeaderAttentionRename = typeof hooks.beforeLeaderAttentionRename === 'function'
|
|
53
|
+
? hooks.beforeLeaderAttentionRename
|
|
54
|
+
: null;
|
|
55
|
+
leaderNudgeTestHooks.afterLeaderAttentionRename = typeof hooks.afterLeaderAttentionRename === 'function'
|
|
56
|
+
? hooks.afterLeaderAttentionRename
|
|
57
|
+
: null;
|
|
58
|
+
leaderNudgeTestHooks.beforeGlobalNudgeStateRename = typeof hooks.beforeGlobalNudgeStateRename === 'function'
|
|
59
|
+
? hooks.beforeGlobalNudgeStateRename
|
|
60
|
+
: null;
|
|
61
|
+
leaderNudgeTestHooks.afterGlobalNudgeStateRename = typeof hooks.afterGlobalNudgeStateRename === 'function'
|
|
62
|
+
? hooks.afterGlobalNudgeStateRename
|
|
63
|
+
: null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function atomicWriteJsonNoParentCreate(path, value, { beforeRename = null, afterRename = null } = {}) {
|
|
67
|
+
const tempPath = `${path}.${process.pid}.${Date.now()}.${++atomicJsonWriteCounter}.tmp`;
|
|
68
|
+
try {
|
|
69
|
+
await writeFile(tempPath, JSON.stringify(value, null, 2));
|
|
70
|
+
if (beforeRename) await beforeRename(tempPath);
|
|
71
|
+
await rename(tempPath, path);
|
|
72
|
+
if (afterRename) await afterRename(path);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
await unlink(tempPath).catch(() => {});
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function cloneLeaderNudgeState(state) {
|
|
80
|
+
return {
|
|
81
|
+
...(state && typeof state === 'object' ? state : {}),
|
|
82
|
+
last_nudged_by_team: { ...(state?.last_nudged_by_team || {}) },
|
|
83
|
+
last_idle_nudged_by_team: { ...(state?.last_idle_nudged_by_team || {}) },
|
|
84
|
+
progress_by_team: { ...(state?.progress_by_team || {}) },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function removeTeamFromLeaderNudgeState(state, teamName) {
|
|
89
|
+
if (state?.progress_by_team && typeof state.progress_by_team === 'object') {
|
|
90
|
+
delete state.progress_by_team[teamName];
|
|
91
|
+
}
|
|
92
|
+
if (state?.last_nudged_by_team && typeof state.last_nudged_by_team === 'object') {
|
|
93
|
+
delete state.last_nudged_by_team[teamName];
|
|
94
|
+
}
|
|
95
|
+
if (state?.last_idle_nudged_by_team && typeof state.last_idle_nudged_by_team === 'object') {
|
|
96
|
+
delete state.last_idle_nudged_by_team[teamName];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function teamStateAllowsLeaderNudge(stateDir, teamName) {
|
|
101
|
+
const teamDir = join(stateDir, 'team', teamName);
|
|
102
|
+
if (!existsSync(teamDir)) return false;
|
|
103
|
+
if (existsSync(join(teamDir, 'shutdown.json'))) return false;
|
|
104
|
+
|
|
105
|
+
const phase = await readJsonIfExists(join(teamDir, 'phase.json'), null);
|
|
106
|
+
const currentPhase = safeString(phase?.current_phase || phase?.phase || '').trim();
|
|
107
|
+
if (currentPhase && isTerminalPhase(currentPhase)) return false;
|
|
108
|
+
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function recordSuppressedLeaderNudge({
|
|
113
|
+
logsDir,
|
|
114
|
+
source,
|
|
115
|
+
teamName,
|
|
116
|
+
reason,
|
|
117
|
+
orchestrationIntent = null,
|
|
118
|
+
}) {
|
|
119
|
+
const nowIso = new Date().toISOString();
|
|
120
|
+
await logTmuxHookEvent(logsDir, {
|
|
121
|
+
timestamp: nowIso,
|
|
122
|
+
type: 'team_leader_nudge_suppressed',
|
|
123
|
+
team: teamName,
|
|
124
|
+
worker: 'leader-fixed',
|
|
125
|
+
to_worker: 'leader-fixed',
|
|
126
|
+
reason,
|
|
127
|
+
orchestration_intent: orchestrationIntent,
|
|
128
|
+
tmux_injection_attempted: false,
|
|
129
|
+
source_type: 'leader_nudge',
|
|
130
|
+
}).catch(() => {});
|
|
131
|
+
await appendTeamDeliveryLog(logsDir, {
|
|
132
|
+
event: 'nudge_triggered',
|
|
133
|
+
source,
|
|
134
|
+
team: teamName,
|
|
135
|
+
to_worker: 'leader-fixed',
|
|
136
|
+
transport: 'none',
|
|
137
|
+
result: 'suppressed',
|
|
138
|
+
reason,
|
|
139
|
+
orchestration_intent: orchestrationIntent,
|
|
140
|
+
}).catch(() => {});
|
|
141
|
+
}
|
|
142
|
+
|
|
40
143
|
function normalizeValidSessionId(value) {
|
|
41
144
|
const trimmed = safeString(value).trim();
|
|
42
145
|
if (!trimmed) return '';
|
|
@@ -613,6 +716,15 @@ export async function maybeNudgeTeamLeader({
|
|
|
613
716
|
const leaderStale = typeof preComputedLeaderStale === 'boolean' ? preComputedLeaderStale : false;
|
|
614
717
|
|
|
615
718
|
for (const teamName of candidateTeamNames) {
|
|
719
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
720
|
+
await recordSuppressedLeaderNudge({
|
|
721
|
+
logsDir,
|
|
722
|
+
source,
|
|
723
|
+
teamName,
|
|
724
|
+
reason: TEAM_SHUTDOWN_NO_INJECTION_REASON,
|
|
725
|
+
});
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
616
728
|
let tmuxSession = '';
|
|
617
729
|
let leaderPaneId = '';
|
|
618
730
|
let ownerSessionId = '';
|
|
@@ -773,60 +885,167 @@ export async function maybeNudgeTeamLeader({
|
|
|
773
885
|
}
|
|
774
886
|
|
|
775
887
|
const unreadLeaderMessageCount = messages.filter((message) => !safeString(message?.delivered_at).trim()).length;
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
stalled_for_ms: null,
|
|
793
|
-
source: source === 'notify_fallback_watcher' ? 'notify_hook' : source,
|
|
888
|
+
const recordShutdownSuppression = async (orchestrationIntent = null) => {
|
|
889
|
+
await recordSuppressedLeaderNudge({
|
|
890
|
+
logsDir,
|
|
891
|
+
source,
|
|
892
|
+
teamName,
|
|
893
|
+
reason: TEAM_SHUTDOWN_NO_INJECTION_REASON,
|
|
894
|
+
orchestrationIntent,
|
|
895
|
+
});
|
|
896
|
+
};
|
|
897
|
+
const cleanupTeamPersistence = async () => {
|
|
898
|
+
await unlink(join(stateDir, 'team', teamName, 'leader-attention.json')).catch(() => {});
|
|
899
|
+
const latestState = await readJsonIfExists(nudgeStatePath, nudgeState);
|
|
900
|
+
const cleanedState = cloneLeaderNudgeState(latestState);
|
|
901
|
+
removeTeamFromLeaderNudgeState(cleanedState, teamName);
|
|
902
|
+
nudgeState = cleanedState;
|
|
903
|
+
await atomicWriteJsonNoParentCreate(nudgeStatePath, cleanedState).catch(() => {});
|
|
794
904
|
};
|
|
795
|
-
await writeTeamLeaderAttention(teamName, {
|
|
796
|
-
team_name: teamName,
|
|
797
|
-
updated_at: nowIso,
|
|
798
|
-
source: 'notify_hook',
|
|
799
|
-
leader_decision_state: leaderActionState,
|
|
800
|
-
leader_attention_pending: !!nudgeReason,
|
|
801
|
-
leader_attention_reason: nudgeReason || null,
|
|
802
|
-
attention_reasons: nudgeReason ? [nudgeReason] : [],
|
|
803
|
-
leader_stale: leaderStale,
|
|
804
|
-
leader_session_active: true,
|
|
805
|
-
leader_session_id: currentSessionId || ownerSessionId || null,
|
|
806
|
-
leader_session_stopped_at: null,
|
|
807
|
-
unread_leader_message_count: unreadLeaderMessageCount,
|
|
808
|
-
work_remaining: progressSnapshot.workRemaining,
|
|
809
|
-
stalled_for_ms: null,
|
|
810
|
-
}, cwd).catch(() => {});
|
|
811
|
-
|
|
812
|
-
if (!nudgeReason) continue;
|
|
813
|
-
const orchestrationIntent = resolveLeaderNudgeIntent({ nudgeReason, leaderActionState });
|
|
814
|
-
const capped = text.length > 180 ? `${text.slice(0, 177)}...` : text;
|
|
815
|
-
const markedText = `${capped} ${DEFAULT_MARKER}`;
|
|
816
905
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
906
|
+
const persistLeaderNudgeBookkeeping = async ({ orchestrationIntent = null, recordLastNudged = false } = {}) => {
|
|
907
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
908
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
const leaderAttention = {
|
|
913
|
+
team_name: teamName,
|
|
914
|
+
updated_at: nowIso,
|
|
915
|
+
source: 'notify_hook',
|
|
916
|
+
leader_decision_state: leaderActionState,
|
|
917
|
+
leader_attention_pending: !!nudgeReason,
|
|
918
|
+
leader_attention_reason: nudgeReason || null,
|
|
919
|
+
attention_reasons: nudgeReason ? [nudgeReason] : [],
|
|
920
|
+
leader_stale: leaderStale,
|
|
921
|
+
leader_session_active: true,
|
|
922
|
+
leader_session_id: currentSessionId || ownerSessionId || null,
|
|
923
|
+
leader_session_stopped_at: null,
|
|
924
|
+
unread_leader_message_count: unreadLeaderMessageCount,
|
|
925
|
+
work_remaining: progressSnapshot.workRemaining,
|
|
926
|
+
stalled_for_ms: null,
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
const leaderAttentionPath = join(stateDir, 'team', teamName, 'leader-attention.json');
|
|
930
|
+
try {
|
|
931
|
+
await atomicWriteJsonNoParentCreate(leaderAttentionPath, leaderAttention, {
|
|
932
|
+
beforeRename: async (tempPath) => {
|
|
933
|
+
if (leaderNudgeTestHooks.beforeLeaderAttentionRename) {
|
|
934
|
+
await leaderNudgeTestHooks.beforeLeaderAttentionRename({ stateDir, teamName, tempPath });
|
|
935
|
+
}
|
|
936
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
937
|
+
throw new Error('team_state_gone_or_shutdown_before_leader_attention_rename');
|
|
938
|
+
}
|
|
939
|
+
},
|
|
940
|
+
afterRename: async () => {
|
|
941
|
+
if (leaderNudgeTestHooks.afterLeaderAttentionRename) {
|
|
942
|
+
await leaderNudgeTestHooks.afterLeaderAttentionRename({ stateDir, teamName, path: leaderAttentionPath });
|
|
943
|
+
}
|
|
944
|
+
},
|
|
945
|
+
});
|
|
946
|
+
} catch {
|
|
947
|
+
await unlink(leaderAttentionPath).catch(() => {});
|
|
948
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
953
|
+
await cleanupTeamPersistence();
|
|
954
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
const nextNudgeState = cloneLeaderNudgeState(nudgeState);
|
|
959
|
+
nextNudgeState.progress_by_team[teamName] = {
|
|
960
|
+
signature: progressSnapshot.signature,
|
|
961
|
+
last_progress_at: effectiveProgressAtIso,
|
|
962
|
+
observed_at: nowIso,
|
|
963
|
+
missing_signal_workers: progressSnapshot.missingSignalWorkers,
|
|
964
|
+
work_remaining: progressSnapshot.workRemaining,
|
|
965
|
+
leader_action_state: leaderActionState,
|
|
966
|
+
leader_attention_pending: !!nudgeReason,
|
|
967
|
+
leader_attention_reason: nudgeReason || null,
|
|
968
|
+
leader_stale: leaderStale,
|
|
969
|
+
all_workers_idle: allWorkersIdle,
|
|
970
|
+
pending_task_count:
|
|
971
|
+
(progressSnapshot.taskCounts.pending || 0)
|
|
972
|
+
+ (progressSnapshot.taskCounts.blocked || 0)
|
|
973
|
+
+ (progressSnapshot.taskCounts.in_progress || 0),
|
|
974
|
+
unread_leader_message_count: unreadLeaderMessageCount,
|
|
975
|
+
stalled_for_ms: null,
|
|
976
|
+
source: source === 'notify_fallback_watcher' ? 'notify_hook' : source,
|
|
823
977
|
};
|
|
824
|
-
if (
|
|
825
|
-
|
|
978
|
+
if (recordLastNudged) {
|
|
979
|
+
nextNudgeState.last_nudged_by_team[teamName] = {
|
|
826
980
|
at: nowIso,
|
|
827
|
-
|
|
981
|
+
last_message_id: newestId || prevMsgId || '',
|
|
982
|
+
reason: nudgeReason,
|
|
828
983
|
orchestration_intent: orchestrationIntent,
|
|
829
984
|
};
|
|
985
|
+
if (shouldSendAllIdleNudge) {
|
|
986
|
+
nextNudgeState.last_idle_nudged_by_team[teamName] = {
|
|
987
|
+
at: nowIso,
|
|
988
|
+
worker_count: workerNames.length,
|
|
989
|
+
orchestration_intent: orchestrationIntent,
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
try {
|
|
995
|
+
await atomicWriteJsonNoParentCreate(nudgeStatePath, nextNudgeState, {
|
|
996
|
+
beforeRename: async (tempPath) => {
|
|
997
|
+
if (leaderNudgeTestHooks.beforeGlobalNudgeStateRename) {
|
|
998
|
+
await leaderNudgeTestHooks.beforeGlobalNudgeStateRename({ stateDir, teamName, tempPath });
|
|
999
|
+
}
|
|
1000
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1001
|
+
throw new Error('team_state_gone_or_shutdown_before_nudge_state_rename');
|
|
1002
|
+
}
|
|
1003
|
+
},
|
|
1004
|
+
afterRename: async () => {
|
|
1005
|
+
if (leaderNudgeTestHooks.afterGlobalNudgeStateRename) {
|
|
1006
|
+
await leaderNudgeTestHooks.afterGlobalNudgeStateRename({ stateDir, teamName, path: nudgeStatePath });
|
|
1007
|
+
}
|
|
1008
|
+
},
|
|
1009
|
+
});
|
|
1010
|
+
} catch {
|
|
1011
|
+
await cleanupTeamPersistence();
|
|
1012
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1013
|
+
return false;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1017
|
+
await cleanupTeamPersistence();
|
|
1018
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
nudgeState = nextNudgeState;
|
|
1023
|
+
return true;
|
|
1024
|
+
};
|
|
1025
|
+
|
|
1026
|
+
if (!nudgeReason) {
|
|
1027
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1028
|
+
await recordShutdownSuppression();
|
|
1029
|
+
continue;
|
|
1030
|
+
}
|
|
1031
|
+
await persistLeaderNudgeBookkeeping();
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
const orchestrationIntent = resolveLeaderNudgeIntent({ nudgeReason, leaderActionState });
|
|
1035
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1036
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
const capped = text.length > 180 ? `${text.slice(0, 177)}...` : text;
|
|
1040
|
+
const markedText = `${capped} ${DEFAULT_MARKER}`;
|
|
1041
|
+
|
|
1042
|
+
if (!tmuxTarget) {
|
|
1043
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1044
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (!(await persistLeaderNudgeBookkeeping({ orchestrationIntent, recordLastNudged: true }))) {
|
|
1048
|
+
continue;
|
|
830
1049
|
}
|
|
831
1050
|
await emitLeaderNudgeDeferredEvent(cwd, teamName, LEADER_PANE_MISSING_NO_INJECTION_REASON, orchestrationIntent, nowIso, {
|
|
832
1051
|
tmuxSession,
|
|
@@ -873,18 +1092,12 @@ export async function maybeNudgeTeamLeader({
|
|
|
873
1092
|
const deferredReason = paneGuard.reason === 'pane_running_shell'
|
|
874
1093
|
? LEADER_PANE_SHELL_NO_INJECTION_REASON
|
|
875
1094
|
: paneGuard.reason;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
if (shouldSendAllIdleNudge) {
|
|
883
|
-
nudgeState.last_idle_nudged_by_team[teamName] = {
|
|
884
|
-
at: nowIso,
|
|
885
|
-
worker_count: workerNames.length,
|
|
886
|
-
orchestration_intent: orchestrationIntent,
|
|
887
|
-
};
|
|
1095
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1096
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1097
|
+
continue;
|
|
1098
|
+
}
|
|
1099
|
+
if (!(await persistLeaderNudgeBookkeeping({ orchestrationIntent, recordLastNudged: true }))) {
|
|
1100
|
+
continue;
|
|
888
1101
|
}
|
|
889
1102
|
await emitLeaderNudgeDeferredEvent(cwd, teamName, deferredReason, orchestrationIntent, nowIso, {
|
|
890
1103
|
tmuxSession,
|
|
@@ -925,20 +1138,13 @@ export async function maybeNudgeTeamLeader({
|
|
|
925
1138
|
}
|
|
926
1139
|
|
|
927
1140
|
if (paneAlreadyShowsVisibleLeaderState(paneGuard.paneCapture, capped)) {
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
if (shouldSendAllIdleNudge) {
|
|
935
|
-
nudgeState.last_idle_nudged_by_team[teamName] = {
|
|
936
|
-
at: nowIso,
|
|
937
|
-
worker_count: workerNames.length,
|
|
938
|
-
orchestration_intent: orchestrationIntent,
|
|
939
|
-
};
|
|
1141
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1142
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1143
|
+
continue;
|
|
1144
|
+
}
|
|
1145
|
+
if (!(await persistLeaderNudgeBookkeeping({ orchestrationIntent, recordLastNudged: true }))) {
|
|
1146
|
+
continue;
|
|
940
1147
|
}
|
|
941
|
-
|
|
942
1148
|
await emitTeamNudgeEvent(cwd, teamName, nudgeReason, orchestrationIntent, nowIso);
|
|
943
1149
|
|
|
944
1150
|
try {
|
|
@@ -973,6 +1179,11 @@ export async function maybeNudgeTeamLeader({
|
|
|
973
1179
|
continue;
|
|
974
1180
|
}
|
|
975
1181
|
|
|
1182
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1183
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
976
1187
|
try {
|
|
977
1188
|
const leaderHasActiveTask = paneHasActiveTask(paneGuard.paneCapture);
|
|
978
1189
|
let deliveryMode = 'sent';
|
|
@@ -996,22 +1207,16 @@ export async function maybeNudgeTeamLeader({
|
|
|
996
1207
|
throw new Error(sendResult.error || sendResult.reason);
|
|
997
1208
|
}
|
|
998
1209
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
at: nowIso,
|
|
1008
|
-
worker_count: workerNames.length,
|
|
1009
|
-
orchestration_intent: orchestrationIntent,
|
|
1010
|
-
};
|
|
1210
|
+
if (await teamStateAllowsLeaderNudge(stateDir, teamName)) {
|
|
1211
|
+
if (!(await persistLeaderNudgeBookkeeping({ orchestrationIntent, recordLastNudged: true }))) {
|
|
1212
|
+
continue;
|
|
1213
|
+
}
|
|
1214
|
+
await emitTeamNudgeEvent(cwd, teamName, nudgeReason, orchestrationIntent, nowIso);
|
|
1215
|
+
} else {
|
|
1216
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1217
|
+
continue;
|
|
1011
1218
|
}
|
|
1012
1219
|
|
|
1013
|
-
await emitTeamNudgeEvent(cwd, teamName, nudgeReason, orchestrationIntent, nowIso);
|
|
1014
|
-
|
|
1015
1220
|
try {
|
|
1016
1221
|
await logTmuxHookEvent(logsDir, {
|
|
1017
1222
|
timestamp: nowIso,
|
|
@@ -1039,6 +1244,10 @@ export async function maybeNudgeTeamLeader({
|
|
|
1039
1244
|
orchestration_intent: orchestrationIntent,
|
|
1040
1245
|
}).catch(() => {});
|
|
1041
1246
|
} catch (err) {
|
|
1247
|
+
if (!(await teamStateAllowsLeaderNudge(stateDir, teamName))) {
|
|
1248
|
+
await recordShutdownSuppression(orchestrationIntent);
|
|
1249
|
+
continue;
|
|
1250
|
+
}
|
|
1042
1251
|
try {
|
|
1043
1252
|
await logTmuxHookEvent(logsDir, {
|
|
1044
1253
|
timestamp: nowIso,
|
|
@@ -1063,6 +1272,4 @@ export async function maybeNudgeTeamLeader({
|
|
|
1063
1272
|
}).catch(() => {});
|
|
1064
1273
|
}
|
|
1065
1274
|
}
|
|
1066
|
-
|
|
1067
|
-
await writeFile(nudgeStatePath, JSON.stringify(nudgeState, null, 2)).catch(() => {});
|
|
1068
1275
|
}
|
|
@@ -11,6 +11,28 @@ import {
|
|
|
11
11
|
} from '../tmux-hook-engine.js';
|
|
12
12
|
|
|
13
13
|
export const PANE_READINESS_UNVERIFIED_REASON = 'pane_readiness_unverified';
|
|
14
|
+
let nextTmuxBufferId = 0;
|
|
15
|
+
|
|
16
|
+
function buildSafePasteArgv(target: string, prompt: string): {
|
|
17
|
+
bufferName: string;
|
|
18
|
+
setBufferArgv: string[];
|
|
19
|
+
showBufferArgv: string[];
|
|
20
|
+
clearComposerArgv: string[];
|
|
21
|
+
pasteBufferArgv: string[];
|
|
22
|
+
deleteBufferArgv: string[];
|
|
23
|
+
} {
|
|
24
|
+
nextTmuxBufferId += 1;
|
|
25
|
+
const bufferName = `omx-pane-input-${process.pid}-${Date.now()}-${nextTmuxBufferId}`;
|
|
26
|
+
return {
|
|
27
|
+
bufferName,
|
|
28
|
+
setBufferArgv: ['set-buffer', '-b', bufferName, '--', prompt],
|
|
29
|
+
showBufferArgv: ['show-buffer', '-b', bufferName],
|
|
30
|
+
clearComposerArgv: ['send-keys', '-t', target, 'C-u'],
|
|
31
|
+
pasteBufferArgv: ['paste-buffer', '-t', target, '-b', bufferName, '-p', '-d'],
|
|
32
|
+
deleteBufferArgv: ['delete-buffer', '-b', bufferName],
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
|
|
15
37
|
export function mapPaneInjectionReadinessReason(reason: any): any {
|
|
16
38
|
return reason === 'pane_running_shell' ? 'agent_not_running' : reason;
|
|
@@ -145,24 +167,82 @@ export async function sendPaneInput({
|
|
|
145
167
|
? Math.max(0, Math.floor(submitKeyPresses))
|
|
146
168
|
: 2;
|
|
147
169
|
const literalPrompt = safeString(prompt);
|
|
148
|
-
const
|
|
149
|
-
?
|
|
150
|
-
typeArgv: ['send-keys', '-t', target, '-l', literalPrompt],
|
|
151
|
-
submitArgv: [] as string[][],
|
|
152
|
-
}
|
|
170
|
+
const submitArgv = normalizedSubmitKeyPresses === 0
|
|
171
|
+
? [] as string[][]
|
|
153
172
|
: buildSendKeysArgv({
|
|
154
173
|
paneTarget: target,
|
|
155
174
|
prompt: literalPrompt,
|
|
156
175
|
dryRun: false,
|
|
157
176
|
submitKeyPresses: normalizedSubmitKeyPresses,
|
|
158
|
-
});
|
|
159
|
-
if (!
|
|
177
|
+
})?.submitArgv;
|
|
178
|
+
if (!submitArgv) {
|
|
160
179
|
return { ok: false, sent: false, reason: 'send_failed', paneTarget: target };
|
|
161
180
|
}
|
|
181
|
+
const pasteArgv = buildSafePasteArgv(target, literalPrompt);
|
|
182
|
+
const argv = {
|
|
183
|
+
typeArgv: pasteArgv.pasteBufferArgv,
|
|
184
|
+
submitArgv,
|
|
185
|
+
bufferName: pasteArgv.bufferName,
|
|
186
|
+
setBufferArgv: pasteArgv.setBufferArgv,
|
|
187
|
+
showBufferArgv: pasteArgv.showBufferArgv,
|
|
188
|
+
clearComposerArgv: pasteArgv.clearComposerArgv,
|
|
189
|
+
pasteBufferArgv: pasteArgv.pasteBufferArgv,
|
|
190
|
+
deleteBufferArgv: pasteArgv.deleteBufferArgv,
|
|
191
|
+
};
|
|
162
192
|
|
|
193
|
+
let bufferSet = false;
|
|
163
194
|
try {
|
|
164
195
|
if (typePrompt) {
|
|
165
|
-
|
|
196
|
+
try {
|
|
197
|
+
await runProcess('tmux', pasteArgv.setBufferArgv, 3000);
|
|
198
|
+
bufferSet = true;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return {
|
|
201
|
+
ok: false,
|
|
202
|
+
sent: false,
|
|
203
|
+
reason: 'buffer_set_failed',
|
|
204
|
+
paneTarget: target,
|
|
205
|
+
argv,
|
|
206
|
+
error: error instanceof Error ? error.message : safeString(error),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
let verifiedBuffer;
|
|
210
|
+
try {
|
|
211
|
+
verifiedBuffer = await runProcess('tmux', pasteArgv.showBufferArgv, 3000);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
sent: false,
|
|
216
|
+
reason: 'buffer_show_failed',
|
|
217
|
+
paneTarget: target,
|
|
218
|
+
argv,
|
|
219
|
+
error: error instanceof Error ? error.message : safeString(error),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (verifiedBuffer.stdout !== literalPrompt) {
|
|
223
|
+
return {
|
|
224
|
+
ok: false,
|
|
225
|
+
sent: false,
|
|
226
|
+
reason: 'buffer_verify_failed',
|
|
227
|
+
paneTarget: target,
|
|
228
|
+
argv,
|
|
229
|
+
expectedBytes: literalPrompt.length,
|
|
230
|
+
actualBytes: verifiedBuffer.stdout.length,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
try {
|
|
234
|
+
await runProcess('tmux', pasteArgv.clearComposerArgv, 3000);
|
|
235
|
+
await runProcess('tmux', pasteArgv.pasteBufferArgv, 3000);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return {
|
|
238
|
+
ok: false,
|
|
239
|
+
sent: false,
|
|
240
|
+
reason: 'buffer_paste_failed',
|
|
241
|
+
paneTarget: target,
|
|
242
|
+
argv,
|
|
243
|
+
error: error instanceof Error ? error.message : safeString(error),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
166
246
|
}
|
|
167
247
|
if (queueFirstSubmit && argv.submitArgv.length > 0) {
|
|
168
248
|
await runProcess('tmux', ['send-keys', '-t', target, 'Tab'], 3000);
|
|
@@ -186,6 +266,10 @@ export async function sendPaneInput({
|
|
|
186
266
|
argv,
|
|
187
267
|
error: error instanceof Error ? error.message : safeString(error),
|
|
188
268
|
};
|
|
269
|
+
} finally {
|
|
270
|
+
if (bufferSet) {
|
|
271
|
+
await runProcess('tmux', pasteArgv.deleteBufferArgv, 3000).catch(() => {});
|
|
272
|
+
}
|
|
189
273
|
}
|
|
190
274
|
}
|
|
191
275
|
|
|
@@ -71,7 +71,7 @@ async function finalizeResolvedPane(paneId: string, reason: string, expectedCwd:
|
|
|
71
71
|
|
|
72
72
|
async function resolveCanonicalPaneFromPaneTarget(paneTarget: any, expectedCwd: any): Promise<any> {
|
|
73
73
|
const paneResult = await runProcess('tmux', ['display-message', '-p', '-t', paneTarget, '#{pane_id}']);
|
|
74
|
-
const paneId = safeString(paneResult.stdout).trim();
|
|
74
|
+
const paneId = safeString(paneResult.stdout).trim() || (safeString(paneTarget).trim().match(/^%\d+$/) ? safeString(paneTarget).trim() : '');
|
|
75
75
|
if (!paneId) return { paneTarget: null, reason: 'target_not_found' };
|
|
76
76
|
|
|
77
77
|
let startCommand = '';
|