oh-my-codex 0.6.4 → 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 +156 -8
- 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 +353 -66
- 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 +10 -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 +18 -4
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +110 -32
- 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/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);
|
|
@@ -172,7 +313,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
172
313
|
const skipWorkerReadyWait = shouldSkipWorkerReadyWait(process.env);
|
|
173
314
|
try {
|
|
174
315
|
// 3. Init state directory + config
|
|
175
|
-
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 }, {
|
|
176
317
|
leader_cwd: leaderCwd,
|
|
177
318
|
team_state_root: teamStateRoot,
|
|
178
319
|
workspace_mode: workspaceMode,
|
|
@@ -217,23 +358,44 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
217
358
|
env,
|
|
218
359
|
};
|
|
219
360
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
+
}
|
|
231
393
|
await saveTeamConfig(config, leaderCwd);
|
|
232
|
-
// 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
|
|
233
395
|
const allTasks = await listTasks(sanitized, leaderCwd);
|
|
234
396
|
for (let i = 1; i <= workerCount; i++) {
|
|
235
397
|
const workerName = `worker-${i}`;
|
|
236
|
-
const paneId =
|
|
398
|
+
const paneId = workerPaneIds[i - 1];
|
|
237
399
|
const workerWorkspace = workerWorkspaceByName.get(workerName) ?? { cwd: leaderCwd };
|
|
238
400
|
// Get tasks assigned to this worker
|
|
239
401
|
const workerTasks = allTasks.filter(t => t.owner === workerName);
|
|
@@ -250,10 +412,15 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
250
412
|
worktree_detached: workerWorkspace.worktreeDetached,
|
|
251
413
|
team_state_root: teamStateRoot,
|
|
252
414
|
};
|
|
253
|
-
// Get pane PID and store it
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
+
}
|
|
257
424
|
if (paneId)
|
|
258
425
|
identity.pane_id = paneId;
|
|
259
426
|
if (config.workers[i - 1]) {
|
|
@@ -267,7 +434,7 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
267
434
|
}
|
|
268
435
|
await writeWorkerIdentity(sanitized, workerName, identity, leaderCwd);
|
|
269
436
|
// Wait for worker readiness
|
|
270
|
-
if (!skipWorkerReadyWait) {
|
|
437
|
+
if (workerLaunchMode === 'interactive' && !skipWorkerReadyWait) {
|
|
271
438
|
const ready = waitForWorkerReady(sessionName, i, workerReadyTimeoutMs, paneId);
|
|
272
439
|
if (!ready) {
|
|
273
440
|
throw new Error(`Worker ${workerName} did not become ready in tmux session ${sessionName}`);
|
|
@@ -350,6 +517,18 @@ export async function startTeam(teamName, task, agentType, workerCount, tasks, c
|
|
|
350
517
|
}
|
|
351
518
|
}
|
|
352
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
|
+
}
|
|
353
532
|
if (workerInstructionsPath) {
|
|
354
533
|
try {
|
|
355
534
|
await removeTeamWorkerInstructionsFile(sanitized, leaderCwd);
|
|
@@ -409,7 +588,9 @@ export async function monitorTeam(teamName, cwd) {
|
|
|
409
588
|
const recommendations = [];
|
|
410
589
|
const workerScanStartMs = performance.now();
|
|
411
590
|
const workerSignals = await Promise.all(config.workers.map(async (worker) => {
|
|
412
|
-
const alive =
|
|
591
|
+
const alive = config.worker_launch_mode === 'prompt'
|
|
592
|
+
? isPromptWorkerAlive(config, worker)
|
|
593
|
+
: isWorkerAlive(sessionName, worker.index, worker.pane_id);
|
|
413
594
|
const [status, heartbeat] = await Promise.all([
|
|
414
595
|
readWorkerStatus(sanitized, worker.name, cwd),
|
|
415
596
|
readWorkerHeartbeat(sanitized, worker.name, cwd),
|
|
@@ -461,9 +642,19 @@ export async function monitorTeam(teamName, cwd) {
|
|
|
461
642
|
completed: allTasks.filter(t => t.status === 'completed').length,
|
|
462
643
|
failed: allTasks.filter(t => t.status === 'failed').length,
|
|
463
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
|
+
}
|
|
464
653
|
const allTasksTerminal = taskCounts.pending === 0 && taskCounts.blocked === 0 && taskCounts.in_progress === 0;
|
|
465
654
|
const persistedPhase = await readTeamPhaseState(sanitized, cwd);
|
|
466
|
-
const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts
|
|
655
|
+
const targetPhase = inferPhaseTargetFromTaskCounts(taskCounts, {
|
|
656
|
+
verificationPending: verificationPendingTasks.length > 0,
|
|
657
|
+
});
|
|
467
658
|
const phaseState = reconcilePhaseStateForMonitor(persistedPhase, targetPhase);
|
|
468
659
|
await writeTeamPhaseState(sanitized, phaseState, cwd);
|
|
469
660
|
const phase = phaseState.current_phase;
|
|
@@ -600,6 +791,27 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
600
791
|
restoreTeamModelInstructionsFile(sanitized);
|
|
601
792
|
return;
|
|
602
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
|
+
}
|
|
603
815
|
const sessionName = config.tmux_session;
|
|
604
816
|
const shutdownRequestTimes = new Map();
|
|
605
817
|
// 1. Send shutdown inbox to each worker
|
|
@@ -649,13 +861,17 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
649
861
|
const detail = rejected.map(r => `${r.worker}:${r.reason}`).join(',');
|
|
650
862
|
throw new Error(`shutdown_rejected:${detail}`);
|
|
651
863
|
}
|
|
652
|
-
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)));
|
|
653
867
|
if (!anyAlive)
|
|
654
868
|
break;
|
|
655
869
|
// Sleep 2s
|
|
656
870
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
657
871
|
}
|
|
658
|
-
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)));
|
|
659
875
|
if (anyAliveAfterWait && !force) {
|
|
660
876
|
// Workers may have accepted shutdown but not exited (Codex TUI requires explicit exit).
|
|
661
877
|
// In this case, proceed to force kill panes (next step) rather than failing and leaving state around.
|
|
@@ -663,38 +879,57 @@ export async function shutdownTeam(teamName, cwd, options = {}) {
|
|
|
663
879
|
// 3. Force kill remaining workers
|
|
664
880
|
const leaderPaneId = config.leader_pane_id;
|
|
665
881
|
const hudPaneId = config.hud_pane_id;
|
|
666
|
-
if (config.
|
|
667
|
-
|
|
668
|
-
if (
|
|
669
|
-
const
|
|
670
|
-
const
|
|
671
|
-
if (
|
|
672
|
-
|
|
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
|
+
}
|
|
673
893
|
}
|
|
674
894
|
}
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
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
|
+
}
|
|
688
911
|
}
|
|
912
|
+
catch { /* ignore */ }
|
|
913
|
+
}
|
|
914
|
+
// 4. Destroy tmux session
|
|
915
|
+
if (!sessionName.includes(':')) {
|
|
916
|
+
try {
|
|
917
|
+
destroyTeamSession(sessionName);
|
|
918
|
+
}
|
|
919
|
+
catch { /* ignore */ }
|
|
689
920
|
}
|
|
690
|
-
catch { /* ignore */ }
|
|
691
921
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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(',')}`);
|
|
696
932
|
}
|
|
697
|
-
catch { /* ignore */ }
|
|
698
933
|
}
|
|
699
934
|
// 5. Remove team-scoped worker instructions file (no mutation of project AGENTS.md)
|
|
700
935
|
try {
|
|
@@ -713,11 +948,40 @@ export async function resumeTeam(teamName, cwd) {
|
|
|
713
948
|
const config = await readTeamConfig(sanitized, cwd);
|
|
714
949
|
if (!config)
|
|
715
950
|
return null;
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
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
|
+
}
|
|
721
985
|
return {
|
|
722
986
|
teamName: sanitized,
|
|
723
987
|
sanitizedName: sanitized,
|
|
@@ -741,12 +1005,21 @@ async function findActiveTeams(cwd, leaderSessionId) {
|
|
|
741
1005
|
const manifest = await readTeamManifestV2(teamName, cwd);
|
|
742
1006
|
if (manifest?.policy?.one_team_per_leader_session === false)
|
|
743
1007
|
continue;
|
|
1008
|
+
const workerLaunchMode = cfg?.worker_launch_mode
|
|
1009
|
+
?? manifest?.policy?.worker_launch_mode
|
|
1010
|
+
?? 'interactive';
|
|
744
1011
|
const tmuxSession = (manifest?.tmux_session || cfg?.tmux_session || `omx-team-${teamName}`).split(':')[0];
|
|
745
1012
|
if (leaderSessionId) {
|
|
746
1013
|
const ownerSessionId = manifest?.leader?.session_id?.trim() ?? '';
|
|
747
1014
|
if (ownerSessionId && ownerSessionId !== leaderSessionId)
|
|
748
1015
|
continue;
|
|
749
1016
|
}
|
|
1017
|
+
if (workerLaunchMode === 'prompt') {
|
|
1018
|
+
if ((cfg?.workers ?? []).some((worker) => isPromptWorkerAlive(cfg, worker))) {
|
|
1019
|
+
active.push(teamName);
|
|
1020
|
+
}
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
750
1023
|
if (sessions.has(tmuxSession))
|
|
751
1024
|
active.push(teamName);
|
|
752
1025
|
}
|
|
@@ -816,11 +1089,25 @@ async function emitMonitorDerivedEvents(teamName, tasks, workers, previous, cwd)
|
|
|
816
1089
|
}
|
|
817
1090
|
}
|
|
818
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
|
+
}
|
|
819
1107
|
if (!config.tmux_session || !isTmuxAvailable())
|
|
820
1108
|
return false;
|
|
821
1109
|
try {
|
|
822
|
-
|
|
823
|
-
sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, workerCli);
|
|
1110
|
+
sendToWorker(config.tmux_session, workerIndex, message, workerPaneId, worker.worker_cli);
|
|
824
1111
|
return true;
|
|
825
1112
|
}
|
|
826
1113
|
catch {
|