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
package/dist/spawn.js
ADDED
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveIconPath = resolveIconPath;
|
|
4
|
+
exports.ensureWindowsTerminalProfile = ensureWindowsTerminalProfile;
|
|
5
|
+
exports.shellQuote = shellQuote;
|
|
6
|
+
exports.resolveClaudePath = resolveClaudePath;
|
|
7
|
+
exports.detectMacTerminal = detectMacTerminal;
|
|
8
|
+
exports.findLinuxTerminal = findLinuxTerminal;
|
|
9
|
+
exports.buildClaudeCommand = buildClaudeCommand;
|
|
10
|
+
exports.spawnInTerminal = spawnInTerminal;
|
|
11
|
+
exports.spawnCopilotBridge = spawnCopilotBridge;
|
|
12
|
+
exports.spawnMockAdapter = spawnMockAdapter;
|
|
13
|
+
exports.spawnClaudeApiAdapter = spawnClaudeApiAdapter;
|
|
14
|
+
exports.spawnOpenCodeAdapter = spawnOpenCodeAdapter;
|
|
15
|
+
exports.spawnClaudeCodeHeadlessAdapter = spawnClaudeCodeHeadlessAdapter;
|
|
16
|
+
const child_process_1 = require("child_process");
|
|
17
|
+
const fs_1 = require("fs");
|
|
18
|
+
const path_1 = require("path");
|
|
19
|
+
const os_1 = require("os");
|
|
20
|
+
const config_1 = require("./config");
|
|
21
|
+
const log = (...args) => console.error('[agent-tempo:spawn]', ...args);
|
|
22
|
+
/** Stable GUID for the agent-tempo Windows Terminal profile. */
|
|
23
|
+
const WT_PROFILE_GUID = '{c1a0d300-0e30-4000-a000-c1a0de00e300}';
|
|
24
|
+
const WT_PROFILE_NAME = 'agent-tempo';
|
|
25
|
+
/** Resolve the absolute path to the package's icon file (PNG for Windows Terminal). */
|
|
26
|
+
function resolveIconPath() {
|
|
27
|
+
// __dirname is src/ in dev or dist/ in production; assets/ is at the package root
|
|
28
|
+
const packageRoot = (0, path_1.resolve)(__dirname, '..');
|
|
29
|
+
return (0, path_1.join)(packageRoot, 'assets', 'icon-dark-32.png');
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Strip // and /* comments from JSON-with-comments (JSONC), leaving strings intact.
|
|
33
|
+
* Handles escaped quotes inside strings correctly.
|
|
34
|
+
*/
|
|
35
|
+
function stripJsonComments(text) {
|
|
36
|
+
let result = '';
|
|
37
|
+
let i = 0;
|
|
38
|
+
while (i < text.length) {
|
|
39
|
+
// String literal — copy verbatim until closing quote
|
|
40
|
+
if (text[i] === '"') {
|
|
41
|
+
result += '"';
|
|
42
|
+
i++;
|
|
43
|
+
while (i < text.length && text[i] !== '"') {
|
|
44
|
+
if (text[i] === '\\') {
|
|
45
|
+
result += text[i++];
|
|
46
|
+
} // skip escaped char
|
|
47
|
+
if (i < text.length) {
|
|
48
|
+
result += text[i++];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (i < text.length) {
|
|
52
|
+
result += text[i++];
|
|
53
|
+
} // closing quote
|
|
54
|
+
// Line comment
|
|
55
|
+
}
|
|
56
|
+
else if (text[i] === '/' && text[i + 1] === '/') {
|
|
57
|
+
while (i < text.length && text[i] !== '\n')
|
|
58
|
+
i++;
|
|
59
|
+
// Block comment
|
|
60
|
+
}
|
|
61
|
+
else if (text[i] === '/' && text[i + 1] === '*') {
|
|
62
|
+
i += 2;
|
|
63
|
+
while (i < text.length && !(text[i] === '*' && text[i + 1] === '/'))
|
|
64
|
+
i++;
|
|
65
|
+
i += 2; // skip closing */
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
result += text[i++];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Ensure a "agent-tempo" profile exists in Windows Terminal settings.json
|
|
75
|
+
* with our icon. Returns true if the profile is ready for use.
|
|
76
|
+
*
|
|
77
|
+
* Windows Terminal settings path:
|
|
78
|
+
* %LOCALAPPDATA%/Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState/settings.json
|
|
79
|
+
*/
|
|
80
|
+
function ensureWindowsTerminalProfile() {
|
|
81
|
+
if (process.platform !== 'win32')
|
|
82
|
+
return false;
|
|
83
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
84
|
+
if (!localAppData)
|
|
85
|
+
return false;
|
|
86
|
+
const settingsPath = (0, path_1.join)(localAppData, 'Packages', 'Microsoft.WindowsTerminal_8wekyb3d8bbwe', 'LocalState', 'settings.json');
|
|
87
|
+
if (!(0, fs_1.existsSync)(settingsPath)) {
|
|
88
|
+
log('Windows Terminal settings.json not found at', settingsPath);
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const raw = (0, fs_1.readFileSync)(settingsPath, 'utf8');
|
|
93
|
+
// Windows Terminal settings.json may contain comments — strip them for JSON.parse.
|
|
94
|
+
// Naive regex would eat "//" inside strings (e.g., URLs). Walk char-by-char instead.
|
|
95
|
+
const settings = JSON.parse(stripJsonComments(raw));
|
|
96
|
+
if (!settings.profiles?.list)
|
|
97
|
+
return false;
|
|
98
|
+
const iconPath = resolveIconPath().replace(/\\/g, '/');
|
|
99
|
+
if (!(0, fs_1.existsSync)(iconPath.replace(/\//g, '\\'))) {
|
|
100
|
+
log('Icon file not found at', iconPath);
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
const profiles = settings.profiles.list;
|
|
104
|
+
// Check if our profile already exists (by GUID or name)
|
|
105
|
+
const existing = profiles.find((p) => p.guid === WT_PROFILE_GUID || p.name === WT_PROFILE_NAME);
|
|
106
|
+
if (existing) {
|
|
107
|
+
// Update icon + closeOnExit if they changed (e.g. package moved, or pre-#165 profile)
|
|
108
|
+
let dirty = false;
|
|
109
|
+
if (existing.icon !== iconPath) {
|
|
110
|
+
existing.icon = iconPath;
|
|
111
|
+
dirty = true;
|
|
112
|
+
}
|
|
113
|
+
// Force-killed sessions exit with code 1; "always" ensures WT closes the tab
|
|
114
|
+
// instead of showing "process exited" with a stale prompt.
|
|
115
|
+
if (existing.closeOnExit !== 'always') {
|
|
116
|
+
existing.closeOnExit = 'always';
|
|
117
|
+
dirty = true;
|
|
118
|
+
}
|
|
119
|
+
if (dirty) {
|
|
120
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(settings, null, 4) + '\n');
|
|
121
|
+
log('Updated agent-tempo profile in Windows Terminal');
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
// Add new profile
|
|
126
|
+
profiles.push({
|
|
127
|
+
guid: WT_PROFILE_GUID,
|
|
128
|
+
name: WT_PROFILE_NAME,
|
|
129
|
+
commandline: 'cmd.exe',
|
|
130
|
+
icon: iconPath,
|
|
131
|
+
hidden: true, // Hide from dropdown — only used programmatically
|
|
132
|
+
closeOnExit: 'always', // Force-killed sessions exit non-zero; auto-close the tab
|
|
133
|
+
});
|
|
134
|
+
// Write back with original formatting style (4-space indent to match WT default)
|
|
135
|
+
(0, fs_1.writeFileSync)(settingsPath, JSON.stringify(settings, null, 4) + '\n');
|
|
136
|
+
log('Created agent-tempo profile in Windows Terminal with icon:', iconPath);
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
log('Failed to update Windows Terminal settings:', e);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** POSIX shell-safe single-quoting (works in bash, zsh, and fish) */
|
|
145
|
+
function shellQuote(s) {
|
|
146
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Resolve the path to the `claude` binary.
|
|
150
|
+
*
|
|
151
|
+
* Resolution order:
|
|
152
|
+
* 1. `configBin` parameter (from Config.claudeBin — env var or config file)
|
|
153
|
+
* 2. `AGENT_TEMPO_CLAUDE_BIN` env var (checked directly for spawned processes that
|
|
154
|
+
* may not have full config resolution, e.g., activities)
|
|
155
|
+
* 3. `which claude` / `where claude` lookup
|
|
156
|
+
* 4. Bare `claude` fallback
|
|
157
|
+
*/
|
|
158
|
+
function resolveClaudePath(configBin) {
|
|
159
|
+
// Priority 1: explicit config value
|
|
160
|
+
if (configBin)
|
|
161
|
+
return configBin;
|
|
162
|
+
// Priority 2: env var (may be set by parent process)
|
|
163
|
+
const envBin = process.env.AGENT_TEMPO_CLAUDE_BIN;
|
|
164
|
+
if (envBin)
|
|
165
|
+
return envBin;
|
|
166
|
+
// Priority 3: which/where lookup
|
|
167
|
+
const cmd = process.platform === 'win32' ? 'where' : 'which';
|
|
168
|
+
try {
|
|
169
|
+
return (0, child_process_1.execFileSync)(cmd, ['claude'], { encoding: 'utf8' }).trim().split('\n')[0];
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return 'claude';
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Detect the macOS terminal the user is actually running in.
|
|
177
|
+
*
|
|
178
|
+
* Priority:
|
|
179
|
+
* 1. TERM_PROGRAM env var (most reliable when available — set by the terminal itself)
|
|
180
|
+
* 2. Check frontmost app via AppleScript (detects what the user is actively using)
|
|
181
|
+
* 3. Fall back to Terminal.app
|
|
182
|
+
*/
|
|
183
|
+
function detectMacTerminal() {
|
|
184
|
+
const termProgram = (process.env.TERM_PROGRAM || '').toLowerCase();
|
|
185
|
+
if (termProgram === 'ghostty')
|
|
186
|
+
return 'ghostty';
|
|
187
|
+
if (termProgram === 'iterm.app' || termProgram === 'iterm2')
|
|
188
|
+
return 'iterm2';
|
|
189
|
+
if (termProgram === 'apple_terminal')
|
|
190
|
+
return 'terminal';
|
|
191
|
+
// MCP servers may not inherit TERM_PROGRAM — check which terminal app is running
|
|
192
|
+
// Prefer frontmost app detection over pgrep to avoid false positives
|
|
193
|
+
try {
|
|
194
|
+
const frontApp = (0, child_process_1.execFileSync)('osascript', ['-e',
|
|
195
|
+
'tell application "System Events" to get name of first application process whose frontmost is true',
|
|
196
|
+
], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim().toLowerCase();
|
|
197
|
+
if (frontApp === 'ghostty')
|
|
198
|
+
return 'ghostty';
|
|
199
|
+
if (frontApp === 'iterm2')
|
|
200
|
+
return 'iterm2';
|
|
201
|
+
if (frontApp === 'terminal')
|
|
202
|
+
return 'terminal';
|
|
203
|
+
}
|
|
204
|
+
catch { /* ignore */ }
|
|
205
|
+
// Last resort: check running processes
|
|
206
|
+
try {
|
|
207
|
+
(0, child_process_1.execFileSync)('pgrep', ['-x', 'ghostty'], { stdio: 'ignore' });
|
|
208
|
+
return 'ghostty';
|
|
209
|
+
}
|
|
210
|
+
catch { /* not running */ }
|
|
211
|
+
try {
|
|
212
|
+
(0, child_process_1.execFileSync)('pgrep', ['-x', 'iTerm2'], { stdio: 'ignore' });
|
|
213
|
+
return 'iterm2';
|
|
214
|
+
}
|
|
215
|
+
catch { /* not running */ }
|
|
216
|
+
return 'terminal';
|
|
217
|
+
}
|
|
218
|
+
/** Find the first available terminal emulator on Linux */
|
|
219
|
+
function findLinuxTerminal() {
|
|
220
|
+
const candidates = ['gnome-terminal', 'konsole', 'x-terminal-emulator', 'xfce4-terminal', 'xterm'];
|
|
221
|
+
for (const term of candidates) {
|
|
222
|
+
try {
|
|
223
|
+
(0, child_process_1.execFileSync)('which', [term], { stdio: 'ignore' });
|
|
224
|
+
return term;
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
// not found, try next
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Build a shell command string that sets env vars and runs claude.
|
|
234
|
+
* Uses inline `KEY=val` syntax which works in bash, zsh, AND fish.
|
|
235
|
+
*/
|
|
236
|
+
function buildClaudeCommand(claudeBin, claudeArgs, envVars) {
|
|
237
|
+
const envInline = Object.entries(envVars)
|
|
238
|
+
.map(([k, v]) => `${k}=${shellQuote(v)}`)
|
|
239
|
+
.join(' ');
|
|
240
|
+
// Quote the binary path if it contains spaces (e.g., "C:\Program Files\...")
|
|
241
|
+
const quotedBin = claudeBin.includes(' ') ? shellQuote(claudeBin) : claudeBin;
|
|
242
|
+
const args = claudeArgs.map(a => shellQuote(a)).join(' ');
|
|
243
|
+
return envInline ? `${envInline} ${quotedBin} ${args}` : `${quotedBin} ${args}`;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Spawn a Claude Code session in a visible terminal window.
|
|
247
|
+
*
|
|
248
|
+
* Strategy per terminal:
|
|
249
|
+
* - Ghostty: `initial input` into a normal window (preserves full shell env)
|
|
250
|
+
* - iTerm2: `write text` via AppleScript (same approach)
|
|
251
|
+
* - Terminal.app: .command script with shell profile sourcing
|
|
252
|
+
* - Windows: shell:true with env vars
|
|
253
|
+
* - Linux: terminal emulator with -e flag
|
|
254
|
+
*/
|
|
255
|
+
function spawnInTerminal(claudeArgs, workDir, envVars, options) {
|
|
256
|
+
const claudeBin = resolveClaudePath(options?.claudeBin);
|
|
257
|
+
const claudeInvocation = buildClaudeCommand(claudeBin, claudeArgs, envVars);
|
|
258
|
+
if (process.platform === 'darwin') {
|
|
259
|
+
const detected = detectMacTerminal();
|
|
260
|
+
log(`Terminal detection: TERM_PROGRAM=${JSON.stringify(process.env.TERM_PROGRAM)}, detected=${detected}`);
|
|
261
|
+
if (detected === 'ghostty') {
|
|
262
|
+
// Append `; exit` so the wrapping shell exits when claude does (clean or killed).
|
|
263
|
+
// Without it, claude exit returns control to the shell prompt and the tab lingers —
|
|
264
|
+
// parity with the Windows WT `closeOnExit: 'always'` + parent-walk fix from #166.
|
|
265
|
+
const osaScript = `
|
|
266
|
+
tell application "Ghostty"
|
|
267
|
+
set cfg to new surface configuration
|
|
268
|
+
set initial working directory of cfg to ${JSON.stringify(workDir)}
|
|
269
|
+
set initial input of cfg to ${JSON.stringify(claudeInvocation + '; exit\n')}
|
|
270
|
+
set win to new window with configuration cfg
|
|
271
|
+
end tell`;
|
|
272
|
+
log('Using Ghostty initial-input path');
|
|
273
|
+
const child = (0, child_process_1.spawn)('osascript', ['-e', osaScript], {
|
|
274
|
+
detached: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
275
|
+
});
|
|
276
|
+
child.stderr?.on('data', (d) => log('osascript stderr:', d.toString()));
|
|
277
|
+
child.stdout?.on('data', (d) => log('osascript stdout:', d.toString()));
|
|
278
|
+
child.unref();
|
|
279
|
+
return { pid: child.pid };
|
|
280
|
+
}
|
|
281
|
+
if (detected === 'iterm2') {
|
|
282
|
+
// Append `; exit` so the wrapping shell exits when claude does. `;` rather than
|
|
283
|
+
// `&&` so exit runs regardless of claude's exit code (force-kill returns non-zero).
|
|
284
|
+
// JSON.stringify embeds the full shell command as a properly-escaped string literal
|
|
285
|
+
// so any `"` or `\` in paths/args doesn't break the AppleScript parser. Parity with
|
|
286
|
+
// the Ghostty path above.
|
|
287
|
+
const shellCmd = `cd ${shellQuote(workDir)} && ${claudeInvocation} ; exit`;
|
|
288
|
+
const osaScript = `
|
|
289
|
+
tell application "iTerm2"
|
|
290
|
+
set newWindow to (create window with default profile)
|
|
291
|
+
tell current session of newWindow
|
|
292
|
+
write text ${JSON.stringify(shellCmd)}
|
|
293
|
+
end tell
|
|
294
|
+
end tell`;
|
|
295
|
+
log('Using iTerm2 write-text path');
|
|
296
|
+
const child = (0, child_process_1.spawn)('osascript', ['-e', osaScript], {
|
|
297
|
+
detached: true, stdio: ['ignore', 'pipe', 'pipe'],
|
|
298
|
+
});
|
|
299
|
+
child.stderr?.on('data', (d) => log('osascript stderr:', d.toString()));
|
|
300
|
+
child.unref();
|
|
301
|
+
return { pid: child.pid };
|
|
302
|
+
}
|
|
303
|
+
// Terminal.app: .command file with shell profile sourcing
|
|
304
|
+
const userShell = process.env.SHELL || '/bin/zsh';
|
|
305
|
+
const scriptPath = (0, path_1.join)((0, os_1.tmpdir)(), `agent-tempo-recruit-${Date.now()}.command`);
|
|
306
|
+
let profileSource;
|
|
307
|
+
if (userShell.endsWith('/fish')) {
|
|
308
|
+
profileSource = `exec fish -c "cd ${shellQuote(workDir)} && ${claudeInvocation}"`;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
profileSource = [
|
|
312
|
+
`[ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" 2>/dev/null`,
|
|
313
|
+
`[ -f "$HOME/.bashrc" ] && source "$HOME/.bashrc" 2>/dev/null`,
|
|
314
|
+
`[ -f "$HOME/.nvm/nvm.sh" ] && source "$HOME/.nvm/nvm.sh" 2>/dev/null`,
|
|
315
|
+
`command -v fnm >/dev/null && eval "$(fnm env)" 2>/dev/null`,
|
|
316
|
+
].join('\n');
|
|
317
|
+
}
|
|
318
|
+
const envExports = Object.entries(envVars)
|
|
319
|
+
.map(([k, v]) => `export ${k}=${shellQuote(v)}`)
|
|
320
|
+
.join('\n');
|
|
321
|
+
const lines = [
|
|
322
|
+
'#!/bin/bash',
|
|
323
|
+
// Env vars BEFORE profile sourcing — profiles that call `exec` (e.g. oh-my-zsh)
|
|
324
|
+
// would otherwise lose the exports and the claude command (#98)
|
|
325
|
+
envExports,
|
|
326
|
+
profileSource,
|
|
327
|
+
`cd ${shellQuote(workDir)}`,
|
|
328
|
+
// `exec` so the shell is replaced by claude — when claude exits (clean or killed),
|
|
329
|
+
// the script process ends and Terminal.app closes the window per its settings.
|
|
330
|
+
// Without `exec`, bash waits for claude and then returns to prompt, leaving the
|
|
331
|
+
// window open. Parity with the WT `closeOnExit: 'always'` fix from #166.
|
|
332
|
+
`exec ${shellQuote(claudeBin)} ${claudeArgs.map(a => shellQuote(a)).join(' ')}`,
|
|
333
|
+
];
|
|
334
|
+
(0, fs_1.writeFileSync)(scriptPath, lines.join('\n') + '\n', { mode: 0o755 });
|
|
335
|
+
log('Using Terminal.app .command path:', scriptPath);
|
|
336
|
+
const child = (0, child_process_1.spawn)('open', [scriptPath], { detached: true, stdio: 'ignore' });
|
|
337
|
+
child.unref();
|
|
338
|
+
return { pid: child.pid };
|
|
339
|
+
}
|
|
340
|
+
if (process.platform === 'win32') {
|
|
341
|
+
// Detect Windows Terminal: WT_SESSION env var is set when running inside it.
|
|
342
|
+
// wt.exe is a UWP app execution alias that Node.js can't resolve directly,
|
|
343
|
+
// but `cmd.exe /c start "" wt.exe ...` works through the Windows shell.
|
|
344
|
+
const hasWt = Boolean(process.env.WT_SESSION);
|
|
345
|
+
if (hasWt) {
|
|
346
|
+
// Extract player name from claudeArgs (-n <name>) for tab title
|
|
347
|
+
const nameIdx = claudeArgs.indexOf('-n');
|
|
348
|
+
const tabTitle = nameIdx !== -1 && nameIdx + 1 < claudeArgs.length
|
|
349
|
+
? claudeArgs[nameIdx + 1]
|
|
350
|
+
: 'agent-tempo';
|
|
351
|
+
// Ensure our profile with icon exists in Windows Terminal settings
|
|
352
|
+
const hasProfile = ensureWindowsTerminalProfile();
|
|
353
|
+
// Build inline env var assignments for cmd /c since wt.exe spawns
|
|
354
|
+
// a new process that won't inherit our env.
|
|
355
|
+
// Escape values for cmd.exe: wrap in quotes and escape inner special chars.
|
|
356
|
+
const cmdEscape = (s) => s.replace(/([&|<>^"%])/g, '^$1');
|
|
357
|
+
const setCmds = Object.entries(envVars)
|
|
358
|
+
.map(([k, v]) => `set "${k}=${cmdEscape(v)}"`)
|
|
359
|
+
.join(' && ');
|
|
360
|
+
// Quote the binary path if it contains spaces (e.g., "C:\Program Files\...")
|
|
361
|
+
const quotedWinBin = claudeBin.includes(' ') ? `"${cmdEscape(claudeBin)}"` : cmdEscape(claudeBin);
|
|
362
|
+
const claudeCmd = `${quotedWinBin} ${claudeArgs.map(a => `"${cmdEscape(a)}"`).join(' ')}`;
|
|
363
|
+
const innerCmd = setCmds
|
|
364
|
+
? `${setCmds} && ${claudeCmd}`
|
|
365
|
+
: claudeCmd;
|
|
366
|
+
// Use `cmd.exe /c start "" wt.exe ...` to resolve the UWP app alias
|
|
367
|
+
// When our profile exists, use --profile to get the tab icon
|
|
368
|
+
const wtArgs = [
|
|
369
|
+
'/c', 'start', '',
|
|
370
|
+
'wt.exe', '-w', '0',
|
|
371
|
+
'new-tab',
|
|
372
|
+
...(hasProfile ? ['--profile', WT_PROFILE_NAME] : []),
|
|
373
|
+
'--title', tabTitle,
|
|
374
|
+
'-d', workDir,
|
|
375
|
+
'cmd', '/k', innerCmd,
|
|
376
|
+
];
|
|
377
|
+
const child = (0, child_process_1.spawn)('cmd.exe', wtArgs, {
|
|
378
|
+
detached: true,
|
|
379
|
+
stdio: 'ignore',
|
|
380
|
+
});
|
|
381
|
+
child.unref();
|
|
382
|
+
return { pid: child.pid };
|
|
383
|
+
}
|
|
384
|
+
// Fallback: open a new cmd.exe window
|
|
385
|
+
const child = (0, child_process_1.spawn)('cmd.exe', ['/c', 'start', '""', claudeBin, ...claudeArgs], {
|
|
386
|
+
cwd: workDir,
|
|
387
|
+
detached: true,
|
|
388
|
+
stdio: 'ignore',
|
|
389
|
+
env: { ...process.env, ...envVars },
|
|
390
|
+
});
|
|
391
|
+
child.unref();
|
|
392
|
+
return { pid: child.pid };
|
|
393
|
+
}
|
|
394
|
+
// Linux
|
|
395
|
+
const envExports = Object.entries(envVars)
|
|
396
|
+
.map(([k, v]) => `export ${k}=${shellQuote(v)}`)
|
|
397
|
+
.join('; ');
|
|
398
|
+
const fullCmd = `${envExports}; cd ${shellQuote(workDir)} && ${shellQuote(claudeBin)} ${claudeArgs.map(a => shellQuote(a)).join(' ')}`;
|
|
399
|
+
const terminal = findLinuxTerminal();
|
|
400
|
+
if (!terminal) {
|
|
401
|
+
log('No terminal emulator found on Linux, falling back to headless spawn');
|
|
402
|
+
const child = (0, child_process_1.spawn)('bash', ['-c', fullCmd], { detached: true, stdio: 'ignore' });
|
|
403
|
+
child.unref();
|
|
404
|
+
return { pid: child.pid };
|
|
405
|
+
}
|
|
406
|
+
let child;
|
|
407
|
+
if (terminal === 'gnome-terminal') {
|
|
408
|
+
child = (0, child_process_1.spawn)(terminal, ['--', 'bash', '-c', fullCmd], { detached: true, stdio: 'ignore' });
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
child = (0, child_process_1.spawn)(terminal, ['-e', 'bash', '-c', fullCmd], { detached: true, stdio: 'ignore' });
|
|
412
|
+
}
|
|
413
|
+
child.unref();
|
|
414
|
+
return { pid: child.pid };
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Resolve the path to the compiled copilot bridge adapter entry point.
|
|
418
|
+
* In dev (ts-node), returns a ts-node command; in production, returns the dist path.
|
|
419
|
+
*
|
|
420
|
+
* PR-B (v0.25 rebuild step 2/7): the bridge moved from `src/copilot-bridge.ts`
|
|
421
|
+
* to `src/adapters/copilot/adapter.ts`. Behavior unchanged.
|
|
422
|
+
*/
|
|
423
|
+
function resolveBridgePath() {
|
|
424
|
+
const isDev = __filename.endsWith('.ts');
|
|
425
|
+
if (isDev) {
|
|
426
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'copilot', 'adapter.ts')] };
|
|
427
|
+
}
|
|
428
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'copilot', 'adapter.js')] };
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Spawn a copilot bridge as a detached headless subprocess.
|
|
432
|
+
* Sets up log file, PID file, and all required env vars.
|
|
433
|
+
*/
|
|
434
|
+
function spawnCopilotBridge(opts) {
|
|
435
|
+
const { cmd, args } = resolveBridgePath();
|
|
436
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
437
|
+
const logName = opts.name || `copilot-${Date.now()}`;
|
|
438
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
439
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
440
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
441
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
442
|
+
let child;
|
|
443
|
+
try {
|
|
444
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
445
|
+
cwd: opts.workDir,
|
|
446
|
+
detached: true,
|
|
447
|
+
stdio: ['ignore', logFd, logFd],
|
|
448
|
+
env: {
|
|
449
|
+
...process.env,
|
|
450
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
451
|
+
[config_1.ENV.BRIDGE_NAME]: opts.name,
|
|
452
|
+
[config_1.ENV.PLAYER_NAME]: '', // Clear parent's player name so child uses BRIDGE_NAME
|
|
453
|
+
[config_1.ENV.BRIDGE_MODE]: '', // Clear parent's bridge mode
|
|
454
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
455
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
456
|
+
// Forward Temporal connection settings so child processes can connect
|
|
457
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
458
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
459
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
460
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
461
|
+
...(opts.sessionId ? { [config_1.ENV.BRIDGE_SESSION_ID]: opts.sessionId } : {}),
|
|
462
|
+
// PR-D attachment handoff — renew rather than fresh-claim in startV2Lifecycle.
|
|
463
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
464
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
465
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
child.unref();
|
|
469
|
+
}
|
|
470
|
+
finally {
|
|
471
|
+
(0, fs_1.closeSync)(logFd);
|
|
472
|
+
}
|
|
473
|
+
if (child.pid != null) {
|
|
474
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
475
|
+
}
|
|
476
|
+
log(`Spawned copilot-bridge (pid ${child.pid}) in ${opts.workDir} as "${opts.name}"`);
|
|
477
|
+
return { pid: child.pid, logPath, pidPath };
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Resolve the path to the mock adapter entry point. Mirrors
|
|
481
|
+
* {@link resolveBridgePath} so dev (ts-node) and prod (compiled .js) both
|
|
482
|
+
* launch the same code through the same `require.main === module` gate.
|
|
483
|
+
*/
|
|
484
|
+
function resolveMockAdapterPath() {
|
|
485
|
+
const isDev = __filename.endsWith('.ts');
|
|
486
|
+
if (isDev) {
|
|
487
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'mock', 'adapter.ts')] };
|
|
488
|
+
}
|
|
489
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'mock', 'adapter.js')] };
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Spawn a mock adapter subprocess. Headless — no terminal window, no
|
|
493
|
+
* "trust this folder" prompt — which is the whole point of the mock for
|
|
494
|
+
* autonomous validation harnesses (ADR 0014 §4.7).
|
|
495
|
+
*/
|
|
496
|
+
function spawnMockAdapter(opts) {
|
|
497
|
+
const { cmd, args } = resolveMockAdapterPath();
|
|
498
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
499
|
+
const logName = opts.name || `mock-${Date.now()}`;
|
|
500
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
501
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
502
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
503
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
504
|
+
let child;
|
|
505
|
+
try {
|
|
506
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
507
|
+
cwd: opts.workDir,
|
|
508
|
+
detached: true,
|
|
509
|
+
stdio: ['ignore', logFd, logFd],
|
|
510
|
+
env: {
|
|
511
|
+
...process.env,
|
|
512
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
513
|
+
[config_1.ENV.PLAYER_NAME]: opts.name,
|
|
514
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
515
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
516
|
+
// Forward Temporal connection settings so the subprocess can connect.
|
|
517
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
518
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
519
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
520
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
521
|
+
// Mock-specific knobs.
|
|
522
|
+
AGENT_TEMPO_MOCK_MODE: opts.mockMode ?? 'echo',
|
|
523
|
+
...(opts.mockScenario ? { AGENT_TEMPO_MOCK_SCENARIO: opts.mockScenario } : {}),
|
|
524
|
+
// Attachment handoff — adapter renews via startV2Lifecycle.
|
|
525
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
526
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
527
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
child.unref();
|
|
531
|
+
}
|
|
532
|
+
finally {
|
|
533
|
+
(0, fs_1.closeSync)(logFd);
|
|
534
|
+
}
|
|
535
|
+
if (child.pid != null) {
|
|
536
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
537
|
+
}
|
|
538
|
+
log(`Spawned mock adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}" (mode=${opts.mockMode ?? 'echo'})`);
|
|
539
|
+
return { pid: child.pid, logPath, pidPath };
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* Resolve the path to the claude-api adapter entry point. Mirrors
|
|
543
|
+
* {@link resolveBridgePath} so dev (ts-node) and prod (compiled .js) both
|
|
544
|
+
* launch the same code through the same `require.main === module` gate.
|
|
545
|
+
*/
|
|
546
|
+
function resolveClaudeApiPath() {
|
|
547
|
+
const isDev = __filename.endsWith('.ts');
|
|
548
|
+
if (isDev) {
|
|
549
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'claude-api', 'adapter.ts')] };
|
|
550
|
+
}
|
|
551
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'claude-api', 'adapter.js')] };
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Spawn the claude-api adapter as a detached headless subprocess.
|
|
555
|
+
*
|
|
556
|
+
* Mirrors {@link spawnCopilotBridge} — no TTY, log + PID files in
|
|
557
|
+
* `logs/<name>.log` and `logs/<name>.pid`, env vars carry identity +
|
|
558
|
+
* Temporal connection settings + optional attachment-handoff. The adapter
|
|
559
|
+
* resolves the LLM model from `AGENT_TEMPO_API_MODEL` (set here when
|
|
560
|
+
* `opts.model` is provided) or falls back to the constants-pinned default
|
|
561
|
+
* (`claude-opus-4-7`) inside the adapter's `run()`. `ANTHROPIC_API_KEY`
|
|
562
|
+
* is inherited from the parent's env (recruit pre-flight checks it).
|
|
563
|
+
*/
|
|
564
|
+
function spawnClaudeApiAdapter(opts) {
|
|
565
|
+
const { cmd, args } = resolveClaudeApiPath();
|
|
566
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
567
|
+
const logName = opts.name || `claude-api-${Date.now()}`;
|
|
568
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
569
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
570
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
571
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
572
|
+
let child;
|
|
573
|
+
try {
|
|
574
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
575
|
+
cwd: opts.workDir,
|
|
576
|
+
detached: true,
|
|
577
|
+
stdio: ['ignore', logFd, logFd],
|
|
578
|
+
env: {
|
|
579
|
+
...process.env,
|
|
580
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
581
|
+
[config_1.ENV.PLAYER_NAME]: opts.name,
|
|
582
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
583
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
584
|
+
// Forward Temporal connection settings so the subprocess can connect.
|
|
585
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
586
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
587
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
588
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
589
|
+
// Model selection: recruit-arg → AGENT_TEMPO_API_MODEL → in-adapter default.
|
|
590
|
+
...(opts.model ? { [config_1.ENV.API_MODEL]: opts.model } : {}),
|
|
591
|
+
// Attachment handoff — adapter renews via startV2Lifecycle.
|
|
592
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
593
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
594
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
595
|
+
},
|
|
596
|
+
});
|
|
597
|
+
child.unref();
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
(0, fs_1.closeSync)(logFd);
|
|
601
|
+
}
|
|
602
|
+
if (child.pid != null) {
|
|
603
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
604
|
+
}
|
|
605
|
+
log(`Spawned claude-api adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}"${opts.model ? ` (model=${opts.model})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
|
|
606
|
+
return { pid: child.pid, logPath, pidPath };
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Resolve the path to the opencode adapter entry point. Mirrors
|
|
610
|
+
* {@link resolveClaudeApiPath} so dev (ts-node) and prod (compiled .js)
|
|
611
|
+
* both launch the same code through the same `require.main === module` gate.
|
|
612
|
+
*/
|
|
613
|
+
function resolveOpenCodePath() {
|
|
614
|
+
const isDev = __filename.endsWith('.ts');
|
|
615
|
+
if (isDev) {
|
|
616
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'opencode', 'adapter.ts')] };
|
|
617
|
+
}
|
|
618
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'opencode', 'adapter.js')] };
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Spawn the opencode adapter as a detached headless subprocess.
|
|
622
|
+
*
|
|
623
|
+
* Pattern matches {@link spawnClaudeApiAdapter} — no TTY, log + PID files
|
|
624
|
+
* in `logs/<name>.log` and `logs/<name>.pid`, env vars carry identity +
|
|
625
|
+
* Temporal connection settings + optional attachment-handoff. Provider
|
|
626
|
+
* env vars (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, …) are inherited
|
|
627
|
+
* from the parent's env unchanged — OpenCode reads whichever ones the
|
|
628
|
+
* `model`'s prefix maps to (recruit pre-flight does NOT validate any
|
|
629
|
+
* specific provider key since the model is opaque pass-through).
|
|
630
|
+
*/
|
|
631
|
+
function spawnOpenCodeAdapter(opts) {
|
|
632
|
+
const { cmd, args } = resolveOpenCodePath();
|
|
633
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
634
|
+
const logName = opts.name || `opencode-${Date.now()}`;
|
|
635
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
636
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
637
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
638
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
639
|
+
let child;
|
|
640
|
+
try {
|
|
641
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
642
|
+
cwd: opts.workDir,
|
|
643
|
+
detached: true,
|
|
644
|
+
stdio: ['ignore', logFd, logFd],
|
|
645
|
+
env: {
|
|
646
|
+
...process.env,
|
|
647
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
648
|
+
[config_1.ENV.PLAYER_NAME]: opts.name,
|
|
649
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
650
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
651
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
652
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
653
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
654
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
655
|
+
// Model selection: recruit-arg → AGENT_TEMPO_OPENCODE_MODEL → in-adapter default.
|
|
656
|
+
...(opts.model ? { [config_1.ENV.OPENCODE_MODEL]: opts.model } : {}),
|
|
657
|
+
// Attachment handoff — adapter renews via startV2Lifecycle.
|
|
658
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
659
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
660
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
child.unref();
|
|
664
|
+
}
|
|
665
|
+
finally {
|
|
666
|
+
(0, fs_1.closeSync)(logFd);
|
|
667
|
+
}
|
|
668
|
+
if (child.pid != null) {
|
|
669
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
670
|
+
}
|
|
671
|
+
log(`Spawned opencode adapter (pid ${child.pid}) in ${opts.workDir} as "${opts.name}"${opts.model ? ` (model=${opts.model})` : ''}${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
|
|
672
|
+
return { pid: child.pid, logPath, pidPath };
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Resolve the path to the claude-code-headless adapter entry point.
|
|
676
|
+
* Mirrors {@link resolveClaudeApiPath} so dev (ts-node) and prod
|
|
677
|
+
* (compiled .js) both launch the same code through the same
|
|
678
|
+
* `require.main === module` gate.
|
|
679
|
+
*/
|
|
680
|
+
function resolveClaudeCodeHeadlessPath() {
|
|
681
|
+
const isDev = __filename.endsWith('.ts');
|
|
682
|
+
if (isDev) {
|
|
683
|
+
return { cmd: 'npx', args: ['ts-node', (0, path_1.resolve)(__dirname, 'adapters', 'claude-code-headless', 'adapter.ts')] };
|
|
684
|
+
}
|
|
685
|
+
return { cmd: 'node', args: [(0, path_1.resolve)(__dirname, 'adapters', 'claude-code-headless', 'adapter.js')] };
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Spawn the claude-code-headless adapter as a detached headless subprocess.
|
|
689
|
+
*
|
|
690
|
+
* Pattern matches {@link spawnClaudeApiAdapter} — no TTY, log + PID files
|
|
691
|
+
* in `logs/<name>.log` and `logs/<name>.pid`, env vars carry identity +
|
|
692
|
+
* Temporal connection settings + optional attachment-handoff.
|
|
693
|
+
*
|
|
694
|
+
* **Env hygiene** (design §3.6): the per-turn `claude -p` child needs to
|
|
695
|
+
* use the host's OAuth keychain — NOT a `ANTHROPIC_API_KEY` env var.
|
|
696
|
+
* The adapter strips `ANTHROPIC_API_KEY` and `CLAUDE_CODE_OAUTH_TOKEN`
|
|
697
|
+
* from its child env at spawn time (in `adapter.ts`'s `invokeSdk`); this
|
|
698
|
+
* spawn helper passes the parent's full env through to the adapter
|
|
699
|
+
* itself (which needs other env vars like PATH).
|
|
700
|
+
*/
|
|
701
|
+
function spawnClaudeCodeHeadlessAdapter(opts) {
|
|
702
|
+
const { cmd, args } = resolveClaudeCodeHeadlessPath();
|
|
703
|
+
const logDirPath = opts.logDir || (0, path_1.join)(opts.workDir, 'logs');
|
|
704
|
+
const logName = opts.name || `claude-code-headless-${Date.now()}`;
|
|
705
|
+
const logPath = (0, path_1.join)(logDirPath, `${logName}.log`);
|
|
706
|
+
const pidPath = (0, path_1.join)(logDirPath, `${logName}.pid`);
|
|
707
|
+
(0, fs_1.mkdirSync)(logDirPath, { recursive: true });
|
|
708
|
+
const logFd = (0, fs_1.openSync)(logPath, 'a');
|
|
709
|
+
let child;
|
|
710
|
+
try {
|
|
711
|
+
child = (0, child_process_1.spawn)(cmd, args, {
|
|
712
|
+
cwd: opts.workDir,
|
|
713
|
+
detached: true,
|
|
714
|
+
stdio: ['ignore', logFd, logFd],
|
|
715
|
+
env: {
|
|
716
|
+
...process.env,
|
|
717
|
+
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
718
|
+
[config_1.ENV.PLAYER_NAME]: opts.name,
|
|
719
|
+
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
720
|
+
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
721
|
+
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
722
|
+
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
|
723
|
+
...(opts.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: opts.temporalTlsCertPath } : {}),
|
|
724
|
+
...(opts.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: opts.temporalTlsKeyPath } : {}),
|
|
725
|
+
// Permission mode: recruit-arg → AGENT_TEMPO_PERMISSION_MODE → in-adapter default.
|
|
726
|
+
...(opts.permissionMode ? { [config_1.ENV.PERMISSION_MODE]: opts.permissionMode } : {}),
|
|
727
|
+
...(opts.dangerouslySkipPermissions ? { [config_1.ENV.DANGEROUSLY_SKIP_PERMISSIONS]: '1' } : {}),
|
|
728
|
+
// Attachment handoff — adapter renews via startV2Lifecycle.
|
|
729
|
+
...(opts.attachmentId ? { [config_1.ENV.ATTACHMENT_ID]: opts.attachmentId } : {}),
|
|
730
|
+
...(opts.attachmentRunId ? { [config_1.ENV.ATTACHMENT_RUN_ID]: opts.attachmentRunId } : {}),
|
|
731
|
+
...(opts.adapterId ? { [config_1.ENV.ADAPTER_ID]: opts.adapterId } : {}),
|
|
732
|
+
},
|
|
733
|
+
});
|
|
734
|
+
child.unref();
|
|
735
|
+
}
|
|
736
|
+
finally {
|
|
737
|
+
(0, fs_1.closeSync)(logFd);
|
|
738
|
+
}
|
|
739
|
+
if (child.pid != null) {
|
|
740
|
+
(0, fs_1.writeFileSync)(pidPath, String(child.pid));
|
|
741
|
+
}
|
|
742
|
+
log(`Spawned claude-code-headless adapter (pid ${child.pid}) in ${opts.workDir} ` +
|
|
743
|
+
`as "${opts.name}"${opts.permissionMode ? ` (permissionMode=${opts.permissionMode})` : ''}` +
|
|
744
|
+
`${opts.dangerouslySkipPermissions ? ' (dangerouslySkipPermissions=true)' : ''}` +
|
|
745
|
+
`${opts.attachmentId ? ` (attachmentId=${opts.attachmentId})` : ''}`);
|
|
746
|
+
return { pid: child.pid, logPath, pidPath };
|
|
747
|
+
}
|