agent-tempo 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +213 -0
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/assets/icon-32.png +0 -0
- package/assets/icon-512.png +0 -0
- package/assets/icon-64.png +0 -0
- package/assets/icon-dark-32.png +0 -0
- package/assets/icon-dark-64.png +0 -0
- package/assets/icon-dark.svg +9 -0
- package/assets/icon.svg +9 -0
- package/assets/logo-dark.svg +11 -0
- package/assets/logo-light.svg +11 -0
- package/dashboard/README.md +91 -0
- package/dashboard/dist/assets/index-CB78ToNE.css +2 -0
- package/dashboard/dist/assets/index-_5jV0Znu.js +62 -0
- package/dashboard/dist/assets/index-_5jV0Znu.js.map +1 -0
- package/dashboard/dist/index.html +21 -0
- package/dashboard/package.json +47 -0
- package/dist/activities/hard-terminate.d.ts +32 -0
- package/dist/activities/hard-terminate.js +460 -0
- package/dist/activities/maestro.d.ts +72 -0
- package/dist/activities/maestro.js +254 -0
- package/dist/activities/outbox.d.ts +188 -0
- package/dist/activities/outbox.js +849 -0
- package/dist/activities/resolve.d.ts +64 -0
- package/dist/activities/resolve.js +129 -0
- package/dist/activities/schedule-fire.d.ts +36 -0
- package/dist/activities/schedule-fire.js +147 -0
- package/dist/adapters/base.d.ts +426 -0
- package/dist/adapters/base.js +1270 -0
- package/dist/adapters/claude-api/adapter.d.ts +168 -0
- package/dist/adapters/claude-api/adapter.js +797 -0
- package/dist/adapters/claude-api/api-error.d.ts +96 -0
- package/dist/adapters/claude-api/api-error.js +191 -0
- package/dist/adapters/claude-api/index.d.ts +16 -0
- package/dist/adapters/claude-api/index.js +21 -0
- package/dist/adapters/claude-api/mcp-bridge.d.ts +50 -0
- package/dist/adapters/claude-api/mcp-bridge.js +157 -0
- package/dist/adapters/claude-code/adapter.d.ts +133 -0
- package/dist/adapters/claude-code/adapter.js +274 -0
- package/dist/adapters/claude-code/index.d.ts +15 -0
- package/dist/adapters/claude-code/index.js +20 -0
- package/dist/adapters/claude-code-headless/adapter.d.ts +131 -0
- package/dist/adapters/claude-code-headless/adapter.js +710 -0
- package/dist/adapters/claude-code-headless/error-mapper.d.ts +107 -0
- package/dist/adapters/claude-code-headless/error-mapper.js +281 -0
- package/dist/adapters/claude-code-headless/index.d.ts +17 -0
- package/dist/adapters/claude-code-headless/index.js +26 -0
- package/dist/adapters/claude-code-headless/pre-flight.d.ts +51 -0
- package/dist/adapters/claude-code-headless/pre-flight.js +207 -0
- package/dist/adapters/claude-code-headless/prompt.d.ts +93 -0
- package/dist/adapters/claude-code-headless/prompt.js +79 -0
- package/dist/adapters/claude-code-headless/stream-json.d.ts +242 -0
- package/dist/adapters/claude-code-headless/stream-json.js +208 -0
- package/dist/adapters/claude-code-headless/types.d.ts +28 -0
- package/dist/adapters/claude-code-headless/types.js +36 -0
- package/dist/adapters/copilot/adapter.d.ts +100 -0
- package/dist/adapters/copilot/adapter.js +730 -0
- package/dist/adapters/copilot/index.d.ts +15 -0
- package/dist/adapters/copilot/index.js +20 -0
- package/dist/adapters/index.d.ts +42 -0
- package/dist/adapters/index.js +115 -0
- package/dist/adapters/opencode/adapter.d.ts +82 -0
- package/dist/adapters/opencode/adapter.js +710 -0
- package/dist/adapters/opencode/config.d.ts +90 -0
- package/dist/adapters/opencode/config.js +137 -0
- package/dist/adapters/opencode/helpers.d.ts +40 -0
- package/dist/adapters/opencode/helpers.js +144 -0
- package/dist/adapters/opencode/index.d.ts +12 -0
- package/dist/adapters/opencode/index.js +17 -0
- package/dist/adapters/opencode/server-bridge.d.ts +124 -0
- package/dist/adapters/opencode/server-bridge.js +216 -0
- package/dist/adapters/sdk/base.d.ts +95 -0
- package/dist/adapters/sdk/base.js +134 -0
- package/dist/adapters/sdk/system-prompt.d.ts +64 -0
- package/dist/adapters/sdk/system-prompt.js +78 -0
- package/dist/adapters/terminal-error.d.ts +27 -0
- package/dist/adapters/terminal-error.js +39 -0
- package/dist/channel.d.ts +3 -0
- package/dist/channel.js +48 -0
- package/dist/cli/commands.d.ts +245 -0
- package/dist/cli/commands.js +2438 -0
- package/dist/cli/config-command.d.ts +8 -0
- package/dist/cli/config-command.js +254 -0
- package/dist/cli/daemon-command.d.ts +57 -0
- package/dist/cli/daemon-command.js +493 -0
- package/dist/cli/daemon.d.ts +217 -0
- package/dist/cli/daemon.js +632 -0
- package/dist/cli/dashboard-command.d.ts +20 -0
- package/dist/cli/dashboard-command.js +241 -0
- package/dist/cli/dev-banner.d.ts +107 -0
- package/dist/cli/dev-banner.js +190 -0
- package/dist/cli/dev-mode-bootstrap.d.ts +29 -0
- package/dist/cli/dev-mode-bootstrap.js +36 -0
- package/dist/cli/dev-verbs.d.ts +43 -0
- package/dist/cli/dev-verbs.js +254 -0
- package/dist/cli/help-text.d.ts +1 -0
- package/dist/cli/help-text.js +158 -0
- package/dist/cli/legacy-migration.d.ts +35 -0
- package/dist/cli/legacy-migration.js +335 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +63 -0
- package/dist/cli/output.d.ts +12 -0
- package/dist/cli/output.js +37 -0
- package/dist/cli/preflight.d.ts +9 -0
- package/dist/cli/preflight.js +96 -0
- package/dist/cli/removed-verbs.d.ts +9 -0
- package/dist/cli/removed-verbs.js +78 -0
- package/dist/cli/sa-preflight.d.ts +99 -0
- package/dist/cli/sa-preflight.js +183 -0
- package/dist/cli/scenarios-command.d.ts +6 -0
- package/dist/cli/scenarios-command.js +167 -0
- package/dist/cli/startup.d.ts +112 -0
- package/dist/cli/startup.js +641 -0
- package/dist/cli/upgrade-command.d.ts +5 -0
- package/dist/cli/upgrade-command.js +240 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +680 -0
- package/dist/client/core.d.ts +33 -0
- package/dist/client/core.js +1260 -0
- package/dist/client/ensure-conductor-spawned.d.ts +35 -0
- package/dist/client/ensure-conductor-spawned.js +48 -0
- package/dist/client/index.d.ts +32 -0
- package/dist/client/index.js +22 -0
- package/dist/client/interface.d.ts +461 -0
- package/dist/client/interface.js +2 -0
- package/dist/client/subscribe.d.ts +108 -0
- package/dist/client/subscribe.js +598 -0
- package/dist/client/with-spawn.d.ts +27 -0
- package/dist/client/with-spawn.js +87 -0
- package/dist/config.d.ts +323 -0
- package/dist/config.js +593 -0
- package/dist/connection.d.ts +7 -0
- package/dist/connection.js +46 -0
- package/dist/constants.d.ts +50 -0
- package/dist/constants.js +74 -0
- package/dist/copilot-bridge.d.ts +22 -0
- package/dist/copilot-bridge.js +565 -0
- package/dist/daemon-adapter-versions.d.ts +52 -0
- package/dist/daemon-adapter-versions.js +170 -0
- package/dist/daemon.d.ts +275 -0
- package/dist/daemon.js +989 -0
- package/dist/ensemble/agent-types.d.ts +23 -0
- package/dist/ensemble/agent-types.js +132 -0
- package/dist/ensemble/loader.d.ts +14 -0
- package/dist/ensemble/loader.js +140 -0
- package/dist/ensemble/saver.d.ts +49 -0
- package/dist/ensemble/saver.js +201 -0
- package/dist/ensemble/schema.d.ts +71 -0
- package/dist/ensemble/schema.js +3 -0
- package/dist/git-info.d.ts +4 -0
- package/dist/git-info.js +29 -0
- package/dist/http/aggregate.d.ts +319 -0
- package/dist/http/aggregate.js +684 -0
- package/dist/http/auth.d.ts +67 -0
- package/dist/http/auth.js +177 -0
- package/dist/http/body.d.ts +71 -0
- package/dist/http/body.js +121 -0
- package/dist/http/catalog.d.ts +67 -0
- package/dist/http/catalog.js +209 -0
- package/dist/http/cors.d.ts +42 -0
- package/dist/http/cors.js +111 -0
- package/dist/http/dashboard-pair.d.ts +94 -0
- package/dist/http/dashboard-pair.js +148 -0
- package/dist/http/dashboard.d.ts +20 -0
- package/dist/http/dashboard.js +160 -0
- package/dist/http/event-bus.d.ts +217 -0
- package/dist/http/event-bus.js +365 -0
- package/dist/http/event-id.d.ts +77 -0
- package/dist/http/event-id.js +117 -0
- package/dist/http/event-types.d.ts +348 -0
- package/dist/http/event-types.js +36 -0
- package/dist/http/fixtures/chat-stress.d.ts +8 -0
- package/dist/http/fixtures/chat-stress.js +63 -0
- package/dist/http/fixtures/conductor-leaving.d.ts +8 -0
- package/dist/http/fixtures/conductor-leaving.js +80 -0
- package/dist/http/fixtures/constants.d.ts +10 -0
- package/dist/http/fixtures/constants.js +13 -0
- package/dist/http/fixtures/eight-player-broadcast.d.ts +10 -0
- package/dist/http/fixtures/eight-player-broadcast.js +81 -0
- package/dist/http/fixtures/empty-ensemble.d.ts +6 -0
- package/dist/http/fixtures/empty-ensemble.js +26 -0
- package/dist/http/fixtures/index.d.ts +55 -0
- package/dist/http/fixtures/index.js +110 -0
- package/dist/http/fixtures/single-conductor.d.ts +7 -0
- package/dist/http/fixtures/single-conductor.js +46 -0
- package/dist/http/fixtures/sse-reconnect.d.ts +8 -0
- package/dist/http/fixtures/sse-reconnect.js +77 -0
- package/dist/http/index.d.ts +21 -0
- package/dist/http/index.js +61 -0
- package/dist/http/port-file.d.ts +22 -0
- package/dist/http/port-file.js +132 -0
- package/dist/http/responses.d.ts +27 -0
- package/dist/http/responses.js +40 -0
- package/dist/http/ring-buffer.d.ts +41 -0
- package/dist/http/ring-buffer.js +80 -0
- package/dist/http/server.d.ts +122 -0
- package/dist/http/server.js +459 -0
- package/dist/http/snapshot.d.ts +85 -0
- package/dist/http/snapshot.js +180 -0
- package/dist/http/sse-handler.d.ts +87 -0
- package/dist/http/sse-handler.js +294 -0
- package/dist/http/writes.d.ts +55 -0
- package/dist/http/writes.js +240 -0
- package/dist/palette/index.d.ts +138 -0
- package/dist/palette/index.js +221 -0
- package/dist/reconcile/orphans.d.ts +255 -0
- package/dist/reconcile/orphans.js +340 -0
- package/dist/scripts/258-spotcheck.js +303 -0
- package/dist/scripts/check-components-css-sync.js +199 -0
- package/dist/scripts/run-shard.js +121 -0
- package/dist/scripts/verify-daemon-isolation-guard.js +128 -0
- package/dist/server-tools.d.ts +87 -0
- package/dist/server-tools.js +146 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +366 -0
- package/dist/spawn.d.ts +296 -0
- package/dist/spawn.js +747 -0
- package/dist/tools/agent-types.d.ts +2 -0
- package/dist/tools/agent-types.js +21 -0
- package/dist/tools/attachment-info.d.ts +4 -0
- package/dist/tools/attachment-info.js +48 -0
- package/dist/tools/broadcast.d.ts +4 -0
- package/dist/tools/broadcast.js +76 -0
- package/dist/tools/cancel-stage.d.ts +3 -0
- package/dist/tools/cancel-stage.js +20 -0
- package/dist/tools/clear-state.d.ts +3 -0
- package/dist/tools/clear-state.js +37 -0
- package/dist/tools/coat-check-evict.d.ts +4 -0
- package/dist/tools/coat-check-evict.js +43 -0
- package/dist/tools/coat-check-get.d.ts +4 -0
- package/dist/tools/coat-check-get.js +56 -0
- package/dist/tools/coat-check-list.d.ts +4 -0
- package/dist/tools/coat-check-list.js +60 -0
- package/dist/tools/coat-check-put.d.ts +4 -0
- package/dist/tools/coat-check-put.js +53 -0
- package/dist/tools/cue.d.ts +44 -0
- package/dist/tools/cue.js +201 -0
- package/dist/tools/destroy.d.ts +4 -0
- package/dist/tools/destroy.js +188 -0
- package/dist/tools/detach.d.ts +4 -0
- package/dist/tools/detach.js +45 -0
- package/dist/tools/encore.d.ts +4 -0
- package/dist/tools/encore.js +31 -0
- package/dist/tools/ensemble.d.ts +32 -0
- package/dist/tools/ensemble.js +198 -0
- package/dist/tools/evaluate-gate.d.ts +3 -0
- package/dist/tools/evaluate-gate.js +32 -0
- package/dist/tools/fetch-state.d.ts +13 -0
- package/dist/tools/fetch-state.js +78 -0
- package/dist/tools/gates.d.ts +3 -0
- package/dist/tools/gates.js +41 -0
- package/dist/tools/helpers.d.ts +21 -0
- package/dist/tools/helpers.js +25 -0
- package/dist/tools/hosts.d.ts +4 -0
- package/dist/tools/hosts.js +40 -0
- package/dist/tools/listen.d.ts +3 -0
- package/dist/tools/listen.js +22 -0
- package/dist/tools/load-lineup.d.ts +5 -0
- package/dist/tools/load-lineup.js +381 -0
- package/dist/tools/migrate.d.ts +4 -0
- package/dist/tools/migrate.js +60 -0
- package/dist/tools/pause-ensemble.d.ts +4 -0
- package/dist/tools/pause-ensemble.js +58 -0
- package/dist/tools/pause.d.ts +4 -0
- package/dist/tools/pause.js +36 -0
- package/dist/tools/play.d.ts +4 -0
- package/dist/tools/play.js +57 -0
- package/dist/tools/quality-gate.d.ts +3 -0
- package/dist/tools/quality-gate.js +26 -0
- package/dist/tools/recall.d.ts +3 -0
- package/dist/tools/recall.js +32 -0
- package/dist/tools/recruit.d.ts +38 -0
- package/dist/tools/recruit.js +447 -0
- package/dist/tools/release.d.ts +4 -0
- package/dist/tools/release.js +98 -0
- package/dist/tools/report.d.ts +3 -0
- package/dist/tools/report.js +29 -0
- package/dist/tools/resolve.d.ts +1 -0
- package/dist/tools/resolve.js +7 -0
- package/dist/tools/restart.d.ts +35 -0
- package/dist/tools/restart.js +131 -0
- package/dist/tools/restore.d.ts +4 -0
- package/dist/tools/restore.js +107 -0
- package/dist/tools/resume-ensemble.d.ts +4 -0
- package/dist/tools/resume-ensemble.js +79 -0
- package/dist/tools/save-lineup.d.ts +4 -0
- package/dist/tools/save-lineup.js +36 -0
- package/dist/tools/save-state.d.ts +3 -0
- package/dist/tools/save-state.js +57 -0
- package/dist/tools/schedule.d.ts +4 -0
- package/dist/tools/schedule.js +152 -0
- package/dist/tools/schedules.d.ts +4 -0
- package/dist/tools/schedules.js +54 -0
- package/dist/tools/set-ensemble-description.d.ts +4 -0
- package/dist/tools/set-ensemble-description.js +37 -0
- package/dist/tools/set-name.d.ts +4 -0
- package/dist/tools/set-name.js +45 -0
- package/dist/tools/set-part.d.ts +3 -0
- package/dist/tools/set-part.js +20 -0
- package/dist/tools/shutdown.d.ts +4 -0
- package/dist/tools/shutdown.js +54 -0
- package/dist/tools/stage.d.ts +3 -0
- package/dist/tools/stage.js +28 -0
- package/dist/tools/stages.d.ts +3 -0
- package/dist/tools/stages.js +35 -0
- package/dist/tools/stop.d.ts +4 -0
- package/dist/tools/stop.js +29 -0
- package/dist/tools/unschedule.d.ts +4 -0
- package/dist/tools/unschedule.js +35 -0
- package/dist/tools/who-am-i.d.ts +3 -0
- package/dist/tools/who-am-i.js +34 -0
- package/dist/tools/worktree.d.ts +4 -0
- package/dist/tools/worktree.js +181 -0
- package/dist/tui/App.d.ts +85 -0
- package/dist/tui/App.js +1791 -0
- package/dist/tui/bootstrap-types.d.ts +46 -0
- package/dist/tui/bootstrap-types.js +7 -0
- package/dist/tui/client.d.ts +6 -0
- package/dist/tui/client.js +9 -0
- package/dist/tui/commands.d.ts +71 -0
- package/dist/tui/commands.js +1375 -0
- package/dist/tui/components/ActivityLog.d.ts +16 -0
- package/dist/tui/components/ActivityLog.js +36 -0
- package/dist/tui/components/ChatView.d.ts +35 -0
- package/dist/tui/components/ChatView.js +54 -0
- package/dist/tui/components/CommandOverlay.d.ts +15 -0
- package/dist/tui/components/CommandOverlay.js +34 -0
- package/dist/tui/components/CommandPalette.d.ts +21 -0
- package/dist/tui/components/CommandPalette.js +67 -0
- package/dist/tui/components/ConductorChat.d.ts +16 -0
- package/dist/tui/components/ConductorChat.js +32 -0
- package/dist/tui/components/ConversationStream.d.ts +114 -0
- package/dist/tui/components/ConversationStream.js +307 -0
- package/dist/tui/components/CreateEnsembleWizard.d.ts +19 -0
- package/dist/tui/components/CreateEnsembleWizard.js +223 -0
- package/dist/tui/components/DestroyConfirmModal.d.ts +17 -0
- package/dist/tui/components/DestroyConfirmModal.js +62 -0
- package/dist/tui/components/EnsembleListView.d.ts +14 -0
- package/dist/tui/components/EnsembleListView.js +32 -0
- package/dist/tui/components/EnsemblePanel.d.ts +12 -0
- package/dist/tui/components/EnsemblePanel.js +40 -0
- package/dist/tui/components/ErrorView.d.ts +31 -0
- package/dist/tui/components/ErrorView.js +129 -0
- package/dist/tui/components/HomeView.d.ts +54 -0
- package/dist/tui/components/HomeView.js +306 -0
- package/dist/tui/components/InputBar.d.ts +13 -0
- package/dist/tui/components/InputBar.js +58 -0
- package/dist/tui/components/LoadLineupModal.d.ts +18 -0
- package/dist/tui/components/LoadLineupModal.js +79 -0
- package/dist/tui/components/MainView.d.ts +21 -0
- package/dist/tui/components/MainView.js +107 -0
- package/dist/tui/components/NewEnsembleModal.d.ts +9 -0
- package/dist/tui/components/NewEnsembleModal.js +73 -0
- package/dist/tui/components/Picker.d.ts +23 -0
- package/dist/tui/components/Picker.js +70 -0
- package/dist/tui/components/PlayerDetailView.d.ts +26 -0
- package/dist/tui/components/PlayerDetailView.js +118 -0
- package/dist/tui/components/PromptArea.d.ts +50 -0
- package/dist/tui/components/PromptArea.js +303 -0
- package/dist/tui/components/RecruitWizard.d.ts +17 -0
- package/dist/tui/components/RecruitWizard.js +221 -0
- package/dist/tui/components/RestoreConfirmModal.d.ts +18 -0
- package/dist/tui/components/RestoreConfirmModal.js +71 -0
- package/dist/tui/components/ScheduleOverlay.d.ts +13 -0
- package/dist/tui/components/ScheduleOverlay.js +113 -0
- package/dist/tui/components/ScheduleWizard.d.ts +19 -0
- package/dist/tui/components/ScheduleWizard.js +259 -0
- package/dist/tui/components/Splash.d.ts +23 -0
- package/dist/tui/components/Splash.js +221 -0
- package/dist/tui/components/StatusBar.d.ts +48 -0
- package/dist/tui/components/StatusBar.js +128 -0
- package/dist/tui/components/StatusOverlay.d.ts +15 -0
- package/dist/tui/components/StatusOverlay.js +76 -0
- package/dist/tui/components/TitleBar.d.ts +10 -0
- package/dist/tui/components/TitleBar.js +21 -0
- package/dist/tui/components/TopBar.d.ts +12 -0
- package/dist/tui/components/TopBar.js +15 -0
- package/dist/tui/core-api.d.ts +26 -0
- package/dist/tui/core-api.js +67 -0
- package/dist/tui/hooks/useEnsembleDiscovery.d.ts +3 -0
- package/dist/tui/hooks/useEnsembleDiscovery.js +30 -0
- package/dist/tui/hooks/useMaestroPoller.d.ts +3 -0
- package/dist/tui/hooks/useMaestroPoller.js +36 -0
- package/dist/tui/hooks/useSendCommand.d.ts +7 -0
- package/dist/tui/hooks/useSendCommand.js +29 -0
- package/dist/tui/index.d.ts +15 -0
- package/dist/tui/index.js +156 -0
- package/dist/tui/ink-context.d.ts +18 -0
- package/dist/tui/ink-context.js +59 -0
- package/dist/tui/ink-loader.d.ts +26 -0
- package/dist/tui/ink-loader.js +42 -0
- package/dist/tui/removed-commands.d.ts +9 -0
- package/dist/tui/removed-commands.js +22 -0
- package/dist/tui/sse-handler.d.ts +52 -0
- package/dist/tui/sse-handler.js +157 -0
- package/dist/tui/store.d.ts +598 -0
- package/dist/tui/store.js +753 -0
- package/dist/tui/utils/format.d.ts +56 -0
- package/dist/tui/utils/format.js +155 -0
- package/dist/tui/utils/fullscreen.d.ts +23 -0
- package/dist/tui/utils/fullscreen.js +71 -0
- package/dist/tui/utils/history.d.ts +10 -0
- package/dist/tui/utils/history.js +85 -0
- package/dist/tui/utils/platform.d.ts +45 -0
- package/dist/tui/utils/platform.js +258 -0
- package/dist/tui/utils/theme.d.ts +21 -0
- package/dist/tui/utils/theme.js +24 -0
- package/dist/types.d.ts +1020 -0
- package/dist/types.js +39 -0
- package/dist/utils/attachment-format.d.ts +22 -0
- package/dist/utils/attachment-format.js +32 -0
- package/dist/utils/default-part.d.ts +43 -0
- package/dist/utils/default-part.js +104 -0
- package/dist/utils/duration.d.ts +30 -0
- package/dist/utils/duration.js +69 -0
- package/dist/utils/ensemble-ops.d.ts +61 -0
- package/dist/utils/ensemble-ops.js +77 -0
- package/dist/utils/format-hosts.d.ts +21 -0
- package/dist/utils/format-hosts.js +73 -0
- package/dist/utils/hosts.d.ts +113 -0
- package/dist/utils/hosts.js +265 -0
- package/dist/utils/parent-death-watchdog.d.ts +1 -0
- package/dist/utils/parent-death-watchdog.js +47 -0
- package/dist/utils/query-timeout.d.ts +103 -0
- package/dist/utils/query-timeout.js +113 -0
- package/dist/utils/recall-format.d.ts +78 -0
- package/dist/utils/recall-format.js +105 -0
- package/dist/utils/restore-format.d.ts +49 -0
- package/dist/utils/restore-format.js +91 -0
- package/dist/utils/safe-path.d.ts +10 -0
- package/dist/utils/safe-path.js +43 -0
- package/dist/utils/sdk-probe.d.ts +9 -0
- package/dist/utils/sdk-probe.js +45 -0
- package/dist/utils/search-attributes.d.ts +76 -0
- package/dist/utils/search-attributes.js +86 -0
- package/dist/utils/validation.d.ts +113 -0
- package/dist/utils/validation.js +163 -0
- package/dist/utils/visibility-deadline.d.ts +186 -0
- package/dist/utils/visibility-deadline.js +158 -0
- package/dist/utils/worktree.d.ts +103 -0
- package/dist/utils/worktree.js +327 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.js +146 -0
- package/dist/workflows/attachment-math.d.ts +56 -0
- package/dist/workflows/attachment-math.js +47 -0
- package/dist/workflows/index.d.ts +3 -0
- package/dist/workflows/index.js +11 -0
- package/dist/workflows/maestro-signals.d.ts +217 -0
- package/dist/workflows/maestro-signals.js +155 -0
- package/dist/workflows/maestro.d.ts +3 -0
- package/dist/workflows/maestro.js +812 -0
- package/dist/workflows/scheduler-signals.d.ts +10 -0
- package/dist/workflows/scheduler-signals.js +14 -0
- package/dist/workflows/scheduler.d.ts +17 -0
- package/dist/workflows/scheduler.js +143 -0
- package/dist/workflows/session.d.ts +2 -0
- package/dist/workflows/session.js +1638 -0
- package/dist/workflows/signals.d.ts +297 -0
- package/dist/workflows/signals.js +239 -0
- package/examples/agents/tempo-composer.md +56 -0
- package/examples/agents/tempo-conductor.md +117 -0
- package/examples/agents/tempo-critic.md +73 -0
- package/examples/agents/tempo-improv.md +74 -0
- package/examples/agents/tempo-liner.md +75 -0
- package/examples/agents/tempo-roadie.md +61 -0
- package/examples/agents/tempo-soloist.md +71 -0
- package/examples/agents/tempo-tuner.md +94 -0
- package/examples/ensembles/tempo-big-band.yaml +146 -0
- package/examples/ensembles/tempo-dev-team.yaml +58 -0
- package/examples/ensembles/tempo-headless-jam.yaml +77 -0
- package/examples/ensembles/tempo-jam-session.yaml +41 -0
- package/examples/ensembles/tempo-mock-jam.yaml +79 -0
- package/examples/ensembles/tempo-review-squad.yaml +32 -0
- package/package.json +172 -0
- package/packaging/launchd/com.agent.tempo.plist +46 -0
- package/packaging/systemd/agent-tempo.service +32 -0
- package/packaging/windows/install-task.ps1 +71 -0
- package/scenarios/conductor-recruit-mock.yaml +33 -0
- package/scenarios/echo-roundtrip.yaml +15 -0
- package/scenarios/multi-player-handoff.yaml +38 -0
- package/scenarios/recruit-cascade.yaml +38 -0
- package/scenarios/two-player-conversation.yaml +33 -0
- package/workflow-bundle.js +14146 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerRecallTool = registerRecallTool;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const helpers_1 = require("./helpers");
|
|
6
|
+
const recall_format_1 = require("../utils/recall-format");
|
|
7
|
+
function registerRecallTool(server, handle, getPlayerId) {
|
|
8
|
+
(0, helpers_1.defineTool)(server, 'recall', 'Read your own message history. Shows received messages by default; use includeSent to also see outgoing messages. `offset` pages the timeline; `previewLength` truncates bodies (unset = full text).', {
|
|
9
|
+
limit: zod_1.z.number().min(1).max(100).optional().describe('Max messages to return (default 20, max 100)'),
|
|
10
|
+
offset: zod_1.z.number().int().min(0).optional().describe('Skip N messages for paging (default 0)'),
|
|
11
|
+
previewLength: zod_1.z.number().int().min(1).optional().describe('Truncate each body to N chars + "…"; omit for full text'),
|
|
12
|
+
since: zod_1.z.string().optional().describe('Only show messages at or after this ISO timestamp'),
|
|
13
|
+
from: zod_1.z.string().optional().describe('Filter received messages by sender name'),
|
|
14
|
+
includeSent: zod_1.z.boolean().optional().describe('Include sent messages in the timeline (default: false)'),
|
|
15
|
+
}, async (args) => {
|
|
16
|
+
const { limit, offset, previewLength, since, from: fromFilter, includeSent } = args;
|
|
17
|
+
// Validate `since` up-front so the formatter never sees a garbage date.
|
|
18
|
+
if (since && Number.isNaN(Date.parse(since))) {
|
|
19
|
+
return (0, helpers_1.fail)(`Invalid ISO timestamp for "since": ${since}`);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const received = await handle.query('allMessages');
|
|
23
|
+
const sent = includeSent ? await handle.query('allSentMessages') : [];
|
|
24
|
+
const timeline = (0, recall_format_1.buildTimeline)(received, sent, Boolean(includeSent));
|
|
25
|
+
const rendered = (0, recall_format_1.formatRecall)(timeline, { limit, offset, previewLength, since, from: fromFilter });
|
|
26
|
+
return (0, helpers_1.ok)(rendered.text);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return (0, helpers_1.fail)(`Failed to recall messages: ${(0, helpers_1.formatError)(err)}`);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { Client, WorkflowHandle } from '@temporalio/client';
|
|
3
|
+
import { Config } from '../config';
|
|
4
|
+
import { AgentType } from '../types';
|
|
5
|
+
import type { HostInfo } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* #274 M15 — dep-injection surface on the recruit tool registrar.
|
|
8
|
+
*
|
|
9
|
+
* `listHostsFn` lets tests stub host discovery without touching
|
|
10
|
+
* `TestWorkflowEnvironment` internals. Production callers (`src/server.ts`)
|
|
11
|
+
* omit this; the default pulls in `src/utils/hosts.ts` lazily.
|
|
12
|
+
*/
|
|
13
|
+
export interface RegisterRecruitToolDeps {
|
|
14
|
+
listHostsFn?: (client: Client) => Promise<HostInfo[]>;
|
|
15
|
+
}
|
|
16
|
+
export declare function registerRecruitTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle, ownAgentType?: AgentType, deps?: RegisterRecruitToolDeps): void;
|
|
17
|
+
/**
|
|
18
|
+
* Given a host liveness+profile snapshot, validate that `targetHost` is
|
|
19
|
+
* (a) known, (b) recruit-ready, and (c) advertises support for
|
|
20
|
+
* `requestedAgent`. Returns a user-facing error string when validation
|
|
21
|
+
* fails, or `null` when all checks pass.
|
|
22
|
+
*
|
|
23
|
+
* Profile-missing is treated as a soft pass (the daemon is alive but
|
|
24
|
+
* didn't signal a profile, e.g. rolling-upgrade scenario or global
|
|
25
|
+
* maestro was unreachable at its boot). We fall through rather than
|
|
26
|
+
* fail — M10 / AC11c says operational UX beats strict enforcement when
|
|
27
|
+
* information is genuinely absent.
|
|
28
|
+
*
|
|
29
|
+
* Exported for unit testing per the M15 dep-injection pattern.
|
|
30
|
+
*/
|
|
31
|
+
export declare function checkHostPreflight(hosts: HostInfo[], targetHost: string, requestedAgent: AgentType): string | null;
|
|
32
|
+
/**
|
|
33
|
+
* Pick the closest hostname to `target` by Levenshtein distance. Returns
|
|
34
|
+
* `null` when `candidates` is empty OR the nearest match is so distant
|
|
35
|
+
* (edit distance ≥ half the longer string's length) that suggesting it
|
|
36
|
+
* would be misleading. Exported for tests.
|
|
37
|
+
*/
|
|
38
|
+
export declare function nearestHostname(target: string, candidates: string[]): string | null;
|
|
@@ -0,0 +1,447 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.registerRecruitTool = registerRecruitTool;
|
|
37
|
+
exports.checkHostPreflight = checkHostPreflight;
|
|
38
|
+
exports.nearestHostname = nearestHostname;
|
|
39
|
+
const zod_1 = require("zod");
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const config_1 = require("../config");
|
|
42
|
+
const types_1 = require("../types");
|
|
43
|
+
const resolve_1 = require("./resolve");
|
|
44
|
+
const signals_1 = require("../workflows/signals");
|
|
45
|
+
const helpers_1 = require("./helpers");
|
|
46
|
+
const agent_types_1 = require("../ensemble/agent-types");
|
|
47
|
+
const validation_1 = require("../utils/validation");
|
|
48
|
+
const sdk_probe_1 = require("../utils/sdk-probe");
|
|
49
|
+
const pre_flight_1 = require("../adapters/claude-code-headless/pre-flight");
|
|
50
|
+
const types_2 = require("../adapters/claude-code-headless/types");
|
|
51
|
+
const toolLog = (...args) => console.error('[agent-tempo:recruit]', ...args);
|
|
52
|
+
/**
|
|
53
|
+
* #449 Phase C — check whether the `opencode` binary is on PATH. Used by
|
|
54
|
+
* the recruit pre-flight to fail fast with an actionable error before the
|
|
55
|
+
* adapter spawn activity runs and hits ENOENT mid-flight.
|
|
56
|
+
*
|
|
57
|
+
* Cross-platform: `where` on Windows, `command -v` on POSIX. Both exit 0
|
|
58
|
+
* when found, non-zero otherwise; stdout/stderr captured silently.
|
|
59
|
+
*/
|
|
60
|
+
function hasOpencodeOnPath() {
|
|
61
|
+
try {
|
|
62
|
+
const cmd = process.platform === 'win32' ? 'where' : 'command';
|
|
63
|
+
const args = process.platform === 'win32' ? ['opencode'] : ['-v', 'opencode'];
|
|
64
|
+
const result = (0, child_process_1.spawnSync)(cmd, args, {
|
|
65
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
66
|
+
shell: process.platform !== 'win32', // POSIX needs the shell built-in
|
|
67
|
+
});
|
|
68
|
+
return result.status === 0;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function registerRecruitTool(server, client, config, getPlayerId, handle, ownAgentType = 'claude', deps = {}) {
|
|
75
|
+
// Lazy default — only imports utils/hosts when actually called, so the
|
|
76
|
+
// MCP server's module load graph doesn't drag the whole join layer
|
|
77
|
+
// into every consumer at import time.
|
|
78
|
+
const listHostsFn = deps.listHostsFn ?? (async (c) => {
|
|
79
|
+
const { listHosts } = await Promise.resolve().then(() => __importStar(require('../utils/hosts')));
|
|
80
|
+
return listHosts(c, {
|
|
81
|
+
force: true, // pre-flight wants "right now" — no cached snapshot
|
|
82
|
+
namespace: config.temporalNamespace,
|
|
83
|
+
taskQueue: config.taskQueue,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
(0, helpers_1.defineTool)(server, 'recruit', `Start a new named session in a directory. Rejects if the name is already active. Supports Claude Code or Copilot CLI agents. Defaults to "${ownAgentType}" (same as this session).`, {
|
|
87
|
+
workDir: zod_1.z.string().max(validation_1.PATH_MAX).describe('The working directory for the new session'),
|
|
88
|
+
name: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('Name for the new session'),
|
|
89
|
+
conductor: zod_1.z.boolean().optional()
|
|
90
|
+
.describe('Whether this session is a conductor (default: false)'),
|
|
91
|
+
initialMessage: zod_1.z.string().max(validation_1.MESSAGE_MAX).optional()
|
|
92
|
+
.describe('Optional task or message for the new session (sent after it sets its name)'),
|
|
93
|
+
agent: zod_1.z.enum(types_1.AGENT_TYPES).optional()
|
|
94
|
+
.describe(`Which agent to use (default: "${ownAgentType}", same as this session). "mock" requires dev mode (--dev). "claude-api" runs headless via the Anthropic Messages API — requires ANTHROPIC_API_KEY env var and the @anthropic-ai/sdk optional dependency installed; has access to agent-tempo MCP tools (cue, report, recall, ensemble, …) but NOT file-edit or shell tools (use "claude" for those). "opencode" runs headless via a local opencode serve subprocess; multi-provider (Anthropic, OpenAI, Bedrock, Ollama, …) — requires the @opencode-ai/sdk optional dep and an opencode binary on PATH. opencode players ARE file-op-capable (file edits / shell / web search via OpenCode's built-in tools). "claude-code-headless" runs the official Claude Code CLI as a headless per-turn \`claude -p\` subprocess — requires the \`claude\` binary on PATH AND a logged-in Claude Code session (\`claude auth login\`); turns bill against the host's existing subscription extra-usage credits, NOT a Console API key. claude-code-headless players have full Claude Code tool access (Bash, Read, Write, Edit, Glob, Grep, WebSearch, WebFetch).`),
|
|
95
|
+
model: zod_1.z.string().regex(/^[a-z0-9][a-z0-9-/.:_]*$/).optional()
|
|
96
|
+
.describe('Model id. For "claude-api": bare Anthropic id (e.g. "claude-opus-4-7"). For "opencode": combined "provider/model" (e.g. "anthropic/claude-opus-4-7", "openai/gpt-4o", "ollama/llama3"). Falls back to AGENT_TEMPO_API_MODEL (claude-api) or AGENT_TEMPO_OPENCODE_MODEL (opencode), then a constants-pinned default. Ignored for claude / copilot / mock adapters.'),
|
|
97
|
+
type: zod_1.z.string().optional()
|
|
98
|
+
.describe('Agent type name — references a Claude Code agent definition (e.g., "tempo-soloist")'),
|
|
99
|
+
systemPrompt: zod_1.z.string().optional()
|
|
100
|
+
.describe('Path to a .md file to use as custom agent system prompt (--system-prompt)'),
|
|
101
|
+
host: zod_1.z.string().optional()
|
|
102
|
+
.describe('Target hostname for cross-machine recruiting. Omit for local spawn.'),
|
|
103
|
+
force: zod_1.z.boolean().optional()
|
|
104
|
+
.describe('Force-terminate any existing session with this name before recruiting. Use when a previous session is orphaned or stuck.'),
|
|
105
|
+
mockMode: zod_1.z.enum(types_1.MOCK_MODES).optional()
|
|
106
|
+
.describe('Dev-mode only (agent: "mock"). Mock adapter mode. Default: "echo". "silent" never replies (heartbeat-stale validation); "chaos" probabilistic fail/crash injection (env-tuned).'),
|
|
107
|
+
mockScenario: zod_1.z.string().optional()
|
|
108
|
+
.describe('Dev-mode only (agent: "mock", mockMode: "scripted"). Bare scenario name (resolved against shipped scenarios/) or absolute path to a scenario YAML.'),
|
|
109
|
+
// #520 — claude-code-headless adapter knobs. Both ignored for other adapters.
|
|
110
|
+
permissionMode: zod_1.z.enum(types_2.CLAUDE_CODE_PERMISSION_MODES).optional()
|
|
111
|
+
.describe('claude-code-headless only. Permission mode forwarded to `claude -p --permission-mode`. Default "acceptEdits" auto-approves writes + common fs commands. "bypassPermissions" / "dangerouslySkipPermissions" trades safety for speed in trusted contexts. "plan" plans without executing — not useful for headless players. Mutually exclusive with `dangerouslySkipPermissions`.'),
|
|
112
|
+
dangerouslySkipPermissions: zod_1.z.boolean().optional()
|
|
113
|
+
.describe('claude-code-headless only. When true, passes `--dangerously-skip-permissions` to `claude -p` instead of `--permission-mode`. Use only in sandboxed/trusted contexts. Mutually exclusive with `permissionMode`.'),
|
|
114
|
+
}, async (args) => {
|
|
115
|
+
const { workDir, name, initialMessage } = args;
|
|
116
|
+
const isConductor = args.conductor === true;
|
|
117
|
+
const agent = args.agent || ownAgentType;
|
|
118
|
+
const model = args.model;
|
|
119
|
+
const agentTypeName = args.type;
|
|
120
|
+
const systemPrompt = args.systemPrompt;
|
|
121
|
+
const host = args.host;
|
|
122
|
+
const force = args.force === true;
|
|
123
|
+
const mockMode = args.mockMode;
|
|
124
|
+
const mockScenario = args.mockScenario;
|
|
125
|
+
const permissionMode = args.permissionMode;
|
|
126
|
+
const dangerouslySkipPermissions = args.dangerouslySkipPermissions === true;
|
|
127
|
+
// ADR 0014 §7 gate 3 — recruit-time rejection of `agent: 'mock'`
|
|
128
|
+
// outside dev mode. Defense-in-depth: even if a hand-edited install
|
|
129
|
+
// had `dist/adapters/mock/` present (gate 1 bypassed) AND somehow
|
|
130
|
+
// got the registry to register the descriptor (gate 2 bypassed),
|
|
131
|
+
// this rejects the request with a clear, actionable error.
|
|
132
|
+
if (agent === 'mock' && !(0, config_1.isDevMode)()) {
|
|
133
|
+
return (0, helpers_1.fail)(`agent: "mock" is only available in dev mode. Restart agent-tempo with --dev (or set AGENT_TEMPO_DEV_MODE=1) to enable.`);
|
|
134
|
+
}
|
|
135
|
+
// mockMode / mockScenario are only meaningful with the mock adapter —
|
|
136
|
+
// reject silently-ignored params so users learn the right flag shape.
|
|
137
|
+
if (mockMode != null && agent !== 'mock') {
|
|
138
|
+
return (0, helpers_1.fail)(`mockMode is only valid when agent: "mock" (got agent: "${agent}").`);
|
|
139
|
+
}
|
|
140
|
+
if (mockScenario != null && agent !== 'mock') {
|
|
141
|
+
return (0, helpers_1.fail)(`mockScenario is only valid when agent: "mock" (got agent: "${agent}").`);
|
|
142
|
+
}
|
|
143
|
+
if (agent === 'mock' && mockMode === 'scripted' && !mockScenario) {
|
|
144
|
+
return (0, helpers_1.fail)(`mockMode: "scripted" requires mockScenario (a bare scenario name or path to a YAML file).`);
|
|
145
|
+
}
|
|
146
|
+
// PR-3: silent + chaos modes don't consult the scenario file. Reject
|
|
147
|
+
// explicitly rather than silently ignore so users learn the right
|
|
148
|
+
// shape and don't sit wondering why their scenario wasn't applied.
|
|
149
|
+
if (mockScenario && (mockMode === 'silent' || mockMode === 'chaos')) {
|
|
150
|
+
return (0, helpers_1.fail)(`mockMode: "${mockMode}" does not use a scenario. Drop mockScenario or switch to mockMode: "scripted".`);
|
|
151
|
+
}
|
|
152
|
+
// #131 / #449 Phase C — model knob is meaningful for claude-api AND
|
|
153
|
+
// opencode (different shapes — bare vs `provider/model` — both flow
|
|
154
|
+
// through the same recruit field). Reject silently-ignored params for
|
|
155
|
+
// the other adapters so users learn the right shape.
|
|
156
|
+
// Local-spawn pre-flight checks (env vars + SDK install + binaries)
|
|
157
|
+
// run only when `host` is unset; cross-host recruits delegate to the
|
|
158
|
+
// target daemon's `availableAgentTypes` advertisement (the existing
|
|
159
|
+
// `checkHostPreflight` path), which already gates on whether the
|
|
160
|
+
// remote daemon resolved the SDK at boot.
|
|
161
|
+
if (model != null && agent !== 'claude-api' && agent !== 'opencode') {
|
|
162
|
+
return (0, helpers_1.fail)(`model is only valid when agent: "claude-api" or agent: "opencode" (got agent: "${agent}").`);
|
|
163
|
+
}
|
|
164
|
+
// #520 — claude-code-headless permission knobs are mutually exclusive
|
|
165
|
+
// and only meaningful for that adapter. Reject silently-ignored
|
|
166
|
+
// params so users learn the right shape.
|
|
167
|
+
if (permissionMode != null && agent !== 'claude-code-headless') {
|
|
168
|
+
return (0, helpers_1.fail)(`permissionMode is only valid when agent: "claude-code-headless" (got agent: "${agent}").`);
|
|
169
|
+
}
|
|
170
|
+
if (dangerouslySkipPermissions && agent !== 'claude-code-headless') {
|
|
171
|
+
return (0, helpers_1.fail)(`dangerouslySkipPermissions is only valid when agent: "claude-code-headless" (got agent: "${agent}").`);
|
|
172
|
+
}
|
|
173
|
+
if (permissionMode != null && dangerouslySkipPermissions) {
|
|
174
|
+
return (0, helpers_1.fail)(`permissionMode and dangerouslySkipPermissions are mutually exclusive — pass at most one.`);
|
|
175
|
+
}
|
|
176
|
+
if (agent === 'claude-api' && !host && !force) {
|
|
177
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
178
|
+
return (0, helpers_1.fail)(`agent: "claude-api" requires the ANTHROPIC_API_KEY environment variable on the spawn host. Set it before recruiting (export ANTHROPIC_API_KEY=sk-...) or use \`force: true\` to bypass this check.`);
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
require.resolve('@anthropic-ai/sdk');
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return (0, helpers_1.fail)(`agent: "claude-api" requires the @anthropic-ai/sdk optional dependency. Install with \`npm install @anthropic-ai/sdk\` and retry, or use \`force: true\` to bypass this check.`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// #449 Phase C — opencode pre-flight. Two checks: the optional SDK
|
|
188
|
+
// (signal that opencode integration is intended on this host) AND
|
|
189
|
+
// the `opencode` binary on PATH (the adapter spawns `opencode serve`
|
|
190
|
+
// as a subprocess). Cross-host recruits skip both — the target
|
|
191
|
+
// daemon's `availableAgentTypes` is the gate there.
|
|
192
|
+
if (agent === 'opencode' && !host && !force) {
|
|
193
|
+
if (!(0, sdk_probe_1.probeSdkInstall)('@opencode-ai/sdk')) {
|
|
194
|
+
return (0, helpers_1.fail)(`agent: "opencode" requires the @opencode-ai/sdk optional dependency. Install with \`npm install @opencode-ai/sdk\` and retry, or use \`force: true\` to bypass this check.`);
|
|
195
|
+
}
|
|
196
|
+
if (!hasOpencodeOnPath()) {
|
|
197
|
+
return (0, helpers_1.fail)(`agent: "opencode" requires the \`opencode\` binary on PATH. Install with \`npm install -g opencode-ai\` and retry, or use \`force: true\` to bypass this check.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// #520 — claude-code-headless pre-flight. Two checks, both bounded by
|
|
201
|
+
// short timeouts (3s + 5s). The auth probe uses the official `claude
|
|
202
|
+
// auth status` subcommand — no billed API call. Cross-host recruits
|
|
203
|
+
// skip both — the target daemon's `availableAgentTypes` is the gate
|
|
204
|
+
// there (PR-2 wires the daemon-side probe).
|
|
205
|
+
if (agent === 'claude-code-headless' && !host && !force) {
|
|
206
|
+
const claudeBin = config.claudeBin ?? 'claude';
|
|
207
|
+
const binProbe = (0, pre_flight_1.probeClaudeBinary)(claudeBin);
|
|
208
|
+
if (!binProbe.ok) {
|
|
209
|
+
return (0, helpers_1.fail)(`agent: "claude-code-headless" pre-flight failed: ${binProbe.error} Use \`force: true\` to bypass.`);
|
|
210
|
+
}
|
|
211
|
+
const authProbe = (0, pre_flight_1.probeClaudeAuth)(claudeBin);
|
|
212
|
+
if (!authProbe.loggedIn) {
|
|
213
|
+
return (0, helpers_1.fail)(`agent: "claude-code-headless" pre-flight failed: ${authProbe.error} Use \`force: true\` to bypass.`);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// #532 — copilot pre-flight. SDK-only probe (mirrors claude-api's
|
|
217
|
+
// pattern; copilot has no subprocess CLI on PATH). The bridge
|
|
218
|
+
// subprocess hard-requires `@github/copilot-sdk` at module load
|
|
219
|
+
// (`src/adapters/copilot/adapter.ts:71`), so without this gate the
|
|
220
|
+
// user only learns of the missing dep AFTER bridge spawn —
|
|
221
|
+
// adapter crashes with `process.exit(1)` and the player sits in
|
|
222
|
+
// `booting` until lease timeout. We use `probeSdkInstall` (FS walk)
|
|
223
|
+
// rather than `require.resolve` because pnpm layouts without a
|
|
224
|
+
// top-level hoisted link otherwise false-negative; see issue #532
|
|
225
|
+
// investigation footnote. GITHUB_TOKEN / Copilot CLI login are
|
|
226
|
+
// intentionally NOT checked: the SDK falls through to the
|
|
227
|
+
// logged-in user (`adapter.ts:31, :263`), so token presence is not
|
|
228
|
+
// a hard requirement. Cross-host recruits skip — the target
|
|
229
|
+
// daemon's `availableAgentTypes` is the gate there.
|
|
230
|
+
if (agent === 'copilot' && !host && !force) {
|
|
231
|
+
if (!(0, sdk_probe_1.probeSdkInstall)('@github/copilot-sdk')) {
|
|
232
|
+
return (0, helpers_1.fail)(`agent: "copilot" requires the @github/copilot-sdk optional dependency. Install with \`npm install @github/copilot-sdk\` and retry, or use \`force: true\` to bypass this check.`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Resolve agent type if provided
|
|
236
|
+
let agentDefinition;
|
|
237
|
+
let agentDefinitionPath;
|
|
238
|
+
let agentDefinitionDescription;
|
|
239
|
+
let nativeResolvable;
|
|
240
|
+
let allowedTools;
|
|
241
|
+
if (agentTypeName) {
|
|
242
|
+
const info = (0, agent_types_1.resolveAgentType)(agentTypeName);
|
|
243
|
+
if (!info) {
|
|
244
|
+
const available = (0, agent_types_1.listAgentTypes)().map(t => t.name);
|
|
245
|
+
return (0, helpers_1.fail)(`Unknown agent type "${agentTypeName}". Available types: ${available.length ? available.join(', ') : '(none)'}`);
|
|
246
|
+
}
|
|
247
|
+
agentDefinition = info.name;
|
|
248
|
+
agentDefinitionPath = info.path;
|
|
249
|
+
agentDefinitionDescription = info.description;
|
|
250
|
+
nativeResolvable = info.nativeResolvable;
|
|
251
|
+
allowedTools = info.allowedTools;
|
|
252
|
+
}
|
|
253
|
+
// Validate name
|
|
254
|
+
const nameError = (0, validation_1.validatePlayerName)(name);
|
|
255
|
+
if (nameError) {
|
|
256
|
+
return (0, helpers_1.fail)(nameError);
|
|
257
|
+
}
|
|
258
|
+
if (name === 'conductor' && !isConductor) {
|
|
259
|
+
return (0, helpers_1.fail)(`The name "conductor" is reserved for conductor sessions. Use a different name, or set conductor: true.`);
|
|
260
|
+
}
|
|
261
|
+
// ── #274 AC12 — cross-host recruit pre-flight ──
|
|
262
|
+
//
|
|
263
|
+
// When `host` is specified, verify that the target is live + has
|
|
264
|
+
// the requested agent runtime available BEFORE submitting to the
|
|
265
|
+
// outbox. Otherwise `recruit --host foo` could queue forever on a
|
|
266
|
+
// per-host task queue nobody's listening on (the exact failure
|
|
267
|
+
// mode #274 set out to close).
|
|
268
|
+
//
|
|
269
|
+
// `force: true` bypasses pre-flight entirely (AC12d) — covers the
|
|
270
|
+
// scripted-recruit-on-about-to-boot-daemon case plus any other
|
|
271
|
+
// "I know what I'm doing" override. RPC failure during pre-flight
|
|
272
|
+
// falls through with a warning (AC12e) so today's recruit
|
|
273
|
+
// availability characteristics are preserved.
|
|
274
|
+
if (host && !force) {
|
|
275
|
+
try {
|
|
276
|
+
const hosts = await listHostsFn(client);
|
|
277
|
+
const preflightError = checkHostPreflight(hosts, host, agent);
|
|
278
|
+
if (preflightError)
|
|
279
|
+
return (0, helpers_1.fail)(preflightError);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
toolLog(`Host pre-flight RPC failed for "${host}"; proceeding without validation (use --force to silence this warning): ${err instanceof Error ? err.message : err}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
// Check if a conductor already exists when recruiting a conductor
|
|
287
|
+
if (isConductor) {
|
|
288
|
+
try {
|
|
289
|
+
const conductorWfId = (0, config_1.conductorWorkflowId)(config.ensemble);
|
|
290
|
+
const conductorHandle = client.workflow.getHandle(conductorWfId);
|
|
291
|
+
const desc = await conductorHandle.describe();
|
|
292
|
+
if (desc.status.name === 'RUNNING') {
|
|
293
|
+
if (force) {
|
|
294
|
+
await conductorHandle.terminate(`Force-terminated for re-recruit by ${getPlayerId()}`);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
return (0, helpers_1.fail)(`A conductor is already running in ensemble "${config.ensemble}". Use \`agent-tempo conduct --replace\` from the CLI to replace it, \`stop\` it first, or use \`force: true\` to replace it.`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
catch {
|
|
302
|
+
// No existing conductor — proceed
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
// Check if a session with this name is already active
|
|
306
|
+
const existing = await (0, resolve_1.resolveSession)(client, config.ensemble, name);
|
|
307
|
+
if (existing) {
|
|
308
|
+
if (force) {
|
|
309
|
+
// Force-terminate the existing session before recruiting
|
|
310
|
+
await existing.terminate(`Force-terminated for re-recruit by ${getPlayerId()}`);
|
|
311
|
+
// Best-effort notify conductor
|
|
312
|
+
try {
|
|
313
|
+
const condId = (0, config_1.conductorWorkflowId)(config.ensemble);
|
|
314
|
+
const condHandle = client.workflow.getHandle(condId);
|
|
315
|
+
await condHandle.signal('receiveMessage', {
|
|
316
|
+
from: 'system',
|
|
317
|
+
text: `Session "${name}" was force-terminated for re-recruit by ${getPlayerId()}.`,
|
|
318
|
+
responseRequested: false,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
catch {
|
|
322
|
+
// Conductor may not exist — that's fine
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
return (0, helpers_1.fail)(`Session **${name}** is already active. Use \`cue\` to send it a message, \`stop\` it first, or use \`force: true\` to replace it.`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const entry = {
|
|
330
|
+
type: 'recruit',
|
|
331
|
+
targetName: name,
|
|
332
|
+
workDir,
|
|
333
|
+
isConductor,
|
|
334
|
+
initialMessage,
|
|
335
|
+
agent,
|
|
336
|
+
systemPrompt: agentDefinition ? undefined : systemPrompt,
|
|
337
|
+
targetHostname: host,
|
|
338
|
+
agentDefinition,
|
|
339
|
+
agentDefinitionPath,
|
|
340
|
+
agentDefinitionDescription,
|
|
341
|
+
nativeResolvable,
|
|
342
|
+
allowedTools,
|
|
343
|
+
claudeBin: config.claudeBin,
|
|
344
|
+
...(agent === 'mock' ? { mockMode: mockMode ?? 'echo' } : {}),
|
|
345
|
+
...(agent === 'mock' && mockScenario ? { mockScenario } : {}),
|
|
346
|
+
...(agent === 'claude-api' && model ? { model } : {}),
|
|
347
|
+
// #520 — claude-code-headless permission knobs flow through the
|
|
348
|
+
// outbox so PR-2's spawn helper picks them up via env. Fields
|
|
349
|
+
// are only present on the entry when actually set; the
|
|
350
|
+
// OutboxEntryInput shape will gain typed `permissionMode` /
|
|
351
|
+
// `dangerouslySkipPermissions` fields in PR-2.
|
|
352
|
+
...(agent === 'claude-code-headless' && permissionMode ? { permissionMode } : {}),
|
|
353
|
+
...(agent === 'claude-code-headless' && dangerouslySkipPermissions ? { dangerouslySkipPermissions: true } : {}),
|
|
354
|
+
};
|
|
355
|
+
const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
|
|
356
|
+
return (0, helpers_1.ok)(`Recruit request submitted for **${name}** in ${workDir}. The session will be spawned shortly. (outbox: ${entryId})`);
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
return (0, helpers_1.fail)(`Failed to recruit: ${(0, helpers_1.formatError)(err)}`);
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
364
|
+
// #274 AC12 — host pre-flight validation (exported for unit tests)
|
|
365
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
366
|
+
/**
|
|
367
|
+
* Given a host liveness+profile snapshot, validate that `targetHost` is
|
|
368
|
+
* (a) known, (b) recruit-ready, and (c) advertises support for
|
|
369
|
+
* `requestedAgent`. Returns a user-facing error string when validation
|
|
370
|
+
* fails, or `null` when all checks pass.
|
|
371
|
+
*
|
|
372
|
+
* Profile-missing is treated as a soft pass (the daemon is alive but
|
|
373
|
+
* didn't signal a profile, e.g. rolling-upgrade scenario or global
|
|
374
|
+
* maestro was unreachable at its boot). We fall through rather than
|
|
375
|
+
* fail — M10 / AC11c says operational UX beats strict enforcement when
|
|
376
|
+
* information is genuinely absent.
|
|
377
|
+
*
|
|
378
|
+
* Exported for unit testing per the M15 dep-injection pattern.
|
|
379
|
+
*/
|
|
380
|
+
function checkHostPreflight(hosts, targetHost, requestedAgent) {
|
|
381
|
+
const match = hosts.find((h) => h.hostname === targetHost);
|
|
382
|
+
if (!match) {
|
|
383
|
+
const knownNames = hosts.map((h) => h.hostname);
|
|
384
|
+
const suggestion = nearestHostname(targetHost, knownNames);
|
|
385
|
+
const available = knownNames.length > 0 ? knownNames.join(', ') : '(none — no daemons currently polling)';
|
|
386
|
+
const hint = suggestion && suggestion !== targetHost ? ` Did you mean "${suggestion}"?` : '';
|
|
387
|
+
return `Unknown host "${targetHost}".${hint} Available: ${available}. Use \`force: true\` to bypass this check.`;
|
|
388
|
+
}
|
|
389
|
+
if (match.freshness !== 'live' || !match.recruitReady) {
|
|
390
|
+
return `Host "${targetHost}" is not recruit-ready right now (freshness=${match.freshness}, recruitReady=${match.recruitReady}). Wait for the daemon to signal again, or use \`force: true\` to bypass.`;
|
|
391
|
+
}
|
|
392
|
+
// Profile-missing → soft pass. Daemon is alive + recruit-ready; we
|
|
393
|
+
// just don't have capability info. Log-level guidance could be added
|
|
394
|
+
// later; for now we accept to avoid false-negative rejection.
|
|
395
|
+
if (match.profile && match.profile.availableAgentTypes) {
|
|
396
|
+
if (!match.profile.availableAgentTypes.includes(requestedAgent)) {
|
|
397
|
+
const supports = match.profile.availableAgentTypes.length > 0
|
|
398
|
+
? match.profile.availableAgentTypes.join(', ')
|
|
399
|
+
: '(none advertised)';
|
|
400
|
+
return `Host "${targetHost}" cannot run \`${requestedAgent}\` (supports: ${supports}). Use \`force: true\` to bypass this check.`;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Pick the closest hostname to `target` by Levenshtein distance. Returns
|
|
407
|
+
* `null` when `candidates` is empty OR the nearest match is so distant
|
|
408
|
+
* (edit distance ≥ half the longer string's length) that suggesting it
|
|
409
|
+
* would be misleading. Exported for tests.
|
|
410
|
+
*/
|
|
411
|
+
function nearestHostname(target, candidates) {
|
|
412
|
+
if (candidates.length === 0)
|
|
413
|
+
return null;
|
|
414
|
+
let best = null;
|
|
415
|
+
for (const cand of candidates) {
|
|
416
|
+
const dist = levenshtein(target, cand);
|
|
417
|
+
if (best === null || dist < best.dist)
|
|
418
|
+
best = { name: cand, dist };
|
|
419
|
+
}
|
|
420
|
+
if (!best)
|
|
421
|
+
return null;
|
|
422
|
+
const ratioThreshold = Math.max(target.length, best.name.length) / 2;
|
|
423
|
+
return best.dist < ratioThreshold ? best.name : null;
|
|
424
|
+
}
|
|
425
|
+
/** Iterative O(n·m) Levenshtein distance. 15 lines, no dep. */
|
|
426
|
+
function levenshtein(a, b) {
|
|
427
|
+
if (a === b)
|
|
428
|
+
return 0;
|
|
429
|
+
if (a.length === 0)
|
|
430
|
+
return b.length;
|
|
431
|
+
if (b.length === 0)
|
|
432
|
+
return a.length;
|
|
433
|
+
// previous row of distances
|
|
434
|
+
let prev = new Array(b.length + 1);
|
|
435
|
+
for (let j = 0; j <= b.length; j++)
|
|
436
|
+
prev[j] = j;
|
|
437
|
+
for (let i = 1; i <= a.length; i++) {
|
|
438
|
+
const curr = new Array(b.length + 1);
|
|
439
|
+
curr[0] = i;
|
|
440
|
+
for (let j = 1; j <= b.length; j++) {
|
|
441
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
442
|
+
curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
|
|
443
|
+
}
|
|
444
|
+
prev = curr;
|
|
445
|
+
}
|
|
446
|
+
return prev[b.length];
|
|
447
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { Client, WorkflowHandle } from '@temporalio/client';
|
|
3
|
+
import { Config } from '../config';
|
|
4
|
+
export declare function registerReleaseTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerReleaseTool = registerReleaseTool;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const resolve_1 = require("../activities/resolve");
|
|
6
|
+
const signals_1 = require("../workflows/signals");
|
|
7
|
+
const helpers_1 = require("./helpers");
|
|
8
|
+
const validation_1 = require("../utils/validation");
|
|
9
|
+
function registerReleaseTool(server, client, config, getPlayerId, handle) {
|
|
10
|
+
(0, helpers_1.defineTool)(server, 'release', 'Release held player sessions — unlocks their outboxes and delivers deferred task messages. Without a player name, releases all held sessions.', {
|
|
11
|
+
player: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional()
|
|
12
|
+
.describe('Name of a specific held player to release. Omit to release all held players.'),
|
|
13
|
+
}, async (args) => {
|
|
14
|
+
const { player } = args;
|
|
15
|
+
if (player) {
|
|
16
|
+
const nameError = (0, validation_1.validatePlayerName)(player);
|
|
17
|
+
if (nameError)
|
|
18
|
+
return (0, helpers_1.fail)(nameError);
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
if (player) {
|
|
22
|
+
// Release a specific player
|
|
23
|
+
const sessions = await (0, resolve_1.scanEnsembleSessions)(client, config.ensemble);
|
|
24
|
+
const target = sessions.find((s) => s.playerId === player);
|
|
25
|
+
if (!target) {
|
|
26
|
+
return (0, helpers_1.fail)(`No session found with name "${player}".`);
|
|
27
|
+
}
|
|
28
|
+
// Check if the session's outbox is actually locked
|
|
29
|
+
const targetHandle = client.workflow.getHandle(target.workflowId);
|
|
30
|
+
let isLocked = false;
|
|
31
|
+
try {
|
|
32
|
+
isLocked = await targetHandle.query(signals_1.outboxLockedQuery);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Query may fail for old workflows without the handler — not held
|
|
36
|
+
}
|
|
37
|
+
if (!isLocked) {
|
|
38
|
+
return (0, helpers_1.fail)(`Session "${player}" is not held (outbox not locked). Only held sessions can be released.`);
|
|
39
|
+
}
|
|
40
|
+
const entry = {
|
|
41
|
+
type: 'release',
|
|
42
|
+
targetPlayerId: player,
|
|
43
|
+
};
|
|
44
|
+
const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
|
|
45
|
+
return (0, helpers_1.ok)(`Release request submitted for **${player}**. Task assignment will be delivered shortly. (outbox: ${entryId})`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Release all held players — scan ensemble and check outboxLocked on each
|
|
49
|
+
const sessions = await (0, resolve_1.scanEnsembleSessions)(client, config.ensemble);
|
|
50
|
+
const heldSessions = [];
|
|
51
|
+
for (const session of sessions) {
|
|
52
|
+
// Skip self and conductors (conductor outbox is never locked)
|
|
53
|
+
if (session.playerId === getPlayerId())
|
|
54
|
+
continue;
|
|
55
|
+
try {
|
|
56
|
+
const sessionHandle = client.workflow.getHandle(session.workflowId);
|
|
57
|
+
const isLocked = await sessionHandle.query(signals_1.outboxLockedQuery);
|
|
58
|
+
if (isLocked) {
|
|
59
|
+
heldSessions.push(session);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip sessions where query fails (old workflows, terminated, etc.)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (heldSessions.length === 0) {
|
|
67
|
+
return (0, helpers_1.ok)('No held sessions found. Nothing to release.');
|
|
68
|
+
}
|
|
69
|
+
const released = [];
|
|
70
|
+
const errors = [];
|
|
71
|
+
for (const session of heldSessions) {
|
|
72
|
+
try {
|
|
73
|
+
const entry = {
|
|
74
|
+
type: 'release',
|
|
75
|
+
targetPlayerId: session.playerId,
|
|
76
|
+
};
|
|
77
|
+
await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
|
|
78
|
+
released.push(session.playerId);
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
errors.push(`${session.playerId}: ${(0, helpers_1.formatError)(err)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const lines = [];
|
|
85
|
+
if (released.length > 0) {
|
|
86
|
+
lines.push(`Released ${released.length} player(s): ${released.join(', ')}`);
|
|
87
|
+
}
|
|
88
|
+
if (errors.length > 0) {
|
|
89
|
+
lines.push(`Errors:\n${errors.map((e) => ` - ${e}`).join('\n')}`);
|
|
90
|
+
}
|
|
91
|
+
return (0, helpers_1.ok)(lines.join('\n'));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
return (0, helpers_1.fail)(`Failed to release: ${(0, helpers_1.formatError)(err)}`);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|