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,710 @@
|
|
|
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.OpenCodeAttachment = exports.opencodeDescriptor = void 0;
|
|
37
|
+
/**
|
|
38
|
+
* Headless OpenCode adapter — SDK class.
|
|
39
|
+
*
|
|
40
|
+
* Issue #449 Phase C. Drives [SST OpenCode](https://opencode.ai) as a
|
|
41
|
+
* headless local subprocess for multi-provider LLM access (Anthropic,
|
|
42
|
+
* OpenAI, Bedrock, Vertex, Ollama, ~70+ providers via OpenCode's
|
|
43
|
+
* `provider/model` selector). Mirrors the claude-api / Copilot bridge
|
|
44
|
+
* structure: detached Node subprocess, dual-purpose entry point,
|
|
45
|
+
* `claimAttachment` + heartbeat lifecycle inherited from `SdkAttachment`.
|
|
46
|
+
*
|
|
47
|
+
* Class hierarchy: `OpenCodeAttachment extends SdkAttachment extends BaseAttachment`.
|
|
48
|
+
* Concrete adapter overrides `invokeSdk` (HTTP/SSE round-trip via
|
|
49
|
+
* `OpenCodeServerBridge`) and `onSuperseded` (graceful `POST /session/:id/abort`
|
|
50
|
+
* with subprocess-kill fallback). Everything else (claim, heartbeat,
|
|
51
|
+
* phase watcher, `processingStart`/`End` pairing, `markDelivered`) is free.
|
|
52
|
+
*
|
|
53
|
+
* What's NEW vs claude-api / copilot:
|
|
54
|
+
* - Adapter manages a sibling `opencode serve` subprocess (probed-free
|
|
55
|
+
* port, hardcoded loopback bind, `mdns: false`).
|
|
56
|
+
* - Tool bridging is MCP-NATIVE — OpenCode spawns `dist/server.js` as
|
|
57
|
+
* its own MCP child via the `OPENCODE_CONFIG_CONTENT` env. No
|
|
58
|
+
* in-process MCP bridge / no schema translation layer.
|
|
59
|
+
* - Server-side history — adapter sends only the new turn's parts;
|
|
60
|
+
* OpenCode appends to its own session record.
|
|
61
|
+
* - PID file is two-line (adapter PID + opencode-serve PID).
|
|
62
|
+
*
|
|
63
|
+
* Design reference: `docs/design/449-opencode-adapter.md`. ADR locked at
|
|
64
|
+
* `docs/adr/0015-opencode-adapter.md`.
|
|
65
|
+
*/
|
|
66
|
+
const fs = __importStar(require("fs"));
|
|
67
|
+
const os = __importStar(require("os"));
|
|
68
|
+
const path = __importStar(require("path"));
|
|
69
|
+
const child_process_1 = require("child_process");
|
|
70
|
+
const client_1 = require("@temporalio/client");
|
|
71
|
+
const base_1 = require("../sdk/base");
|
|
72
|
+
const config_1 = require("../../config");
|
|
73
|
+
const connection_1 = require("../../connection");
|
|
74
|
+
const signals_1 = require("../../workflows/signals");
|
|
75
|
+
const server_tools_1 = require("../../server-tools");
|
|
76
|
+
const config_2 = require("./config");
|
|
77
|
+
const server_bridge_1 = require("./server-bridge");
|
|
78
|
+
const helpers_1 = require("./helpers");
|
|
79
|
+
const sdk_probe_1 = require("../../utils/sdk-probe");
|
|
80
|
+
/**
|
|
81
|
+
* Descriptor for the opencode adapter. Colocated with the class so
|
|
82
|
+
* `adapter.ts` has no import dependency on `index.ts` (avoids the
|
|
83
|
+
* circular module-graph cycle QA flagged on copilot's PR-B).
|
|
84
|
+
*/
|
|
85
|
+
exports.opencodeDescriptor = {
|
|
86
|
+
adapterId: 'opencode',
|
|
87
|
+
adapterClass: 'sdk',
|
|
88
|
+
blocksOnLLMTurn: true,
|
|
89
|
+
heartbeatMs: 30_000,
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Optional-dep gate. The adapter uses raw `fetch` for the hot path (per
|
|
93
|
+
* design §8.3 #2) so the SDK isn't actually imported, but the install
|
|
94
|
+
* presence is the operator's signal that opencode integration is intended
|
|
95
|
+
* on this host. ADR 0015 §85.
|
|
96
|
+
*
|
|
97
|
+
* `probeSdkInstall` walks the filesystem rather than calling
|
|
98
|
+
* `require.resolve` because `@opencode-ai/sdk` ships an ESM-only
|
|
99
|
+
* `exports` map with no CJS-resolvable entry — see the helper's docblock.
|
|
100
|
+
*/
|
|
101
|
+
const opencodeSdkAvailable = (0, sdk_probe_1.probeSdkInstall)('@opencode-ai/sdk');
|
|
102
|
+
if (!opencodeSdkAvailable && require.main === module) {
|
|
103
|
+
console.error('Error: @opencode-ai/sdk is not installed.\n' +
|
|
104
|
+
'Install it with: npm install @opencode-ai/sdk\n' +
|
|
105
|
+
'And install the opencode binary: npm install -g opencode-ai\n' +
|
|
106
|
+
'Or recruit with a different agent (claude / copilot / claude-api).');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Unbuffered stderr logger. Errors are unpacked into `message + stack`
|
|
111
|
+
* because the default `JSON.stringify(err)` drops both fields (they're
|
|
112
|
+
* non-enumerable) and renders `Error` as `{}` — useless for debugging.
|
|
113
|
+
*/
|
|
114
|
+
const log = (...args) => {
|
|
115
|
+
const msg = `[agent-tempo:opencode] ${args.map((a) => {
|
|
116
|
+
if (typeof a === 'string')
|
|
117
|
+
return a;
|
|
118
|
+
if (a instanceof Error)
|
|
119
|
+
return a.stack ? `${a.message}\n${a.stack}` : a.message;
|
|
120
|
+
try {
|
|
121
|
+
return JSON.stringify(a);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return String(a);
|
|
125
|
+
}
|
|
126
|
+
}).join(' ')}\n`;
|
|
127
|
+
fs.writeSync(2, msg);
|
|
128
|
+
};
|
|
129
|
+
/** Default model id — reviewable at next minor SDK bump per ADR 0015 §51. */
|
|
130
|
+
const DEFAULT_MODEL = 'anthropic/claude-opus-4-7';
|
|
131
|
+
/** Tested-pinned OpenCode SDK version — drift triggers a stderr WARNING. */
|
|
132
|
+
const TESTED_OPENCODE_VERSION = '~1.14.29';
|
|
133
|
+
/** Idle poll cadence — short enough for snappy cue delivery, loose on Temporal. */
|
|
134
|
+
const POLL_INTERVAL_MS = 2000;
|
|
135
|
+
/** Workflow-register poll bounds. */
|
|
136
|
+
const WORKFLOW_REGISTER_ATTEMPTS = 30;
|
|
137
|
+
const WORKFLOW_REGISTER_INTERVAL_MS = 1000;
|
|
138
|
+
/** Workflow status check cadence (every N polls). */
|
|
139
|
+
const WORKFLOW_STATUS_CHECK_INTERVAL = 15;
|
|
140
|
+
/** Per-turn timeout — opencode tool-use chains can run minutes. */
|
|
141
|
+
const TURN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
142
|
+
/** opencode serve health-probe timeout. */
|
|
143
|
+
const HEALTH_PROBE_TIMEOUT_MS = 10_000;
|
|
144
|
+
/** Subprocess SIGTERM grace before SIGKILL escalation. */
|
|
145
|
+
const SIGTERM_TIMEOUT_MS = 5_000;
|
|
146
|
+
/** OpenCode-specific system-prompt addendum — design §10. */
|
|
147
|
+
const HEADLESS_OPENCODE_ADDENDUM = '\n\nYou are an **opencode** player — you have access to the agent-tempo MCP tools ' +
|
|
148
|
+
'(cue, report, recall, ensemble, broadcast, recruit, set_part, …) AND OpenCode\'s built-in ' +
|
|
149
|
+
'tools (file edits, shell, web search). Use the agent-tempo tools for ensemble coordination ' +
|
|
150
|
+
'and OpenCode\'s built-ins for local task work. Your model is delivered via OpenCode, so the ' +
|
|
151
|
+
'underlying provider (Anthropic, OpenAI, Bedrock, Ollama, …) is opaque to you and to the rest ' +
|
|
152
|
+
'of the ensemble.';
|
|
153
|
+
/**
|
|
154
|
+
* SDK-class adapter for OpenCode. Pull-based delivery: poll workflow for
|
|
155
|
+
* pending messages, post each batch to `prompt_async`, observe SSE until
|
|
156
|
+
* `finish`, ack via `markDelivered`. `processingStart`/`End` paired by
|
|
157
|
+
* `SdkAttachment.deliver()`.
|
|
158
|
+
*/
|
|
159
|
+
class OpenCodeAttachment extends base_1.SdkAttachment {
|
|
160
|
+
descriptor = exports.opencodeDescriptor;
|
|
161
|
+
/** Resolved model id (recruit-arg → ENV.OPENCODE_MODEL → DEFAULT_MODEL). */
|
|
162
|
+
model;
|
|
163
|
+
/** Probed-free port the opencode serve subprocess binds. */
|
|
164
|
+
port = 0;
|
|
165
|
+
/** Subprocess handle — `null` before run() spawns, after cleanup kills. */
|
|
166
|
+
serveProcess = null;
|
|
167
|
+
/** HTTP/SSE bridge — `null` before run() boots the subprocess. */
|
|
168
|
+
bridge = null;
|
|
169
|
+
/** OpenCode session id — created on first turn, stashed on workflow metadata. */
|
|
170
|
+
openCodeSessionId = null;
|
|
171
|
+
/** AbortController for the in-flight SSE consumer / prompt_async fetch. */
|
|
172
|
+
inFlightAbortController = null;
|
|
173
|
+
/** Cached for the per-turn telemetry log. */
|
|
174
|
+
playerName = '';
|
|
175
|
+
/** Built once at session start; sent every turn (cheap; OpenCode caches server-side). */
|
|
176
|
+
systemPrompt = '';
|
|
177
|
+
constructor(opts = {}) {
|
|
178
|
+
super();
|
|
179
|
+
this.model = opts.model ?? process.env[config_1.ENV.OPENCODE_MODEL] ?? DEFAULT_MODEL;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Lease-revocation hook. Two-step graceful → SIGTERM → SIGKILL fallback
|
|
183
|
+
* per design §6.1: post `/session/:id/abort` first (cleanest, no
|
|
184
|
+
* subprocess kill), abort in-flight fetch via `inFlightAbortController`,
|
|
185
|
+
* fall back to subprocess kill if HTTP abort hangs.
|
|
186
|
+
*/
|
|
187
|
+
onSuperseded() {
|
|
188
|
+
log('lease revoked — aborting in-flight + posting /session/abort');
|
|
189
|
+
const ctrl = this.inFlightAbortController;
|
|
190
|
+
this.inFlightAbortController = null;
|
|
191
|
+
if (ctrl) {
|
|
192
|
+
try {
|
|
193
|
+
ctrl.abort();
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
log('abort threw:', err?.message ?? err);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (this.openCodeSessionId && this.bridge) {
|
|
200
|
+
// Fire-and-forget — the SSE consumer above will tear down on its
|
|
201
|
+
// own; this just nudges OpenCode to release the session.
|
|
202
|
+
void this.bridge.abortSession(this.openCodeSessionId)
|
|
203
|
+
.catch((err) => log('graceful abort failed:', err?.message ?? err));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Subprocess entry point. Boots `opencode serve`, connects Temporal,
|
|
208
|
+
* claims the attachment, runs the poll loop. The optional-dep gate at
|
|
209
|
+
* module load (above) has already exited the process if the SDK isn't
|
|
210
|
+
* installed, so by the time `run()` executes we know it's available.
|
|
211
|
+
*/
|
|
212
|
+
async run() {
|
|
213
|
+
const config = (0, config_1.getConfig)();
|
|
214
|
+
const isConductor = process.env[config_1.ENV.CONDUCTOR] === 'true';
|
|
215
|
+
const requestedName = process.env[config_1.ENV.PLAYER_NAME] || '';
|
|
216
|
+
const playerIdForWorkflow = isConductor
|
|
217
|
+
? 'conductor'
|
|
218
|
+
: (requestedName && requestedName !== 'conductor' ? requestedName : '') || `opencode-${Date.now()}`;
|
|
219
|
+
const expectedWorkflowId = `agent-session-${config.ensemble}-${playerIdForWorkflow}`;
|
|
220
|
+
const workDir = process.cwd();
|
|
221
|
+
log(`Starting opencode adapter in ${workDir} (ensemble: ${config.ensemble}, player: ${playerIdForWorkflow}, model: ${this.model})`);
|
|
222
|
+
// (1) Probe a free port on loopback. Cheap insurance against port 4096
|
|
223
|
+
// collisions in CI / multi-ensemble setups.
|
|
224
|
+
this.port = await (0, helpers_1.probeFreePort)();
|
|
225
|
+
log(`Probed free port: ${this.port}`);
|
|
226
|
+
// (2) Synthesize OPENCODE_CONFIG_CONTENT. Provider env auto-detected
|
|
227
|
+
// from the model's `provider/...` prefix; secrets stay as `{env:VAR}`
|
|
228
|
+
// markers (OpenCode resolves at read time).
|
|
229
|
+
const mcpServerPath = path.resolve(__dirname, '../../server.js');
|
|
230
|
+
const configContent = (0, config_2.synthesizeOpenCodeConfig)({
|
|
231
|
+
model: this.model,
|
|
232
|
+
port: this.port,
|
|
233
|
+
mcpServerPath,
|
|
234
|
+
ensemble: config.ensemble,
|
|
235
|
+
playerName: playerIdForWorkflow,
|
|
236
|
+
temporalAddress: config.temporalAddress,
|
|
237
|
+
temporalNamespace: config.temporalNamespace,
|
|
238
|
+
});
|
|
239
|
+
log(`Synthesized OPENCODE_CONFIG_CONTENT: ${(0, helpers_1.redactSecrets)(configContent)}`);
|
|
240
|
+
// (3) Spawn opencode serve. Stdio redirected to a per-player log file
|
|
241
|
+
// so terminal noise from opencode doesn't clutter the adapter's log.
|
|
242
|
+
const logDir = path.join(workDir, 'logs');
|
|
243
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
244
|
+
const opencodeLogFile = path.join(logDir, `opencode-${playerIdForWorkflow}.log`);
|
|
245
|
+
const logFd = fs.openSync(opencodeLogFile, 'a');
|
|
246
|
+
// Windows: npm-installed binaries land as `.cmd` shims. Node's
|
|
247
|
+
// `CreateProcess` won't run `.cmd` files directly, and `shell: true`
|
|
248
|
+
// trips DEP0190 (the args-not-escaped warning). Use the same
|
|
249
|
+
// `cmd.exe /c <bin> <args>` pattern that `spawnInTerminal` uses for
|
|
250
|
+
// Windows Terminal — explicit, no shell: true, no deprecation.
|
|
251
|
+
const isWindows = process.platform === 'win32';
|
|
252
|
+
const spawnCmd = isWindows ? 'cmd.exe' : 'opencode';
|
|
253
|
+
const spawnArgs = isWindows
|
|
254
|
+
? ['/c', 'opencode', 'serve', '--port', String(this.port), '--hostname', '127.0.0.1']
|
|
255
|
+
: ['serve', '--port', String(this.port), '--hostname', '127.0.0.1'];
|
|
256
|
+
try {
|
|
257
|
+
this.serveProcess = (0, child_process_1.spawn)(spawnCmd, spawnArgs, {
|
|
258
|
+
// Loopback hardcoded — see config.ts SECURITY note + ADR 0015 §53.
|
|
259
|
+
stdio: ['ignore', logFd, logFd],
|
|
260
|
+
env: { ...process.env, OPENCODE_CONFIG_CONTENT: configContent },
|
|
261
|
+
// `detached: false` — child dies when parent dies (Linux) or is
|
|
262
|
+
// killed via job object (Windows). We add explicit signal
|
|
263
|
+
// handling below to clean up on SIGTERM/SIGINT.
|
|
264
|
+
detached: false,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
// Close our fd handle immediately — the spawn cloned it for the child.
|
|
269
|
+
try {
|
|
270
|
+
fs.closeSync(logFd);
|
|
271
|
+
}
|
|
272
|
+
catch { /* ignore */ }
|
|
273
|
+
}
|
|
274
|
+
if (!this.serveProcess.pid) {
|
|
275
|
+
throw new Error('Failed to spawn `opencode serve` — is the binary on PATH?');
|
|
276
|
+
}
|
|
277
|
+
log(`Spawned opencode serve (pid=${this.serveProcess.pid}, port=${this.port}, log=${opencodeLogFile})`);
|
|
278
|
+
// Surface early child-exit (e.g. ENOENT, missing config) loudly. The
|
|
279
|
+
// health-probe loop below would also catch this but with a less
|
|
280
|
+
// diagnostic error.
|
|
281
|
+
this.serveProcess.once('exit', (code, signal) => {
|
|
282
|
+
log(`opencode serve exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})`);
|
|
283
|
+
});
|
|
284
|
+
// (4) Health-probe + version-drift gate.
|
|
285
|
+
this.bridge = new server_bridge_1.OpenCodeServerBridge({
|
|
286
|
+
baseUrl: `http://127.0.0.1:${this.port}`,
|
|
287
|
+
log,
|
|
288
|
+
});
|
|
289
|
+
await this.bridge.waitForHealth(HEALTH_PROBE_TIMEOUT_MS);
|
|
290
|
+
try {
|
|
291
|
+
const health = await this.bridge.getHealth();
|
|
292
|
+
if (!(0, helpers_1.isVersionMatch)(health.version, TESTED_OPENCODE_VERSION)) {
|
|
293
|
+
log(`WARNING: opencode version ${health.version} drift from tested ${TESTED_OPENCODE_VERSION}`);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
log(`opencode version ${health.version} (matches tested ${TESTED_OPENCODE_VERSION})`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
// Health endpoint returned but version field missing / malformed —
|
|
301
|
+
// log + continue. Version-drift is diagnostic, not blocking.
|
|
302
|
+
log(`getHealth post-ready failed: ${err?.message ?? err}`);
|
|
303
|
+
}
|
|
304
|
+
// (5) Connect Temporal, wait for workflow, hand client to BaseAttachment.
|
|
305
|
+
const connection = await (0, connection_1.createTemporalConnection)(config);
|
|
306
|
+
const client = new client_1.Client({ connection, namespace: config.temporalNamespace });
|
|
307
|
+
this.configureV2(client, os.hostname());
|
|
308
|
+
log(`Waiting for workflow ${expectedWorkflowId} to register...`);
|
|
309
|
+
let handle = client.workflow.getHandle(expectedWorkflowId);
|
|
310
|
+
let pinnedRunId;
|
|
311
|
+
let workflowReady = false;
|
|
312
|
+
for (let attempt = 0; attempt < WORKFLOW_REGISTER_ATTEMPTS; attempt++) {
|
|
313
|
+
try {
|
|
314
|
+
const desc = await handle.describe();
|
|
315
|
+
if (desc.status.name === 'RUNNING') {
|
|
316
|
+
workflowReady = true;
|
|
317
|
+
pinnedRunId = desc.runId;
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
catch { /* not yet started */ }
|
|
322
|
+
await sleep(WORKFLOW_REGISTER_INTERVAL_MS);
|
|
323
|
+
if (attempt % 5 === 4)
|
|
324
|
+
log(`Still waiting for workflow... attempt ${attempt + 1}/${WORKFLOW_REGISTER_ATTEMPTS}`);
|
|
325
|
+
}
|
|
326
|
+
if (!workflowReady) {
|
|
327
|
+
log(`ERROR: Workflow ${expectedWorkflowId} did not register within ${WORKFLOW_REGISTER_ATTEMPTS}s — exiting`);
|
|
328
|
+
await this.killSubprocessChain();
|
|
329
|
+
process.exit(1);
|
|
330
|
+
}
|
|
331
|
+
handle = client.workflow.getHandle(expectedWorkflowId, pinnedRunId);
|
|
332
|
+
log(`Workflow ready: ${expectedWorkflowId} (pinned runId ${pinnedRunId})`);
|
|
333
|
+
// (6) Build cached system prompt — agent-tempo MCP_INSTRUCTIONS plus
|
|
334
|
+
// the opencode-specific addendum. Sent every turn; OpenCode caches
|
|
335
|
+
// server-side per its provider transform layer.
|
|
336
|
+
this.systemPrompt = (0, server_tools_1.buildServerInstructions)({
|
|
337
|
+
ensemble: config.ensemble,
|
|
338
|
+
playerId: playerIdForWorkflow,
|
|
339
|
+
isConductor,
|
|
340
|
+
hasRequestedName: true,
|
|
341
|
+
}) + HEADLESS_OPENCODE_ADDENDUM;
|
|
342
|
+
this.playerName = playerIdForWorkflow;
|
|
343
|
+
// (7) Wire terminal-cleanup hook BEFORE claiming so a race between
|
|
344
|
+
// claim + lease loss can't drop the event.
|
|
345
|
+
let shuttingDown = false;
|
|
346
|
+
const cleanup = async () => {
|
|
347
|
+
if (shuttingDown)
|
|
348
|
+
return;
|
|
349
|
+
shuttingDown = true;
|
|
350
|
+
log('Cleanup running...');
|
|
351
|
+
// Best-effort opencode session teardown — avoids leaking server-side
|
|
352
|
+
// state if the subprocess outlives this call.
|
|
353
|
+
if (this.openCodeSessionId && this.bridge) {
|
|
354
|
+
await this.bridge.abortSession(this.openCodeSessionId).catch(() => { });
|
|
355
|
+
await this.bridge.deleteSession(this.openCodeSessionId).catch(() => { });
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
await this.detachGracefully('user-stop');
|
|
359
|
+
}
|
|
360
|
+
catch (err) {
|
|
361
|
+
log('detachGracefully error:', err?.message ?? err);
|
|
362
|
+
}
|
|
363
|
+
await this.killSubprocessChain();
|
|
364
|
+
};
|
|
365
|
+
this.onTerminal((reason) => {
|
|
366
|
+
log(`V2 terminal (${reason}) — triggering cleanup`);
|
|
367
|
+
cleanup().catch((err) => log('terminal cleanup error:', err?.message ?? err));
|
|
368
|
+
});
|
|
369
|
+
// (8) Claim the attachment via V2 lifecycle.
|
|
370
|
+
try {
|
|
371
|
+
const expectedAttachmentId = process.env[config_1.ENV.ATTACHMENT_ID] || undefined;
|
|
372
|
+
handle = await this.startV2Lifecycle(expectedWorkflowId, expectedAttachmentId);
|
|
373
|
+
log(`V2 attachment claimed (attachmentId=${this.token?.attachmentId}${expectedAttachmentId ? ', renewed' : ''})`);
|
|
374
|
+
}
|
|
375
|
+
catch (err) {
|
|
376
|
+
log(`ERROR: V2 claimAttachment failed: ${err?.message ?? err}`);
|
|
377
|
+
await cleanup();
|
|
378
|
+
process.exit(1);
|
|
379
|
+
}
|
|
380
|
+
// (9) PID file — two lines (adapter PID + opencode-serve PID) per
|
|
381
|
+
// design §6.5. Operators can grep / kill either.
|
|
382
|
+
const pidFile = path.join(logDir, `${playerIdForWorkflow}.pid`);
|
|
383
|
+
try {
|
|
384
|
+
fs.writeFileSync(pidFile, `${process.pid}\n${this.serveProcess.pid ?? ''}\n`);
|
|
385
|
+
}
|
|
386
|
+
catch (err) {
|
|
387
|
+
log(`Warning: PID file write failed: ${err?.message ?? err}`);
|
|
388
|
+
}
|
|
389
|
+
// (10) Signal handlers. SIGTERM behaviour on Windows differs from POSIX
|
|
390
|
+
// (ADR 0015 §84) — handled below in killSubprocessChain.
|
|
391
|
+
const shutdown = async () => {
|
|
392
|
+
log('Shutting down (signal received)...');
|
|
393
|
+
await cleanup();
|
|
394
|
+
try {
|
|
395
|
+
fs.unlinkSync(pidFile);
|
|
396
|
+
}
|
|
397
|
+
catch { /* gone */ }
|
|
398
|
+
process.exit(0);
|
|
399
|
+
};
|
|
400
|
+
process.on('SIGINT', shutdown);
|
|
401
|
+
process.on('SIGTERM', shutdown);
|
|
402
|
+
// (11) Drive the poll loop.
|
|
403
|
+
await this.pollLoop(handle);
|
|
404
|
+
try {
|
|
405
|
+
fs.unlinkSync(pidFile);
|
|
406
|
+
}
|
|
407
|
+
catch { /* gone */ }
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Poll the workflow for pending messages; call `deliver()` per batch.
|
|
411
|
+
* Mirrors claude-api / copilot — only the `invokeSdk` body differs.
|
|
412
|
+
*/
|
|
413
|
+
async pollLoop(handle) {
|
|
414
|
+
let polling = true;
|
|
415
|
+
let processing = false;
|
|
416
|
+
let pollCount = 0;
|
|
417
|
+
while (polling && !this.shouldStop()) {
|
|
418
|
+
pollCount++;
|
|
419
|
+
// Periodic workflow-status check — detect external destroy / completion.
|
|
420
|
+
if (pollCount % WORKFLOW_STATUS_CHECK_INTERVAL === 0) {
|
|
421
|
+
try {
|
|
422
|
+
const desc = await handle.describe();
|
|
423
|
+
if (desc.status.name !== 'RUNNING') {
|
|
424
|
+
log(`Workflow status is ${desc.status.name} — exiting cleanly`);
|
|
425
|
+
polling = false;
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
try {
|
|
429
|
+
const isDestroyed = await handle.query(signals_1.isDestroyedQuery);
|
|
430
|
+
if (isDestroyed) {
|
|
431
|
+
log('Workflow destroyed — exiting cleanly');
|
|
432
|
+
polling = false;
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
catch { /* isDestroyed query unavailable on pre-upgrade workflows — safe to skip */ }
|
|
437
|
+
}
|
|
438
|
+
catch (err) {
|
|
439
|
+
log(`Workflow describe failed: ${err?.message ?? err} — treating as terminated`);
|
|
440
|
+
polling = false;
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (processing) {
|
|
445
|
+
await sleep(POLL_INTERVAL_MS);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
let messages = [];
|
|
449
|
+
try {
|
|
450
|
+
messages = await handle.query(signals_1.pendingMessagesQuery);
|
|
451
|
+
}
|
|
452
|
+
catch (err) {
|
|
453
|
+
log(`pendingMessages query failed: ${err?.message ?? err}`);
|
|
454
|
+
await sleep(POLL_INTERVAL_MS);
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
if (messages.length === 0) {
|
|
458
|
+
await sleep(POLL_INTERVAL_MS);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
processing = true;
|
|
462
|
+
try {
|
|
463
|
+
const ackIds = messages.map((m) => m.id);
|
|
464
|
+
// The whole batch is delivered as one OpenCode prompt — the
|
|
465
|
+
// representative `messages[0]` only drives processingStart/End.
|
|
466
|
+
await this.deliver(handle, messages[0],
|
|
467
|
+
/* prompt unused — invokeSdk reads `messages` directly via closure */ '', TURN_TIMEOUT_MS, (timeoutPrompt, timeoutMs) => this.invokeSdkWithBatch(messages, timeoutPrompt, timeoutMs), ackIds);
|
|
468
|
+
}
|
|
469
|
+
catch (err) {
|
|
470
|
+
log(`deliver error: ${err?.message ?? err}`);
|
|
471
|
+
// Don't exit the loop — transient failures leave messages PENDING
|
|
472
|
+
// for the next poll. Only terminal events (lease loss, destroy)
|
|
473
|
+
// exit via onTerminal / status-check above.
|
|
474
|
+
}
|
|
475
|
+
finally {
|
|
476
|
+
processing = false;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
log('Poll loop exiting');
|
|
480
|
+
}
|
|
481
|
+
/** True once `onTerminal` fired and cleanup tore down the V2 token. */
|
|
482
|
+
shouldStop() {
|
|
483
|
+
return this.token === null;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Per-turn LLM dispatch. Closes over the message batch so SdkAttachment's
|
|
487
|
+
* `invokeSdk` callback can stay (prompt, timeoutMs) shaped while the
|
|
488
|
+
* actual driving data is the `messages[]` from the poll.
|
|
489
|
+
*
|
|
490
|
+
* Steps per design §5.4:
|
|
491
|
+
* 1. Ensure OpenCode session exists (create on first turn, stash id
|
|
492
|
+
* on workflow metadata via the existing updateMetadataSignal
|
|
493
|
+
* sessionId field — same field copilot uses).
|
|
494
|
+
* 2. Subscribe to `/event` SSE.
|
|
495
|
+
* 3. POST `/session/:id/prompt_async` (returns 204, turn streams).
|
|
496
|
+
* 4. Consume SSE until `finish` reason — accumulate text, observe
|
|
497
|
+
* tool-use breadcrumbs.
|
|
498
|
+
* 5. Log per-turn `turn-usage` line (design §5.6).
|
|
499
|
+
*/
|
|
500
|
+
async invokeSdkWithBatch(messages, _prompt, _timeoutMs) {
|
|
501
|
+
// `bridge` and `pinnedHandle` are guaranteed initialized by `run()`
|
|
502
|
+
// before the poll loop ever enters `deliver()`. Narrow once via
|
|
503
|
+
// non-null assertions so the rest of the method reads cleanly.
|
|
504
|
+
const bridge = this.bridge;
|
|
505
|
+
const handle = this.pinnedHandle;
|
|
506
|
+
// (1) Lazy-create the OpenCode session on first turn. Q6 carry-forward
|
|
507
|
+
// (design §5.2): Path A is implemented (re-attach to stashed id on
|
|
508
|
+
// restart). If the impl-time experiment shows OpenCode does NOT
|
|
509
|
+
// persist sessions across server restart, swap this for the
|
|
510
|
+
// workflow-history rebuild path documented in README.md.
|
|
511
|
+
if (!this.openCodeSessionId) {
|
|
512
|
+
const session = await bridge.createSession();
|
|
513
|
+
this.openCodeSessionId = session.id;
|
|
514
|
+
try {
|
|
515
|
+
await handle.signal(signals_1.updateMetadataSignal, { sessionId: session.id });
|
|
516
|
+
}
|
|
517
|
+
catch (err) {
|
|
518
|
+
log(`updateMetadata sessionId signal failed: ${err?.message ?? err}`);
|
|
519
|
+
}
|
|
520
|
+
log(`Created OpenCode session ${session.id}`);
|
|
521
|
+
}
|
|
522
|
+
const sessionId = this.openCodeSessionId;
|
|
523
|
+
// (2) Build the new turn's parts. Multi-cue batching mirrors
|
|
524
|
+
// claude-api's history-fold pattern: every queued message becomes
|
|
525
|
+
// one labelled text part; OpenCode appends to its server-side history.
|
|
526
|
+
const parts = messages.map((m) => ({
|
|
527
|
+
type: 'text',
|
|
528
|
+
text: `[from ${m.from}]: ${m.text}`,
|
|
529
|
+
}));
|
|
530
|
+
this.inFlightAbortController = new AbortController();
|
|
531
|
+
const t0 = Date.now();
|
|
532
|
+
let assistantText = '';
|
|
533
|
+
let stopReason = null;
|
|
534
|
+
let usage = null;
|
|
535
|
+
let providerSeen = null;
|
|
536
|
+
try {
|
|
537
|
+
// (3) Subscribe to SSE BEFORE posting prompt_async — a tight race
|
|
538
|
+
// window otherwise drops the early `Message` events that announce
|
|
539
|
+
// the turn's session. SSE consumer iterates concurrently with the
|
|
540
|
+
// prompt_async fetch.
|
|
541
|
+
const sse = bridge.subscribeEvents(this.inFlightAbortController.signal);
|
|
542
|
+
// (4) Post the turn. Returns 204 immediately; output streams over SSE.
|
|
543
|
+
await bridge.promptAsync(sessionId, {
|
|
544
|
+
model: this.model,
|
|
545
|
+
system: this.systemPrompt,
|
|
546
|
+
parts,
|
|
547
|
+
});
|
|
548
|
+
// (5) Consume SSE until finish.
|
|
549
|
+
for await (const event of sse) {
|
|
550
|
+
if (!isEventForSession(event, sessionId))
|
|
551
|
+
continue;
|
|
552
|
+
const summary = consumeEvent(event);
|
|
553
|
+
if (summary.text)
|
|
554
|
+
assistantText += summary.text;
|
|
555
|
+
if (summary.toolName)
|
|
556
|
+
log(`tool=${summary.toolName} session=${sessionId}`);
|
|
557
|
+
if (summary.providerSeen && !providerSeen)
|
|
558
|
+
providerSeen = summary.providerSeen;
|
|
559
|
+
if (summary.usage)
|
|
560
|
+
usage = { ...(usage ?? {}), ...summary.usage };
|
|
561
|
+
if (summary.stopReason) {
|
|
562
|
+
stopReason = summary.stopReason;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
if (summary.errorMessage) {
|
|
566
|
+
throw new Error(`OpenCode SSE error: ${summary.errorMessage}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
finally {
|
|
571
|
+
this.inFlightAbortController = null;
|
|
572
|
+
}
|
|
573
|
+
// (6) Per-turn telemetry — design §5.6. Provider attribution from the
|
|
574
|
+
// event stream if available, otherwise inferred from the model prefix.
|
|
575
|
+
const provider = providerSeen ?? (this.model.split('/')[0] || 'unknown');
|
|
576
|
+
if (usage) {
|
|
577
|
+
log(`turn-usage provider=${provider} model=${this.model} input=${usage.input_tokens ?? 0} output=${usage.output_tokens ?? 0} cache_read=${usage.cache_read_input_tokens ?? 0} elapsed_ms=${Date.now() - t0} player=${this.playerName} stop_reason=${stopReason ?? 'none'}`);
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
sdkResult: { assistantText, stopReason, usage, provider },
|
|
581
|
+
elapsedMs: Date.now() - t0,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* SIGTERM → SIGKILL escalation on the `opencode serve` subprocess.
|
|
586
|
+
* Idempotent — racing signals during cleanup are safe.
|
|
587
|
+
*
|
|
588
|
+
* On Windows, Bun-runtime SIGTERM may not propagate cleanly (ADR 0015
|
|
589
|
+
* §84). The fallback to SIGKILL after `SIGTERM_TIMEOUT_MS` covers this
|
|
590
|
+
* case without per-platform branching.
|
|
591
|
+
*/
|
|
592
|
+
async killSubprocessChain() {
|
|
593
|
+
const p = this.serveProcess;
|
|
594
|
+
if (!p || p.killed)
|
|
595
|
+
return;
|
|
596
|
+
try {
|
|
597
|
+
p.kill('SIGTERM');
|
|
598
|
+
}
|
|
599
|
+
catch (err) {
|
|
600
|
+
log(`SIGTERM threw: ${err?.message ?? err}`);
|
|
601
|
+
}
|
|
602
|
+
const exitCode = await (0, helpers_1.waitForExit)(p, SIGTERM_TIMEOUT_MS);
|
|
603
|
+
if (exitCode === null) {
|
|
604
|
+
log(`opencode serve did not exit within ${SIGTERM_TIMEOUT_MS}ms — escalating to SIGKILL`);
|
|
605
|
+
try {
|
|
606
|
+
p.kill('SIGKILL');
|
|
607
|
+
}
|
|
608
|
+
catch (err) {
|
|
609
|
+
log(`SIGKILL threw: ${err?.message ?? err}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
exports.OpenCodeAttachment = OpenCodeAttachment;
|
|
615
|
+
/**
|
|
616
|
+
* Filter SSE events to a specific session id. OpenCode's `/event` stream
|
|
617
|
+
* is global per `opencode serve` instance — we only act on events whose
|
|
618
|
+
* payload carries our session's id. Permissive shape — events that
|
|
619
|
+
* don't carry a session id are passed through (they're typically
|
|
620
|
+
* server-level signals like `error` or `health.update`).
|
|
621
|
+
*/
|
|
622
|
+
function isEventForSession(event, sessionId) {
|
|
623
|
+
// Common shapes seen in v1.14.x: `properties.sessionID`, `properties.session_id`,
|
|
624
|
+
// top-level `sessionID`. Be lenient — drop only events that explicitly carry
|
|
625
|
+
// a different session id.
|
|
626
|
+
const candidates = [
|
|
627
|
+
event.sessionID,
|
|
628
|
+
event.session_id,
|
|
629
|
+
event.properties?.sessionID,
|
|
630
|
+
event.properties?.session_id,
|
|
631
|
+
event.info?.sessionID,
|
|
632
|
+
];
|
|
633
|
+
for (const c of candidates) {
|
|
634
|
+
if (typeof c === 'string') {
|
|
635
|
+
return c === sessionId;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
// No session id on the event — not session-scoped, pass through.
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Reduce one OpenCode SSE event into the bits the consumer needs. v1.14.x
|
|
643
|
+
* event shapes are still labeled experimental (ADR 0015 §82), so this is
|
|
644
|
+
* deliberately tolerant — unknown shapes are silently ignored.
|
|
645
|
+
*/
|
|
646
|
+
const EVENT_TYPES = {
|
|
647
|
+
/** Text deltas; `properties.part.type === 'text'` carries the chunk. */
|
|
648
|
+
MESSAGE_PART_UPDATED: 'message.part.updated',
|
|
649
|
+
/** Assistant turn metadata — stop_reason + usage when role=assistant. */
|
|
650
|
+
MESSAGE_UPDATED: 'message.updated',
|
|
651
|
+
/** Tool-use breadcrumb — observability only; OpenCode owns dispatch. */
|
|
652
|
+
TOOL_CALLED: 'tool.called',
|
|
653
|
+
/** Server-level error — propagated as a thrown Error in the consumer. */
|
|
654
|
+
ERROR: 'error',
|
|
655
|
+
};
|
|
656
|
+
function consumeEvent(event) {
|
|
657
|
+
const summary = {};
|
|
658
|
+
const props = event.properties;
|
|
659
|
+
if (event.type === EVENT_TYPES.MESSAGE_PART_UPDATED && props) {
|
|
660
|
+
const part = props.part;
|
|
661
|
+
if (part && part.type === 'text' && typeof part.text === 'string') {
|
|
662
|
+
summary.text = part.text;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
if (event.type === EVENT_TYPES.TOOL_CALLED && props) {
|
|
666
|
+
const name = props.name ?? props.tool;
|
|
667
|
+
if (typeof name === 'string')
|
|
668
|
+
summary.toolName = name;
|
|
669
|
+
}
|
|
670
|
+
if (event.type === EVENT_TYPES.MESSAGE_UPDATED && props) {
|
|
671
|
+
const message = props.message;
|
|
672
|
+
if (message) {
|
|
673
|
+
const role = message.role;
|
|
674
|
+
const stopReason = message.stop_reason ?? message.finishReason ?? message.finish_reason;
|
|
675
|
+
const usageBlock = message.usage;
|
|
676
|
+
const provider = (message.providerID ?? message.provider);
|
|
677
|
+
if (provider && typeof provider === 'string')
|
|
678
|
+
summary.providerSeen = provider;
|
|
679
|
+
if (role === 'assistant' && typeof stopReason === 'string') {
|
|
680
|
+
summary.stopReason = stopReason;
|
|
681
|
+
}
|
|
682
|
+
if (usageBlock) {
|
|
683
|
+
const numericUsage = {};
|
|
684
|
+
for (const [k, v] of Object.entries(usageBlock)) {
|
|
685
|
+
if (typeof v === 'number')
|
|
686
|
+
numericUsage[k] = v;
|
|
687
|
+
}
|
|
688
|
+
if (Object.keys(numericUsage).length > 0)
|
|
689
|
+
summary.usage = numericUsage;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (event.type === EVENT_TYPES.ERROR && props) {
|
|
694
|
+
const message = props.message ?? props.error?.message;
|
|
695
|
+
summary.errorMessage = typeof message === 'string' ? message : 'unknown OpenCode error';
|
|
696
|
+
}
|
|
697
|
+
return summary;
|
|
698
|
+
}
|
|
699
|
+
function sleep(ms) {
|
|
700
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
701
|
+
}
|
|
702
|
+
if (require.main === module) {
|
|
703
|
+
if (!opencodeSdkAvailable)
|
|
704
|
+
process.exit(1);
|
|
705
|
+
const model = process.env[config_1.ENV.OPENCODE_MODEL];
|
|
706
|
+
new OpenCodeAttachment(model ? { model } : {}).run().catch((err) => {
|
|
707
|
+
log('Fatal error:', err);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
});
|
|
710
|
+
}
|