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,598 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `TempoClient.subscribe` — AsyncIterable wrapper over the daemon's
|
|
4
|
+
* `/v1/events*` SSE endpoints. PR-3 of #94/#95.
|
|
5
|
+
*
|
|
6
|
+
* **Reference**: [`docs/SSE-PROTOCOL.md`](../../docs/SSE-PROTOCOL.md),
|
|
7
|
+
* [`docs/adr/0010-drop-caller-controllable-event-cursor.md`](../../docs/adr/0010-drop-caller-controllable-event-cursor.md),
|
|
8
|
+
* and the wire types in [`src/http/event-types.ts`](../http/event-types.ts).
|
|
9
|
+
*
|
|
10
|
+
* ## Design
|
|
11
|
+
*
|
|
12
|
+
* - **Dual transport** — the wrapper picks the best transport for the
|
|
13
|
+
* environment + auth context:
|
|
14
|
+
* - **Native `EventSource`** when available AND no bearer token is set
|
|
15
|
+
* (loopback browser dashboard). Native auto-reconnect handles
|
|
16
|
+
* `Last-Event-ID` for free.
|
|
17
|
+
* - **`fetch().body`** otherwise (Node 20+, or any path that needs the
|
|
18
|
+
* `Authorization` header). Manual reconnect + backoff + `Last-Event-ID`
|
|
19
|
+
* tracking implemented here.
|
|
20
|
+
*
|
|
21
|
+
* Native `EventSource` cannot set custom request headers on initial
|
|
22
|
+
* connect, so any path that requires `Authorization: Bearer …` must use
|
|
23
|
+
* fetch. The transport choice is hidden from the consumer — the
|
|
24
|
+
* `AsyncIterable<TempoEvent>` shape is identical on both paths.
|
|
25
|
+
*
|
|
26
|
+
* - **Caller-controllable cursor resume is not supported (ADR 0010).**
|
|
27
|
+
* `SubscribeOptions` exposes `signal` and `topics` only. Snapshot-then-
|
|
28
|
+
* stream (server emits `event: snapshot` on fresh connect; SSE-PROTOCOL.md
|
|
29
|
+
* §7.2) covers every realistic case. In-session reconnects use
|
|
30
|
+
* `Last-Event-ID` under the hood — auto-managed by `EventSource`,
|
|
31
|
+
* tracked manually by the fetch wrapper.
|
|
32
|
+
*
|
|
33
|
+
* - **Transparent reconnect on the fetch path (§7.5).** TCP drops,
|
|
34
|
+
* premature stream ends, and 5xx responses are retried with exponential
|
|
35
|
+
* backoff (`100 ms × 2^attempt`, cap 30 s, reset on first event of a
|
|
36
|
+
* new connection). The most-recently-seen event id is replayed on each
|
|
37
|
+
* retry so consumers see seamless replay/`gap` behaviour. Native
|
|
38
|
+
* `EventSource` does the equivalent automatically.
|
|
39
|
+
*
|
|
40
|
+
* - **Gap events surface to consumer (§7.5/§7.6).** The wrapper does NOT
|
|
41
|
+
* re-fetch `/v1/state/:ensemble` on `gap` or `chat.compressed`; those
|
|
42
|
+
* are application-level concerns that the consumer (TUI / web dashboard)
|
|
43
|
+
* handles. The wrapper just relays events faithfully.
|
|
44
|
+
*
|
|
45
|
+
* - **Cancellation contract (§7.4).** Two paths:
|
|
46
|
+
* - `subscribe(..., { signal })` — when the external `AbortSignal`
|
|
47
|
+
* aborts, the wrapper closes the underlying transport and ends the
|
|
48
|
+
* iterator.
|
|
49
|
+
* - `for await … break` — the JavaScript runtime invokes the iterator's
|
|
50
|
+
* `return()` method, which in turn calls `controller.abort('iterator.return')`
|
|
51
|
+
* and tears down the transport. Without this, an idle SSE stream would
|
|
52
|
+
* leak the underlying socket.
|
|
53
|
+
*/
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.SubscribeHttpError = void 0;
|
|
56
|
+
exports.createSubscribe = createSubscribe;
|
|
57
|
+
const port_file_1 = require("../http/port-file");
|
|
58
|
+
const event_types_1 = require("../http/event-types");
|
|
59
|
+
// ── Constants ──────────────────────────────────────────────────────────────
|
|
60
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
61
|
+
const DEFAULT_PORT = 8473;
|
|
62
|
+
const RECONNECT_INITIAL_MS = 100;
|
|
63
|
+
const RECONNECT_MAX_MS = 30_000;
|
|
64
|
+
const SSE_KIND_SET = new Set(event_types_1.SSE_EVENT_KINDS);
|
|
65
|
+
/**
|
|
66
|
+
* Build the `subscribe` method for a TempoClient instance. Returns a
|
|
67
|
+
* function with two overloads — per-ensemble (`subscribe(ensemble, opts)`)
|
|
68
|
+
* and global (`subscribe(opts)`).
|
|
69
|
+
*/
|
|
70
|
+
function createSubscribe(deps = {}) {
|
|
71
|
+
const sleepFn = deps.sleep ?? defaultSleep;
|
|
72
|
+
function subscribe(arg1, arg2) {
|
|
73
|
+
const scope = typeof arg1 === 'string'
|
|
74
|
+
? { type: 'ensemble', ensemble: arg1 }
|
|
75
|
+
: { type: 'global' };
|
|
76
|
+
const opts = (typeof arg1 === 'string' ? arg2 : arg1) ?? {};
|
|
77
|
+
return makeIterable({ scope, opts, deps, sleep: sleepFn });
|
|
78
|
+
}
|
|
79
|
+
return subscribe;
|
|
80
|
+
}
|
|
81
|
+
// ── AsyncIterable plumbing ────────────────────────────────────────────────
|
|
82
|
+
function makeIterable(args) {
|
|
83
|
+
return {
|
|
84
|
+
[Symbol.asyncIterator]() {
|
|
85
|
+
return makeIterator(args);
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Build the AsyncIterator. Bridges the run loop (which pushes events) to
|
|
91
|
+
* the consumer (which pulls via `next()`) through a single-element queue
|
|
92
|
+
* with promise-based backpressure.
|
|
93
|
+
*/
|
|
94
|
+
function makeIterator(args) {
|
|
95
|
+
const externalSignal = args.opts.signal;
|
|
96
|
+
const wrapperController = new AbortController();
|
|
97
|
+
const wrapperSignal = wrapperController.signal;
|
|
98
|
+
// Forward external abort onto the wrapper controller.
|
|
99
|
+
let externalAbortListener = null;
|
|
100
|
+
if (externalSignal) {
|
|
101
|
+
if (externalSignal.aborted) {
|
|
102
|
+
wrapperController.abort(externalSignal.reason);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
externalAbortListener = () => {
|
|
106
|
+
wrapperController.abort(externalSignal.reason);
|
|
107
|
+
};
|
|
108
|
+
externalSignal.addEventListener('abort', externalAbortListener, {
|
|
109
|
+
once: true,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function detachExternal() {
|
|
114
|
+
if (externalSignal && externalAbortListener) {
|
|
115
|
+
externalSignal.removeEventListener('abort', externalAbortListener);
|
|
116
|
+
externalAbortListener = null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Buffered events ready for the consumer.
|
|
120
|
+
const buffer = [];
|
|
121
|
+
// Promise-based handoff for the next() call awaiting an event.
|
|
122
|
+
let pendingResolve = null;
|
|
123
|
+
let pendingReject = null;
|
|
124
|
+
// Run loop terminal state.
|
|
125
|
+
let done = false;
|
|
126
|
+
let runError = null;
|
|
127
|
+
function tryDrainPending() {
|
|
128
|
+
if (!pendingResolve)
|
|
129
|
+
return;
|
|
130
|
+
if (buffer.length > 0) {
|
|
131
|
+
const ev = buffer.shift();
|
|
132
|
+
const resolve = pendingResolve;
|
|
133
|
+
pendingResolve = null;
|
|
134
|
+
pendingReject = null;
|
|
135
|
+
resolve({ value: ev, done: false });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (done) {
|
|
139
|
+
const resolve = pendingResolve;
|
|
140
|
+
const reject = pendingReject;
|
|
141
|
+
pendingResolve = null;
|
|
142
|
+
pendingReject = null;
|
|
143
|
+
if (runError)
|
|
144
|
+
reject(runError);
|
|
145
|
+
else
|
|
146
|
+
resolve({ value: undefined, done: true });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const transport = canUseEventSource(args.deps) ? runEventSourceLoop : runFetchLoop;
|
|
150
|
+
const runPromise = transport({
|
|
151
|
+
...args,
|
|
152
|
+
signal: wrapperSignal,
|
|
153
|
+
onEvent(ev) {
|
|
154
|
+
buffer.push(ev);
|
|
155
|
+
tryDrainPending();
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
.catch((err) => {
|
|
159
|
+
runError = err;
|
|
160
|
+
})
|
|
161
|
+
.finally(() => {
|
|
162
|
+
done = true;
|
|
163
|
+
detachExternal();
|
|
164
|
+
tryDrainPending();
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
next() {
|
|
168
|
+
if (buffer.length > 0) {
|
|
169
|
+
return Promise.resolve({ value: buffer.shift(), done: false });
|
|
170
|
+
}
|
|
171
|
+
if (done) {
|
|
172
|
+
if (runError)
|
|
173
|
+
return Promise.reject(runError);
|
|
174
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
175
|
+
}
|
|
176
|
+
return new Promise((resolve, reject) => {
|
|
177
|
+
pendingResolve = resolve;
|
|
178
|
+
pendingReject = reject;
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
async return() {
|
|
182
|
+
// Consumer is exiting — `for await … break` lands here. Abort the
|
|
183
|
+
// run loop, drain it, ensure the underlying transport tore down.
|
|
184
|
+
// Without this the SSE socket would leak (§7.4).
|
|
185
|
+
if (!wrapperSignal.aborted)
|
|
186
|
+
wrapperController.abort('iterator.return');
|
|
187
|
+
detachExternal();
|
|
188
|
+
try {
|
|
189
|
+
await runPromise;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// Swallow — the consumer has explicitly chosen to stop.
|
|
193
|
+
}
|
|
194
|
+
return { value: undefined, done: true };
|
|
195
|
+
},
|
|
196
|
+
async throw(err) {
|
|
197
|
+
if (!wrapperSignal.aborted)
|
|
198
|
+
wrapperController.abort(err);
|
|
199
|
+
detachExternal();
|
|
200
|
+
try {
|
|
201
|
+
await runPromise;
|
|
202
|
+
}
|
|
203
|
+
catch {
|
|
204
|
+
// Swallow — caller chose to throw.
|
|
205
|
+
}
|
|
206
|
+
throw err;
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Native `EventSource` is the preferred transport when:
|
|
212
|
+
* - it's available in the runtime (browsers; Node 22+ optionally), AND
|
|
213
|
+
* - no bearer token is set (loopback / local dev).
|
|
214
|
+
*
|
|
215
|
+
* Otherwise we fall back to fetch — which gives us full header control
|
|
216
|
+
* for `Authorization: Bearer …` and is the only option in Node 20.
|
|
217
|
+
*/
|
|
218
|
+
function canUseEventSource(deps) {
|
|
219
|
+
if (deps.token)
|
|
220
|
+
return false;
|
|
221
|
+
return resolveEventSource(deps) !== undefined;
|
|
222
|
+
}
|
|
223
|
+
function resolveEventSource(deps) {
|
|
224
|
+
if (deps.EventSourceImpl)
|
|
225
|
+
return deps.EventSourceImpl;
|
|
226
|
+
return globalThis.EventSource;
|
|
227
|
+
}
|
|
228
|
+
// ── EventSource transport ──────────────────────────────────────────────────
|
|
229
|
+
/**
|
|
230
|
+
* Open a native `EventSource` and pipe each parsed event to `onEvent`.
|
|
231
|
+
* Native auto-reconnect handles `Last-Event-ID` for in-session drops, so
|
|
232
|
+
* this loop is just: open, listen, close on abort. Errors that the
|
|
233
|
+
* `EventSource` cannot recover from (e.g. 404) bubble up via the silent
|
|
234
|
+
* reconnect cycle — for hard-error visibility callers should set a token
|
|
235
|
+
* (which forces the fetch path) or use `signal` with their own timeout.
|
|
236
|
+
*/
|
|
237
|
+
async function runEventSourceLoop(args) {
|
|
238
|
+
const { scope, opts, deps, signal, onEvent } = args;
|
|
239
|
+
const ES = resolveEventSource(deps);
|
|
240
|
+
if (!ES)
|
|
241
|
+
throw new Error('[agent-tempo:subscribe] EventSource not available');
|
|
242
|
+
const url = buildUrl(scope, opts.topics, deps.baseUrl);
|
|
243
|
+
const es = new ES(url);
|
|
244
|
+
const listeners = [];
|
|
245
|
+
for (const kind of event_types_1.SSE_EVENT_KINDS) {
|
|
246
|
+
const listener = (ev) => {
|
|
247
|
+
const tempoEvent = parseEventSourceMessage(kind, ev);
|
|
248
|
+
if (tempoEvent)
|
|
249
|
+
onEvent(tempoEvent);
|
|
250
|
+
};
|
|
251
|
+
es.addEventListener(kind, listener);
|
|
252
|
+
listeners.push([kind, listener]);
|
|
253
|
+
}
|
|
254
|
+
return new Promise((resolve) => {
|
|
255
|
+
const teardown = () => {
|
|
256
|
+
// Inline call preserves `this` binding on the EventSource — extracting
|
|
257
|
+
// `es.removeEventListener` to a const would lose it under strict mode.
|
|
258
|
+
for (const [k, l] of listeners) {
|
|
259
|
+
es.removeEventListener(k, l);
|
|
260
|
+
}
|
|
261
|
+
es.close();
|
|
262
|
+
resolve();
|
|
263
|
+
};
|
|
264
|
+
if (signal.aborted) {
|
|
265
|
+
teardown();
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
signal.addEventListener('abort', teardown, { once: true });
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function parseEventSourceMessage(kind, ev) {
|
|
272
|
+
let envelope;
|
|
273
|
+
try {
|
|
274
|
+
envelope = JSON.parse(ev.data);
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
return liftEnvelope(envelope, kind, ev.lastEventId);
|
|
280
|
+
}
|
|
281
|
+
// ── Fetch transport ────────────────────────────────────────────────────────
|
|
282
|
+
/**
|
|
283
|
+
* The fetch reconnect loop. Opens a `fetch`, parses SSE frames, yields
|
|
284
|
+
* events via `onEvent`, and on transport failure backs off and reopens
|
|
285
|
+
* with the latest tracked `Last-Event-ID`. Returns when `signal.aborted`
|
|
286
|
+
* flips.
|
|
287
|
+
*/
|
|
288
|
+
async function runFetchLoop(args) {
|
|
289
|
+
const { scope, opts, deps, sleep, signal, onEvent } = args;
|
|
290
|
+
const fetchImpl = deps.fetchImpl ?? globalFetch();
|
|
291
|
+
// In-session reconnect cursor — populated on each event the wrapper
|
|
292
|
+
// sees, replayed via `Last-Event-ID` on every retry. Caller-controllable
|
|
293
|
+
// resume via `opts.lastEventId` was deliberately dropped (ADR 0010).
|
|
294
|
+
let lastEventId = undefined;
|
|
295
|
+
let attempt = 0;
|
|
296
|
+
while (!signal.aborted) {
|
|
297
|
+
let connectionYieldedAny = false;
|
|
298
|
+
try {
|
|
299
|
+
const url = buildUrl(scope, opts.topics, deps.baseUrl);
|
|
300
|
+
const headers = buildHeaders(lastEventId, deps.token);
|
|
301
|
+
const response = await fetchImpl(url, { headers, signal });
|
|
302
|
+
if (signal.aborted)
|
|
303
|
+
break;
|
|
304
|
+
if (!response.ok) {
|
|
305
|
+
if (isPermanentHttpStatus(response.status)) {
|
|
306
|
+
// 401/404 — surface a hard error; do not retry.
|
|
307
|
+
const body = await safeReadBodyText(response);
|
|
308
|
+
throw new SubscribeHttpError(response.status, body);
|
|
309
|
+
}
|
|
310
|
+
if (response.status === 503) {
|
|
311
|
+
// Connection-cap-exceeded (§9). Honor `Retry-After` if present.
|
|
312
|
+
const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));
|
|
313
|
+
await sleep(retryAfter ?? backoffMs(attempt), signal);
|
|
314
|
+
attempt++;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
// Other 4xx/5xx — backoff retry. Drain body so the connection can close.
|
|
318
|
+
await safeReadBodyText(response);
|
|
319
|
+
await sleep(backoffMs(attempt), signal);
|
|
320
|
+
attempt++;
|
|
321
|
+
continue;
|
|
322
|
+
}
|
|
323
|
+
if (!response.body) {
|
|
324
|
+
// Server returned no body — treat as transient, back off and retry.
|
|
325
|
+
await sleep(backoffMs(attempt), signal);
|
|
326
|
+
attempt++;
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
// Stream the body. parseSseStream throws on signal abort.
|
|
330
|
+
for await (const raw of parseSseStream(response.body, signal)) {
|
|
331
|
+
if (raw.id)
|
|
332
|
+
lastEventId = raw.id;
|
|
333
|
+
const tempoEvent = parseTempoEvent(raw);
|
|
334
|
+
if (!tempoEvent)
|
|
335
|
+
continue; // malformed / unknown — skip
|
|
336
|
+
if (!connectionYieldedAny) {
|
|
337
|
+
// First event on a fresh connection — reset the backoff so a
|
|
338
|
+
// brief network blip doesn't poison the retry cadence (§7.5).
|
|
339
|
+
attempt = 0;
|
|
340
|
+
connectionYieldedAny = true;
|
|
341
|
+
}
|
|
342
|
+
onEvent(tempoEvent);
|
|
343
|
+
}
|
|
344
|
+
// Stream ended without abort — server closed the connection or the
|
|
345
|
+
// body iterator finished. Reconnect after backoff with the latest
|
|
346
|
+
// `Last-Event-ID`. A connection that yielded no events at all is
|
|
347
|
+
// treated as a transient failure (count it against the backoff
|
|
348
|
+
// schedule) — otherwise an empty-body server bug would tight-loop.
|
|
349
|
+
if (signal.aborted)
|
|
350
|
+
break;
|
|
351
|
+
await sleep(connectionYieldedAny ? RECONNECT_INITIAL_MS : backoffMs(attempt), signal);
|
|
352
|
+
if (!connectionYieldedAny)
|
|
353
|
+
attempt++;
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
if (signal.aborted)
|
|
357
|
+
break;
|
|
358
|
+
if (err instanceof SubscribeHttpError) {
|
|
359
|
+
// Permanent — surface to consumer.
|
|
360
|
+
throw err;
|
|
361
|
+
}
|
|
362
|
+
// Network error / parse error / abort during sleep → backoff retry.
|
|
363
|
+
try {
|
|
364
|
+
await sleep(backoffMs(attempt), signal);
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
// sleep aborted — outer loop check will exit.
|
|
368
|
+
}
|
|
369
|
+
attempt++;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// ── Errors ─────────────────────────────────────────────────────────────────
|
|
374
|
+
/**
|
|
375
|
+
* Surfaced to the consumer when the daemon returned a permanent HTTP
|
|
376
|
+
* status (401 unauthorized, 404 ensemble-not-found). Consumers should not
|
|
377
|
+
* retry on these.
|
|
378
|
+
*/
|
|
379
|
+
class SubscribeHttpError extends Error {
|
|
380
|
+
status;
|
|
381
|
+
body;
|
|
382
|
+
constructor(status, body) {
|
|
383
|
+
super(`SSE subscribe failed: HTTP ${status}${body ? ` — ${body}` : ''}`);
|
|
384
|
+
this.status = status;
|
|
385
|
+
this.body = body;
|
|
386
|
+
this.name = 'SubscribeHttpError';
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
exports.SubscribeHttpError = SubscribeHttpError;
|
|
390
|
+
function isPermanentHttpStatus(status) {
|
|
391
|
+
return status === 401 || status === 404;
|
|
392
|
+
}
|
|
393
|
+
// ── URL + header construction ──────────────────────────────────────────────
|
|
394
|
+
function buildUrl(scope, topics, baseUrl) {
|
|
395
|
+
const base = resolveBaseUrl(baseUrl);
|
|
396
|
+
const path = scope.type === 'ensemble'
|
|
397
|
+
? `/v1/events/${encodeURIComponent(scope.ensemble)}`
|
|
398
|
+
: '/v1/events';
|
|
399
|
+
if (!topics || topics.length === 0)
|
|
400
|
+
return `${base}${path}`;
|
|
401
|
+
const query = topics.map((t) => encodeURIComponent(t)).join(',');
|
|
402
|
+
return `${base}${path}?topics=${query}`;
|
|
403
|
+
}
|
|
404
|
+
function resolveBaseUrl(override) {
|
|
405
|
+
if (override)
|
|
406
|
+
return override.replace(/\/$/, '');
|
|
407
|
+
const port = (0, port_file_1.readPortFile)() ?? DEFAULT_PORT;
|
|
408
|
+
return `http://${DEFAULT_HOST}:${port}`;
|
|
409
|
+
}
|
|
410
|
+
function buildHeaders(lastEventId, token) {
|
|
411
|
+
const h = {
|
|
412
|
+
Accept: 'text/event-stream',
|
|
413
|
+
};
|
|
414
|
+
if (lastEventId)
|
|
415
|
+
h['Last-Event-ID'] = lastEventId;
|
|
416
|
+
if (token)
|
|
417
|
+
h.Authorization = `Bearer ${token}`;
|
|
418
|
+
return h;
|
|
419
|
+
}
|
|
420
|
+
// ── SSE parser ─────────────────────────────────────────────────────────────
|
|
421
|
+
/**
|
|
422
|
+
* Parse an SSE response body into raw frames. Each yielded `RawSseEvent`
|
|
423
|
+
* corresponds to one `\n\n`-delimited block. Comments (lines starting with
|
|
424
|
+
* `:`) and unrecognised fields (e.g. `retry:`) are skipped per the SSE
|
|
425
|
+
* spec. Throws on abort.
|
|
426
|
+
*/
|
|
427
|
+
async function* parseSseStream(body, signal) {
|
|
428
|
+
const decoder = new TextDecoder('utf-8');
|
|
429
|
+
const reader = body.getReader();
|
|
430
|
+
let buffer = '';
|
|
431
|
+
// Resume scanning at this index instead of `0`. After we've scanned a
|
|
432
|
+
// chunk and found no `\n`, we record the buffer length so the next
|
|
433
|
+
// chunk's scan starts at the boundary — avoids O(n²) on pathologically
|
|
434
|
+
// large `data:` payloads (spec §5: payloads MAY exceed typical SSE
|
|
435
|
+
// samples; consumers MUST NOT cap line length).
|
|
436
|
+
let scanStart = 0;
|
|
437
|
+
let id;
|
|
438
|
+
let event;
|
|
439
|
+
let data = [];
|
|
440
|
+
const onAbort = () => {
|
|
441
|
+
try {
|
|
442
|
+
reader.cancel().catch(() => { });
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
// Reader already released.
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
if (signal.aborted)
|
|
449
|
+
onAbort();
|
|
450
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
451
|
+
try {
|
|
452
|
+
while (!signal.aborted) {
|
|
453
|
+
const { value, done } = await reader.read();
|
|
454
|
+
if (done)
|
|
455
|
+
break;
|
|
456
|
+
buffer += decoder.decode(value, { stream: true });
|
|
457
|
+
let nlIdx;
|
|
458
|
+
while ((nlIdx = buffer.indexOf('\n', scanStart)) !== -1) {
|
|
459
|
+
const line = buffer.slice(0, nlIdx).replace(/\r$/, '');
|
|
460
|
+
buffer = buffer.slice(nlIdx + 1);
|
|
461
|
+
scanStart = 0;
|
|
462
|
+
if (line === '') {
|
|
463
|
+
// Frame complete.
|
|
464
|
+
if (data.length > 0 || event !== undefined || id !== undefined) {
|
|
465
|
+
yield { id, event, data: data.join('\n') };
|
|
466
|
+
}
|
|
467
|
+
id = undefined;
|
|
468
|
+
event = undefined;
|
|
469
|
+
data = [];
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (line.startsWith(':'))
|
|
473
|
+
continue; // comment
|
|
474
|
+
const colonIdx = line.indexOf(':');
|
|
475
|
+
const field = colonIdx === -1 ? line : line.slice(0, colonIdx);
|
|
476
|
+
let val = colonIdx === -1 ? '' : line.slice(colonIdx + 1);
|
|
477
|
+
if (val.startsWith(' '))
|
|
478
|
+
val = val.slice(1);
|
|
479
|
+
if (field === 'id')
|
|
480
|
+
id = val;
|
|
481
|
+
else if (field === 'event')
|
|
482
|
+
event = val;
|
|
483
|
+
else if (field === 'data')
|
|
484
|
+
data.push(val);
|
|
485
|
+
// `retry:` and unknown fields ignored per SSE spec.
|
|
486
|
+
}
|
|
487
|
+
// No `\n` left in the buffer — record the boundary so the next
|
|
488
|
+
// decoded chunk's scan starts here instead of re-scanning the
|
|
489
|
+
// already-checked prefix.
|
|
490
|
+
scanStart = buffer.length;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
signal.removeEventListener('abort', onAbort);
|
|
495
|
+
try {
|
|
496
|
+
reader.releaseLock();
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
// Already released.
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Lift a raw SSE frame into a typed `TempoEvent`. Returns `null` for
|
|
505
|
+
* unknown event kinds (forward compatibility per §6) or invalid JSON
|
|
506
|
+
* payloads — both are skipped silently per the spec.
|
|
507
|
+
*/
|
|
508
|
+
function parseTempoEvent(raw) {
|
|
509
|
+
if (!raw.event || !SSE_KIND_SET.has(raw.event))
|
|
510
|
+
return null;
|
|
511
|
+
let envelope;
|
|
512
|
+
try {
|
|
513
|
+
envelope = JSON.parse(raw.data);
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
return liftEnvelope(envelope, raw.event, raw.id);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Unwrap the on-wire envelope `{ v, eventId, payload }` produced by
|
|
522
|
+
* `frameSseEvent` in `src/http/sse-handler.ts`. Returns `null` for
|
|
523
|
+
* structurally-invalid frames so a single bad event can't kill the
|
|
524
|
+
* stream (§6 spec — consumers MUST skip).
|
|
525
|
+
*/
|
|
526
|
+
function liftEnvelope(envelope, type, rawId) {
|
|
527
|
+
if (typeof envelope !== 'object' || envelope === null)
|
|
528
|
+
return null;
|
|
529
|
+
const env = envelope;
|
|
530
|
+
if (typeof env.payload !== 'object' || env.payload === null)
|
|
531
|
+
return null;
|
|
532
|
+
// `||` (not `??`) — `EventSource.lastEventId` is always a `string` and
|
|
533
|
+
// is `''` when no `id:` line was sent, so we have to coerce the empty
|
|
534
|
+
// string to the envelope's `eventId` like the pre-#351 EventSource
|
|
535
|
+
// parser did. The fetch parser produces `string | undefined`; both
|
|
536
|
+
// paths share this fallback chain.
|
|
537
|
+
const fromEnv = typeof env.eventId === 'string' ? env.eventId : '';
|
|
538
|
+
const eventId = rawId || fromEnv || '0:0';
|
|
539
|
+
return {
|
|
540
|
+
v: 1,
|
|
541
|
+
eventId,
|
|
542
|
+
type,
|
|
543
|
+
payload: env.payload,
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
// ── Helpers ────────────────────────────────────────────────────────────────
|
|
547
|
+
function backoffMs(attempt) {
|
|
548
|
+
// 100, 200, 400, 800, … capped at 30 000 ms per §7.5.
|
|
549
|
+
return Math.min(RECONNECT_INITIAL_MS * Math.pow(2, attempt), RECONNECT_MAX_MS);
|
|
550
|
+
}
|
|
551
|
+
function parseRetryAfter(header) {
|
|
552
|
+
if (!header)
|
|
553
|
+
return null;
|
|
554
|
+
const seconds = Number(header);
|
|
555
|
+
if (!Number.isFinite(seconds) || seconds < 0)
|
|
556
|
+
return null;
|
|
557
|
+
return Math.round(seconds * 1000);
|
|
558
|
+
}
|
|
559
|
+
async function safeReadBodyText(response) {
|
|
560
|
+
try {
|
|
561
|
+
return await response.text();
|
|
562
|
+
}
|
|
563
|
+
catch {
|
|
564
|
+
return '';
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function defaultSleep(ms, signal) {
|
|
568
|
+
return new Promise((resolve, reject) => {
|
|
569
|
+
if (signal?.aborted) {
|
|
570
|
+
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const timer = setTimeout(() => {
|
|
574
|
+
cleanup();
|
|
575
|
+
resolve();
|
|
576
|
+
}, ms);
|
|
577
|
+
const onAbort = () => {
|
|
578
|
+
clearTimeout(timer);
|
|
579
|
+
cleanup();
|
|
580
|
+
reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
|
|
581
|
+
};
|
|
582
|
+
const cleanup = () => {
|
|
583
|
+
signal?.removeEventListener('abort', onAbort);
|
|
584
|
+
};
|
|
585
|
+
if (signal)
|
|
586
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
function globalFetch() {
|
|
590
|
+
// Node 20+ ships fetch as a global; modern browsers do too. We resolve
|
|
591
|
+
// it lazily so the import doesn't fail at module-load on environments
|
|
592
|
+
// that polyfill late (e.g. tests injecting `fetchImpl`).
|
|
593
|
+
const f = globalThis.fetch;
|
|
594
|
+
if (!f) {
|
|
595
|
+
throw new Error('[agent-tempo:subscribe] global fetch is not available; pass deps.fetchImpl');
|
|
596
|
+
}
|
|
597
|
+
return f;
|
|
598
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `TempoClientWithSpawn` — TTY-bound superset of {@link TempoClientCore}.
|
|
3
|
+
*
|
|
4
|
+
* Composes {@link createTempoClientCore} with the two methods that shell
|
|
5
|
+
* out to a local `agent-tempo up …` invocation. **Never import this from
|
|
6
|
+
* the daemon, MCP tools, or the SSE event source** — those contexts have
|
|
7
|
+
* no TTY and a stray spawn would launch a terminal nothing can render.
|
|
8
|
+
*
|
|
9
|
+
* The boundary is enforced by the type system: headless callers depend on
|
|
10
|
+
* `TempoClientCore`; only the TUI (and its `ensure-conductor-spawned`
|
|
11
|
+
* helper) opts into the spawn surface.
|
|
12
|
+
*
|
|
13
|
+
* See `docs/adr/0007-tempoclient-core-withspawn-split.md`.
|
|
14
|
+
*/
|
|
15
|
+
import type { Client } from '@temporalio/client';
|
|
16
|
+
import { type CreateTempoClientOpts } from './core';
|
|
17
|
+
import type { TempoClientWithSpawn } from './interface';
|
|
18
|
+
export type { CreateTempoClientOpts } from './core';
|
|
19
|
+
/**
|
|
20
|
+
* Build a `TempoClientWithSpawn` over a configured Temporal `Client`.
|
|
21
|
+
* Composes {@link createTempoClientCore} and adds the two TTY-bound
|
|
22
|
+
* spawn methods. Use this for TUI-side consumers; headless callers
|
|
23
|
+
* should use {@link createTempoClientCore} directly.
|
|
24
|
+
*
|
|
25
|
+
* `opts` (e.g. `subscribeDeps`) is forwarded to the Core factory.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createTempoClientWithSpawn(client: Client, opts?: CreateTempoClientOpts): TempoClientWithSpawn;
|