oh-my-codex 0.11.13 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +5 -5
- package/Cargo.toml +1 -1
- package/README.md +34 -17
- package/crates/omx-runtime/src/main.rs +6 -2
- package/dist/agents/native-config.js +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js +74 -2
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/cleanup.test.js +37 -30
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js +3 -1
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +276 -5
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +95 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +49 -9
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +9 -0
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +136 -11
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +10 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +1 -0
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/autoresearch-guided.d.ts.map +1 -1
- package/dist/cli/autoresearch-guided.js +2 -1
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch.d.ts.map +1 -1
- package/dist/cli/autoresearch.js +2 -1
- package/dist/cli/autoresearch.js.map +1 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +10 -5
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/index.d.ts +21 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +298 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/omx.js +2 -0
- package/dist/cli/omx.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +41 -7
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +16 -557
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +34 -9
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +79 -2
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +2 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +11 -0
- package/dist/config/codex-hooks.d.ts.map +1 -0
- package/dist/config/codex-hooks.js +50 -0
- package/dist/config/codex-hooks.js.map +1 -0
- package/dist/config/generator.d.ts +5 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +24 -14
- package/dist/config/generator.js.map +1 -1
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js +20 -0
- package/dist/hooks/__tests__/debugger-log-recency-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +132 -0
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +292 -4
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +86 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +40 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +54 -0
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js +31 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +51 -0
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +136 -0
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +120 -0
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +145 -20
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +116 -0
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +86 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js +1 -0
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/runtime.test.js +49 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
- package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
- package/dist/hooks/extensibility/runtime.js +10 -0
- package/dist/hooks/extensibility/runtime.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +1 -1
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +2 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +76 -4
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +12 -8
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +5 -1
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +10 -6
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +6 -1
- package/dist/hud/index.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +0 -3
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +27 -1
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +0 -5
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +0 -1
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts +20 -0
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +6 -5
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js +24 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
- package/dist/notifications/__tests__/reply-listener.test.js +20 -1
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/tmux.test.js +41 -0
- package/dist/notifications/__tests__/tmux.test.js.map +1 -1
- package/dist/notifications/idle-cooldown.d.ts +13 -0
- package/dist/notifications/idle-cooldown.d.ts.map +1 -1
- package/dist/notifications/idle-cooldown.js +50 -16
- package/dist/notifications/idle-cooldown.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +2 -0
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +4 -0
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts +2 -0
- package/dist/scripts/__tests__/codex-native-hook.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js +1050 -0
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +22 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -0
- package/dist/scripts/codex-native-hook.js +792 -0
- package/dist/scripts/codex-native-hook.js.map +1 -0
- package/dist/scripts/codex-native-pre-post.d.ts +26 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -0
- package/dist/scripts/codex-native-pre-post.js +118 -0
- package/dist/scripts/codex-native-pre-post.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +322 -21
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +5 -6
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/log.d.ts +2 -2
- package/dist/scripts/notify-hook/log.d.ts.map +1 -1
- package/dist/scripts/notify-hook/log.js +10 -2
- package/dist/scripts/notify-hook/log.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +2 -0
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/orchestration-intent.d.ts +18 -0
- package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -0
- package/dist/scripts/notify-hook/orchestration-intent.js +72 -0
- package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -0
- package/dist/scripts/notify-hook/process-runner.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +7 -0
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +15 -6
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +125 -6
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +3 -2
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +165 -37
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +4 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +33 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +68 -5
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/utils.d.ts +1 -1
- package/dist/scripts/notify-hook/utils.d.ts.map +1 -1
- package/dist/scripts/notify-hook/utils.js.map +1 -1
- package/dist/scripts/notify-hook.js +55 -32
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +344 -18
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts +2 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.d.ts.map +1 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +671 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -0
- package/dist/team/__tests__/mcp-comm.test.js +5 -0
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +543 -15
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +133 -8
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +4 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +160 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +19 -1
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +95 -23
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/contracts.d.ts +11 -1
- package/dist/team/contracts.d.ts.map +1 -1
- package/dist/team/contracts.js +29 -0
- package/dist/team/contracts.js.map +1 -1
- package/dist/team/delivery-log.d.ts +14 -0
- package/dist/team/delivery-log.d.ts.map +1 -0
- package/dist/team/delivery-log.js +35 -0
- package/dist/team/delivery-log.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +2 -2
- package/dist/team/idle-nudge.js +2 -2
- package/dist/team/mcp-comm.d.ts +4 -0
- package/dist/team/mcp-comm.d.ts.map +1 -1
- package/dist/team/mcp-comm.js +84 -1
- package/dist/team/mcp-comm.js.map +1 -1
- package/dist/team/pane-status.d.ts +149 -0
- package/dist/team/pane-status.d.ts.map +1 -0
- package/dist/team/pane-status.js +558 -0
- package/dist/team/pane-status.js.map +1 -0
- package/dist/team/reminder-intents.d.ts +11 -0
- package/dist/team/reminder-intents.d.ts.map +1 -0
- package/dist/team/reminder-intents.js +40 -0
- package/dist/team/reminder-intents.js.map +1 -0
- package/dist/team/runtime-cli.d.ts +1 -1
- package/dist/team/runtime-cli.js +2 -2
- package/dist/team/runtime-cli.js.map +1 -1
- package/dist/team/runtime.d.ts +2 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +409 -191
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +6 -5
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts +4 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +59 -18
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/mailbox.d.ts.map +1 -1
- package/dist/team/state/mailbox.js +45 -2
- package/dist/team/state/mailbox.js.map +1 -1
- package/dist/team/state/monitor.d.ts +2 -1
- package/dist/team/state/monitor.d.ts.map +1 -1
- package/dist/team/state/monitor.js +30 -1
- package/dist/team/state/monitor.js.map +1 -1
- package/dist/team/state/types.d.ts +5 -2
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +30 -3
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +170 -2
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +19 -3
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +4 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +33 -6
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +63 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/__tests__/platform-command.test.js +50 -4
- package/dist/utils/__tests__/platform-command.test.js.map +1 -1
- package/dist/utils/paths.d.ts +12 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +44 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.d.ts.map +1 -1
- package/dist/utils/platform-command.js +13 -5
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/utils/sleep.d.ts.map +1 -1
- package/dist/utils/sleep.js +10 -1
- package/dist/utils/sleep.js.map +1 -1
- package/package.json +1 -1
- package/prompts/analyst.md +2 -2
- package/prompts/api-reviewer.md +2 -2
- package/prompts/architect.md +2 -2
- package/prompts/build-fixer.md +2 -2
- package/prompts/code-reviewer.md +2 -2
- package/prompts/code-simplifier.md +1 -1
- package/prompts/critic.md +2 -2
- package/prompts/debugger.md +3 -2
- package/prompts/dependency-expert.md +2 -2
- package/prompts/designer.md +2 -2
- package/prompts/executor.md +3 -2
- package/prompts/explore.md +2 -2
- package/prompts/git-master.md +2 -2
- package/prompts/information-architect.md +15 -102
- package/prompts/performance-reviewer.md +2 -2
- package/prompts/planner.md +3 -2
- package/prompts/product-analyst.md +2 -2
- package/prompts/product-manager.md +2 -2
- package/prompts/qa-tester.md +2 -2
- package/prompts/quality-reviewer.md +2 -2
- package/prompts/quality-strategist.md +2 -2
- package/prompts/researcher.md +2 -2
- package/prompts/security-reviewer.md +2 -2
- package/prompts/sisyphus-lite.md +2 -2
- package/prompts/style-reviewer.md +2 -2
- package/prompts/team-executor.md +2 -2
- package/prompts/test-engineer.md +2 -2
- package/prompts/ux-researcher.md +2 -2
- package/prompts/verifier.md +3 -2
- package/prompts/vision.md +2 -2
- package/prompts/writer.md +2 -2
- package/skills/team/SKILL.md +18 -33
- package/src/scripts/__tests__/codex-native-hook.test.ts +1346 -0
- package/src/scripts/codex-native-hook.ts +983 -0
- package/src/scripts/codex-native-pre-post.ts +161 -0
- package/src/scripts/notify-fallback-watcher.ts +378 -29
- package/src/scripts/notify-hook/auto-nudge.ts +5 -10
- package/src/scripts/notify-hook/log.ts +18 -4
- package/src/scripts/notify-hook/managed-tmux.ts +1 -0
- package/src/scripts/notify-hook/orchestration-intent.ts +82 -0
- package/src/scripts/notify-hook/process-runner.ts +4 -4
- package/src/scripts/notify-hook/ralph-session-resume.ts +9 -0
- package/src/scripts/notify-hook/team-dispatch.ts +134 -6
- package/src/scripts/notify-hook/team-leader-nudge.ts +183 -37
- package/src/scripts/notify-hook/team-tmux-guard.ts +35 -43
- package/src/scripts/notify-hook/team-worker.ts +73 -4
- package/src/scripts/notify-hook/utils.ts +1 -1
- package/src/scripts/notify-hook.ts +64 -32
- package/templates/AGENTS.md +21 -11
- package/README.de.md +0 -263
- package/README.el.md +0 -223
- package/README.es.md +0 -263
- package/README.fr.md +0 -263
- package/README.it.md +0 -263
- package/README.ja.md +0 -264
- package/README.ko.md +0 -264
- package/README.pl.md +0 -216
- package/README.pt.md +0 -263
- package/README.ru.md +0 -263
- package/README.tr.md +0 -263
- package/README.vi.md +0 -223
- package/README.zh-TW.md +0 -293
- package/README.zh.md +0 -264
- package/dist/mcp/__tests__/team-server-cleanup.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-cleanup.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-cleanup.test.js +0 -219
- package/dist/mcp/__tests__/team-server-cleanup.test.js.map +0 -1
- package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-runtime-deps.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-runtime-deps.test.js +0 -13
- package/dist/mcp/__tests__/team-server-runtime-deps.test.js.map +0 -1
- package/dist/mcp/__tests__/team-server-wait.test.d.ts +0 -2
- package/dist/mcp/__tests__/team-server-wait.test.d.ts.map +0 -1
- package/dist/mcp/__tests__/team-server-wait.test.js +0 -155
- package/dist/mcp/__tests__/team-server-wait.test.js.map +0 -1
- package/dist/mcp/team-server.d.ts +0 -24
- package/dist/mcp/team-server.d.ts.map +0 -1
- package/dist/mcp/team-server.js +0 -482
- package/dist/mcp/team-server.js.map +0 -1
|
@@ -6,8 +6,8 @@ import { join } from 'path';
|
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { existsSync } from 'fs';
|
|
8
8
|
import { HUD_TMUX_TEAM_HEIGHT_LINES } from '../../hud/constants.js';
|
|
9
|
-
import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, writeWorkerStatus, } from '../state.js';
|
|
10
|
-
import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, resolveWorkerLaunchArgsFromEnv, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
|
|
9
|
+
import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, writeWorkerStatus, } from '../state.js';
|
|
10
|
+
import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, applyCreatedInteractiveSessionToConfig, resolveWorkerLaunchArgsFromEnv, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
|
|
11
11
|
import { resolveTeamLowComplexityDefaultModel } from '../model-contract.js';
|
|
12
12
|
import { readTeamEvents } from '../state/events.js';
|
|
13
13
|
async function initRepo() {
|
|
@@ -28,6 +28,15 @@ async function addWorktree(repo, branchName, pathPrefix) {
|
|
|
28
28
|
function expectedLowComplexityModel(codexHomeOverride) {
|
|
29
29
|
return resolveTeamLowComplexityDefaultModel(codexHomeOverride);
|
|
30
30
|
}
|
|
31
|
+
async function readTeamDeliveryLog(cwd) {
|
|
32
|
+
const path = join(cwd, '.omx', 'logs', `team-delivery-${new Date().toISOString().slice(0, 10)}.jsonl`);
|
|
33
|
+
const raw = await readFile(path, 'utf-8').catch(() => '');
|
|
34
|
+
return raw
|
|
35
|
+
.split('\n')
|
|
36
|
+
.map((line) => line.trim())
|
|
37
|
+
.filter(Boolean)
|
|
38
|
+
.map((line) => JSON.parse(line));
|
|
39
|
+
}
|
|
31
40
|
function withEmptyPath(fn) {
|
|
32
41
|
const prev = process.env.PATH;
|
|
33
42
|
process.env.PATH = '';
|
|
@@ -437,8 +446,9 @@ sleep 5
|
|
|
437
446
|
runtime = null;
|
|
438
447
|
}
|
|
439
448
|
finally {
|
|
440
|
-
|
|
441
|
-
|
|
449
|
+
const runtimeToShutdown = runtime;
|
|
450
|
+
if (runtimeToShutdown) {
|
|
451
|
+
await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
|
|
442
452
|
}
|
|
443
453
|
if (typeof prevPath === 'string')
|
|
444
454
|
process.env.PATH = prevPath;
|
|
@@ -486,6 +496,292 @@ sleep 5
|
|
|
486
496
|
await rm(cwd, { recursive: true, force: true });
|
|
487
497
|
}
|
|
488
498
|
});
|
|
499
|
+
it('startTeam accepts native Windows tmux clients even when TMUX env vars are absent', async () => {
|
|
500
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-win32-no-env-'));
|
|
501
|
+
const prevTmux = process.env.TMUX;
|
|
502
|
+
const prevTmuxPane = process.env.TMUX_PANE;
|
|
503
|
+
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
504
|
+
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
505
|
+
const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
506
|
+
const prevMsystem = process.env.MSYSTEM;
|
|
507
|
+
const prevOstype = process.env.OSTYPE;
|
|
508
|
+
const prevWsl = process.env.WSL_DISTRO_NAME;
|
|
509
|
+
const prevWslInterop = process.env.WSL_INTEROP;
|
|
510
|
+
const origPlatform = Object.getOwnPropertyDescriptor(process, 'platform');
|
|
511
|
+
let runtime = null;
|
|
512
|
+
let teamNameForCleanup = null;
|
|
513
|
+
try {
|
|
514
|
+
await withMockTmuxFixture({
|
|
515
|
+
dirPrefix: 'omx-runtime-win32-no-env-',
|
|
516
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
517
|
+
set -eu
|
|
518
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
519
|
+
case "\${1:-}" in
|
|
520
|
+
-V)
|
|
521
|
+
echo "tmux 3.4"
|
|
522
|
+
exit 0
|
|
523
|
+
;;
|
|
524
|
+
display-message)
|
|
525
|
+
case "$*" in
|
|
526
|
+
*"#{window_width}"*)
|
|
527
|
+
echo "120"
|
|
528
|
+
;;
|
|
529
|
+
*)
|
|
530
|
+
echo "leader:0 %1"
|
|
531
|
+
;;
|
|
532
|
+
esac
|
|
533
|
+
exit 0
|
|
534
|
+
;;
|
|
535
|
+
list-panes)
|
|
536
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
537
|
+
exit 0
|
|
538
|
+
;;
|
|
539
|
+
split-window)
|
|
540
|
+
case "$*" in
|
|
541
|
+
*" -h "*)
|
|
542
|
+
echo "%2"
|
|
543
|
+
;;
|
|
544
|
+
*)
|
|
545
|
+
echo "%3"
|
|
546
|
+
;;
|
|
547
|
+
esac
|
|
548
|
+
exit 0
|
|
549
|
+
;;
|
|
550
|
+
resize-pane|select-layout|set-window-option|select-pane|kill-pane|set-hook|run-shell)
|
|
551
|
+
exit 0
|
|
552
|
+
;;
|
|
553
|
+
*)
|
|
554
|
+
exit 0
|
|
555
|
+
;;
|
|
556
|
+
esac
|
|
557
|
+
`,
|
|
558
|
+
binaries: [{
|
|
559
|
+
name: 'gemini',
|
|
560
|
+
content: '#!/bin/sh\nexit 0\n',
|
|
561
|
+
}],
|
|
562
|
+
}, async ({ tmuxLogPath }) => {
|
|
563
|
+
delete process.env.TMUX;
|
|
564
|
+
delete process.env.TMUX_PANE;
|
|
565
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
566
|
+
process.env.OMX_TEAM_WORKER_CLI = 'gemini';
|
|
567
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
568
|
+
delete process.env.MSYSTEM;
|
|
569
|
+
delete process.env.OSTYPE;
|
|
570
|
+
delete process.env.WSL_DISTRO_NAME;
|
|
571
|
+
delete process.env.WSL_INTEROP;
|
|
572
|
+
Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
|
|
573
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('team-win32-no-env', 'native windows current-client detection', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
|
|
574
|
+
teamNameForCleanup = runtime.teamName;
|
|
575
|
+
assert.equal(runtime.config.tmux_session, 'leader:0');
|
|
576
|
+
assert.equal(runtime.config.leader_pane_id, '%1');
|
|
577
|
+
assert.equal(runtime.config.hud_pane_id, '%3');
|
|
578
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
579
|
+
assert.match(tmuxLog, /display-message -p #S:#I #{pane_id}/);
|
|
580
|
+
assert.match(tmuxLog, new RegExp(`resize-pane -t %3 -y ${HUD_TMUX_TEAM_HEIGHT_LINES}`));
|
|
581
|
+
if (teamNameForCleanup) {
|
|
582
|
+
await shutdownTeam(teamNameForCleanup, cwd, { force: true });
|
|
583
|
+
}
|
|
584
|
+
runtime = null;
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
finally {
|
|
588
|
+
if (teamNameForCleanup) {
|
|
589
|
+
await shutdownTeam(teamNameForCleanup, cwd, { force: true }).catch(() => { });
|
|
590
|
+
}
|
|
591
|
+
if (origPlatform)
|
|
592
|
+
Object.defineProperty(process, 'platform', origPlatform);
|
|
593
|
+
if (typeof prevTmux === 'string')
|
|
594
|
+
process.env.TMUX = prevTmux;
|
|
595
|
+
else
|
|
596
|
+
delete process.env.TMUX;
|
|
597
|
+
if (typeof prevTmuxPane === 'string')
|
|
598
|
+
process.env.TMUX_PANE = prevTmuxPane;
|
|
599
|
+
else
|
|
600
|
+
delete process.env.TMUX_PANE;
|
|
601
|
+
if (typeof prevLaunchMode === 'string')
|
|
602
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
|
|
603
|
+
else
|
|
604
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
605
|
+
if (typeof prevWorkerCli === 'string')
|
|
606
|
+
process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
|
|
607
|
+
else
|
|
608
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
609
|
+
if (typeof prevSkipReadyWait === 'string')
|
|
610
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
|
|
611
|
+
else
|
|
612
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
613
|
+
if (typeof prevMsystem === 'string')
|
|
614
|
+
process.env.MSYSTEM = prevMsystem;
|
|
615
|
+
else
|
|
616
|
+
delete process.env.MSYSTEM;
|
|
617
|
+
if (typeof prevOstype === 'string')
|
|
618
|
+
process.env.OSTYPE = prevOstype;
|
|
619
|
+
else
|
|
620
|
+
delete process.env.OSTYPE;
|
|
621
|
+
if (typeof prevWsl === 'string')
|
|
622
|
+
process.env.WSL_DISTRO_NAME = prevWsl;
|
|
623
|
+
else
|
|
624
|
+
delete process.env.WSL_DISTRO_NAME;
|
|
625
|
+
if (typeof prevWslInterop === 'string')
|
|
626
|
+
process.env.WSL_INTEROP = prevWslInterop;
|
|
627
|
+
else
|
|
628
|
+
delete process.env.WSL_INTEROP;
|
|
629
|
+
await rm(cwd, { recursive: true, force: true });
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
it('applyCreatedInteractiveSessionToConfig persists worker pane ids before readiness waits', async () => {
|
|
633
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-persist-race-'));
|
|
634
|
+
try {
|
|
635
|
+
const config = await initTeamState('team-pane-persist-race', 'persist pane ids before readiness wait', 'executor', 2, cwd);
|
|
636
|
+
const workerPaneIds = Array.from({ length: 2 }, () => undefined);
|
|
637
|
+
applyCreatedInteractiveSessionToConfig(config, {
|
|
638
|
+
name: 'leader:0',
|
|
639
|
+
workerCount: 2,
|
|
640
|
+
cwd,
|
|
641
|
+
workerPaneIds: ['%2', '%3'],
|
|
642
|
+
leaderPaneId: '%1',
|
|
643
|
+
hudPaneId: '%4',
|
|
644
|
+
resizeHookName: 'resize-hook',
|
|
645
|
+
resizeHookTarget: 'leader:0',
|
|
646
|
+
}, workerPaneIds);
|
|
647
|
+
assert.equal(config.tmux_session, 'leader:0');
|
|
648
|
+
assert.equal(config.leader_pane_id, '%1');
|
|
649
|
+
assert.equal(config.hud_pane_id, '%4');
|
|
650
|
+
assert.deepEqual(workerPaneIds, ['%2', '%3']);
|
|
651
|
+
assert.equal(config.workers[0]?.pane_id, '%2');
|
|
652
|
+
assert.equal(config.workers[1]?.pane_id, '%3');
|
|
653
|
+
}
|
|
654
|
+
finally {
|
|
655
|
+
await rm(cwd, { recursive: true, force: true });
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
it('startTeam captures interactive worker pid from the resolved pane id', async () => {
|
|
659
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-pane-pid-'));
|
|
660
|
+
const prevTmux = process.env.TMUX;
|
|
661
|
+
const prevTmuxPane = process.env.TMUX_PANE;
|
|
662
|
+
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
663
|
+
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
664
|
+
const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
665
|
+
let runtime = null;
|
|
666
|
+
try {
|
|
667
|
+
await withMockTmuxFixture({
|
|
668
|
+
dirPrefix: 'omx-runtime-pane-pid-bin-',
|
|
669
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
670
|
+
set -eu
|
|
671
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
672
|
+
case "\${1:-}" in
|
|
673
|
+
-V)
|
|
674
|
+
echo "tmux 3.4"
|
|
675
|
+
exit 0
|
|
676
|
+
;;
|
|
677
|
+
display-message)
|
|
678
|
+
case "$*" in
|
|
679
|
+
*"#{window_width}"*)
|
|
680
|
+
echo "120"
|
|
681
|
+
;;
|
|
682
|
+
*)
|
|
683
|
+
echo "leader:0 %1"
|
|
684
|
+
;;
|
|
685
|
+
esac
|
|
686
|
+
exit 0
|
|
687
|
+
;;
|
|
688
|
+
list-panes)
|
|
689
|
+
case "$*" in
|
|
690
|
+
*"pane_current_command"*)
|
|
691
|
+
printf "%%1\tnode\t'codex'\n"
|
|
692
|
+
;;
|
|
693
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
694
|
+
echo "1 999999"
|
|
695
|
+
;;
|
|
696
|
+
*"-t %2"*"#{pane_pid}"*)
|
|
697
|
+
echo "2222"
|
|
698
|
+
;;
|
|
699
|
+
*"-t %3"*"#{pane_pid}"*)
|
|
700
|
+
echo "3333"
|
|
701
|
+
;;
|
|
702
|
+
*"#{pane_pid}"*)
|
|
703
|
+
echo "1111"
|
|
704
|
+
;;
|
|
705
|
+
*)
|
|
706
|
+
exit 0
|
|
707
|
+
;;
|
|
708
|
+
esac
|
|
709
|
+
exit 0
|
|
710
|
+
;;
|
|
711
|
+
split-window)
|
|
712
|
+
case "$*" in
|
|
713
|
+
*" -h "*)
|
|
714
|
+
echo "%2"
|
|
715
|
+
;;
|
|
716
|
+
*)
|
|
717
|
+
echo "%3"
|
|
718
|
+
;;
|
|
719
|
+
esac
|
|
720
|
+
exit 0
|
|
721
|
+
;;
|
|
722
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session)
|
|
723
|
+
exit 0
|
|
724
|
+
;;
|
|
725
|
+
*)
|
|
726
|
+
exit 0
|
|
727
|
+
;;
|
|
728
|
+
esac
|
|
729
|
+
`,
|
|
730
|
+
binaries: [{ name: 'codex', content: '#!/bin/sh\nexit 0\n' }],
|
|
731
|
+
}, async () => {
|
|
732
|
+
delete process.env.TMUX;
|
|
733
|
+
process.env.TMUX_PANE = '%1';
|
|
734
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
735
|
+
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
736
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
737
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam('team-pane-pid', 'interactive pane pid capture', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
|
|
738
|
+
assert.equal(runtime.config.workers[0]?.pane_id, '%2');
|
|
739
|
+
assert.equal(runtime.config.workers[0]?.pid, 2222);
|
|
740
|
+
const identityPath = join(cwd, '.omx', 'state', 'team', runtime.teamName, 'workers', 'worker-1', 'identity.json');
|
|
741
|
+
const identity = JSON.parse(await readFile(identityPath, 'utf-8'));
|
|
742
|
+
assert.equal(identity.pane_id, '%2');
|
|
743
|
+
assert.equal(identity.pid, 2222);
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
finally {
|
|
747
|
+
const runtimeToShutdown = runtime;
|
|
748
|
+
if (runtimeToShutdown) {
|
|
749
|
+
await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
|
|
750
|
+
}
|
|
751
|
+
if (typeof prevTmux === 'string')
|
|
752
|
+
process.env.TMUX = prevTmux;
|
|
753
|
+
else
|
|
754
|
+
delete process.env.TMUX;
|
|
755
|
+
if (typeof prevTmuxPane === 'string')
|
|
756
|
+
process.env.TMUX_PANE = prevTmuxPane;
|
|
757
|
+
else
|
|
758
|
+
delete process.env.TMUX_PANE;
|
|
759
|
+
if (typeof prevLaunchMode === 'string')
|
|
760
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
|
|
761
|
+
else
|
|
762
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
763
|
+
if (typeof prevWorkerCli === 'string')
|
|
764
|
+
process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
|
|
765
|
+
else
|
|
766
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
767
|
+
if (typeof prevSkipReadyWait === 'string')
|
|
768
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
|
|
769
|
+
else
|
|
770
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
771
|
+
await rm(cwd, { recursive: true, force: true });
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
it('startTeam saves interactive pane ids before readiness waits in source order', async () => {
|
|
775
|
+
const source = await readFile(join(process.cwd(), 'src', 'team', 'runtime.ts'), 'utf-8');
|
|
776
|
+
const applyIndex = source.indexOf('applyCreatedInteractiveSessionToConfig(config, createdSession, workerPaneIds);');
|
|
777
|
+
const saveIndex = source.indexOf('await saveTeamConfig(config, leaderCwd);', applyIndex);
|
|
778
|
+
const readyIndex = source.indexOf('const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);', saveIndex);
|
|
779
|
+
assert.notEqual(applyIndex, -1);
|
|
780
|
+
assert.notEqual(saveIndex, -1);
|
|
781
|
+
assert.notEqual(readyIndex, -1);
|
|
782
|
+
assert.equal(applyIndex < saveIndex, true);
|
|
783
|
+
assert.equal(saveIndex < readyIndex, true);
|
|
784
|
+
});
|
|
489
785
|
it('startTeam rejects dirty leader workspace before provisioning worker worktrees', async () => {
|
|
490
786
|
const repo = await initRepo();
|
|
491
787
|
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
@@ -536,21 +832,25 @@ sleep 5
|
|
|
536
832
|
runtime = await withoutTeamWorkerEnv(() => startTeam('team-gemini-prompt', 'gemini prompt-mode team bootstrap', 'explore', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd));
|
|
537
833
|
assert.equal(runtime.config.worker_launch_mode, 'prompt');
|
|
538
834
|
assert.equal((runtime.config.workers[0]?.pid ?? 0) > 0, true);
|
|
835
|
+
const expectedArgv = [
|
|
836
|
+
'--approval-mode',
|
|
837
|
+
'yolo',
|
|
838
|
+
'-i',
|
|
839
|
+
'Read .omx/state/team/team-gemini-prompt/workers/worker-1/inbox.md, start work now, report concrete progress, then continue assigned work or next feasible task.',
|
|
840
|
+
];
|
|
539
841
|
let argv = null;
|
|
540
842
|
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
541
843
|
if (existsSync(capturePath)) {
|
|
542
|
-
|
|
543
|
-
|
|
844
|
+
const captured = (await readFile(capturePath, 'utf-8')).trim().split('\n').filter(Boolean);
|
|
845
|
+
if (captured.length >= expectedArgv.length) {
|
|
846
|
+
argv = captured;
|
|
847
|
+
break;
|
|
848
|
+
}
|
|
544
849
|
}
|
|
545
850
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
546
851
|
}
|
|
547
852
|
assert.ok(argv, 'gemini argv capture file should be written');
|
|
548
|
-
assert.deepEqual(argv,
|
|
549
|
-
'--approval-mode',
|
|
550
|
-
'yolo',
|
|
551
|
-
'-i',
|
|
552
|
-
'Read .omx/state/team/team-gemini-prompt/workers/worker-1/inbox.md, start work now, report concrete progress, then continue assigned work or next feasible task.',
|
|
553
|
-
]);
|
|
853
|
+
assert.deepEqual(argv, expectedArgv);
|
|
554
854
|
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
555
855
|
runtime = null;
|
|
556
856
|
}
|
|
@@ -1010,9 +1310,9 @@ exit 0
|
|
|
1010
1310
|
});
|
|
1011
1311
|
}
|
|
1012
1312
|
finally {
|
|
1013
|
-
const
|
|
1014
|
-
if (
|
|
1015
|
-
await shutdownTeam(
|
|
1313
|
+
const runtimeToShutdown = runtime;
|
|
1314
|
+
if (runtimeToShutdown) {
|
|
1315
|
+
await shutdownTeam(runtimeToShutdown.teamName, cwd, { force: true }).catch(() => { });
|
|
1016
1316
|
}
|
|
1017
1317
|
if (typeof previousTmux === 'string')
|
|
1018
1318
|
process.env.TMUX = previousTmux;
|
|
@@ -1825,6 +2125,72 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1825
2125
|
await rm(repo, { recursive: true, force: true });
|
|
1826
2126
|
}
|
|
1827
2127
|
});
|
|
2128
|
+
it('monitorTeam does not emit INTEGRATED when merge reports success but leader HEAD never advances', async () => {
|
|
2129
|
+
const repo = await initRepo();
|
|
2130
|
+
let workerPath = '';
|
|
2131
|
+
const fakeBinDir = await mkdtemp(join(tmpdir(), 'omx-runtime-fake-git-'));
|
|
2132
|
+
const previousPath = process.env.PATH;
|
|
2133
|
+
const previousFakeMode = process.env.OMX_FAKE_GIT_SUCCESS_NOOP;
|
|
2134
|
+
try {
|
|
2135
|
+
workerPath = await addWorktree(repo, 'wk1-merge-noadvance-branch', 'omx-runtime-wk1-merge-noadvance-');
|
|
2136
|
+
await writeFile(join(workerPath, 'feature.txt'), 'new feature\n', 'utf-8');
|
|
2137
|
+
execFileSync('git', ['add', 'feature.txt'], { cwd: workerPath, stdio: 'ignore' });
|
|
2138
|
+
execFileSync('git', ['commit', '-m', 'worker feature'], { cwd: workerPath, stdio: 'ignore' });
|
|
2139
|
+
await initTeamState('team-merge-noadvance', 'merge no advance test', 'executor', 1, repo);
|
|
2140
|
+
const cfg = await readTeamConfig('team-merge-noadvance', repo);
|
|
2141
|
+
assert.ok(cfg);
|
|
2142
|
+
if (!cfg)
|
|
2143
|
+
throw new Error('missing config');
|
|
2144
|
+
cfg.leader_pane_id = '';
|
|
2145
|
+
cfg.workers[0] = {
|
|
2146
|
+
...cfg.workers[0],
|
|
2147
|
+
assigned_tasks: ['1'],
|
|
2148
|
+
worktree_repo_root: repo,
|
|
2149
|
+
worktree_path: workerPath,
|
|
2150
|
+
worktree_branch: 'wk1-merge-noadvance-branch',
|
|
2151
|
+
worktree_detached: false,
|
|
2152
|
+
worktree_created: false,
|
|
2153
|
+
};
|
|
2154
|
+
await saveTeamConfig(cfg, repo);
|
|
2155
|
+
const realGit = execFileSync('bash', ['-lc', 'command -v git'], { encoding: 'utf-8' }).trim();
|
|
2156
|
+
await writeFile(join(fakeBinDir, 'git'), `#!/usr/bin/env bash
|
|
2157
|
+
set -euo pipefail
|
|
2158
|
+
if [[ "\${OMX_FAKE_GIT_SUCCESS_NOOP:-}" == "merge" && "\${1:-}" == "merge" ]]; then
|
|
2159
|
+
exit 0
|
|
2160
|
+
fi
|
|
2161
|
+
exec "${realGit}" "$@"
|
|
2162
|
+
`, { mode: 0o755 });
|
|
2163
|
+
process.env.PATH = `${fakeBinDir}:${previousPath ?? ''}`;
|
|
2164
|
+
process.env.OMX_FAKE_GIT_SUCCESS_NOOP = 'merge';
|
|
2165
|
+
const leaderHeadBefore = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: repo, encoding: 'utf-8' }).trim();
|
|
2166
|
+
await monitorTeam('team-merge-noadvance', repo);
|
|
2167
|
+
const leaderHeadAfter = execFileSync(realGit, ['rev-parse', 'HEAD'], { cwd: repo, encoding: 'utf-8' }).trim();
|
|
2168
|
+
assert.equal(leaderHeadAfter, leaderHeadBefore, 'leader HEAD should stay unchanged in regression setup');
|
|
2169
|
+
const snapshot = await readMonitorSnapshot('team-merge-noadvance', repo);
|
|
2170
|
+
assert.equal(snapshot?.integrationByWorker?.['worker-1']?.status, 'integration_failed');
|
|
2171
|
+
assert.equal(snapshot?.integrationByWorker?.['worker-1']?.last_integrated_head, undefined);
|
|
2172
|
+
const leaderMailbox = await listMailboxMessages('team-merge-noadvance', 'leader-fixed', repo);
|
|
2173
|
+
assert.equal(leaderMailbox.some((message) => /INTEGRATED:/.test(message.body)), false);
|
|
2174
|
+
assert.equal(leaderMailbox.some((message) => /INTEGRATION FAILED:/.test(message.body)), true);
|
|
2175
|
+
const events = await readTeamEvents('team-merge-noadvance', repo, { wakeableOnly: false });
|
|
2176
|
+
assert.equal(events.some((event) => event.type === 'worker_integration_failed'), true);
|
|
2177
|
+
}
|
|
2178
|
+
finally {
|
|
2179
|
+
if (typeof previousPath === 'string')
|
|
2180
|
+
process.env.PATH = previousPath;
|
|
2181
|
+
else
|
|
2182
|
+
delete process.env.PATH;
|
|
2183
|
+
if (typeof previousFakeMode === 'string')
|
|
2184
|
+
process.env.OMX_FAKE_GIT_SUCCESS_NOOP = previousFakeMode;
|
|
2185
|
+
else
|
|
2186
|
+
delete process.env.OMX_FAKE_GIT_SUCCESS_NOOP;
|
|
2187
|
+
await rm(fakeBinDir, { recursive: true, force: true });
|
|
2188
|
+
if (workerPath) {
|
|
2189
|
+
await rm(workerPath, { recursive: true, force: true });
|
|
2190
|
+
}
|
|
2191
|
+
await rm(repo, { recursive: true, force: true });
|
|
2192
|
+
}
|
|
2193
|
+
});
|
|
1828
2194
|
it('monitorTeam uses cherry-pick for diverged worker (hybrid cherry-pick path)', async () => {
|
|
1829
2195
|
const repo = await initRepo();
|
|
1830
2196
|
let workerPath = '';
|
|
@@ -2443,6 +2809,92 @@ esac
|
|
|
2443
2809
|
await rm(cwd, { recursive: true, force: true });
|
|
2444
2810
|
}
|
|
2445
2811
|
});
|
|
2812
|
+
it('shutdownTeam reconciles persisted worker panes with live tmux panes before teardown', async () => {
|
|
2813
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-pane-reconcile-'));
|
|
2814
|
+
try {
|
|
2815
|
+
await withMockTmuxFixture({
|
|
2816
|
+
dirPrefix: 'omx-runtime-shutdown-pane-reconcile-bin-',
|
|
2817
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
2818
|
+
set -eu
|
|
2819
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
2820
|
+
restored_marker="${tmuxLogPath}.restored"
|
|
2821
|
+
case "$1" in
|
|
2822
|
+
-V)
|
|
2823
|
+
echo "tmux 3.4"
|
|
2824
|
+
exit 0
|
|
2825
|
+
;;
|
|
2826
|
+
list-panes)
|
|
2827
|
+
case "$*" in
|
|
2828
|
+
*"-t leader:0 -F #{pane_dead} #{pane_pid}"*)
|
|
2829
|
+
exit 1
|
|
2830
|
+
;;
|
|
2831
|
+
*"-t %13 -F #{pane_pid}"*)
|
|
2832
|
+
echo "1013"
|
|
2833
|
+
exit 0
|
|
2834
|
+
;;
|
|
2835
|
+
*"-t %14 -F #{pane_pid}"*)
|
|
2836
|
+
echo "1014"
|
|
2837
|
+
exit 0
|
|
2838
|
+
;;
|
|
2839
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
2840
|
+
printf "%%11\\tzsh\\tzsh\\n%%12\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%13\\tcodex\\tcodex\\n%%14\\tcodex\\tcodex\\n"
|
|
2841
|
+
if [ -f "$restored_marker" ]; then
|
|
2842
|
+
printf "%%44\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n"
|
|
2843
|
+
fi
|
|
2844
|
+
exit 0
|
|
2845
|
+
;;
|
|
2846
|
+
*)
|
|
2847
|
+
exit 1
|
|
2848
|
+
;;
|
|
2849
|
+
esac
|
|
2850
|
+
;;
|
|
2851
|
+
split-window)
|
|
2852
|
+
: > "$restored_marker"
|
|
2853
|
+
printf '%%44\\n'
|
|
2854
|
+
exit 0
|
|
2855
|
+
;;
|
|
2856
|
+
kill-pane)
|
|
2857
|
+
if [ "\${3:-}" = "%999" ]; then
|
|
2858
|
+
echo "missing pane" >&2
|
|
2859
|
+
exit 1
|
|
2860
|
+
fi
|
|
2861
|
+
exit 0
|
|
2862
|
+
;;
|
|
2863
|
+
kill-session|select-pane|run-shell)
|
|
2864
|
+
exit 0
|
|
2865
|
+
;;
|
|
2866
|
+
*)
|
|
2867
|
+
exit 0
|
|
2868
|
+
;;
|
|
2869
|
+
esac
|
|
2870
|
+
`,
|
|
2871
|
+
}, async ({ tmuxLogPath }) => {
|
|
2872
|
+
await initTeamState('team-shutdown-pane-reconcile', 'shutdown pane reconcile test', 'executor', 2, cwd);
|
|
2873
|
+
const config = await readTeamConfig('team-shutdown-pane-reconcile', cwd);
|
|
2874
|
+
assert.ok(config);
|
|
2875
|
+
if (!config)
|
|
2876
|
+
return;
|
|
2877
|
+
config.tmux_session = 'leader:0';
|
|
2878
|
+
config.leader_pane_id = '%11';
|
|
2879
|
+
config.hud_pane_id = '%12';
|
|
2880
|
+
config.workers[0].pane_id = '';
|
|
2881
|
+
config.workers[1].pane_id = '%999';
|
|
2882
|
+
await saveTeamConfig(config, cwd);
|
|
2883
|
+
await shutdownTeam('team-shutdown-pane-reconcile', cwd, { force: true });
|
|
2884
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
2885
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %11/);
|
|
2886
|
+
assert.match(tmuxLog, /kill-pane -t %12/);
|
|
2887
|
+
assert.match(tmuxLog, /kill-pane -t %13/);
|
|
2888
|
+
assert.match(tmuxLog, /kill-pane -t %14/);
|
|
2889
|
+
assert.match(tmuxLog, /kill-pane -t %999/);
|
|
2890
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %11 -d -P -F #\{pane_id\}`));
|
|
2891
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %44/);
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
finally {
|
|
2895
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2446
2898
|
it('shutdownTeam restores a standalone HUD pane after tearing down the team HUD', async () => {
|
|
2447
2899
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-restore-hud-'));
|
|
2448
2900
|
try {
|
|
@@ -2922,6 +3374,7 @@ esac
|
|
|
2922
3374
|
if (!cfg)
|
|
2923
3375
|
throw new Error('missing team config');
|
|
2924
3376
|
cfg.leader_pane_id = '%55';
|
|
3377
|
+
cfg.team_state_root = '/tmp/custom-team-state-root';
|
|
2925
3378
|
await saveTeamConfig(cfg, cwd);
|
|
2926
3379
|
const manifestPath = teamStateTestPath(cwd, 'team', 'team-leader-inject', 'manifest.v2.json');
|
|
2927
3380
|
const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
|
|
@@ -2937,6 +3390,74 @@ esac
|
|
|
2937
3390
|
const latest = requests[requests.length - 1];
|
|
2938
3391
|
assert.equal(latest?.status, 'notified');
|
|
2939
3392
|
assert.equal(latest?.last_reason, 'fallback_confirmed:leader_mailbox_notified');
|
|
3393
|
+
assert.match(latest?.trigger_message ?? '', /Read \/tmp\/custom-team-state-root\/team\/team-leader-inject\/mailbox\/leader-fixed\.json; new msg from worker-1\./);
|
|
3394
|
+
const deliveryLog = await readTeamDeliveryLog(cwd);
|
|
3395
|
+
const runtimeEntries = deliveryLog.filter((entry) => entry.event === 'dispatch_result'
|
|
3396
|
+
&& entry.source === 'team.runtime'
|
|
3397
|
+
&& entry.to_worker === 'leader-fixed'
|
|
3398
|
+
&& entry.transport === 'mailbox'
|
|
3399
|
+
&& entry.result === 'confirmed'
|
|
3400
|
+
&& typeof entry.reason === 'string'
|
|
3401
|
+
&& String(entry.reason).includes('leader_mailbox_notified'));
|
|
3402
|
+
assert.equal(runtimeEntries.length, 1, 'leader hook-preferred confirmation should emit exactly one runtime dispatch_result entry');
|
|
3403
|
+
});
|
|
3404
|
+
}
|
|
3405
|
+
finally {
|
|
3406
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3407
|
+
}
|
|
3408
|
+
});
|
|
3409
|
+
it('sendWorkerMessage keeps failed hook receipts failed when fallback mailbox persistence confirms delivery', async () => {
|
|
3410
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-leader-failed-receipt-'));
|
|
3411
|
+
try {
|
|
3412
|
+
await withMockTmuxFixture({
|
|
3413
|
+
dirPrefix: 'omx-runtime-leader-failed-receipt-bin-',
|
|
3414
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
3415
|
+
set -eu
|
|
3416
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
3417
|
+
case "\${1:-}" in
|
|
3418
|
+
send-keys)
|
|
3419
|
+
exit 0
|
|
3420
|
+
;;
|
|
3421
|
+
*)
|
|
3422
|
+
exit 0
|
|
3423
|
+
;;
|
|
3424
|
+
esac
|
|
3425
|
+
`,
|
|
3426
|
+
}, async () => {
|
|
3427
|
+
await initTeamState('team-leader-failed-receipt', 'leader failed receipt fallback test', 'executor', 1, cwd);
|
|
3428
|
+
const cfg = await readTeamConfig('team-leader-failed-receipt', cwd);
|
|
3429
|
+
assert.ok(cfg);
|
|
3430
|
+
if (!cfg)
|
|
3431
|
+
throw new Error('missing team config');
|
|
3432
|
+
cfg.leader_pane_id = '%55';
|
|
3433
|
+
await saveTeamConfig(cfg, cwd);
|
|
3434
|
+
const manifestPath = teamStateTestPath(cwd, 'team', 'team-leader-failed-receipt', 'manifest.v2.json');
|
|
3435
|
+
const manifest = JSON.parse(await readFile(manifestPath, 'utf-8'));
|
|
3436
|
+
manifest.policy = { ...(manifest.policy || {}), dispatch_ack_timeout_ms: 250 };
|
|
3437
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
3438
|
+
const sendPromise = sendWorkerMessage('team-leader-failed-receipt', 'worker-1', 'leader-fixed', 'hello failed receipt', cwd);
|
|
3439
|
+
const deadline = Date.now() + 2_000;
|
|
3440
|
+
let requestId = null;
|
|
3441
|
+
while (Date.now() < deadline && !requestId) {
|
|
3442
|
+
const requests = await listDispatchRequests('team-leader-failed-receipt', cwd, { kind: 'mailbox', to_worker: 'leader-fixed' });
|
|
3443
|
+
requestId = requests[requests.length - 1]?.request_id ?? null;
|
|
3444
|
+
if (!requestId)
|
|
3445
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
3446
|
+
}
|
|
3447
|
+
assert.ok(requestId, 'expected mailbox dispatch request to be queued');
|
|
3448
|
+
if (!requestId)
|
|
3449
|
+
throw new Error('missing request id');
|
|
3450
|
+
await transitionDispatchRequest('team-leader-failed-receipt', requestId, 'pending', 'failed', { last_reason: 'hook_failed:test_receipt' }, cwd);
|
|
3451
|
+
const outcome = await sendPromise;
|
|
3452
|
+
assert.equal(outcome.ok, true);
|
|
3453
|
+
assert.equal(outcome.reason, 'fallback_confirmed_after_failed_receipt:leader_mailbox_notified');
|
|
3454
|
+
const requests = await listDispatchRequests('team-leader-failed-receipt', cwd, { kind: 'mailbox', to_worker: 'leader-fixed' });
|
|
3455
|
+
const latest = requests[requests.length - 1];
|
|
3456
|
+
assert.equal(latest?.request_id, requestId);
|
|
3457
|
+
assert.equal(latest?.status, 'failed');
|
|
3458
|
+
assert.equal(latest?.last_reason, 'fallback_confirmed_after_failed_receipt:leader_mailbox_notified');
|
|
3459
|
+
const mailbox = await listMailboxMessages('team-leader-failed-receipt', 'leader-fixed', cwd);
|
|
3460
|
+
assert.ok(mailbox[0]?.notified_at, 'fallback mailbox persistence should still mark notified_at');
|
|
2940
3461
|
});
|
|
2941
3462
|
}
|
|
2942
3463
|
finally {
|
|
@@ -2963,6 +3484,13 @@ esac
|
|
|
2963
3484
|
const pending = requests.find((r) => r.status === 'pending' && r.to_worker === 'leader-fixed');
|
|
2964
3485
|
assert.ok(pending, 'expected a pending leader-fixed dispatch request');
|
|
2965
3486
|
assert.equal(pending?.last_reason, 'leader_pane_missing_deferred');
|
|
3487
|
+
const deliveryLog = await readTeamDeliveryLog(cwd);
|
|
3488
|
+
const runtimeEntries = deliveryLog.filter((entry) => entry.event === 'dispatch_result'
|
|
3489
|
+
&& entry.source === 'team.runtime'
|
|
3490
|
+
&& entry.to_worker === 'leader-fixed'
|
|
3491
|
+
&& entry.transport === 'mailbox'
|
|
3492
|
+
&& entry.reason === 'leader_pane_missing_mailbox_persisted');
|
|
3493
|
+
assert.equal(runtimeEntries.length, 1, 'leader missing-pane fallback should emit exactly one runtime dispatch_result entry');
|
|
2966
3494
|
}
|
|
2967
3495
|
finally {
|
|
2968
3496
|
await rm(cwd, { recursive: true, force: true });
|