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,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WRITE_ACTIONS = exports.WRITE_BODY_MAX = void 0;
|
|
4
|
+
exports.isWriteAction = isWriteAction;
|
|
5
|
+
exports.handleWriteRoute = handleWriteRoute;
|
|
6
|
+
const responses_1 = require("./responses");
|
|
7
|
+
const validation_1 = require("../utils/validation");
|
|
8
|
+
const body_1 = require("./body");
|
|
9
|
+
Object.defineProperty(exports, "WRITE_BODY_MAX", { enumerable: true, get: function () { return body_1.WRITE_BODY_MAX; } });
|
|
10
|
+
/**
|
|
11
|
+
* Names of the write actions exposed under `/v1/ensembles/:ensemble/<action>`.
|
|
12
|
+
*
|
|
13
|
+
* Two semantic groups, kept in this order so the table reads top-to-bottom
|
|
14
|
+
* by surface intent:
|
|
15
|
+
* - **Ensemble-scoped** (cue / pause / play / release / recruit) — the
|
|
16
|
+
* original PR-7a #340 surface; bodies don't carry `playerId`.
|
|
17
|
+
* - **Per-player destructive** (restart / destroy / detach / recall) —
|
|
18
|
+
* added so the dashboard's PlayerDetail action row can wire to live
|
|
19
|
+
* mutations. Bodies are uniform `{ playerId, reason? }` (plus per-action
|
|
20
|
+
* extras); the ensemble lives in the URL.
|
|
21
|
+
*/
|
|
22
|
+
exports.WRITE_ACTIONS = [
|
|
23
|
+
'cue',
|
|
24
|
+
'pause',
|
|
25
|
+
'play',
|
|
26
|
+
'release',
|
|
27
|
+
'recruit',
|
|
28
|
+
'restart',
|
|
29
|
+
'destroy',
|
|
30
|
+
'detach',
|
|
31
|
+
'recall',
|
|
32
|
+
];
|
|
33
|
+
/** Type guard — narrows an arbitrary string to a known `WriteAction`. */
|
|
34
|
+
function isWriteAction(s) {
|
|
35
|
+
return exports.WRITE_ACTIONS.includes(s);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Top-level dispatch — called from `server.ts` when the URL matches
|
|
39
|
+
* `/v1/ensembles/:ensemble/<action>` with a known `<action>`. The
|
|
40
|
+
* caller has already bearer/CORS-gated the request; this function
|
|
41
|
+
* trusts the gates and focuses on body parsing + per-action routing.
|
|
42
|
+
*/
|
|
43
|
+
async function handleWriteRoute(req, res, client, ensemble, action) {
|
|
44
|
+
if ((0, validation_1.validateEnsembleName)(ensemble) !== null) {
|
|
45
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-ensemble-name', ensemble });
|
|
46
|
+
}
|
|
47
|
+
// `pause` / `play` / `release` / `recruit` accept an empty body or a
|
|
48
|
+
// small JSON object; `cue` requires the `{ to, message }` body.
|
|
49
|
+
// Parse universally so unknown fields surface as clean 400s.
|
|
50
|
+
const body = await (0, body_1.readJsonBody)(req);
|
|
51
|
+
if (body === body_1.BODY_TOO_LARGE) {
|
|
52
|
+
return (0, responses_1.errorResponse)(res, 413, { error: 'body-too-large', limit: body_1.WRITE_BODY_MAX });
|
|
53
|
+
}
|
|
54
|
+
if (body === body_1.BODY_INVALID_JSON) {
|
|
55
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-json' });
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
switch (action) {
|
|
59
|
+
case 'cue': return await handleCue(res, client, ensemble, body);
|
|
60
|
+
case 'pause': return await handlePause(res, client, ensemble);
|
|
61
|
+
case 'play': return await handlePlay(res, client, ensemble, body);
|
|
62
|
+
case 'release': return await handleRelease(res, client, ensemble, body);
|
|
63
|
+
case 'recruit': return await handleRecruit(res, client, ensemble, body);
|
|
64
|
+
case 'restart': return await handleRestart(res, client, ensemble, body);
|
|
65
|
+
case 'destroy': return await handleDestroy(res, client, ensemble, body);
|
|
66
|
+
case 'detach': return await handleDetach(res, client, ensemble, body);
|
|
67
|
+
case 'recall': return await handleRecall(res, client, ensemble, body);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
return mapWriteError(res, action, ensemble, err);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// ── Per-action handlers ──────────────────────────────────────────────────
|
|
75
|
+
async function handleCue(res, client, ensemble, body) {
|
|
76
|
+
const to = (0, body_1.stringField)(body, 'to');
|
|
77
|
+
const message = (0, body_1.stringField)(body, 'message');
|
|
78
|
+
if (!to)
|
|
79
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'missing-field', field: 'to' });
|
|
80
|
+
if (!message)
|
|
81
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'missing-field', field: 'message' });
|
|
82
|
+
if ((0, validation_1.validatePlayerName)(to) !== null) {
|
|
83
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-player-name', field: 'to' });
|
|
84
|
+
}
|
|
85
|
+
if (message.length > validation_1.MESSAGE_MAX) {
|
|
86
|
+
return (0, responses_1.errorResponse)(res, 413, { error: 'message-too-long', limit: validation_1.MESSAGE_MAX });
|
|
87
|
+
}
|
|
88
|
+
// `sendAsMaestro` writes through the maestro session's outbox so the
|
|
89
|
+
// chat row shows `role: 'maestro-out'` — matches the dashboard's
|
|
90
|
+
// "you, the operator" semantic. `sendMessage` is conductor-sourced
|
|
91
|
+
// and would mis-label the row.
|
|
92
|
+
await client.ensureMaestroSession(ensemble);
|
|
93
|
+
await client.sendAsMaestro(ensemble, to, message);
|
|
94
|
+
(0, responses_1.jsonResponse)(res, 202, { ok: true, ensemble, to });
|
|
95
|
+
}
|
|
96
|
+
async function handlePause(res, client, ensemble) {
|
|
97
|
+
await client.pause(ensemble);
|
|
98
|
+
(0, responses_1.jsonResponse)(res, 202, { ok: true, ensemble });
|
|
99
|
+
}
|
|
100
|
+
async function handlePlay(res, client, ensemble, body) {
|
|
101
|
+
// `release` is opt-in; only forward when explicitly true so the client
|
|
102
|
+
// method's optional-prop signature stays meaningful.
|
|
103
|
+
const release = body.release === true;
|
|
104
|
+
const opts = release ? { release: true } : undefined;
|
|
105
|
+
await client.play(ensemble, opts);
|
|
106
|
+
(0, responses_1.jsonResponse)(res, 202, { ok: true, ensemble, released: release });
|
|
107
|
+
}
|
|
108
|
+
async function handleRelease(res, client, ensemble, body) {
|
|
109
|
+
const playerId = (0, body_1.stringField)(body, 'playerId');
|
|
110
|
+
if (playerId !== undefined && (0, validation_1.validatePlayerName)(playerId) !== null) {
|
|
111
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-player-name', field: 'playerId' });
|
|
112
|
+
}
|
|
113
|
+
const result = await client.release(ensemble, playerId);
|
|
114
|
+
(0, responses_1.jsonResponse)(res, 200, result);
|
|
115
|
+
}
|
|
116
|
+
async function handleRecruit(res, client, ensemble, body) {
|
|
117
|
+
const name = (0, body_1.stringField)(body, 'name');
|
|
118
|
+
const workDir = (0, body_1.stringField)(body, 'workDir');
|
|
119
|
+
if (!name)
|
|
120
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'missing-field', field: 'name' });
|
|
121
|
+
if (!workDir)
|
|
122
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'missing-field', field: 'workDir' });
|
|
123
|
+
if ((0, validation_1.validatePlayerName)(name) !== null) {
|
|
124
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-player-name', field: 'name' });
|
|
125
|
+
}
|
|
126
|
+
const agent = (0, body_1.stringField)(body, 'agent');
|
|
127
|
+
// Dev-mode parity with the MCP `recruit` tool: `'mock'` is only valid when
|
|
128
|
+
// `AGENT_TEMPO_DEV_MODE=1`. The mock adapter's registry registration is
|
|
129
|
+
// also dev-gated (ADR 0014 §7 gate 2); rejecting here gives a clearer 400
|
|
130
|
+
// than the registry's downstream "Unknown adapter" error.
|
|
131
|
+
const allowed = (0, body_1.allowedAgentsForCurrentMode)();
|
|
132
|
+
if (agent !== undefined && !(0, body_1.isAllowedAgent)(agent, allowed)) {
|
|
133
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-agent', allowed });
|
|
134
|
+
}
|
|
135
|
+
// Pluck each optional field once so the spread below doesn't run
|
|
136
|
+
// `stringField` twice per key.
|
|
137
|
+
const playerType = (0, body_1.stringField)(body, 'playerType');
|
|
138
|
+
const host = (0, body_1.stringField)(body, 'host');
|
|
139
|
+
const initialMessage = (0, body_1.stringField)(body, 'initialMessage');
|
|
140
|
+
const systemPrompt = (0, body_1.stringField)(body, 'systemPrompt');
|
|
141
|
+
const result = await client.recruit(ensemble, {
|
|
142
|
+
name,
|
|
143
|
+
workDir,
|
|
144
|
+
...(agent !== undefined && (0, body_1.isAllowedAgent)(agent, allowed) ? { agent } : {}),
|
|
145
|
+
...(playerType !== undefined ? { playerType } : {}),
|
|
146
|
+
...(host !== undefined ? { host } : {}),
|
|
147
|
+
...(initialMessage !== undefined ? { initialMessage } : {}),
|
|
148
|
+
...(systemPrompt !== undefined ? { systemPrompt } : {}),
|
|
149
|
+
...(body.isConductor === true ? { isConductor: true } : {}),
|
|
150
|
+
...(body.held === true ? { held: true } : {}),
|
|
151
|
+
});
|
|
152
|
+
(0, responses_1.jsonResponse)(res, 202, result);
|
|
153
|
+
}
|
|
154
|
+
// ── Per-player destructive actions ──────────────────────────────────────
|
|
155
|
+
//
|
|
156
|
+
// Each handler validates `playerId` (required) + optional `reason` and
|
|
157
|
+
// shims to the matching TempoClient method. Body shape is uniform:
|
|
158
|
+
// `{ playerId: string; reason?: string }`. Errors flow through
|
|
159
|
+
// `mapWriteError` so a missing player surfaces as 404.
|
|
160
|
+
async function handleRestart(res, client, ensemble, body) {
|
|
161
|
+
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
162
|
+
if (!playerId)
|
|
163
|
+
return;
|
|
164
|
+
// `reason` isn't part of `RestartClientOpts` (the client carries audit
|
|
165
|
+
// strings via `invokerPlayerId`); accepted in the body for consistency
|
|
166
|
+
// with the other player actions but currently a no-op pass-through.
|
|
167
|
+
// Surface remains future-compatible — if `reason` lands in
|
|
168
|
+
// RestartClientOpts later, the field is already accepted.
|
|
169
|
+
const result = await client.restart(ensemble, playerId);
|
|
170
|
+
(0, responses_1.jsonResponse)(res, 202, result);
|
|
171
|
+
}
|
|
172
|
+
async function handleDestroy(res, client, ensemble, body) {
|
|
173
|
+
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
174
|
+
if (!playerId)
|
|
175
|
+
return;
|
|
176
|
+
const reason = (0, body_1.stringField)(body, 'reason');
|
|
177
|
+
// Single-player destroy returns void — give the dashboard a stable
|
|
178
|
+
// shape to read (`{ ok: true, ensemble, playerId }`) so action-row
|
|
179
|
+
// mutations can confirm success without sniffing for `undefined`.
|
|
180
|
+
await client.destroy(ensemble, playerId, reason);
|
|
181
|
+
(0, responses_1.jsonResponse)(res, 202, { ok: true, ensemble, playerId });
|
|
182
|
+
}
|
|
183
|
+
async function handleDetach(res, client, ensemble, body) {
|
|
184
|
+
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
185
|
+
if (!playerId)
|
|
186
|
+
return;
|
|
187
|
+
// `reason` is accepted on the body for shape parity with the other
|
|
188
|
+
// per-player actions but the client method doesn't carry it (matches
|
|
189
|
+
// `restart`'s posture — future-compatible if the signature gains it).
|
|
190
|
+
//
|
|
191
|
+
// `detach` accepts `deadlineMs` (graceful drain window). Optional —
|
|
192
|
+
// TempoClient defaults when omitted. Strict-validate the type so
|
|
193
|
+
// proxy-stringified JSON values like `"8000"` fast-fail with a clean
|
|
194
|
+
// 400 instead of silently dropping into the default. Matches the
|
|
195
|
+
// strict body-validation philosophy at the file header.
|
|
196
|
+
let deadlineMs;
|
|
197
|
+
if (body.deadlineMs !== undefined) {
|
|
198
|
+
if (typeof body.deadlineMs !== 'number' || !Number.isFinite(body.deadlineMs)) {
|
|
199
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'invalid-field', field: 'deadlineMs' });
|
|
200
|
+
}
|
|
201
|
+
deadlineMs = body.deadlineMs;
|
|
202
|
+
}
|
|
203
|
+
await client.detach(ensemble, playerId, deadlineMs);
|
|
204
|
+
(0, responses_1.jsonResponse)(res, 202, { ok: true, ensemble, playerId });
|
|
205
|
+
}
|
|
206
|
+
async function handleRecall(res, client, ensemble, body) {
|
|
207
|
+
const playerId = (0, body_1.requirePlayerId)(res, body);
|
|
208
|
+
if (!playerId)
|
|
209
|
+
return;
|
|
210
|
+
// Recall is read-only (it returns the player's message timeline) but
|
|
211
|
+
// groups with the destructive actions for routing because the dashboard
|
|
212
|
+
// surfaces it on the same PlayerDetail action row. 200 (not 202)
|
|
213
|
+
// because the result is the operation, not a queued effect.
|
|
214
|
+
//
|
|
215
|
+
// The dashboard's `RecallResult` consumer expects a count, not the raw
|
|
216
|
+
// arrays — projecting `received` + `sent` lengths here keeps the wire
|
|
217
|
+
// payload tight and avoids leaking inbox + sent-history shape into the
|
|
218
|
+
// browser-side type. Callers wanting the full timeline use the MCP
|
|
219
|
+
// `recall` tool / TempoClient method directly.
|
|
220
|
+
const result = await client.recall(ensemble, playerId);
|
|
221
|
+
const messages = result.received.length + result.sent.length;
|
|
222
|
+
(0, responses_1.jsonResponse)(res, 200, { ok: true, ensemble, playerId, messages });
|
|
223
|
+
}
|
|
224
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
225
|
+
/**
|
|
226
|
+
* Map a thrown error from a TempoClient call to an HTTP response.
|
|
227
|
+
* Recognises the "no session" / "no maestro" / "no ensemble" error
|
|
228
|
+
* messages the existing client throws and surfaces them as 404 so
|
|
229
|
+
* dashboard error UIs can show a meaningful message instead of a 500.
|
|
230
|
+
*/
|
|
231
|
+
function mapWriteError(res, action, ensemble, err) {
|
|
232
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
+
if (/no session found|no maestro|workflow not found/i.test(message)) {
|
|
234
|
+
return (0, responses_1.errorResponse)(res, 404, { error: 'session-not-found', action, ensemble, detail: message });
|
|
235
|
+
}
|
|
236
|
+
if (/Unknown agent type/i.test(message)) {
|
|
237
|
+
return (0, responses_1.errorResponse)(res, 400, { error: 'unknown-agent-type', action, ensemble, detail: message });
|
|
238
|
+
}
|
|
239
|
+
return (0, responses_1.errorResponse)(res, 500, { error: 'write-failed', action, ensemble, detail: message });
|
|
240
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure palette-input classification + filtering helpers.
|
|
3
|
+
*
|
|
4
|
+
* Shared between the TUI (`src/tui/commands.ts`) and the dashboard
|
|
5
|
+
* (`dashboard/src/components/chat/`) so the two surfaces' chat-input
|
|
6
|
+
* autocomplete machinery stays in lockstep — same prefix detection,
|
|
7
|
+
* same filtering semantics, same quote-aware tokenizer.
|
|
8
|
+
*
|
|
9
|
+
* Extracted from `src/tui/commands.ts` in #471/#472 (dashboard chat-input
|
|
10
|
+
* TUI parity bundle). The TUI re-exports every symbol from here so existing
|
|
11
|
+
* callers see no behaviour change. The dashboard imports directly from
|
|
12
|
+
* `agent-tempo/palette` (Vite alias `agent-tempo` → `../src`).
|
|
13
|
+
*
|
|
14
|
+
* **No imports allowed.** Keep this module dependency-free so it
|
|
15
|
+
* tree-shakes cleanly into the dashboard bundle and runs unmodified in
|
|
16
|
+
* any JS host (Node, browser, web workers). If you find yourself
|
|
17
|
+
* reaching for Temporal / MCP / Ink / React / DOM types here, the
|
|
18
|
+
* helper belongs somewhere else.
|
|
19
|
+
*/
|
|
20
|
+
export interface ParsedCommand {
|
|
21
|
+
/** Command name (without the leading slash). */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Positional arguments after the command name. */
|
|
24
|
+
args: string[];
|
|
25
|
+
/** The original raw input string. */
|
|
26
|
+
raw: string;
|
|
27
|
+
/**
|
|
28
|
+
* #109: `true` when tokenization encountered an opening quote that never
|
|
29
|
+
* closed (e.g. user is mid-typing `/x "hello`). Strictly additive — existing
|
|
30
|
+
* handlers that don't inspect this field keep their forgiving-input behavior
|
|
31
|
+
* unchanged. Future strict-mode callers can opt in by checking the flag and
|
|
32
|
+
* surfacing an error sentinel before dispatch. Absent on well-formed input.
|
|
33
|
+
*/
|
|
34
|
+
unterminatedQuote?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Quote-aware tokenizer (#109). Splits on whitespace EXCEPT within balanced
|
|
38
|
+
* `"…"` or `'…'` runs. Backslash escapes are intentionally NOT supported —
|
|
39
|
+
* the command surface is small and escape handling adds failure modes
|
|
40
|
+
* (Windows paths mis-escaping, for example). Callers that need literal
|
|
41
|
+
* quotes inside an argument can use the other quote kind:
|
|
42
|
+
* `/x "with 'apostrophe'"`.
|
|
43
|
+
*
|
|
44
|
+
* Returns `{ tokens, unterminatedQuote }`:
|
|
45
|
+
* - Well-formed input: `unterminatedQuote === false`, all tokens split.
|
|
46
|
+
* - Mid-typed input with an open quote (e.g. `/x "hello`): everything still
|
|
47
|
+
* flushes as the final token and `unterminatedQuote === true` so downstream
|
|
48
|
+
* strict callers can distinguish. The TUI's on-every-keystroke consumer
|
|
49
|
+
* ignores the flag — forgiving input by design.
|
|
50
|
+
*
|
|
51
|
+
* Exported for unit tests; callers outside this module should use
|
|
52
|
+
* {@link parseCommand} which wraps this with slash-prefix validation and
|
|
53
|
+
* command-name lowercasing.
|
|
54
|
+
*/
|
|
55
|
+
export declare function tokenize(input: string): {
|
|
56
|
+
tokens: string[];
|
|
57
|
+
unterminatedQuote: boolean;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Parse raw input into a structured command.
|
|
61
|
+
* Returns null if input is not a slash command (doesn't start with "/").
|
|
62
|
+
*
|
|
63
|
+
* Uses the quote-aware {@link tokenize} helper so
|
|
64
|
+
* `/schedule create foo cron "0 * * * *"` correctly binds the cron
|
|
65
|
+
* expression as a single argument (#109). When the input has an
|
|
66
|
+
* unterminated quote (user mid-typing), the `unterminatedQuote` flag is
|
|
67
|
+
* surfaced on the returned ParsedCommand — existing callers that ignore
|
|
68
|
+
* it keep their pre-#109 forgiving behavior.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseCommand(input: string): ParsedCommand | null;
|
|
71
|
+
/** Commands that take a player name as their first parameter. */
|
|
72
|
+
export declare const PLAYER_PARAM_COMMANDS: ReadonlySet<string>;
|
|
73
|
+
/**
|
|
74
|
+
* Commands with hardcoded subcommands (shown in autocomplete). Keys are
|
|
75
|
+
* command names without the leading slash; values are subcommand tokens
|
|
76
|
+
* the palette shows as completions for the first positional argument.
|
|
77
|
+
*/
|
|
78
|
+
export declare const SUBCOMMAND_MAP: Readonly<Record<string, readonly string[]>>;
|
|
79
|
+
/** What the chat input is currently completing, if anything. */
|
|
80
|
+
export type PaletteMode = 'command' | 'player' | 'player-arg';
|
|
81
|
+
export interface PaletteContext {
|
|
82
|
+
mode: PaletteMode;
|
|
83
|
+
/** Lowercased partial token being completed (may be empty). */
|
|
84
|
+
partial: string;
|
|
85
|
+
/**
|
|
86
|
+
* Prefix of the current input that should be preserved when the user
|
|
87
|
+
* selects a palette item. The selected name is appended to this prefix.
|
|
88
|
+
* For `command`/`player` modes this is always `'/'` or `'@'`. For
|
|
89
|
+
* `player-arg` mode this is `/<cmd> ` (note trailing space).
|
|
90
|
+
*/
|
|
91
|
+
replacePrefix: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Classify the current input for palette-autocomplete purposes.
|
|
95
|
+
*
|
|
96
|
+
* The palette is visible in three modes:
|
|
97
|
+
* - `command`: user is typing `/` + partial command name (no space yet)
|
|
98
|
+
* - `player` : user is typing `@` + partial player name (no space yet)
|
|
99
|
+
* - `player-arg`: user typed a {@link PLAYER_PARAM_COMMANDS} command
|
|
100
|
+
* followed by a space, and is now typing the first positional
|
|
101
|
+
* argument (a player name)
|
|
102
|
+
*
|
|
103
|
+
* Returns `null` when the input does not match any palette-eligible shape
|
|
104
|
+
* (e.g. plain chat, or a second positional arg).
|
|
105
|
+
*
|
|
106
|
+
* Pure function — safe to call from React render and unit tests.
|
|
107
|
+
*/
|
|
108
|
+
export declare function classifyPaletteInput(raw: string): PaletteContext | null;
|
|
109
|
+
/**
|
|
110
|
+
* Map a palette mode to the glyph prefix used when displaying or
|
|
111
|
+
* inserting an autofill selection.
|
|
112
|
+
*
|
|
113
|
+
* `command` / `player-arg` → `/`
|
|
114
|
+
* `player` → `@`
|
|
115
|
+
*
|
|
116
|
+
* Centralising the mapping here means renderers (TUI's CommandPalette,
|
|
117
|
+
* dashboard's AutofillPopup) can't drift from the classifier. If a new
|
|
118
|
+
* mode lands, the compiler flags every consumer at once.
|
|
119
|
+
*/
|
|
120
|
+
export declare function modeToPrefix(mode: PaletteMode): '/' | '@';
|
|
121
|
+
/**
|
|
122
|
+
* Filter palette command entries by a typed prefix.
|
|
123
|
+
* Prefix may optionally include a leading '/' — it's stripped before matching.
|
|
124
|
+
* An empty prefix returns all commands in order.
|
|
125
|
+
*
|
|
126
|
+
* Pure function — safe to call from React render paths and unit tests.
|
|
127
|
+
*/
|
|
128
|
+
export declare function filterPaletteCommands<T extends {
|
|
129
|
+
name: string;
|
|
130
|
+
}>(commands: readonly T[], filter: string): T[];
|
|
131
|
+
/**
|
|
132
|
+
* Filter a list of player names by a partial, using prefix + segment match
|
|
133
|
+
* (e.g. partial `co` matches `conductor` and `tempo-composer`). Exact
|
|
134
|
+
* matches are excluded (nothing left to complete).
|
|
135
|
+
*
|
|
136
|
+
* Pure function — safe to call from React render and unit tests.
|
|
137
|
+
*/
|
|
138
|
+
export declare function filterPlayerNames(names: readonly string[], partial: string): string[];
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Pure palette-input classification + filtering helpers.
|
|
4
|
+
*
|
|
5
|
+
* Shared between the TUI (`src/tui/commands.ts`) and the dashboard
|
|
6
|
+
* (`dashboard/src/components/chat/`) so the two surfaces' chat-input
|
|
7
|
+
* autocomplete machinery stays in lockstep — same prefix detection,
|
|
8
|
+
* same filtering semantics, same quote-aware tokenizer.
|
|
9
|
+
*
|
|
10
|
+
* Extracted from `src/tui/commands.ts` in #471/#472 (dashboard chat-input
|
|
11
|
+
* TUI parity bundle). The TUI re-exports every symbol from here so existing
|
|
12
|
+
* callers see no behaviour change. The dashboard imports directly from
|
|
13
|
+
* `agent-tempo/palette` (Vite alias `agent-tempo` → `../src`).
|
|
14
|
+
*
|
|
15
|
+
* **No imports allowed.** Keep this module dependency-free so it
|
|
16
|
+
* tree-shakes cleanly into the dashboard bundle and runs unmodified in
|
|
17
|
+
* any JS host (Node, browser, web workers). If you find yourself
|
|
18
|
+
* reaching for Temporal / MCP / Ink / React / DOM types here, the
|
|
19
|
+
* helper belongs somewhere else.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.SUBCOMMAND_MAP = exports.PLAYER_PARAM_COMMANDS = void 0;
|
|
23
|
+
exports.tokenize = tokenize;
|
|
24
|
+
exports.parseCommand = parseCommand;
|
|
25
|
+
exports.classifyPaletteInput = classifyPaletteInput;
|
|
26
|
+
exports.modeToPrefix = modeToPrefix;
|
|
27
|
+
exports.filterPaletteCommands = filterPaletteCommands;
|
|
28
|
+
exports.filterPlayerNames = filterPlayerNames;
|
|
29
|
+
/**
|
|
30
|
+
* Quote-aware tokenizer (#109). Splits on whitespace EXCEPT within balanced
|
|
31
|
+
* `"…"` or `'…'` runs. Backslash escapes are intentionally NOT supported —
|
|
32
|
+
* the command surface is small and escape handling adds failure modes
|
|
33
|
+
* (Windows paths mis-escaping, for example). Callers that need literal
|
|
34
|
+
* quotes inside an argument can use the other quote kind:
|
|
35
|
+
* `/x "with 'apostrophe'"`.
|
|
36
|
+
*
|
|
37
|
+
* Returns `{ tokens, unterminatedQuote }`:
|
|
38
|
+
* - Well-formed input: `unterminatedQuote === false`, all tokens split.
|
|
39
|
+
* - Mid-typed input with an open quote (e.g. `/x "hello`): everything still
|
|
40
|
+
* flushes as the final token and `unterminatedQuote === true` so downstream
|
|
41
|
+
* strict callers can distinguish. The TUI's on-every-keystroke consumer
|
|
42
|
+
* ignores the flag — forgiving input by design.
|
|
43
|
+
*
|
|
44
|
+
* Exported for unit tests; callers outside this module should use
|
|
45
|
+
* {@link parseCommand} which wraps this with slash-prefix validation and
|
|
46
|
+
* command-name lowercasing.
|
|
47
|
+
*/
|
|
48
|
+
function tokenize(input) {
|
|
49
|
+
const tokens = [];
|
|
50
|
+
let cur = '';
|
|
51
|
+
let inSingle = false;
|
|
52
|
+
let inDouble = false;
|
|
53
|
+
// Tracks whether any char has been emitted into `cur` during the current
|
|
54
|
+
// token — needed so that an explicit empty string (`""`) becomes a real
|
|
55
|
+
// zero-length token rather than being collapsed with the surrounding
|
|
56
|
+
// whitespace boundary.
|
|
57
|
+
let hasContent = false;
|
|
58
|
+
const flush = () => {
|
|
59
|
+
if (cur.length > 0 || hasContent) {
|
|
60
|
+
tokens.push(cur);
|
|
61
|
+
cur = '';
|
|
62
|
+
hasContent = false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
for (let i = 0; i < input.length; i++) {
|
|
66
|
+
const ch = input[i];
|
|
67
|
+
if (ch === '"' && !inSingle) {
|
|
68
|
+
inDouble = !inDouble;
|
|
69
|
+
hasContent = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === "'" && !inDouble) {
|
|
73
|
+
inSingle = !inSingle;
|
|
74
|
+
hasContent = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (/\s/.test(ch) && !inSingle && !inDouble) {
|
|
78
|
+
flush();
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
cur += ch;
|
|
82
|
+
hasContent = true;
|
|
83
|
+
}
|
|
84
|
+
flush();
|
|
85
|
+
return { tokens, unterminatedQuote: inSingle || inDouble };
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Parse raw input into a structured command.
|
|
89
|
+
* Returns null if input is not a slash command (doesn't start with "/").
|
|
90
|
+
*
|
|
91
|
+
* Uses the quote-aware {@link tokenize} helper so
|
|
92
|
+
* `/schedule create foo cron "0 * * * *"` correctly binds the cron
|
|
93
|
+
* expression as a single argument (#109). When the input has an
|
|
94
|
+
* unterminated quote (user mid-typing), the `unterminatedQuote` flag is
|
|
95
|
+
* surfaced on the returned ParsedCommand — existing callers that ignore
|
|
96
|
+
* it keep their pre-#109 forgiving behavior.
|
|
97
|
+
*/
|
|
98
|
+
function parseCommand(input) {
|
|
99
|
+
const trimmed = input.trim();
|
|
100
|
+
if (!trimmed.startsWith('/'))
|
|
101
|
+
return null;
|
|
102
|
+
const { tokens, unterminatedQuote } = tokenize(trimmed.slice(1));
|
|
103
|
+
if (tokens.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
const name = tokens[0].toLowerCase();
|
|
106
|
+
const args = tokens.slice(1);
|
|
107
|
+
return unterminatedQuote
|
|
108
|
+
? { name, args, raw: trimmed, unterminatedQuote: true }
|
|
109
|
+
: { name, args, raw: trimmed };
|
|
110
|
+
}
|
|
111
|
+
// ── Palette classifier + filter helpers ──
|
|
112
|
+
/** Commands that take a player name as their first parameter. */
|
|
113
|
+
exports.PLAYER_PARAM_COMMANDS = new Set([
|
|
114
|
+
'worktree',
|
|
115
|
+
'restart',
|
|
116
|
+
'destroy',
|
|
117
|
+
'attachment-info',
|
|
118
|
+
]);
|
|
119
|
+
/**
|
|
120
|
+
* Commands with hardcoded subcommands (shown in autocomplete). Keys are
|
|
121
|
+
* command names without the leading slash; values are subcommand tokens
|
|
122
|
+
* the palette shows as completions for the first positional argument.
|
|
123
|
+
*/
|
|
124
|
+
exports.SUBCOMMAND_MAP = {
|
|
125
|
+
worktree: ['create', 'remove', 'list'],
|
|
126
|
+
stage: ['create', 'list', 'cancel'],
|
|
127
|
+
schedule: ['create', 'delete'],
|
|
128
|
+
lineup: ['load', 'save'],
|
|
129
|
+
ensemble: ['save', 'list', 'show'],
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Classify the current input for palette-autocomplete purposes.
|
|
133
|
+
*
|
|
134
|
+
* The palette is visible in three modes:
|
|
135
|
+
* - `command`: user is typing `/` + partial command name (no space yet)
|
|
136
|
+
* - `player` : user is typing `@` + partial player name (no space yet)
|
|
137
|
+
* - `player-arg`: user typed a {@link PLAYER_PARAM_COMMANDS} command
|
|
138
|
+
* followed by a space, and is now typing the first positional
|
|
139
|
+
* argument (a player name)
|
|
140
|
+
*
|
|
141
|
+
* Returns `null` when the input does not match any palette-eligible shape
|
|
142
|
+
* (e.g. plain chat, or a second positional arg).
|
|
143
|
+
*
|
|
144
|
+
* Pure function — safe to call from React render and unit tests.
|
|
145
|
+
*/
|
|
146
|
+
function classifyPaletteInput(raw) {
|
|
147
|
+
const trimmed = raw.trimStart();
|
|
148
|
+
if (!trimmed)
|
|
149
|
+
return null;
|
|
150
|
+
// Command-name completion: "/rec"
|
|
151
|
+
if (trimmed.startsWith('/') && !trimmed.includes(' ')) {
|
|
152
|
+
return { mode: 'command', partial: trimmed.slice(1).toLowerCase(), replacePrefix: '/' };
|
|
153
|
+
}
|
|
154
|
+
// @-player completion: "@al"
|
|
155
|
+
if (trimmed.startsWith('@') && !trimmed.includes(' ')) {
|
|
156
|
+
return { mode: 'player', partial: trimmed.slice(1).toLowerCase(), replacePrefix: '@' };
|
|
157
|
+
}
|
|
158
|
+
// Player-arg completion: "/restart co"
|
|
159
|
+
if (trimmed.startsWith('/') && trimmed.includes(' ')) {
|
|
160
|
+
const spaceIdx = trimmed.indexOf(' ');
|
|
161
|
+
const cmd = trimmed.slice(1, spaceIdx).toLowerCase();
|
|
162
|
+
const afterCmd = trimmed.slice(spaceIdx + 1);
|
|
163
|
+
// Only surface palette for the FIRST positional arg (no further space yet).
|
|
164
|
+
if (afterCmd.includes(' '))
|
|
165
|
+
return null;
|
|
166
|
+
if (exports.PLAYER_PARAM_COMMANDS.has(cmd)) {
|
|
167
|
+
return {
|
|
168
|
+
mode: 'player-arg',
|
|
169
|
+
partial: afterCmd.toLowerCase(),
|
|
170
|
+
replacePrefix: `/${cmd} `,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Map a palette mode to the glyph prefix used when displaying or
|
|
178
|
+
* inserting an autofill selection.
|
|
179
|
+
*
|
|
180
|
+
* `command` / `player-arg` → `/`
|
|
181
|
+
* `player` → `@`
|
|
182
|
+
*
|
|
183
|
+
* Centralising the mapping here means renderers (TUI's CommandPalette,
|
|
184
|
+
* dashboard's AutofillPopup) can't drift from the classifier. If a new
|
|
185
|
+
* mode lands, the compiler flags every consumer at once.
|
|
186
|
+
*/
|
|
187
|
+
function modeToPrefix(mode) {
|
|
188
|
+
return mode === 'player' ? '@' : '/';
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Filter palette command entries by a typed prefix.
|
|
192
|
+
* Prefix may optionally include a leading '/' — it's stripped before matching.
|
|
193
|
+
* An empty prefix returns all commands in order.
|
|
194
|
+
*
|
|
195
|
+
* Pure function — safe to call from React render paths and unit tests.
|
|
196
|
+
*/
|
|
197
|
+
function filterPaletteCommands(commands, filter) {
|
|
198
|
+
const prefix = filter.startsWith('/') ? filter.slice(1) : filter;
|
|
199
|
+
if (!prefix)
|
|
200
|
+
return [...commands];
|
|
201
|
+
const lower = prefix.toLowerCase();
|
|
202
|
+
return commands.filter((c) => c.name.toLowerCase().startsWith(lower));
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Filter a list of player names by a partial, using prefix + segment match
|
|
206
|
+
* (e.g. partial `co` matches `conductor` and `tempo-composer`). Exact
|
|
207
|
+
* matches are excluded (nothing left to complete).
|
|
208
|
+
*
|
|
209
|
+
* Pure function — safe to call from React render and unit tests.
|
|
210
|
+
*/
|
|
211
|
+
function filterPlayerNames(names, partial) {
|
|
212
|
+
const lower = partial.toLowerCase();
|
|
213
|
+
if (!lower)
|
|
214
|
+
return [...names];
|
|
215
|
+
return names.filter((n) => {
|
|
216
|
+
const l = n.toLowerCase();
|
|
217
|
+
if (l === lower)
|
|
218
|
+
return false;
|
|
219
|
+
return l.startsWith(lower) || l.split('-').some((seg) => seg.startsWith(lower));
|
|
220
|
+
});
|
|
221
|
+
}
|