oh-my-codex 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/bin/omx.js +3 -5
- package/dist/agents/__tests__/definitions.test.d.ts +2 -0
- package/dist/agents/__tests__/definitions.test.d.ts.map +1 -0
- package/dist/agents/__tests__/definitions.test.js +35 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -0
- package/dist/agents/__tests__/native-config.test.d.ts +2 -0
- package/dist/agents/__tests__/native-config.test.d.ts.map +1 -0
- package/dist/agents/__tests__/native-config.test.js +48 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -0
- package/dist/catalog/__tests__/schema.test.js +15 -0
- package/dist/catalog/__tests__/schema.test.js.map +1 -1
- package/dist/catalog/schema.d.ts.map +1 -1
- package/dist/catalog/schema.js +6 -0
- package/dist/catalog/schema.js.map +1 -1
- package/dist/cli/__tests__/catalog-contract.test.d.ts +2 -0
- package/dist/cli/__tests__/catalog-contract.test.d.ts.map +1 -0
- package/dist/cli/__tests__/catalog-contract.test.js +18 -0
- package/dist/cli/__tests__/catalog-contract.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-team.test.js +3 -2
- package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
- package/dist/cli/__tests__/error-handling-warnings.test.d.ts +2 -0
- package/dist/cli/__tests__/error-handling-warnings.test.d.ts.map +1 -0
- package/dist/cli/__tests__/error-handling-warnings.test.js +35 -0
- package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -0
- package/dist/cli/__tests__/index.test.js +81 -8
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +124 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -0
- package/dist/cli/__tests__/setup-scope.test.js +79 -21
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +32 -0
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -0
- package/dist/cli/__tests__/star-prompt.test.js +74 -0
- package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +8 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +75 -18
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +10 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +153 -45
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts +2 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +104 -60
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/star-prompt.d.ts +21 -1
- package/dist/cli/star-prompt.d.ts.map +1 -1
- package/dist/cli/star-prompt.js +34 -13
- package/dist/cli/star-prompt.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +10 -3
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +16 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +9 -1
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.js +9 -10
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +8 -1
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +27 -5
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +24 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts +18 -0
- package/dist/hooks/__tests__/consensus-execution-handoff.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js +204 -0
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -0
- package/dist/hooks/__tests__/emulator.test.d.ts +2 -0
- package/dist/hooks/__tests__/emulator.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/emulator.test.js +47 -0
- package/dist/hooks/__tests__/emulator.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +330 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +101 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +13 -7
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +61 -0
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +47 -0
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts +2 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +560 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -0
- package/dist/hooks/__tests__/session.test.d.ts +2 -0
- package/dist/hooks/__tests__/session.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/session.test.js +161 -0
- package/dist/hooks/__tests__/session.test.js.map +1 -0
- package/dist/hooks/__tests__/task-size-detector.test.d.ts +2 -0
- package/dist/hooks/__tests__/task-size-detector.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/task-size-detector.test.js +336 -0
- package/dist/hooks/__tests__/task-size-detector.test.js.map +1 -0
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts +2 -0
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js +24 -0
- package/dist/hooks/__tests__/tmux-hook-engine-types-sync.test.js.map +1 -0
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +46 -2
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/code-simplifier/__tests__/index.test.js +67 -15
- package/dist/hooks/code-simplifier/__tests__/index.test.js.map +1 -1
- package/dist/hooks/code-simplifier/index.d.ts +10 -4
- package/dist/hooks/code-simplifier/index.d.ts.map +1 -1
- package/dist/hooks/code-simplifier/index.js +38 -12
- package/dist/hooks/code-simplifier/index.js.map +1 -1
- package/dist/hooks/codebase-map.d.ts.map +1 -1
- package/dist/hooks/codebase-map.js +5 -32
- package/dist/hooks/codebase-map.js.map +1 -1
- package/dist/hooks/emulator.d.ts.map +1 -1
- package/dist/hooks/emulator.js +11 -18
- package/dist/hooks/emulator.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +59 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/loader.test.js +19 -0
- package/dist/hooks/extensibility/__tests__/loader.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +51 -39
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/loader.d.ts.map +1 -1
- package/dist/hooks/extensibility/loader.js +25 -13
- package/dist/hooks/extensibility/loader.js.map +1 -1
- package/dist/hooks/extensibility/logging.d.ts.map +1 -1
- package/dist/hooks/extensibility/logging.js +6 -1
- package/dist/hooks/extensibility/logging.js.map +1 -1
- package/dist/hooks/extensibility/sdk.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +87 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +235 -23
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts +15 -0
- package/dist/hooks/keyword-registry.d.ts.map +1 -0
- package/dist/hooks/keyword-registry.js +41 -0
- package/dist/hooks/keyword-registry.js.map +1 -0
- package/dist/hooks/session.d.ts +18 -2
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +84 -11
- package/dist/hooks/session.js.map +1 -1
- package/dist/hooks/task-size-detector.d.ts +72 -0
- package/dist/hooks/task-size-detector.d.ts.map +1 -0
- package/dist/hooks/task-size-detector.js +204 -0
- package/dist/hooks/task-size-detector.js.map +1 -0
- package/dist/hud/__tests__/colors.test.js +1 -103
- package/dist/hud/__tests__/colors.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.d.ts +2 -0
- package/dist/hud/__tests__/index.test.d.ts.map +1 -0
- package/dist/hud/__tests__/index.test.js +131 -0
- package/dist/hud/__tests__/index.test.js.map +1 -0
- package/dist/hud/__tests__/render.test.js +53 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/watch.test.d.ts +2 -0
- package/dist/hud/__tests__/watch.test.d.ts.map +1 -0
- package/dist/hud/__tests__/watch.test.js +63 -0
- package/dist/hud/__tests__/watch.test.js.map +1 -0
- package/dist/hud/colors.d.ts +2 -9
- package/dist/hud/colors.d.ts.map +1 -1
- package/dist/hud/colors.js +19 -34
- package/dist/hud/colors.js.map +1 -1
- package/dist/hud/constants.d.ts +1 -0
- package/dist/hud/constants.d.ts.map +1 -1
- package/dist/hud/constants.js +1 -0
- package/dist/hud/constants.js.map +1 -1
- package/dist/hud/index.d.ts +27 -0
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +149 -9
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +20 -7
- package/dist/hud/render.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.d.ts +2 -0
- package/dist/mcp/__tests__/bootstrap.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/bootstrap.test.js +25 -0
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -0
- package/dist/mcp/__tests__/code-intel-server.test.d.ts +2 -0
- package/dist/mcp/__tests__/code-intel-server.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/code-intel-server.test.js +43 -0
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -0
- package/dist/mcp/__tests__/memory-server.test.d.ts +2 -0
- package/dist/mcp/__tests__/memory-server.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/memory-server.test.js +34 -0
- package/dist/mcp/__tests__/memory-server.test.js.map +1 -0
- package/dist/mcp/__tests__/memory-validation.test.d.ts +2 -0
- package/dist/mcp/__tests__/memory-validation.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/memory-validation.test.js +29 -0
- package/dist/mcp/__tests__/memory-validation.test.js.map +1 -0
- package/dist/mcp/__tests__/path-traversal.test.js +55 -0
- package/dist/mcp/__tests__/path-traversal.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +43 -6
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server-ralph-phase.test.js +50 -0
- package/dist/mcp/__tests__/state-server-ralph-phase.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server-schema.test.js +3 -7
- package/dist/mcp/__tests__/state-server-schema.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +30 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/trace-server.test.js +58 -0
- package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +3 -0
- package/dist/mcp/bootstrap.d.ts.map +1 -0
- package/dist/mcp/bootstrap.js +13 -0
- package/dist/mcp/bootstrap.js.map +1 -0
- package/dist/mcp/code-intel-server.d.ts +8 -0
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +50 -24
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/memory-server.js +34 -13
- package/dist/mcp/memory-server.js.map +1 -1
- package/dist/mcp/memory-validation.d.ts +9 -0
- package/dist/mcp/memory-validation.d.ts.map +1 -0
- package/dist/mcp/memory-validation.js +11 -0
- package/dist/mcp/memory-validation.js.map +1 -0
- package/dist/mcp/state-paths.d.ts +2 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +83 -12
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +85 -47
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/mcp/trace-server.d.ts +16 -0
- package/dist/mcp/trace-server.d.ts.map +1 -1
- package/dist/mcp/trace-server.js +84 -24
- package/dist/mcp/trace-server.js.map +1 -1
- package/dist/modes/__tests__/base-ralph-contract.test.d.ts +2 -0
- package/dist/modes/__tests__/base-ralph-contract.test.d.ts.map +1 -0
- package/dist/modes/__tests__/base-ralph-contract.test.js +49 -0
- package/dist/modes/__tests__/base-ralph-contract.test.js.map +1 -0
- package/dist/modes/__tests__/base-tmux-pane.test.js +13 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts +0 -4
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +31 -11
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/config.test.js +47 -1
- package/dist/notifications/__tests__/config.test.js.map +1 -1
- package/dist/notifications/__tests__/formatter.test.js +54 -2
- package/dist/notifications/__tests__/formatter.test.js.map +1 -1
- package/dist/notifications/__tests__/hook-config.test.d.ts +5 -0
- package/dist/notifications/__tests__/hook-config.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/hook-config.test.js +139 -0
- package/dist/notifications/__tests__/hook-config.test.js.map +1 -0
- package/dist/notifications/__tests__/idle-cooldown.test.d.ts +5 -0
- package/dist/notifications/__tests__/idle-cooldown.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/idle-cooldown.test.js +100 -0
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -0
- package/dist/notifications/__tests__/notifier.test.js +89 -1
- package/dist/notifications/__tests__/notifier.test.js.map +1 -1
- package/dist/notifications/__tests__/reply-config.test.d.ts +2 -0
- package/dist/notifications/__tests__/reply-config.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/reply-config.test.js +79 -0
- package/dist/notifications/__tests__/reply-config.test.js.map +1 -0
- package/dist/notifications/__tests__/reply-listener.test.js +35 -1
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/session-registry.test.js +40 -0
- package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
- package/dist/notifications/__tests__/template-engine.test.d.ts +5 -0
- package/dist/notifications/__tests__/template-engine.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/template-engine.test.js +147 -0
- package/dist/notifications/__tests__/template-engine.test.js.map +1 -0
- package/dist/notifications/config.d.ts +8 -0
- package/dist/notifications/config.d.ts.map +1 -1
- package/dist/notifications/config.js +110 -19
- package/dist/notifications/config.js.map +1 -1
- package/dist/notifications/formatter.d.ts +5 -0
- package/dist/notifications/formatter.d.ts.map +1 -1
- package/dist/notifications/formatter.js +45 -10
- package/dist/notifications/formatter.js.map +1 -1
- package/dist/notifications/hook-config-types.d.ts +43 -0
- package/dist/notifications/hook-config-types.d.ts.map +1 -0
- package/dist/notifications/hook-config-types.js +8 -0
- package/dist/notifications/hook-config-types.js.map +1 -0
- package/dist/notifications/hook-config.d.ts +40 -0
- package/dist/notifications/hook-config.d.ts.map +1 -0
- package/dist/notifications/hook-config.js +127 -0
- package/dist/notifications/hook-config.js.map +1 -0
- package/dist/notifications/idle-cooldown.d.ts +35 -0
- package/dist/notifications/idle-cooldown.d.ts.map +1 -0
- package/dist/notifications/idle-cooldown.js +108 -0
- package/dist/notifications/idle-cooldown.js.map +1 -0
- package/dist/notifications/index.d.ts +3 -0
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +43 -1
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/notifier.d.ts +9 -0
- package/dist/notifications/notifier.d.ts.map +1 -1
- package/dist/notifications/notifier.js +36 -30
- package/dist/notifications/notifier.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts +3 -0
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +61 -7
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/session-registry.d.ts +1 -1
- package/dist/notifications/session-registry.d.ts.map +1 -1
- package/dist/notifications/session-registry.js +18 -6
- package/dist/notifications/session-registry.js.map +1 -1
- package/dist/notifications/template-engine.d.ts +34 -0
- package/dist/notifications/template-engine.d.ts.map +1 -0
- package/dist/notifications/template-engine.js +246 -0
- package/dist/notifications/template-engine.js.map +1 -0
- package/dist/notifications/types.d.ts +6 -0
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/openclaw/__tests__/config.test.d.ts +6 -0
- package/dist/openclaw/__tests__/config.test.d.ts.map +1 -0
- package/dist/openclaw/__tests__/config.test.js +174 -0
- package/dist/openclaw/__tests__/config.test.js.map +1 -0
- package/dist/openclaw/__tests__/dispatcher.test.d.ts +5 -0
- package/dist/openclaw/__tests__/dispatcher.test.d.ts.map +1 -0
- package/dist/openclaw/__tests__/dispatcher.test.js +104 -0
- package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -0
- package/dist/openclaw/__tests__/index.test.d.ts +6 -0
- package/dist/openclaw/__tests__/index.test.d.ts.map +1 -0
- package/dist/openclaw/__tests__/index.test.js +131 -0
- package/dist/openclaw/__tests__/index.test.js.map +1 -0
- package/dist/openclaw/config.d.ts +37 -0
- package/dist/openclaw/config.d.ts.map +1 -0
- package/dist/openclaw/config.js +106 -0
- package/dist/openclaw/config.js.map +1 -0
- package/dist/openclaw/dispatcher.d.ts +63 -0
- package/dist/openclaw/dispatcher.d.ts.map +1 -0
- package/dist/openclaw/dispatcher.js +223 -0
- package/dist/openclaw/dispatcher.js.map +1 -0
- package/dist/openclaw/index.d.ts +27 -0
- package/dist/openclaw/index.d.ts.map +1 -0
- package/dist/openclaw/index.js +130 -0
- package/dist/openclaw/index.js.map +1 -0
- package/dist/openclaw/types.d.ts +105 -0
- package/dist/openclaw/types.d.ts.map +1 -0
- package/dist/openclaw/types.js +12 -0
- package/dist/openclaw/types.js.map +1 -0
- package/dist/ralph/contract.d.ts.map +1 -1
- package/dist/ralph/contract.js +13 -4
- package/dist/ralph/contract.js.map +1 -1
- package/dist/team/__tests__/phase-controller.test.js +14 -0
- package/dist/team/__tests__/phase-controller.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +328 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.d.ts +2 -0
- package/dist/team/__tests__/scaling.test.d.ts.map +1 -0
- package/dist/team/__tests__/scaling.test.js +295 -0
- package/dist/team/__tests__/scaling.test.js.map +1 -0
- package/dist/team/__tests__/state.test.js +62 -1
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +219 -14
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +4 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/contracts.d.ts +14 -0
- package/dist/team/contracts.d.ts.map +1 -0
- package/dist/team/contracts.js +30 -0
- package/dist/team/contracts.js.map +1 -0
- package/dist/team/model-contract.d.ts +1 -0
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +5 -1
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/phase-controller.d.ts +2 -0
- package/dist/team/phase-controller.d.ts.map +1 -1
- package/dist/team/phase-controller.js +16 -2
- package/dist/team/phase-controller.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +356 -65
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +58 -0
- package/dist/team/scaling.d.ts.map +1 -0
- package/dist/team/scaling.js +319 -0
- package/dist/team/scaling.js.map +1 -0
- package/dist/team/state.d.ts +11 -2
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +97 -27
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +2 -0
- 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 +34 -5
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +177 -49
- 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 +20 -0
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +8 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -6
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/verifier.test.js +20 -1
- package/dist/verification/__tests__/verifier.test.js.map +1 -1
- package/dist/verification/verifier.d.ts +5 -0
- package/dist/verification/verifier.d.ts.map +1 -1
- package/dist/verification/verifier.js +19 -0
- package/dist/verification/verifier.js.map +1 -1
- package/package.json +2 -1
- package/prompts/architect.md +11 -0
- package/prompts/critic.md +14 -1
- package/prompts/planner.md +21 -0
- package/scripts/notify-hook/auto-nudge.js +80 -1
- package/scripts/notify-hook/payload-parser.js +21 -0
- package/scripts/notify-hook/team-worker.js +142 -0
- package/scripts/notify-hook/tmux-injection.js +3 -3
- package/scripts/notify-hook.js +55 -4
- package/skills/configure-notifications/SKILL.md +278 -0
- package/skills/configure-openclaw/SKILL.md +267 -0
- package/skills/configure-slack/SKILL.md +226 -0
- package/skills/omx-setup/SKILL.md +14 -19
- package/skills/plan/SKILL.md +57 -33
- package/skills/ralplan/SKILL.md +107 -21
- package/skills/team/SKILL.md +10 -1
- package/templates/AGENTS.md +11 -3
package/dist/team/runtime.js
CHANGED
|
@@ -2,18 +2,24 @@ import { join, resolve } from 'path';
|
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { readdir, readFile } from 'fs/promises';
|
|
4
4
|
import { performance } from 'perf_hooks';
|
|
5
|
-
import {
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
import { sanitizeTeamName, isTmuxAvailable, createTeamSession, buildWorkerProcessLaunchSpec, resolveTeamWorkerCli, resolveTeamWorkerCliPlan, resolveTeamWorkerLaunchMode, waitForWorkerReady, sendToWorker, sendToWorkerStdin, notifyLeaderStatus, isWorkerAlive, getWorkerPanePid, killWorker, killWorkerByPaneId, unregisterResizeHook, destroyTeamSession, listTeamSessions, } from './tmux-session.js';
|
|
6
7
|
import { teamInit as initTeamState, DEFAULT_MAX_WORKERS, teamReadConfig as readTeamConfig, teamWriteWorkerIdentity as writeWorkerIdentity, teamReadWorkerHeartbeat as readWorkerHeartbeat, teamReadWorkerStatus as readWorkerStatus, teamWriteWorkerInbox as writeWorkerInbox, teamCreateTask as createStateTask, teamReadTask as readTask, teamListTasks as listTasks, teamReadManifest as readTeamManifestV2, teamClaimTask as claimTask, teamReleaseTaskClaim as releaseTaskClaim, teamAppendEvent as appendTeamEvent, teamReadTaskApproval as readTaskApproval, teamListMailbox as listMailboxMessages, teamMarkMessageNotified as markMessageNotified, teamCleanup as cleanupTeamState, teamSaveConfig as saveTeamConfig, teamWriteShutdownRequest as writeShutdownRequest, teamReadShutdownAck as readShutdownAck, teamReadMonitorSnapshot as readMonitorSnapshot, teamWriteMonitorSnapshot as writeMonitorSnapshot, teamReadPhase as readTeamPhaseState, teamWritePhase as writeTeamPhaseState, } from './team-ops.js';
|
|
7
8
|
import { queueInboxInstruction, queueDirectMailboxMessage, queueBroadcastMailboxMessage, } from './mcp-comm.js';
|
|
8
9
|
import { generateWorkerOverlay, writeTeamWorkerInstructionsFile, removeTeamWorkerInstructionsFile, generateInitialInbox, generateTaskAssignmentInbox, generateShutdownInbox, generateTriggerMessage, generateMailboxTriggerMessage, } from './worker-bootstrap.js';
|
|
9
|
-
import { isLowComplexityAgentType, resolveTeamWorkerLaunchArgs, TEAM_LOW_COMPLEXITY_DEFAULT_MODEL, parseTeamWorkerLaunchArgs, splitWorkerLaunchArgs, } from './model-contract.js';
|
|
10
|
+
import { isLowComplexityAgentType, resolveTeamWorkerLaunchArgs, TEAM_LOW_COMPLEXITY_DEFAULT_MODEL, resolveTeamLowComplexityDefaultModel, parseTeamWorkerLaunchArgs, splitWorkerLaunchArgs, } from './model-contract.js';
|
|
10
11
|
import { inferPhaseTargetFromTaskCounts, reconcilePhaseStateForMonitor } from './phase-controller.js';
|
|
11
12
|
import { getTeamTmuxSessions } from '../notifications/tmux.js';
|
|
13
|
+
import { hasStructuredVerificationEvidence } from '../verification/verifier.js';
|
|
12
14
|
import { ensureWorktree, planWorktreeTarget, rollbackProvisionedWorktrees, } from './worktree.js';
|
|
13
15
|
const MODEL_INSTRUCTIONS_FILE_ENV = 'OMX_MODEL_INSTRUCTIONS_FILE';
|
|
14
16
|
const TEAM_STATE_ROOT_ENV = 'OMX_TEAM_STATE_ROOT';
|
|
15
17
|
const TEAM_LEADER_CWD_ENV = 'OMX_TEAM_LEADER_CWD';
|
|
18
|
+
const promptWorkerRegistry = new Map();
|
|
16
19
|
const previousModelInstructionsFileByTeam = new Map();
|
|
20
|
+
const PROMPT_WORKER_SIGTERM_WAIT_MS = 3_000;
|
|
21
|
+
const PROMPT_WORKER_SIGKILL_WAIT_MS = 2_000;
|
|
22
|
+
const PROMPT_WORKER_EXIT_POLL_MS = 100;
|
|
17
23
|
function resolveWorkerReadyTimeoutMs(env) {
|
|
18
24
|
const raw = env.OMX_TEAM_READY_TIMEOUT_MS;
|
|
19
25
|
const parsed = Number.parseInt(String(raw ?? ''), 10);
|
|
@@ -41,16 +47,149 @@ function restoreTeamModelInstructionsFile(teamName) {
|
|
|
41
47
|
}
|
|
42
48
|
delete process.env[MODEL_INSTRUCTIONS_FILE_ENV];
|
|
43
49
|
}
|
|
50
|
+
function registerPromptWorkerHandle(teamName, workerName, child) {
|
|
51
|
+
const { pid } = child;
|
|
52
|
+
if (!Number.isFinite(pid) || (pid ?? 0) < 1) {
|
|
53
|
+
throw new Error(`failed to spawn prompt worker process for ${workerName}`);
|
|
54
|
+
}
|
|
55
|
+
const processPid = pid;
|
|
56
|
+
const existingTeamHandles = promptWorkerRegistry.get(teamName) ?? new Map();
|
|
57
|
+
existingTeamHandles.set(workerName, { child, pid: processPid });
|
|
58
|
+
promptWorkerRegistry.set(teamName, existingTeamHandles);
|
|
59
|
+
child.on('exit', () => {
|
|
60
|
+
const teamHandles = promptWorkerRegistry.get(teamName);
|
|
61
|
+
if (!teamHandles)
|
|
62
|
+
return;
|
|
63
|
+
teamHandles.delete(workerName);
|
|
64
|
+
if (teamHandles.size === 0)
|
|
65
|
+
promptWorkerRegistry.delete(teamName);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function getPromptWorkerHandle(teamName, workerName) {
|
|
69
|
+
return promptWorkerRegistry.get(teamName)?.get(workerName) ?? null;
|
|
70
|
+
}
|
|
71
|
+
function removePromptWorkerHandle(teamName, workerName) {
|
|
72
|
+
const teamHandles = promptWorkerRegistry.get(teamName);
|
|
73
|
+
if (!teamHandles)
|
|
74
|
+
return;
|
|
75
|
+
teamHandles.delete(workerName);
|
|
76
|
+
if (teamHandles.size === 0)
|
|
77
|
+
promptWorkerRegistry.delete(teamName);
|
|
78
|
+
}
|
|
79
|
+
function isPidAlive(pid) {
|
|
80
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
81
|
+
return false;
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, 0);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function waitForPidExit(pid, timeoutMs) {
|
|
91
|
+
if (!isPidAlive(pid))
|
|
92
|
+
return true;
|
|
93
|
+
const deadline = Date.now() + Math.max(0, timeoutMs);
|
|
94
|
+
while (Date.now() < deadline) {
|
|
95
|
+
await new Promise((resolve) => setTimeout(resolve, PROMPT_WORKER_EXIT_POLL_MS));
|
|
96
|
+
if (!isPidAlive(pid))
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return !isPidAlive(pid);
|
|
100
|
+
}
|
|
101
|
+
async function teardownPromptWorker(teamName, workerName, fallbackPid, cwd, context) {
|
|
102
|
+
const handle = getPromptWorkerHandle(teamName, workerName);
|
|
103
|
+
const pid = Number.isFinite(handle?.pid)
|
|
104
|
+
? handle.pid
|
|
105
|
+
: (Number.isFinite(fallbackPid) && (fallbackPid ?? 0) > 0 ? fallbackPid : null);
|
|
106
|
+
if (pid === null) {
|
|
107
|
+
removePromptWorkerHandle(teamName, workerName);
|
|
108
|
+
return { terminated: true, forcedKill: false, pid: null };
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
if (handle && handle.child.exitCode === null && !handle.child.killed) {
|
|
112
|
+
handle.child.kill('SIGTERM');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
process.kill(pid, 'SIGTERM');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// Best effort.
|
|
120
|
+
}
|
|
121
|
+
const exitedOnTerm = await waitForPidExit(pid, PROMPT_WORKER_SIGTERM_WAIT_MS);
|
|
122
|
+
if (exitedOnTerm) {
|
|
123
|
+
removePromptWorkerHandle(teamName, workerName);
|
|
124
|
+
return { terminated: true, forcedKill: false, pid };
|
|
125
|
+
}
|
|
126
|
+
await appendTeamEvent(teamName, {
|
|
127
|
+
type: 'worker_stopped',
|
|
128
|
+
worker: workerName,
|
|
129
|
+
reason: `prompt_force_kill:${context}:pid=${pid}`,
|
|
130
|
+
}, cwd).catch(() => { });
|
|
131
|
+
try {
|
|
132
|
+
if (handle && handle.child.exitCode === null) {
|
|
133
|
+
handle.child.kill('SIGKILL');
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
process.kill(pid, 'SIGKILL');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Best effort.
|
|
141
|
+
}
|
|
142
|
+
const exitedOnKill = await waitForPidExit(pid, PROMPT_WORKER_SIGKILL_WAIT_MS);
|
|
143
|
+
if (!exitedOnKill) {
|
|
144
|
+
await appendTeamEvent(teamName, {
|
|
145
|
+
type: 'worker_stopped',
|
|
146
|
+
worker: workerName,
|
|
147
|
+
reason: `prompt_teardown_failed:${context}:pid=${pid}`,
|
|
148
|
+
}, cwd).catch(() => { });
|
|
149
|
+
return {
|
|
150
|
+
terminated: false,
|
|
151
|
+
forcedKill: true,
|
|
152
|
+
pid,
|
|
153
|
+
error: 'still_alive_after_sigkill',
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
removePromptWorkerHandle(teamName, workerName);
|
|
157
|
+
return { terminated: true, forcedKill: true, pid };
|
|
158
|
+
}
|
|
159
|
+
function isPromptWorkerAlive(config, worker) {
|
|
160
|
+
const handle = getPromptWorkerHandle(config.name, worker.name);
|
|
161
|
+
if (handle?.child.exitCode === null && !handle.child.killed)
|
|
162
|
+
return true;
|
|
163
|
+
if (!Number.isFinite(worker.pid) || (worker.pid ?? 0) <= 0)
|
|
164
|
+
return false;
|
|
165
|
+
try {
|
|
166
|
+
process.kill(worker.pid, 0);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
44
173
|
export { TEAM_LOW_COMPLEXITY_DEFAULT_MODEL };
|
|
45
174
|
export function resolveCanonicalTeamStateRoot(leaderCwd) {
|
|
46
175
|
return resolve(join(leaderCwd, '.omx', 'state'));
|
|
47
176
|
}
|
|
177
|
+
function spawnPromptWorker(teamName, workerName, workerIndex, workerCwd, launchArgs, workerEnv, workerCli) {
|
|
178
|
+
const processSpec = buildWorkerProcessLaunchSpec(teamName, workerIndex, launchArgs, workerCwd, workerEnv, workerCli);
|
|
179
|
+
const child = spawn(processSpec.command, processSpec.args, {
|
|
180
|
+
cwd: workerCwd,
|
|
181
|
+
env: { ...process.env, ...processSpec.env },
|
|
182
|
+
stdio: ['pipe', 'ignore', 'ignore'],
|
|
183
|
+
});
|
|
184
|
+
registerPromptWorkerHandle(teamName, workerName, child);
|
|
185
|
+
return child;
|
|
186
|
+
}
|
|
48
187
|
export function resolveWorkerLaunchArgsFromEnv(env, agentType, inheritedLeaderModel) {
|
|
49
188
|
const inheritedArgs = (typeof inheritedLeaderModel === 'string' && inheritedLeaderModel.trim() !== '')
|
|
50
189
|
? ['--model', inheritedLeaderModel.trim()]
|
|
51
190
|
: [];
|
|
52
191
|
const fallbackModel = isLowComplexityAgentType(agentType)
|
|
53
|
-
?
|
|
192
|
+
? resolveTeamLowComplexityDefaultModel(env.CODEX_HOME)
|
|
54
193
|
: undefined;
|
|
55
194
|
// Detect if an explicit reasoning override exists before resolving (for log source labelling)
|
|
56
195
|
const preEnvArgs = splitWorkerLaunchArgs(env.OMX_TEAM_WORKER_LAUNCH_ARGS);
|
|
@@ -110,13 +249,15 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
110
249
|
if (process.env.OMX_TEAM_WORKER) {
|
|
111
250
|
throw new Error('nested_team_disallowed');
|
|
112
251
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
252
|
+
const workerLaunchMode = resolveTeamWorkerLaunchMode(process.env);
|
|
253
|
+
const displayMode = workerLaunchMode === 'interactive' ? 'split_pane' : 'auto';
|
|
254
|
+
if (workerLaunchMode === 'interactive') {
|
|
255
|
+
if (!isTmuxAvailable()) {
|
|
256
|
+
throw new Error('Team mode requires tmux. Install with: apt install tmux / brew install tmux');
|
|
257
|
+
}
|
|
258
|
+
if (!process.env.TMUX) {
|
|
259
|
+
throw new Error('Team mode requires running inside tmux current leader pane');
|
|
260
|
+
}
|
|
120
261
|
}
|
|
121
262
|
const leaderCwd = resolve(cwd);
|
|
122
263
|
const sanitized = sanitizeTeamName(teamName);
|
|
@@ -167,11 +308,12 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
167
308
|
let createdLeaderPaneId;
|
|
168
309
|
let config = null;
|
|
169
310
|
const workerLaunchArgs = resolveWorkerLaunchArgsFromEnv(process.env, agentType);
|
|
311
|
+
const workerCliPlan = resolveTeamWorkerCliPlan(workerCount, workerLaunchArgs, process.env);
|
|
170
312
|
const workerReadyTimeoutMs = resolveWorkerReadyTimeoutMs(process.env);
|
|
171
313
|
const skipWorkerReadyWait = shouldSkipWorkerReadyWait(process.env);
|
|
172
314
|
try {
|
|
173
315
|
// 3. Init state directory + config
|
|
174
|
-
config = await initTeamState(sanitized, task, agentType, workerCount, leaderCwd, DEFAULT_MAX_WORKERS, { ...process.env, OMX_TEAM_DISPLAY_MODE: displayMode }, {
|
|
316
|
+
config = await initTeamState(sanitized, task, agentType, workerCount, leaderCwd, DEFAULT_MAX_WORKERS, { ...process.env, OMX_TEAM_DISPLAY_MODE: displayMode, OMX_TEAM_WORKER_LAUNCH_MODE: workerLaunchMode }, {
|
|
175
317
|
leader_cwd: leaderCwd,
|
|
176
318
|
team_state_root: teamStateRoot,
|
|
177
319
|
workspace_mode: workspaceMode,
|
|
@@ -216,23 +358,44 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
216
358
|
env,
|
|
217
359
|
};
|
|
218
360
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
361
|
+
const workerPaneIds = Array.from({ length: workerCount }, () => undefined);
|
|
362
|
+
// 6. Create worker runtime (interactive tmux panes or prompt-mode child processes)
|
|
363
|
+
if (workerLaunchMode === 'interactive') {
|
|
364
|
+
const createdSession = createTeamSession(sanitized, workerCount, leaderCwd, workerLaunchArgs, workerStartups);
|
|
365
|
+
sessionName = createdSession.name;
|
|
366
|
+
sessionCreated = true;
|
|
367
|
+
createdWorkerPaneIds.push(...createdSession.workerPaneIds);
|
|
368
|
+
createdLeaderPaneId = createdSession.leaderPaneId;
|
|
369
|
+
config.tmux_session = sessionName;
|
|
370
|
+
config.leader_pane_id = createdSession.leaderPaneId;
|
|
371
|
+
config.hud_pane_id = createdSession.hudPaneId;
|
|
372
|
+
config.resize_hook_name = createdSession.resizeHookName;
|
|
373
|
+
config.resize_hook_target = createdSession.resizeHookTarget;
|
|
374
|
+
for (let i = 0; i < createdSession.workerPaneIds.length; i++) {
|
|
375
|
+
workerPaneIds[i] = createdSession.workerPaneIds[i];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
config.tmux_session = `prompt-${sanitized}`;
|
|
380
|
+
config.leader_pane_id = null;
|
|
381
|
+
config.hud_pane_id = null;
|
|
382
|
+
config.resize_hook_name = null;
|
|
383
|
+
config.resize_hook_target = null;
|
|
384
|
+
for (let i = 1; i <= workerCount; i++) {
|
|
385
|
+
const startup = workerStartups[i - 1] || {};
|
|
386
|
+
const workerName = `worker-${i}`;
|
|
387
|
+
const child = spawnPromptWorker(sanitized, workerName, i, startup.cwd || leaderCwd, workerLaunchArgs, startup.env || {}, workerCliPlan[i - 1]);
|
|
388
|
+
if (config.workers[i - 1]) {
|
|
389
|
+
config.workers[i - 1].pid = child.pid;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
230
393
|
await saveTeamConfig(config, leaderCwd);
|
|
231
|
-
// 7. Wait for all workers to be ready, then bootstrap them
|
|
394
|
+
// 7. Wait for all workers to be ready (interactive mode), then bootstrap them
|
|
232
395
|
const allTasks = await listTasks(sanitized, leaderCwd);
|
|
233
396
|
for (let i = 1; i <= workerCount; i++) {
|
|
234
397
|
const workerName = `worker-${i}`;
|
|
235
|
-
const paneId =
|
|
398
|
+
const paneId = workerPaneIds[i - 1];
|
|
236
399
|
const workerWorkspace = workerWorkspaceByName.get(workerName) ?? { cwd: leaderCwd };
|
|
237
400
|
// Get tasks assigned to this worker
|
|
238
401
|
const workerTasks = allTasks.filter(t => t.owner === workerName);
|
|
@@ -241,6 +404,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
241
404
|
name: workerName,
|
|
242
405
|
index: i,
|
|
243
406
|
role: agentType,
|
|
407
|
+
worker_cli: workerCliPlan[i - 1],
|
|
244
408
|
assigned_tasks: workerTasks.map(t => t.id),
|
|
245
409
|
working_dir: workerWorkspace.cwd,
|
|
246
410
|
worktree_path: workerWorkspace.worktreePath,
|
|
@@ -248,14 +412,20 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
248
412
|
worktree_detached: workerWorkspace.worktreeDetached,
|
|
249
413
|
team_state_root: teamStateRoot,
|
|
250
414
|
};
|
|
251
|
-
// Get pane PID and store it
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
415
|
+
// Get pane PID and store it (interactive mode) or process PID (prompt mode)
|
|
416
|
+
if (workerLaunchMode === 'interactive') {
|
|
417
|
+
const panePid = getWorkerPanePid(sessionName, i);
|
|
418
|
+
if (panePid)
|
|
419
|
+
identity.pid = panePid;
|
|
420
|
+
}
|
|
421
|
+
else if (config.workers[i - 1]?.pid) {
|
|
422
|
+
identity.pid = config.workers[i - 1].pid;
|
|
423
|
+
}
|
|
255
424
|
if (paneId)
|
|
256
425
|
identity.pane_id = paneId;
|
|
257
426
|
if (config.workers[i - 1]) {
|
|
258
427
|
config.workers[i - 1].pane_id = paneId;
|
|
428
|
+
config.workers[i - 1].worker_cli = workerCliPlan[i - 1];
|
|
259
429
|
config.workers[i - 1].working_dir = workerWorkspace.cwd;
|
|
260
430
|
config.workers[i - 1].worktree_path = workerWorkspace.worktreePath;
|
|
261
431
|
config.workers[i - 1].worktree_branch = workerWorkspace.worktreeBranch;
|
|
@@ -264,7 +434,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
264
434
|
}
|
|
265
435
|
await writeWorkerIdentity(sanitized, workerName, identity, leaderCwd);
|
|
266
436
|
// Wait for worker readiness
|
|
267
|
-
if (!skipWorkerReadyWait) {
|
|
437
|
+
if (workerLaunchMode === 'interactive' && !skipWorkerReadyWait) {
|
|
268
438
|
const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);
|
|
269
439
|
if (!ready) {
|
|
270
440
|
throw new Error(`Worker ${workerName} did not become ready in tmux session ${sessionName}`);
|
|
@@ -347,6 +517,18 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
347
517
|
}
|
|
348
518
|
}
|
|
349
519
|
}
|
|
520
|
+
if (workerLaunchMode === 'prompt' && config) {
|
|
521
|
+
const promptTeardownFailures = [];
|
|
522
|
+
for (const worker of config.workers) {
|
|
523
|
+
const teardown = await teardownPromptWorker(sanitized, worker.name, worker.pid, leaderCwd, 'startup_rollback');
|
|
524
|
+
if (!teardown.terminated) {
|
|
525
|
+
promptTeardownFailures.push(`${worker.name}:${teardown.error || 'unknown_error'}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (promptTeardownFailures.length > 0) {
|
|
529
|
+
rollbackErrors.push(`promptTeardown:${promptTeardownFailures.join(',')}`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
350
532
|
if (workerInstructionsPath) {
|
|
351
533
|
try {
|
|
352
534
|
await removeTeamWorkerInstructionsFile(sanitized, leaderCwd);
|
|
@@ -406,7 +588,9 @@ export async function monitorTeam(teamName, cwd) {
|
|
|
406
588
|
const recommendations = [];
|
|
407
589
|
const workerScanStartMs = performance.now();
|
|
408
590
|
const workerSignals = await Promise.all(config.workers.map(async (worker) => {
|
|
409
|
-
const alive =
|
|
591
|
+
const alive = config.worker_launch_mode === 'prompt'
|
|
592
|
+
? isPromptWorkerAlive(config, worker)
|
|
593
|
+
: isWorkerAlive(sessionName, worker.index, worker.pane_id);
|
|
410
594
|
const [status, heartbeat] = await Promise.all([
|
|
411
595
|
readWorkerStatus(sanitized, worker.name, cwd),
|
|
412
596
|
readWorkerHeartbeat(sanitized, worker.name, cwd),
|
|
@@ -458,9 +642,19 @@ export async function monitorTeam(teamName, cwd) {
|
|
|
458
642
|
completed: allTasks.filter(t => t.status === 'completed').length,
|
|
459
643
|
failed: allTasks.filter(t => t.status === 'failed').length,
|
|
460
644
|
};
|
|
645
|
+
const verificationPendingTasks = allTasks.filter((task) => task.status === 'completed'
|
|
646
|
+
&& task.requires_code_change === true
|
|
647
|
+
&& !hasStructuredVerificationEvidence(task.result));
|
|
648
|
+
if (verificationPendingTasks.length > 0) {
|
|
649
|
+
for (const task of verificationPendingTasks) {
|
|
650
|
+
recommendations.push(`Verification evidence missing for task-${task.id}; require structured PASS/FAIL evidence before terminal success`);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
461
653
|
const allTasksTerminal = taskCounts.pending === 0 && taskCounts.blocked === 0 && taskCounts.in_progress === 0;
|
|
462
654
|
const persistedPhase = await readTeamPhaseState(sanitized, cwd);
|
|
463
|
-
const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts
|
|
655
|
+
const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts, {
|
|
656
|
+
verificationPending: verificationPendingTasks.length > 0,
|
|
657
|
+
});
|
|
464
658
|
const phaseState = reconcilePhaseStateForMonitor(persistedPhase, targetPhase);
|
|
465
659
|
await writeTeamPhaseState(sanitized, phaseState, cwd);
|
|
466
660
|
const phase = phaseState.current_phase;
|
|
@@ -597,6 +791,27 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
597
791
|
restoreTeamModelInstructionsFile(sanitized);
|
|
598
792
|
return;
|
|
599
793
|
}
|
|
794
|
+
if (!force) {
|
|
795
|
+
const allTasks = await listTasks(sanitized, cwd);
|
|
796
|
+
const gate = {
|
|
797
|
+
total: allTasks.length,
|
|
798
|
+
pending: allTasks.filter((t) => t.status === 'pending').length,
|
|
799
|
+
blocked: allTasks.filter((t) => t.status === 'blocked').length,
|
|
800
|
+
in_progress: allTasks.filter((t) => t.status === 'in_progress').length,
|
|
801
|
+
completed: allTasks.filter((t) => t.status === 'completed').length,
|
|
802
|
+
failed: allTasks.filter((t) => t.status === 'failed').length,
|
|
803
|
+
allowed: false,
|
|
804
|
+
};
|
|
805
|
+
gate.allowed = gate.pending === 0 && gate.blocked === 0 && gate.in_progress === 0 && gate.failed === 0;
|
|
806
|
+
await appendTeamEvent(sanitized, {
|
|
807
|
+
type: 'shutdown_gate',
|
|
808
|
+
worker: 'leader-fixed',
|
|
809
|
+
reason: `allowed=${gate.allowed} total=${gate.total} pending=${gate.pending} blocked=${gate.blocked} in_progress=${gate.in_progress} completed=${gate.completed} failed=${gate.failed}`,
|
|
810
|
+
}, cwd).catch(() => { });
|
|
811
|
+
if (!gate.allowed) {
|
|
812
|
+
throw new Error(`shutdown_gate_blocked:pending=${gate.pending},blocked=${gate.blocked},in_progress=${gate.in_progress},failed=${gate.failed}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
600
815
|
const sessionName = config.tmux_session;
|
|
601
816
|
const shutdownRequestTimes = new Map();
|
|
602
817
|
// 1. Send shutdown inbox to each worker
|
|
@@ -646,13 +861,17 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
646
861
|
const detail = rejected.map(r => `${r.worker}:${r.reason}`).join(',');
|
|
647
862
|
throw new Error(`shutdown_rejected:${detail}`);
|
|
648
863
|
}
|
|
649
|
-
const anyAlive = config.workers.some(w =>
|
|
864
|
+
const anyAlive = config.workers.some((w) => (config.worker_launch_mode === 'prompt'
|
|
865
|
+
? isPromptWorkerAlive(config, w)
|
|
866
|
+
: isWorkerAlive(sessionName, w.index, w.pane_id)));
|
|
650
867
|
if (!anyAlive)
|
|
651
868
|
break;
|
|
652
869
|
// Sleep 2s
|
|
653
870
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
654
871
|
}
|
|
655
|
-
const anyAliveAfterWait = config.workers.some(w =>
|
|
872
|
+
const anyAliveAfterWait = config.workers.some((w) => (config.worker_launch_mode === 'prompt'
|
|
873
|
+
? isPromptWorkerAlive(config, w)
|
|
874
|
+
: isWorkerAlive(sessionName, w.index, w.pane_id)));
|
|
656
875
|
if (anyAliveAfterWait && !force) {
|
|
657
876
|
// Workers may have accepted shutdown but not exited (Codex TUI requires explicit exit).
|
|
658
877
|
// In this case, proceed to force kill panes (next step) rather than failing and leaving state around.
|
|
@@ -660,38 +879,57 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
660
879
|
// 3. Force kill remaining workers
|
|
661
880
|
const leaderPaneId = config.leader_pane_id;
|
|
662
881
|
const hudPaneId = config.hud_pane_id;
|
|
663
|
-
if (config.
|
|
664
|
-
|
|
665
|
-
if (
|
|
666
|
-
const
|
|
667
|
-
const
|
|
668
|
-
if (
|
|
669
|
-
|
|
882
|
+
if (config.worker_launch_mode === 'interactive') {
|
|
883
|
+
let resizeHookWarning = null;
|
|
884
|
+
if (config.resize_hook_name && config.resize_hook_target) {
|
|
885
|
+
const resizeHookName = config.resize_hook_name;
|
|
886
|
+
const unregistered = unregisterResizeHook(config.resize_hook_target, resizeHookName);
|
|
887
|
+
if (!unregistered && isTmuxAvailable()) {
|
|
888
|
+
const baseSession = sessionName.split(':')[0];
|
|
889
|
+
const sessionStillActive = listTeamSessions().includes(baseSession);
|
|
890
|
+
if (sessionStillActive) {
|
|
891
|
+
resizeHookWarning = `failed to unregister resize hook ${resizeHookName}`;
|
|
892
|
+
}
|
|
670
893
|
}
|
|
671
894
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
895
|
+
config.resize_hook_name = null;
|
|
896
|
+
config.resize_hook_target = null;
|
|
897
|
+
await saveTeamConfig(config, cwd);
|
|
898
|
+
if (resizeHookWarning) {
|
|
899
|
+
console.warn(`[team shutdown] ${sanitized}: ${resizeHookWarning}; continuing teardown`);
|
|
900
|
+
}
|
|
901
|
+
for (const w of config.workers) {
|
|
902
|
+
try {
|
|
903
|
+
// Guard: never kill the leader's own pane or the HUD pane.
|
|
904
|
+
if (leaderPaneId && w.pane_id === leaderPaneId)
|
|
905
|
+
continue;
|
|
906
|
+
if (hudPaneId && w.pane_id === hudPaneId)
|
|
907
|
+
continue;
|
|
908
|
+
if (isWorkerAlive(sessionName, w.index, w.pane_id)) {
|
|
909
|
+
killWorker(sessionName, w.index, w.pane_id, leaderPaneId ?? undefined);
|
|
910
|
+
}
|
|
685
911
|
}
|
|
912
|
+
catch { /* ignore */ }
|
|
913
|
+
}
|
|
914
|
+
// 4. Destroy tmux session
|
|
915
|
+
if (!sessionName.includes(':')) {
|
|
916
|
+
try {
|
|
917
|
+
destroyTeamSession(sessionName);
|
|
918
|
+
}
|
|
919
|
+
catch { /* ignore */ }
|
|
686
920
|
}
|
|
687
|
-
catch { /* ignore */ }
|
|
688
921
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
922
|
+
else {
|
|
923
|
+
const promptTeardownFailures = [];
|
|
924
|
+
for (const w of config.workers) {
|
|
925
|
+
const teardown = await teardownPromptWorker(sanitized, w.name, w.pid, cwd, 'shutdown');
|
|
926
|
+
if (!teardown.terminated) {
|
|
927
|
+
promptTeardownFailures.push(`${w.name}:${teardown.error || 'unknown_error'}`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (promptTeardownFailures.length > 0) {
|
|
931
|
+
throw new Error(`shutdown_prompt_teardown_failed:${promptTeardownFailures.join(',')}`);
|
|
693
932
|
}
|
|
694
|
-
catch { /* ignore */ }
|
|
695
933
|
}
|
|
696
934
|
// 5. Remove team-scoped worker instructions file (no mutation of project AGENTS.md)
|
|
697
935
|
try {
|
|
@@ -710,11 +948,40 @@ export async function resumeTeam(teamName, cwd) {
|
|
|
710
948
|
const config = await readTeamConfig(sanitized, cwd);
|
|
711
949
|
if (!config)
|
|
712
950
|
return null;
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
951
|
+
if (config.worker_launch_mode === 'prompt') {
|
|
952
|
+
const hasLivePromptWorker = config.workers.some((worker) => isPromptWorkerAlive(config, worker));
|
|
953
|
+
if (!hasLivePromptWorker)
|
|
954
|
+
return null;
|
|
955
|
+
const missingHandles = config.workers
|
|
956
|
+
.filter((worker) => {
|
|
957
|
+
if (!Number.isFinite(worker.pid) || (worker.pid ?? 0) <= 0)
|
|
958
|
+
return false;
|
|
959
|
+
try {
|
|
960
|
+
process.kill(worker.pid, 0);
|
|
961
|
+
return true;
|
|
962
|
+
}
|
|
963
|
+
catch {
|
|
964
|
+
return false;
|
|
965
|
+
}
|
|
966
|
+
})
|
|
967
|
+
.filter((worker) => !getPromptWorkerHandle(sanitized, worker.name));
|
|
968
|
+
if (missingHandles.length > 0) {
|
|
969
|
+
const detail = missingHandles.map((worker) => `${worker.name}:${worker.pid ?? 'unknown'}`).join(',');
|
|
970
|
+
await appendTeamEvent(sanitized, {
|
|
971
|
+
type: 'worker_stopped',
|
|
972
|
+
worker: 'leader-fixed',
|
|
973
|
+
reason: `prompt_resume_unavailable:missing_handle:${detail}`,
|
|
974
|
+
}, cwd).catch(() => { });
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
// Check if tmux session still exists
|
|
980
|
+
const baseSession = config.tmux_session.split(':')[0];
|
|
981
|
+
const teamSessions = getTeamTmuxSessions(sanitized);
|
|
982
|
+
if (!teamSessions.includes(baseSession))
|
|
983
|
+
return null;
|
|
984
|
+
}
|
|
718
985
|
return {
|
|
719
986
|
teamName: sanitized,
|
|
720
987
|
sanitizedName: sanitized,
|
|
@@ -738,12 +1005,21 @@ async function findActiveTeams(cwd, leaderSessionId) {
|
|
|
738
1005
|
const manifest = await readTeamManifestV2(teamName, cwd);
|
|
739
1006
|
if (manifest?.policy?.one_team_per_leader_session === false)
|
|
740
1007
|
continue;
|
|
1008
|
+
const workerLaunchMode = cfg?.worker_launch_mode
|
|
1009
|
+
?? manifest?.policy?.worker_launch_mode
|
|
1010
|
+
?? 'interactive';
|
|
741
1011
|
const tmuxSession = (manifest?.tmux_session || cfg?.tmux_session || `omx-team-${teamName}`).split(':')[0];
|
|
742
1012
|
if (leaderSessionId) {
|
|
743
1013
|
const ownerSessionId = manifest?.leader?.session_id?.trim() ?? '';
|
|
744
1014
|
if (ownerSessionId && ownerSessionId !== leaderSessionId)
|
|
745
1015
|
continue;
|
|
746
1016
|
}
|
|
1017
|
+
if (workerLaunchMode === 'prompt') {
|
|
1018
|
+
if ((cfg?.workers ?? []).some((worker) => isPromptWorkerAlive(cfg, worker))) {
|
|
1019
|
+
active.push(teamName);
|
|
1020
|
+
}
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
747
1023
|
if (sessions.has(tmuxSession))
|
|
748
1024
|
active.push(teamName);
|
|
749
1025
|
}
|
|
@@ -813,10 +1089,25 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previous, cwd)
|
|
|
813
1089
|
}
|
|
814
1090
|
}
|
|
815
1091
|
function notifyWorker(config, workerIndex, message, workerPaneId) {
|
|
1092
|
+
const worker = config.workers.find((candidate) => candidate.index === workerIndex);
|
|
1093
|
+
if (!worker)
|
|
1094
|
+
return false;
|
|
1095
|
+
if (config.worker_launch_mode === 'prompt') {
|
|
1096
|
+
const handle = getPromptWorkerHandle(config.name, worker.name);
|
|
1097
|
+
if (!handle)
|
|
1098
|
+
return false;
|
|
1099
|
+
try {
|
|
1100
|
+
sendToWorkerStdin(handle.child.stdin, message);
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
catch {
|
|
1104
|
+
return false;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
816
1107
|
if (!config.tmux_session || !isTmuxAvailable())
|
|
817
1108
|
return false;
|
|
818
1109
|
try {
|
|
819
|
-
sendToWorker(config.tmux_session, workerIndex, message, workerPaneId);
|
|
1110
|
+
sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
|
|
820
1111
|
return true;
|
|
821
1112
|
}
|
|
822
1113
|
catch {
|