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,459 @@
|
|
|
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.DEFAULT_PORT = exports.DEFAULT_BIND_ADDR = void 0;
|
|
37
|
+
exports.startHttpServer = startHttpServer;
|
|
38
|
+
exports.handle = handle;
|
|
39
|
+
/**
|
|
40
|
+
* Daemon HTTP server — PR-1 of #94/#95.
|
|
41
|
+
*
|
|
42
|
+
* Hosts the read-only snapshot endpoints (`/v1/health`, `/v1/ensembles`,
|
|
43
|
+
* `/v1/state/:ensemble`, `/v1/hosts`) defined in `docs/SSE-PROTOCOL.md`.
|
|
44
|
+
* The SSE streaming endpoints (`/v1/events*`) are PR-2.
|
|
45
|
+
*
|
|
46
|
+
* Boot lifecycle:
|
|
47
|
+
* 1. Caller passes a {@link TempoClient}, namespace, and version to
|
|
48
|
+
* {@link startHttpServer}.
|
|
49
|
+
* 2. Server binds to `127.0.0.1:8473` in production / `:8474` in dev mode
|
|
50
|
+
* (defaults overridable via `AGENT_TEMPO_HTTP_BIND` /
|
|
51
|
+
* `AGENT_TEMPO_DAEMON_PORT`; dev profile per ADR 0014 §5.1).
|
|
52
|
+
* 3. The bound port is written atomically to `~/.agent-tempo/daemon.port`.
|
|
53
|
+
* 4. Caller `await`s {@link HttpServerHandle.close} on shutdown — drains
|
|
54
|
+
* in-flight requests, removes the port file, then resolves.
|
|
55
|
+
*/
|
|
56
|
+
const http = __importStar(require("http"));
|
|
57
|
+
const config_1 = require("../config");
|
|
58
|
+
const auth_1 = require("./auth");
|
|
59
|
+
const cors_1 = require("./cors");
|
|
60
|
+
const dashboard_1 = require("./dashboard");
|
|
61
|
+
const dashboard_pair_1 = require("./dashboard-pair");
|
|
62
|
+
const fixtures_1 = require("./fixtures");
|
|
63
|
+
const writes_1 = require("./writes");
|
|
64
|
+
const catalog_1 = require("./catalog");
|
|
65
|
+
const port_file_1 = require("./port-file");
|
|
66
|
+
const responses_1 = require("./responses");
|
|
67
|
+
const snapshot_1 = require("./snapshot");
|
|
68
|
+
const sse_handler_1 = require("./sse-handler");
|
|
69
|
+
const log = (...args) => console.error(`[agent-tempo:http ${new Date().toISOString()}]`, ...args);
|
|
70
|
+
/** Default bind addr per SSE-PROTOCOL.md §1. */
|
|
71
|
+
exports.DEFAULT_BIND_ADDR = '127.0.0.1';
|
|
72
|
+
/**
|
|
73
|
+
* Default port — `t-e-m-p-o` mnemonic for prod (8473), `+1` for dev (8474).
|
|
74
|
+
* ADR 0014 §5.1 flips this with the rest of the dev profile so prod and dev
|
|
75
|
+
* daemons can coexist on the same machine without binding the same port.
|
|
76
|
+
*
|
|
77
|
+
* Evaluated at module load time. The dev daemon child process inherits
|
|
78
|
+
* `AGENT_TEMPO_DEV_MODE=1` from its parent CLI (set by the
|
|
79
|
+
* `dev-mode-bootstrap` side-effect), so by the time this module loads,
|
|
80
|
+
* `isDevMode()` already returns the correct value.
|
|
81
|
+
*/
|
|
82
|
+
exports.DEFAULT_PORT = (0, config_1.isDevMode)() ? config_1.DEV_DAEMON_PORT : config_1.PROD_DAEMON_PORT;
|
|
83
|
+
/**
|
|
84
|
+
* Start the HTTP server. Resolves once the listener is bound and the
|
|
85
|
+
* port file has been written.
|
|
86
|
+
*/
|
|
87
|
+
async function startHttpServer(opts) {
|
|
88
|
+
const bindAddr = opts.bindAddr ?? process.env[config_1.ENV.HTTP_BIND] ?? exports.DEFAULT_BIND_ADDR;
|
|
89
|
+
const portEnv = process.env[config_1.ENV.DAEMON_PORT];
|
|
90
|
+
const port = opts.port ?? (portEnv != null && portEnv !== '' ? Number(portEnv) : exports.DEFAULT_PORT);
|
|
91
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
92
|
+
throw new Error(`Invalid HTTP port: ${port}`);
|
|
93
|
+
}
|
|
94
|
+
const allowedOrigins = opts.allowedOrigins ?? (0, cors_1.parseCorsOrigins)(process.env[config_1.ENV.CORS_ORIGINS]);
|
|
95
|
+
const corsConfig = { allowedOrigins };
|
|
96
|
+
// Bearer token resolution: if bind addr is non-loopback, force token
|
|
97
|
+
// generation now so the daemon doesn't crash mid-request when the first
|
|
98
|
+
// bearer-required call shows up.
|
|
99
|
+
const bindIsLoopback = (0, auth_1.isLoopbackBindAddr)(bindAddr);
|
|
100
|
+
const httpToken = opts.httpToken ?? (0, auth_1.loadOrGenerateHttpToken)({ bearerRequired: !bindIsLoopback });
|
|
101
|
+
if (!bindIsLoopback && !httpToken) {
|
|
102
|
+
throw new Error('Bearer token required for non-loopback bind but none configured. ' +
|
|
103
|
+
'Set httpToken in ~/.agent-tempo/config.json or unset AGENT_TEMPO_HTTP_BIND.');
|
|
104
|
+
}
|
|
105
|
+
const startedAt = opts.startedAtMs ?? Date.now();
|
|
106
|
+
// §7.3 process-wide cap. Defaults to 100 per spec; env var override.
|
|
107
|
+
const capLimitEnv = process.env[config_1.ENV.SSE_MAX_CONNECTIONS];
|
|
108
|
+
const capLimit = opts.maxSseConnections ??
|
|
109
|
+
(capLimitEnv != null && capLimitEnv !== '' ? Number(capLimitEnv) : sse_handler_1.DEFAULT_MAX_CONNECTIONS);
|
|
110
|
+
if (!Number.isInteger(capLimit) || capLimit < 1) {
|
|
111
|
+
throw new Error(`Invalid SSE max connections: ${capLimit}`);
|
|
112
|
+
}
|
|
113
|
+
const sseConnectionCap = new sse_handler_1.ConnectionCap(capLimit);
|
|
114
|
+
// Subscriber count = aggregate's live SSE subscribers. Falls back to
|
|
115
|
+
// 0 when no aggregate (PR-1 server, no SSE).
|
|
116
|
+
const subscriberCount = () => opts.aggregate ? opts.aggregate.totalSubscriberCount() : 0;
|
|
117
|
+
const server = http.createServer((req, res) => handle(req, res, {
|
|
118
|
+
client: opts.client,
|
|
119
|
+
namespace: opts.namespace,
|
|
120
|
+
taskQueue: opts.taskQueue,
|
|
121
|
+
version: opts.version,
|
|
122
|
+
bindAddr,
|
|
123
|
+
corsConfig,
|
|
124
|
+
httpToken,
|
|
125
|
+
startedAt,
|
|
126
|
+
subscriberCount,
|
|
127
|
+
aggregate: opts.aggregate ?? null,
|
|
128
|
+
sseConnectionCap,
|
|
129
|
+
}).catch((err) => {
|
|
130
|
+
log('unhandled handler error:', err instanceof Error ? err.message : err);
|
|
131
|
+
if (!res.headersSent) {
|
|
132
|
+
try {
|
|
133
|
+
(0, responses_1.errorResponse)(res, 500, { error: 'internal-error' });
|
|
134
|
+
}
|
|
135
|
+
catch { /* socket already closed */ }
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
try {
|
|
139
|
+
res.end();
|
|
140
|
+
}
|
|
141
|
+
catch { /* ignore */ }
|
|
142
|
+
}
|
|
143
|
+
}));
|
|
144
|
+
// Defensive: per-connection timeout so a wedged client can't hold a
|
|
145
|
+
// socket indefinitely on the daemon. Snapshot endpoints respond fast;
|
|
146
|
+
// PR-2 will need a much longer / disabled timeout for SSE streams.
|
|
147
|
+
server.requestTimeout = 30_000;
|
|
148
|
+
server.headersTimeout = 35_000;
|
|
149
|
+
// TCP keepalive — matches §1 contract.
|
|
150
|
+
server.keepAliveTimeout = 60_000;
|
|
151
|
+
await new Promise((resolve, reject) => {
|
|
152
|
+
server.once('error', reject);
|
|
153
|
+
server.listen(port, bindAddr, () => {
|
|
154
|
+
server.removeListener('error', reject);
|
|
155
|
+
resolve();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
const address = server.address();
|
|
159
|
+
const boundPort = typeof address === 'object' && address ? address.port : port;
|
|
160
|
+
// Port file → atomic write so a racing reader never sees a half-written
|
|
161
|
+
// value. Skip write if the caller asked for ephemeral binding without a
|
|
162
|
+
// port-file override (tests).
|
|
163
|
+
const portFilePath = opts.portFilePath ?? port_file_1.DAEMON_PORT_PATH;
|
|
164
|
+
try {
|
|
165
|
+
await (0, port_file_1.writePortFileAtomic)(portFilePath, boundPort);
|
|
166
|
+
log(`listening on http://${bindAddr}:${boundPort} (port file: ${portFilePath})`);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
log('failed to write port file (non-fatal):', err instanceof Error ? err.message : err);
|
|
170
|
+
}
|
|
171
|
+
// Track open sockets so `close()` can force-close them after a graceful
|
|
172
|
+
// window — Node's `server.close()` only stops accepting new connections.
|
|
173
|
+
const sockets = new Set();
|
|
174
|
+
server.on('connection', (socket) => {
|
|
175
|
+
sockets.add(socket);
|
|
176
|
+
socket.on('close', () => sockets.delete(socket));
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
port: boundPort,
|
|
180
|
+
bindAddr,
|
|
181
|
+
subscriberCount,
|
|
182
|
+
async close() {
|
|
183
|
+
// Stop accepting new connections immediately, then give live sockets
|
|
184
|
+
// a 5 s window to drain before destroying them.
|
|
185
|
+
const closing = new Promise((resolve) => {
|
|
186
|
+
server.close(() => resolve());
|
|
187
|
+
});
|
|
188
|
+
const drainDeadline = setTimeout(() => {
|
|
189
|
+
for (const s of sockets)
|
|
190
|
+
s.destroy();
|
|
191
|
+
}, 5_000);
|
|
192
|
+
drainDeadline.unref();
|
|
193
|
+
try {
|
|
194
|
+
await closing;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
clearTimeout(drainDeadline);
|
|
198
|
+
}
|
|
199
|
+
(0, port_file_1.removePortFile)(portFilePath);
|
|
200
|
+
log('server closed');
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Top-level request dispatcher — exported for unit tests that want to
|
|
206
|
+
* exercise the handler without spinning up a real listener.
|
|
207
|
+
*/
|
|
208
|
+
async function handle(req, res, ctx) {
|
|
209
|
+
const method = (req.method ?? 'GET').toUpperCase();
|
|
210
|
+
const url = new URL(req.url ?? '/', `http://${ctx.bindAddr}`);
|
|
211
|
+
const pathname = url.pathname;
|
|
212
|
+
// CORS preflight short-circuit. Always answer OPTIONS — even in
|
|
213
|
+
// loopback mode — with `204 No Content` and the static headers, so
|
|
214
|
+
// browsers performing a preflight against a loopback dashboard get a
|
|
215
|
+
// clean answer.
|
|
216
|
+
if (method === 'OPTIONS') {
|
|
217
|
+
return handleOptions(req, res, ctx);
|
|
218
|
+
}
|
|
219
|
+
// Health is always reachable, never authenticated, never CORS-gated.
|
|
220
|
+
if (method === 'GET' && pathname === '/v1/health') {
|
|
221
|
+
return handleHealth(res, ctx);
|
|
222
|
+
}
|
|
223
|
+
// Pre-auth carve-out for the cross-device pairing-token consume endpoint
|
|
224
|
+
// (see docs/design/340-web-dashboard.md §4.1). The token IS the auth —
|
|
225
|
+
// single-use, 5-min TTL, opaque base64url. Real pair flow lands here
|
|
226
|
+
// in PR-8 of #340.
|
|
227
|
+
//
|
|
228
|
+
// Regex is intentionally tight — `[^/]+` rejects extra path segments and
|
|
229
|
+
// (after URL-parser normalisation) traversal attempts; both fall through
|
|
230
|
+
// to the post-auth `/dashboard/api/*` 404.
|
|
231
|
+
const pairConsumeMatch = pathname.match(/^\/dashboard\/api\/pair\/([^/]+)$/);
|
|
232
|
+
if (method === 'GET' && pairConsumeMatch) {
|
|
233
|
+
return (0, dashboard_pair_1.handlePairConsume)(req, res, pairConsumeMatch[1]);
|
|
234
|
+
}
|
|
235
|
+
// Authentication gate.
|
|
236
|
+
const originHeader = headerString(req.headers.origin);
|
|
237
|
+
const reqBearer = (0, auth_1.bearerRequired)(ctx.bindAddr, originHeader);
|
|
238
|
+
if (reqBearer) {
|
|
239
|
+
const provided = (0, auth_1.extractBearerToken)(headerString(req.headers.authorization));
|
|
240
|
+
if (!provided || !ctx.httpToken || !(0, auth_1.tokensMatch)(provided, ctx.httpToken)) {
|
|
241
|
+
writeCorsHeaders(res, originHeader, ctx, reqBearer);
|
|
242
|
+
return (0, responses_1.errorResponse)(res, 401, { error: 'unauthorized' });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
// CORS allowlist check (only enforced in bearer mode).
|
|
246
|
+
const cors = (0, cors_1.evaluateOrigin)(originHeader, ctx.corsConfig, reqBearer);
|
|
247
|
+
if (!cors.allowed) {
|
|
248
|
+
return (0, responses_1.errorResponse)(res, 403, { error: 'origin-not-allowed' });
|
|
249
|
+
}
|
|
250
|
+
if (cors.echo) {
|
|
251
|
+
res.setHeader('Access-Control-Allow-Origin', cors.echo);
|
|
252
|
+
res.setHeader('Vary', 'Origin');
|
|
253
|
+
}
|
|
254
|
+
// Write surface (PR-7a of #340) — POST `/v1/ensembles/:ensemble/<action>`
|
|
255
|
+
// Match BEFORE the GET-only method gate; everything else (POST to a
|
|
256
|
+
// read endpoint, GET to a write endpoint) flows into the 405 fallback
|
|
257
|
+
// below with the right `Allow` header.
|
|
258
|
+
const writeMatch = pathname.match(/^\/v1\/ensembles\/([^/]+)\/([^/]+)$/);
|
|
259
|
+
if (writeMatch) {
|
|
260
|
+
const ensemble = decodeURIComponent(writeMatch[1]);
|
|
261
|
+
const action = writeMatch[2];
|
|
262
|
+
if ((0, writes_1.isWriteAction)(action)) {
|
|
263
|
+
if (method !== 'POST') {
|
|
264
|
+
return (0, responses_1.errorResponse)(res, 405, { error: 'method-not-allowed' }, { Allow: 'POST, OPTIONS' });
|
|
265
|
+
}
|
|
266
|
+
return (0, writes_1.handleWriteRoute)(req, res, ctx.client, ensemble, action);
|
|
267
|
+
}
|
|
268
|
+
// Unknown action under /v1/ensembles/:e/<x> → 404 (don't 405; the
|
|
269
|
+
// path simply isn't a known endpoint).
|
|
270
|
+
return (0, responses_1.errorResponse)(res, 404, { error: 'not-found' });
|
|
271
|
+
}
|
|
272
|
+
// Create-ensemble route (issue #400) — POST `/v1/ensembles`. Sits
|
|
273
|
+
// alongside the writeMatch above so it's reached before the GET-only
|
|
274
|
+
// gate; the GET on the same path (list ensembles) is handled below.
|
|
275
|
+
if (pathname === '/v1/ensembles' && method === 'POST') {
|
|
276
|
+
return (0, catalog_1.handleCreateEnsemble)(req, res, ctx.client);
|
|
277
|
+
}
|
|
278
|
+
// POST `/dashboard/api/pair` — mint a pairing for cross-device QR (PR-8
|
|
279
|
+
// of #340). Sits AFTER the bearer-auth gate so the operator on the host
|
|
280
|
+
// proves authority before issuing a token; the token's GET-side consume
|
|
281
|
+
// is the carve-out above the auth gate.
|
|
282
|
+
if (method === 'POST' && pathname === '/dashboard/api/pair') {
|
|
283
|
+
const provided = (0, auth_1.extractBearerToken)(headerString(req.headers.authorization));
|
|
284
|
+
return (0, dashboard_pair_1.handlePairCreate)(req, res, provided);
|
|
285
|
+
}
|
|
286
|
+
// Method gate — read endpoints are GET-only. Both POST paths above
|
|
287
|
+
// (PR-7a writes, PR-8 pair-mint) handle their own method matching;
|
|
288
|
+
// everything else falls through here.
|
|
289
|
+
if (method !== 'GET') {
|
|
290
|
+
return (0, responses_1.errorResponse)(res, 405, { error: 'method-not-allowed' }, { Allow: 'GET, OPTIONS' });
|
|
291
|
+
}
|
|
292
|
+
// Static dashboard SPA (PR-1 of #340). Sits behind the bearer-auth gate
|
|
293
|
+
// so non-loopback binds (LAN, Tailscale) require the same bearer the
|
|
294
|
+
// `/v1/*` API uses. The pre-auth pair-token carve-out above is the
|
|
295
|
+
// single exception that bootstraps cross-device pairing.
|
|
296
|
+
if (pathname === '/dashboard' || pathname.startsWith('/dashboard/')) {
|
|
297
|
+
return (0, dashboard_1.handleDashboardStatic)(req, res, pathname);
|
|
298
|
+
}
|
|
299
|
+
if (pathname === '/v1/ensembles') {
|
|
300
|
+
return handleListEnsembles(res, ctx);
|
|
301
|
+
}
|
|
302
|
+
if (pathname === '/v1/hosts') {
|
|
303
|
+
return handleHosts(res, ctx);
|
|
304
|
+
}
|
|
305
|
+
// Catalog reads (issue #400) — `listAgentTypes` / `listLineups`
|
|
306
|
+
// touch local fs only, no Temporal calls; cheap to serve per-request.
|
|
307
|
+
if (pathname === '/v1/agent-types') {
|
|
308
|
+
return (0, catalog_1.handleListAgentTypes)(res);
|
|
309
|
+
}
|
|
310
|
+
if (pathname === '/v1/lineups') {
|
|
311
|
+
return (0, catalog_1.handleListLineups)(res);
|
|
312
|
+
}
|
|
313
|
+
// /v1/state/:ensemble — single capture group.
|
|
314
|
+
const stateMatch = pathname.match(/^\/v1\/state\/([^/]+)$/);
|
|
315
|
+
if (stateMatch) {
|
|
316
|
+
const ensemble = decodeURIComponent(stateMatch[1]);
|
|
317
|
+
// Fixture mode (PR-3 of #340) — `?fixture=<name>` short-circuits the
|
|
318
|
+
// live snapshot with canned data. Sits behind the bearer-auth gate.
|
|
319
|
+
const fixtureName = url.searchParams.get('fixture');
|
|
320
|
+
if (fixtureName) {
|
|
321
|
+
return (0, fixtures_1.handleFixtureSnapshot)(res, fixtureName);
|
|
322
|
+
}
|
|
323
|
+
return handleState(res, ctx, ensemble);
|
|
324
|
+
}
|
|
325
|
+
// /v1/events* (SSE). Dispatch to the SSE handler when an aggregate
|
|
326
|
+
// is wired; otherwise stay on the PR-1 503 placeholder so the route
|
|
327
|
+
// signals "feature exists, not yet wired" rather than "ensemble
|
|
328
|
+
// doesn't exist".
|
|
329
|
+
if (pathname === '/v1/events') {
|
|
330
|
+
if (!ctx.aggregate) {
|
|
331
|
+
return (0, responses_1.errorResponse)(res, 503, { error: 'streaming-not-implemented' }, { 'Retry-After': '60' });
|
|
332
|
+
}
|
|
333
|
+
return (0, sse_handler_1.handleSseRequest)(req, res, {
|
|
334
|
+
client: ctx.client,
|
|
335
|
+
bus: ctx.aggregate.globalBus(),
|
|
336
|
+
emitSnapshot: false,
|
|
337
|
+
cap: ctx.sseConnectionCap,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const evtMatch = pathname.match(/^\/v1\/events\/([^/]+)$/);
|
|
341
|
+
if (evtMatch) {
|
|
342
|
+
const ensemble = decodeURIComponent(evtMatch[1]);
|
|
343
|
+
// Fixture mode (PR-3 of #340) — `?fixture=<name>` short-circuits both
|
|
344
|
+
// the existence check and the aggregate poll loop with a canned event
|
|
345
|
+
// sequence. Sits behind the bearer-auth gate.
|
|
346
|
+
const fixtureName = url.searchParams.get('fixture');
|
|
347
|
+
if (fixtureName) {
|
|
348
|
+
return (0, fixtures_1.handleFixtureSse)(req, res, fixtureName);
|
|
349
|
+
}
|
|
350
|
+
if (!ctx.aggregate) {
|
|
351
|
+
return (0, responses_1.errorResponse)(res, 503, { error: 'streaming-not-implemented' }, { 'Retry-After': '60' });
|
|
352
|
+
}
|
|
353
|
+
// Validate existence before opening the SSE stream — clean 404 when
|
|
354
|
+
// the ensemble was never live, instead of an empty stream.
|
|
355
|
+
const list = await ctx.client.listEnsembles().catch(() => []);
|
|
356
|
+
if (!list.find((e) => e.name === ensemble)) {
|
|
357
|
+
return (0, responses_1.errorResponse)(res, 404, { error: 'ensemble-not-found', ensemble });
|
|
358
|
+
}
|
|
359
|
+
const bus = ctx.aggregate.getOrCreateEnsembleBus(ensemble);
|
|
360
|
+
return (0, sse_handler_1.handleSseRequest)(req, res, {
|
|
361
|
+
client: ctx.client,
|
|
362
|
+
bus,
|
|
363
|
+
emitSnapshot: true,
|
|
364
|
+
ensemble,
|
|
365
|
+
cap: ctx.sseConnectionCap,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return (0, responses_1.errorResponse)(res, 404, { error: 'not-found' });
|
|
369
|
+
}
|
|
370
|
+
/** Pull a single string from a possibly-array header. */
|
|
371
|
+
function headerString(v) {
|
|
372
|
+
if (Array.isArray(v))
|
|
373
|
+
return v[0];
|
|
374
|
+
return v;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Apply CORS response headers when bearer mode is active. Used by
|
|
378
|
+
* paths that bypass the main route flow (e.g. 401 short-circuit) so
|
|
379
|
+
* the browser still sees a CORS-compliant response.
|
|
380
|
+
*/
|
|
381
|
+
function writeCorsHeaders(res, originHeader, ctx, bearerActive) {
|
|
382
|
+
if (!bearerActive)
|
|
383
|
+
return;
|
|
384
|
+
const cors = (0, cors_1.evaluateOrigin)(originHeader, ctx.corsConfig, bearerActive);
|
|
385
|
+
if (cors.echo) {
|
|
386
|
+
res.setHeader('Access-Control-Allow-Origin', cors.echo);
|
|
387
|
+
res.setHeader('Vary', 'Origin');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function handleOptions(req, res, ctx) {
|
|
391
|
+
const originHeader = headerString(req.headers.origin);
|
|
392
|
+
const bearerActive = (0, auth_1.bearerRequired)(ctx.bindAddr, originHeader);
|
|
393
|
+
const cors = (0, cors_1.evaluateOrigin)(originHeader, ctx.corsConfig, bearerActive);
|
|
394
|
+
// Origin rejected → 403, no ACAO header (browser will block fetch).
|
|
395
|
+
if (!cors.allowed) {
|
|
396
|
+
res.writeHead(403, { 'Content-Type': 'text/plain; charset=utf-8' });
|
|
397
|
+
res.end('CORS: origin not allowed');
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const headers = { ...(0, cors_1.corsResponseHeaders)() };
|
|
401
|
+
if (cors.echo)
|
|
402
|
+
headers['Access-Control-Allow-Origin'] = cors.echo;
|
|
403
|
+
res.writeHead(204, headers);
|
|
404
|
+
res.end();
|
|
405
|
+
}
|
|
406
|
+
function handleHealth(res, ctx) {
|
|
407
|
+
// #336 — sample process memory at request time. `process.memoryUsage()`
|
|
408
|
+
// is synchronous + cheap (~microseconds), safe inside a request handler.
|
|
409
|
+
const mem = process.memoryUsage();
|
|
410
|
+
const body = {
|
|
411
|
+
ok: true,
|
|
412
|
+
namespace: ctx.namespace,
|
|
413
|
+
taskQueue: ctx.taskQueue,
|
|
414
|
+
version: ctx.version,
|
|
415
|
+
uptimeMs: Math.max(0, Date.now() - ctx.startedAt),
|
|
416
|
+
ensembleCount: 0, // populated below
|
|
417
|
+
subscriberCount: ctx.subscriberCount(),
|
|
418
|
+
memory: {
|
|
419
|
+
rss: mem.rss,
|
|
420
|
+
heapTotal: mem.heapTotal,
|
|
421
|
+
heapUsed: mem.heapUsed,
|
|
422
|
+
external: mem.external,
|
|
423
|
+
arrayBuffers: mem.arrayBuffers,
|
|
424
|
+
},
|
|
425
|
+
};
|
|
426
|
+
// Best-effort ensemble count — soft-fail to 0 rather than 500ing on a
|
|
427
|
+
// healthcheck. `/v1/health` MUST stay reachable even when Temporal is
|
|
428
|
+
// wedged; supervisord depends on it.
|
|
429
|
+
ctx.client
|
|
430
|
+
.listEnsembles()
|
|
431
|
+
.then((list) => {
|
|
432
|
+
body.ensembleCount = list.length;
|
|
433
|
+
(0, responses_1.jsonResponse)(res, 200, body);
|
|
434
|
+
})
|
|
435
|
+
.catch(() => {
|
|
436
|
+
(0, responses_1.jsonResponse)(res, 200, body);
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async function handleListEnsembles(res, ctx) {
|
|
440
|
+
const list = await ctx.client.listEnsembles();
|
|
441
|
+
(0, responses_1.jsonResponse)(res, 200, list);
|
|
442
|
+
}
|
|
443
|
+
async function handleHosts(res, ctx) {
|
|
444
|
+
// The 3 s cache lives inside `listHosts`; we don't add another layer.
|
|
445
|
+
const hosts = await ctx.client.listHosts();
|
|
446
|
+
(0, responses_1.jsonResponse)(res, 200, hosts);
|
|
447
|
+
}
|
|
448
|
+
async function handleState(res, ctx, ensemble) {
|
|
449
|
+
try {
|
|
450
|
+
const snapshot = await (0, snapshot_1.buildEnsembleSnapshot)(ctx.client, ensemble);
|
|
451
|
+
(0, responses_1.jsonResponse)(res, 200, snapshot);
|
|
452
|
+
}
|
|
453
|
+
catch (err) {
|
|
454
|
+
if (err instanceof snapshot_1.EnsembleNotFoundError) {
|
|
455
|
+
return (0, responses_1.errorResponse)(res, 404, { error: 'ensemble-not-found', ensemble });
|
|
456
|
+
}
|
|
457
|
+
throw err;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot builder for `/v1/state/:ensemble`.
|
|
3
|
+
*
|
|
4
|
+
* **PR-1 strategy**: project the existing TempoClient queries through HTTP
|
|
5
|
+
* without an aggregate. PR-2 will replace this with a 750 ms aggregate
|
|
6
|
+
* poll loop that diffs and emits events; the snapshot endpoint stays
|
|
7
|
+
* stable across that swap because the projection lives in the client
|
|
8
|
+
* surface, not the SSE infrastructure.
|
|
9
|
+
*
|
|
10
|
+
* **Ensemble existence gate**: `listEnsembles()` is the one query that
|
|
11
|
+
* cleanly distinguishes "ensemble doesn't exist" from "ensemble exists
|
|
12
|
+
* but is empty". Calling it first lets us return `404 ensemble-not-found`
|
|
13
|
+
* (SSE-PROTOCOL.md §9) before we fan out the rest of the queries.
|
|
14
|
+
*
|
|
15
|
+
* **Atomicity** (§4.3 contract): in PR-1 the snapshot reflects whatever
|
|
16
|
+
* Temporal queries returned at the time of fan-out. The `lastEventId` is
|
|
17
|
+
* the sentinel `"0:0"` — PR-2 subscribers passing it back will hit
|
|
18
|
+
* `epoch-mismatch` and re-fetch, which is the right behavior since the
|
|
19
|
+
* PR-1 snapshot has no provable atomicity against an event log that
|
|
20
|
+
* doesn't yet exist.
|
|
21
|
+
*/
|
|
22
|
+
import type { TempoClient } from '../client/interface';
|
|
23
|
+
import type { MaestroPlayerInfo } from '../types';
|
|
24
|
+
import { type EnsembleStateV1, type PlayerSummaryV1 } from './event-types';
|
|
25
|
+
/**
|
|
26
|
+
* Maximum chat messages embedded in the snapshot. Larger paging is left
|
|
27
|
+
* to the explicit `getEnsembleChat` query / `recall` tool — the snapshot
|
|
28
|
+
* is a steady-state view, not a paginator.
|
|
29
|
+
*/
|
|
30
|
+
export declare const SNAPSHOT_CHAT_LIMIT = 50;
|
|
31
|
+
/** Thrown when the ensemble doesn't exist; route handler maps to 404. */
|
|
32
|
+
export declare class EnsembleNotFoundError extends Error {
|
|
33
|
+
readonly ensemble: string;
|
|
34
|
+
constructor(ensemble: string);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Per-player wire-extension fields layered on top of `toPlayerSummaryV1`.
|
|
38
|
+
* Issue #399 W2 — `getPlayerWireMeta` fans out 3 session queries; the
|
|
39
|
+
* resulting object is merged into the projection here. `null` (session
|
|
40
|
+
* unreachable) drops the whole wire-meta block rather than emitting
|
|
41
|
+
* half-populated fields.
|
|
42
|
+
*/
|
|
43
|
+
export interface PlayerWireMeta {
|
|
44
|
+
runId?: string;
|
|
45
|
+
messaging?: {
|
|
46
|
+
received: number;
|
|
47
|
+
sent: number;
|
|
48
|
+
outbox: string;
|
|
49
|
+
};
|
|
50
|
+
lease?: {
|
|
51
|
+
expiresAt: number | null;
|
|
52
|
+
leaseMs: number | null;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Project a `MaestroPlayerInfo` into the wire-stable `PlayerSummaryV1`.
|
|
57
|
+
* Drops fields that aren't part of the v1 contract; passes `agentType`
|
|
58
|
+
* through verbatim — the wire union mirrors {@link AgentType} from
|
|
59
|
+
* `src/types.ts`, so every shipped adapter projects to its own label
|
|
60
|
+
* (#535). Pre-#535 the wire union was closed at `'claude' | 'copilot' |
|
|
61
|
+
* 'mock'` and headless adapters were coerced to `'claude'`, which made
|
|
62
|
+
* them indistinguishable from interactive Claude Code players in the
|
|
63
|
+
* dashboard. The union expansion is additive per the §6 stability rule
|
|
64
|
+
* in `event-types.ts` — no `/v1/` → `/v2/` bump.
|
|
65
|
+
*
|
|
66
|
+
* `wireMeta` is the session-level projection from
|
|
67
|
+
* `TempoClient.getPlayerWireMeta` (Issue #399 W2). Pass `null` (or omit)
|
|
68
|
+
* when the session workflow couldn't be queried — the projection then
|
|
69
|
+
* carries no `runId` / `messaging` / `lease` fields and the dashboard
|
|
70
|
+
* renders `—` placeholders.
|
|
71
|
+
*
|
|
72
|
+
* `activityCount` passes through from `MaestroPlayerInfo` (Q5.6).
|
|
73
|
+
* `MaestroPlayerInfo.lastActivityAt` maps to the wire field
|
|
74
|
+
* `PlayerSummaryV1.lastHeartbeatAt` — same data, rename happens at the
|
|
75
|
+
* wire boundary so consumers read one canonical name.
|
|
76
|
+
*/
|
|
77
|
+
export declare function toPlayerSummaryV1(p: MaestroPlayerInfo, wireMeta?: PlayerWireMeta | null): PlayerSummaryV1;
|
|
78
|
+
/**
|
|
79
|
+
* Build the `/v1/state/:ensemble` payload. Throws
|
|
80
|
+
* {@link EnsembleNotFoundError} when the requested ensemble has no live
|
|
81
|
+
* workflows; route handler turns that into a 404.
|
|
82
|
+
*/
|
|
83
|
+
export declare function buildEnsembleSnapshot(client: TempoClient, ensemble: string, opts?: {
|
|
84
|
+
now?: () => Date;
|
|
85
|
+
}): Promise<EnsembleStateV1>;
|