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.
Files changed (484) hide show
  1. package/CLAUDE.md +213 -0
  2. package/LICENSE +21 -0
  3. package/README.md +289 -0
  4. package/assets/icon-32.png +0 -0
  5. package/assets/icon-512.png +0 -0
  6. package/assets/icon-64.png +0 -0
  7. package/assets/icon-dark-32.png +0 -0
  8. package/assets/icon-dark-64.png +0 -0
  9. package/assets/icon-dark.svg +9 -0
  10. package/assets/icon.svg +9 -0
  11. package/assets/logo-dark.svg +11 -0
  12. package/assets/logo-light.svg +11 -0
  13. package/dashboard/README.md +91 -0
  14. package/dashboard/dist/assets/index-CB78ToNE.css +2 -0
  15. package/dashboard/dist/assets/index-_5jV0Znu.js +62 -0
  16. package/dashboard/dist/assets/index-_5jV0Znu.js.map +1 -0
  17. package/dashboard/dist/index.html +21 -0
  18. package/dashboard/package.json +47 -0
  19. package/dist/activities/hard-terminate.d.ts +32 -0
  20. package/dist/activities/hard-terminate.js +460 -0
  21. package/dist/activities/maestro.d.ts +72 -0
  22. package/dist/activities/maestro.js +254 -0
  23. package/dist/activities/outbox.d.ts +188 -0
  24. package/dist/activities/outbox.js +849 -0
  25. package/dist/activities/resolve.d.ts +64 -0
  26. package/dist/activities/resolve.js +129 -0
  27. package/dist/activities/schedule-fire.d.ts +36 -0
  28. package/dist/activities/schedule-fire.js +147 -0
  29. package/dist/adapters/base.d.ts +426 -0
  30. package/dist/adapters/base.js +1270 -0
  31. package/dist/adapters/claude-api/adapter.d.ts +168 -0
  32. package/dist/adapters/claude-api/adapter.js +797 -0
  33. package/dist/adapters/claude-api/api-error.d.ts +96 -0
  34. package/dist/adapters/claude-api/api-error.js +191 -0
  35. package/dist/adapters/claude-api/index.d.ts +16 -0
  36. package/dist/adapters/claude-api/index.js +21 -0
  37. package/dist/adapters/claude-api/mcp-bridge.d.ts +50 -0
  38. package/dist/adapters/claude-api/mcp-bridge.js +157 -0
  39. package/dist/adapters/claude-code/adapter.d.ts +133 -0
  40. package/dist/adapters/claude-code/adapter.js +274 -0
  41. package/dist/adapters/claude-code/index.d.ts +15 -0
  42. package/dist/adapters/claude-code/index.js +20 -0
  43. package/dist/adapters/claude-code-headless/adapter.d.ts +131 -0
  44. package/dist/adapters/claude-code-headless/adapter.js +710 -0
  45. package/dist/adapters/claude-code-headless/error-mapper.d.ts +107 -0
  46. package/dist/adapters/claude-code-headless/error-mapper.js +281 -0
  47. package/dist/adapters/claude-code-headless/index.d.ts +17 -0
  48. package/dist/adapters/claude-code-headless/index.js +26 -0
  49. package/dist/adapters/claude-code-headless/pre-flight.d.ts +51 -0
  50. package/dist/adapters/claude-code-headless/pre-flight.js +207 -0
  51. package/dist/adapters/claude-code-headless/prompt.d.ts +93 -0
  52. package/dist/adapters/claude-code-headless/prompt.js +79 -0
  53. package/dist/adapters/claude-code-headless/stream-json.d.ts +242 -0
  54. package/dist/adapters/claude-code-headless/stream-json.js +208 -0
  55. package/dist/adapters/claude-code-headless/types.d.ts +28 -0
  56. package/dist/adapters/claude-code-headless/types.js +36 -0
  57. package/dist/adapters/copilot/adapter.d.ts +100 -0
  58. package/dist/adapters/copilot/adapter.js +730 -0
  59. package/dist/adapters/copilot/index.d.ts +15 -0
  60. package/dist/adapters/copilot/index.js +20 -0
  61. package/dist/adapters/index.d.ts +42 -0
  62. package/dist/adapters/index.js +115 -0
  63. package/dist/adapters/opencode/adapter.d.ts +82 -0
  64. package/dist/adapters/opencode/adapter.js +710 -0
  65. package/dist/adapters/opencode/config.d.ts +90 -0
  66. package/dist/adapters/opencode/config.js +137 -0
  67. package/dist/adapters/opencode/helpers.d.ts +40 -0
  68. package/dist/adapters/opencode/helpers.js +144 -0
  69. package/dist/adapters/opencode/index.d.ts +12 -0
  70. package/dist/adapters/opencode/index.js +17 -0
  71. package/dist/adapters/opencode/server-bridge.d.ts +124 -0
  72. package/dist/adapters/opencode/server-bridge.js +216 -0
  73. package/dist/adapters/sdk/base.d.ts +95 -0
  74. package/dist/adapters/sdk/base.js +134 -0
  75. package/dist/adapters/sdk/system-prompt.d.ts +64 -0
  76. package/dist/adapters/sdk/system-prompt.js +78 -0
  77. package/dist/adapters/terminal-error.d.ts +27 -0
  78. package/dist/adapters/terminal-error.js +39 -0
  79. package/dist/channel.d.ts +3 -0
  80. package/dist/channel.js +48 -0
  81. package/dist/cli/commands.d.ts +245 -0
  82. package/dist/cli/commands.js +2438 -0
  83. package/dist/cli/config-command.d.ts +8 -0
  84. package/dist/cli/config-command.js +254 -0
  85. package/dist/cli/daemon-command.d.ts +57 -0
  86. package/dist/cli/daemon-command.js +493 -0
  87. package/dist/cli/daemon.d.ts +217 -0
  88. package/dist/cli/daemon.js +632 -0
  89. package/dist/cli/dashboard-command.d.ts +20 -0
  90. package/dist/cli/dashboard-command.js +241 -0
  91. package/dist/cli/dev-banner.d.ts +107 -0
  92. package/dist/cli/dev-banner.js +190 -0
  93. package/dist/cli/dev-mode-bootstrap.d.ts +29 -0
  94. package/dist/cli/dev-mode-bootstrap.js +36 -0
  95. package/dist/cli/dev-verbs.d.ts +43 -0
  96. package/dist/cli/dev-verbs.js +254 -0
  97. package/dist/cli/help-text.d.ts +1 -0
  98. package/dist/cli/help-text.js +158 -0
  99. package/dist/cli/legacy-migration.d.ts +35 -0
  100. package/dist/cli/legacy-migration.js +335 -0
  101. package/dist/cli/mcp.d.ts +8 -0
  102. package/dist/cli/mcp.js +63 -0
  103. package/dist/cli/output.d.ts +12 -0
  104. package/dist/cli/output.js +37 -0
  105. package/dist/cli/preflight.d.ts +9 -0
  106. package/dist/cli/preflight.js +96 -0
  107. package/dist/cli/removed-verbs.d.ts +9 -0
  108. package/dist/cli/removed-verbs.js +78 -0
  109. package/dist/cli/sa-preflight.d.ts +99 -0
  110. package/dist/cli/sa-preflight.js +183 -0
  111. package/dist/cli/scenarios-command.d.ts +6 -0
  112. package/dist/cli/scenarios-command.js +167 -0
  113. package/dist/cli/startup.d.ts +112 -0
  114. package/dist/cli/startup.js +641 -0
  115. package/dist/cli/upgrade-command.d.ts +5 -0
  116. package/dist/cli/upgrade-command.js +240 -0
  117. package/dist/cli.d.ts +2 -0
  118. package/dist/cli.js +680 -0
  119. package/dist/client/core.d.ts +33 -0
  120. package/dist/client/core.js +1260 -0
  121. package/dist/client/ensure-conductor-spawned.d.ts +35 -0
  122. package/dist/client/ensure-conductor-spawned.js +48 -0
  123. package/dist/client/index.d.ts +32 -0
  124. package/dist/client/index.js +22 -0
  125. package/dist/client/interface.d.ts +461 -0
  126. package/dist/client/interface.js +2 -0
  127. package/dist/client/subscribe.d.ts +108 -0
  128. package/dist/client/subscribe.js +598 -0
  129. package/dist/client/with-spawn.d.ts +27 -0
  130. package/dist/client/with-spawn.js +87 -0
  131. package/dist/config.d.ts +323 -0
  132. package/dist/config.js +593 -0
  133. package/dist/connection.d.ts +7 -0
  134. package/dist/connection.js +46 -0
  135. package/dist/constants.d.ts +50 -0
  136. package/dist/constants.js +74 -0
  137. package/dist/copilot-bridge.d.ts +22 -0
  138. package/dist/copilot-bridge.js +565 -0
  139. package/dist/daemon-adapter-versions.d.ts +52 -0
  140. package/dist/daemon-adapter-versions.js +170 -0
  141. package/dist/daemon.d.ts +275 -0
  142. package/dist/daemon.js +989 -0
  143. package/dist/ensemble/agent-types.d.ts +23 -0
  144. package/dist/ensemble/agent-types.js +132 -0
  145. package/dist/ensemble/loader.d.ts +14 -0
  146. package/dist/ensemble/loader.js +140 -0
  147. package/dist/ensemble/saver.d.ts +49 -0
  148. package/dist/ensemble/saver.js +201 -0
  149. package/dist/ensemble/schema.d.ts +71 -0
  150. package/dist/ensemble/schema.js +3 -0
  151. package/dist/git-info.d.ts +4 -0
  152. package/dist/git-info.js +29 -0
  153. package/dist/http/aggregate.d.ts +319 -0
  154. package/dist/http/aggregate.js +684 -0
  155. package/dist/http/auth.d.ts +67 -0
  156. package/dist/http/auth.js +177 -0
  157. package/dist/http/body.d.ts +71 -0
  158. package/dist/http/body.js +121 -0
  159. package/dist/http/catalog.d.ts +67 -0
  160. package/dist/http/catalog.js +209 -0
  161. package/dist/http/cors.d.ts +42 -0
  162. package/dist/http/cors.js +111 -0
  163. package/dist/http/dashboard-pair.d.ts +94 -0
  164. package/dist/http/dashboard-pair.js +148 -0
  165. package/dist/http/dashboard.d.ts +20 -0
  166. package/dist/http/dashboard.js +160 -0
  167. package/dist/http/event-bus.d.ts +217 -0
  168. package/dist/http/event-bus.js +365 -0
  169. package/dist/http/event-id.d.ts +77 -0
  170. package/dist/http/event-id.js +117 -0
  171. package/dist/http/event-types.d.ts +348 -0
  172. package/dist/http/event-types.js +36 -0
  173. package/dist/http/fixtures/chat-stress.d.ts +8 -0
  174. package/dist/http/fixtures/chat-stress.js +63 -0
  175. package/dist/http/fixtures/conductor-leaving.d.ts +8 -0
  176. package/dist/http/fixtures/conductor-leaving.js +80 -0
  177. package/dist/http/fixtures/constants.d.ts +10 -0
  178. package/dist/http/fixtures/constants.js +13 -0
  179. package/dist/http/fixtures/eight-player-broadcast.d.ts +10 -0
  180. package/dist/http/fixtures/eight-player-broadcast.js +81 -0
  181. package/dist/http/fixtures/empty-ensemble.d.ts +6 -0
  182. package/dist/http/fixtures/empty-ensemble.js +26 -0
  183. package/dist/http/fixtures/index.d.ts +55 -0
  184. package/dist/http/fixtures/index.js +110 -0
  185. package/dist/http/fixtures/single-conductor.d.ts +7 -0
  186. package/dist/http/fixtures/single-conductor.js +46 -0
  187. package/dist/http/fixtures/sse-reconnect.d.ts +8 -0
  188. package/dist/http/fixtures/sse-reconnect.js +77 -0
  189. package/dist/http/index.d.ts +21 -0
  190. package/dist/http/index.js +61 -0
  191. package/dist/http/port-file.d.ts +22 -0
  192. package/dist/http/port-file.js +132 -0
  193. package/dist/http/responses.d.ts +27 -0
  194. package/dist/http/responses.js +40 -0
  195. package/dist/http/ring-buffer.d.ts +41 -0
  196. package/dist/http/ring-buffer.js +80 -0
  197. package/dist/http/server.d.ts +122 -0
  198. package/dist/http/server.js +459 -0
  199. package/dist/http/snapshot.d.ts +85 -0
  200. package/dist/http/snapshot.js +180 -0
  201. package/dist/http/sse-handler.d.ts +87 -0
  202. package/dist/http/sse-handler.js +294 -0
  203. package/dist/http/writes.d.ts +55 -0
  204. package/dist/http/writes.js +240 -0
  205. package/dist/palette/index.d.ts +138 -0
  206. package/dist/palette/index.js +221 -0
  207. package/dist/reconcile/orphans.d.ts +255 -0
  208. package/dist/reconcile/orphans.js +340 -0
  209. package/dist/scripts/258-spotcheck.js +303 -0
  210. package/dist/scripts/check-components-css-sync.js +199 -0
  211. package/dist/scripts/run-shard.js +121 -0
  212. package/dist/scripts/verify-daemon-isolation-guard.js +128 -0
  213. package/dist/server-tools.d.ts +87 -0
  214. package/dist/server-tools.js +146 -0
  215. package/dist/server.d.ts +2 -0
  216. package/dist/server.js +366 -0
  217. package/dist/spawn.d.ts +296 -0
  218. package/dist/spawn.js +747 -0
  219. package/dist/tools/agent-types.d.ts +2 -0
  220. package/dist/tools/agent-types.js +21 -0
  221. package/dist/tools/attachment-info.d.ts +4 -0
  222. package/dist/tools/attachment-info.js +48 -0
  223. package/dist/tools/broadcast.d.ts +4 -0
  224. package/dist/tools/broadcast.js +76 -0
  225. package/dist/tools/cancel-stage.d.ts +3 -0
  226. package/dist/tools/cancel-stage.js +20 -0
  227. package/dist/tools/clear-state.d.ts +3 -0
  228. package/dist/tools/clear-state.js +37 -0
  229. package/dist/tools/coat-check-evict.d.ts +4 -0
  230. package/dist/tools/coat-check-evict.js +43 -0
  231. package/dist/tools/coat-check-get.d.ts +4 -0
  232. package/dist/tools/coat-check-get.js +56 -0
  233. package/dist/tools/coat-check-list.d.ts +4 -0
  234. package/dist/tools/coat-check-list.js +60 -0
  235. package/dist/tools/coat-check-put.d.ts +4 -0
  236. package/dist/tools/coat-check-put.js +53 -0
  237. package/dist/tools/cue.d.ts +44 -0
  238. package/dist/tools/cue.js +201 -0
  239. package/dist/tools/destroy.d.ts +4 -0
  240. package/dist/tools/destroy.js +188 -0
  241. package/dist/tools/detach.d.ts +4 -0
  242. package/dist/tools/detach.js +45 -0
  243. package/dist/tools/encore.d.ts +4 -0
  244. package/dist/tools/encore.js +31 -0
  245. package/dist/tools/ensemble.d.ts +32 -0
  246. package/dist/tools/ensemble.js +198 -0
  247. package/dist/tools/evaluate-gate.d.ts +3 -0
  248. package/dist/tools/evaluate-gate.js +32 -0
  249. package/dist/tools/fetch-state.d.ts +13 -0
  250. package/dist/tools/fetch-state.js +78 -0
  251. package/dist/tools/gates.d.ts +3 -0
  252. package/dist/tools/gates.js +41 -0
  253. package/dist/tools/helpers.d.ts +21 -0
  254. package/dist/tools/helpers.js +25 -0
  255. package/dist/tools/hosts.d.ts +4 -0
  256. package/dist/tools/hosts.js +40 -0
  257. package/dist/tools/listen.d.ts +3 -0
  258. package/dist/tools/listen.js +22 -0
  259. package/dist/tools/load-lineup.d.ts +5 -0
  260. package/dist/tools/load-lineup.js +381 -0
  261. package/dist/tools/migrate.d.ts +4 -0
  262. package/dist/tools/migrate.js +60 -0
  263. package/dist/tools/pause-ensemble.d.ts +4 -0
  264. package/dist/tools/pause-ensemble.js +58 -0
  265. package/dist/tools/pause.d.ts +4 -0
  266. package/dist/tools/pause.js +36 -0
  267. package/dist/tools/play.d.ts +4 -0
  268. package/dist/tools/play.js +57 -0
  269. package/dist/tools/quality-gate.d.ts +3 -0
  270. package/dist/tools/quality-gate.js +26 -0
  271. package/dist/tools/recall.d.ts +3 -0
  272. package/dist/tools/recall.js +32 -0
  273. package/dist/tools/recruit.d.ts +38 -0
  274. package/dist/tools/recruit.js +447 -0
  275. package/dist/tools/release.d.ts +4 -0
  276. package/dist/tools/release.js +98 -0
  277. package/dist/tools/report.d.ts +3 -0
  278. package/dist/tools/report.js +29 -0
  279. package/dist/tools/resolve.d.ts +1 -0
  280. package/dist/tools/resolve.js +7 -0
  281. package/dist/tools/restart.d.ts +35 -0
  282. package/dist/tools/restart.js +131 -0
  283. package/dist/tools/restore.d.ts +4 -0
  284. package/dist/tools/restore.js +107 -0
  285. package/dist/tools/resume-ensemble.d.ts +4 -0
  286. package/dist/tools/resume-ensemble.js +79 -0
  287. package/dist/tools/save-lineup.d.ts +4 -0
  288. package/dist/tools/save-lineup.js +36 -0
  289. package/dist/tools/save-state.d.ts +3 -0
  290. package/dist/tools/save-state.js +57 -0
  291. package/dist/tools/schedule.d.ts +4 -0
  292. package/dist/tools/schedule.js +152 -0
  293. package/dist/tools/schedules.d.ts +4 -0
  294. package/dist/tools/schedules.js +54 -0
  295. package/dist/tools/set-ensemble-description.d.ts +4 -0
  296. package/dist/tools/set-ensemble-description.js +37 -0
  297. package/dist/tools/set-name.d.ts +4 -0
  298. package/dist/tools/set-name.js +45 -0
  299. package/dist/tools/set-part.d.ts +3 -0
  300. package/dist/tools/set-part.js +20 -0
  301. package/dist/tools/shutdown.d.ts +4 -0
  302. package/dist/tools/shutdown.js +54 -0
  303. package/dist/tools/stage.d.ts +3 -0
  304. package/dist/tools/stage.js +28 -0
  305. package/dist/tools/stages.d.ts +3 -0
  306. package/dist/tools/stages.js +35 -0
  307. package/dist/tools/stop.d.ts +4 -0
  308. package/dist/tools/stop.js +29 -0
  309. package/dist/tools/unschedule.d.ts +4 -0
  310. package/dist/tools/unschedule.js +35 -0
  311. package/dist/tools/who-am-i.d.ts +3 -0
  312. package/dist/tools/who-am-i.js +34 -0
  313. package/dist/tools/worktree.d.ts +4 -0
  314. package/dist/tools/worktree.js +181 -0
  315. package/dist/tui/App.d.ts +85 -0
  316. package/dist/tui/App.js +1791 -0
  317. package/dist/tui/bootstrap-types.d.ts +46 -0
  318. package/dist/tui/bootstrap-types.js +7 -0
  319. package/dist/tui/client.d.ts +6 -0
  320. package/dist/tui/client.js +9 -0
  321. package/dist/tui/commands.d.ts +71 -0
  322. package/dist/tui/commands.js +1375 -0
  323. package/dist/tui/components/ActivityLog.d.ts +16 -0
  324. package/dist/tui/components/ActivityLog.js +36 -0
  325. package/dist/tui/components/ChatView.d.ts +35 -0
  326. package/dist/tui/components/ChatView.js +54 -0
  327. package/dist/tui/components/CommandOverlay.d.ts +15 -0
  328. package/dist/tui/components/CommandOverlay.js +34 -0
  329. package/dist/tui/components/CommandPalette.d.ts +21 -0
  330. package/dist/tui/components/CommandPalette.js +67 -0
  331. package/dist/tui/components/ConductorChat.d.ts +16 -0
  332. package/dist/tui/components/ConductorChat.js +32 -0
  333. package/dist/tui/components/ConversationStream.d.ts +114 -0
  334. package/dist/tui/components/ConversationStream.js +307 -0
  335. package/dist/tui/components/CreateEnsembleWizard.d.ts +19 -0
  336. package/dist/tui/components/CreateEnsembleWizard.js +223 -0
  337. package/dist/tui/components/DestroyConfirmModal.d.ts +17 -0
  338. package/dist/tui/components/DestroyConfirmModal.js +62 -0
  339. package/dist/tui/components/EnsembleListView.d.ts +14 -0
  340. package/dist/tui/components/EnsembleListView.js +32 -0
  341. package/dist/tui/components/EnsemblePanel.d.ts +12 -0
  342. package/dist/tui/components/EnsemblePanel.js +40 -0
  343. package/dist/tui/components/ErrorView.d.ts +31 -0
  344. package/dist/tui/components/ErrorView.js +129 -0
  345. package/dist/tui/components/HomeView.d.ts +54 -0
  346. package/dist/tui/components/HomeView.js +306 -0
  347. package/dist/tui/components/InputBar.d.ts +13 -0
  348. package/dist/tui/components/InputBar.js +58 -0
  349. package/dist/tui/components/LoadLineupModal.d.ts +18 -0
  350. package/dist/tui/components/LoadLineupModal.js +79 -0
  351. package/dist/tui/components/MainView.d.ts +21 -0
  352. package/dist/tui/components/MainView.js +107 -0
  353. package/dist/tui/components/NewEnsembleModal.d.ts +9 -0
  354. package/dist/tui/components/NewEnsembleModal.js +73 -0
  355. package/dist/tui/components/Picker.d.ts +23 -0
  356. package/dist/tui/components/Picker.js +70 -0
  357. package/dist/tui/components/PlayerDetailView.d.ts +26 -0
  358. package/dist/tui/components/PlayerDetailView.js +118 -0
  359. package/dist/tui/components/PromptArea.d.ts +50 -0
  360. package/dist/tui/components/PromptArea.js +303 -0
  361. package/dist/tui/components/RecruitWizard.d.ts +17 -0
  362. package/dist/tui/components/RecruitWizard.js +221 -0
  363. package/dist/tui/components/RestoreConfirmModal.d.ts +18 -0
  364. package/dist/tui/components/RestoreConfirmModal.js +71 -0
  365. package/dist/tui/components/ScheduleOverlay.d.ts +13 -0
  366. package/dist/tui/components/ScheduleOverlay.js +113 -0
  367. package/dist/tui/components/ScheduleWizard.d.ts +19 -0
  368. package/dist/tui/components/ScheduleWizard.js +259 -0
  369. package/dist/tui/components/Splash.d.ts +23 -0
  370. package/dist/tui/components/Splash.js +221 -0
  371. package/dist/tui/components/StatusBar.d.ts +48 -0
  372. package/dist/tui/components/StatusBar.js +128 -0
  373. package/dist/tui/components/StatusOverlay.d.ts +15 -0
  374. package/dist/tui/components/StatusOverlay.js +76 -0
  375. package/dist/tui/components/TitleBar.d.ts +10 -0
  376. package/dist/tui/components/TitleBar.js +21 -0
  377. package/dist/tui/components/TopBar.d.ts +12 -0
  378. package/dist/tui/components/TopBar.js +15 -0
  379. package/dist/tui/core-api.d.ts +26 -0
  380. package/dist/tui/core-api.js +67 -0
  381. package/dist/tui/hooks/useEnsembleDiscovery.d.ts +3 -0
  382. package/dist/tui/hooks/useEnsembleDiscovery.js +30 -0
  383. package/dist/tui/hooks/useMaestroPoller.d.ts +3 -0
  384. package/dist/tui/hooks/useMaestroPoller.js +36 -0
  385. package/dist/tui/hooks/useSendCommand.d.ts +7 -0
  386. package/dist/tui/hooks/useSendCommand.js +29 -0
  387. package/dist/tui/index.d.ts +15 -0
  388. package/dist/tui/index.js +156 -0
  389. package/dist/tui/ink-context.d.ts +18 -0
  390. package/dist/tui/ink-context.js +59 -0
  391. package/dist/tui/ink-loader.d.ts +26 -0
  392. package/dist/tui/ink-loader.js +42 -0
  393. package/dist/tui/removed-commands.d.ts +9 -0
  394. package/dist/tui/removed-commands.js +22 -0
  395. package/dist/tui/sse-handler.d.ts +52 -0
  396. package/dist/tui/sse-handler.js +157 -0
  397. package/dist/tui/store.d.ts +598 -0
  398. package/dist/tui/store.js +753 -0
  399. package/dist/tui/utils/format.d.ts +56 -0
  400. package/dist/tui/utils/format.js +155 -0
  401. package/dist/tui/utils/fullscreen.d.ts +23 -0
  402. package/dist/tui/utils/fullscreen.js +71 -0
  403. package/dist/tui/utils/history.d.ts +10 -0
  404. package/dist/tui/utils/history.js +85 -0
  405. package/dist/tui/utils/platform.d.ts +45 -0
  406. package/dist/tui/utils/platform.js +258 -0
  407. package/dist/tui/utils/theme.d.ts +21 -0
  408. package/dist/tui/utils/theme.js +24 -0
  409. package/dist/types.d.ts +1020 -0
  410. package/dist/types.js +39 -0
  411. package/dist/utils/attachment-format.d.ts +22 -0
  412. package/dist/utils/attachment-format.js +32 -0
  413. package/dist/utils/default-part.d.ts +43 -0
  414. package/dist/utils/default-part.js +104 -0
  415. package/dist/utils/duration.d.ts +30 -0
  416. package/dist/utils/duration.js +69 -0
  417. package/dist/utils/ensemble-ops.d.ts +61 -0
  418. package/dist/utils/ensemble-ops.js +77 -0
  419. package/dist/utils/format-hosts.d.ts +21 -0
  420. package/dist/utils/format-hosts.js +73 -0
  421. package/dist/utils/hosts.d.ts +113 -0
  422. package/dist/utils/hosts.js +265 -0
  423. package/dist/utils/parent-death-watchdog.d.ts +1 -0
  424. package/dist/utils/parent-death-watchdog.js +47 -0
  425. package/dist/utils/query-timeout.d.ts +103 -0
  426. package/dist/utils/query-timeout.js +113 -0
  427. package/dist/utils/recall-format.d.ts +78 -0
  428. package/dist/utils/recall-format.js +105 -0
  429. package/dist/utils/restore-format.d.ts +49 -0
  430. package/dist/utils/restore-format.js +91 -0
  431. package/dist/utils/safe-path.d.ts +10 -0
  432. package/dist/utils/safe-path.js +43 -0
  433. package/dist/utils/sdk-probe.d.ts +9 -0
  434. package/dist/utils/sdk-probe.js +45 -0
  435. package/dist/utils/search-attributes.d.ts +76 -0
  436. package/dist/utils/search-attributes.js +86 -0
  437. package/dist/utils/validation.d.ts +113 -0
  438. package/dist/utils/validation.js +163 -0
  439. package/dist/utils/visibility-deadline.d.ts +186 -0
  440. package/dist/utils/visibility-deadline.js +158 -0
  441. package/dist/utils/worktree.d.ts +103 -0
  442. package/dist/utils/worktree.js +327 -0
  443. package/dist/worker.d.ts +14 -0
  444. package/dist/worker.js +146 -0
  445. package/dist/workflows/attachment-math.d.ts +56 -0
  446. package/dist/workflows/attachment-math.js +47 -0
  447. package/dist/workflows/index.d.ts +3 -0
  448. package/dist/workflows/index.js +11 -0
  449. package/dist/workflows/maestro-signals.d.ts +217 -0
  450. package/dist/workflows/maestro-signals.js +155 -0
  451. package/dist/workflows/maestro.d.ts +3 -0
  452. package/dist/workflows/maestro.js +812 -0
  453. package/dist/workflows/scheduler-signals.d.ts +10 -0
  454. package/dist/workflows/scheduler-signals.js +14 -0
  455. package/dist/workflows/scheduler.d.ts +17 -0
  456. package/dist/workflows/scheduler.js +143 -0
  457. package/dist/workflows/session.d.ts +2 -0
  458. package/dist/workflows/session.js +1638 -0
  459. package/dist/workflows/signals.d.ts +297 -0
  460. package/dist/workflows/signals.js +239 -0
  461. package/examples/agents/tempo-composer.md +56 -0
  462. package/examples/agents/tempo-conductor.md +117 -0
  463. package/examples/agents/tempo-critic.md +73 -0
  464. package/examples/agents/tempo-improv.md +74 -0
  465. package/examples/agents/tempo-liner.md +75 -0
  466. package/examples/agents/tempo-roadie.md +61 -0
  467. package/examples/agents/tempo-soloist.md +71 -0
  468. package/examples/agents/tempo-tuner.md +94 -0
  469. package/examples/ensembles/tempo-big-band.yaml +146 -0
  470. package/examples/ensembles/tempo-dev-team.yaml +58 -0
  471. package/examples/ensembles/tempo-headless-jam.yaml +77 -0
  472. package/examples/ensembles/tempo-jam-session.yaml +41 -0
  473. package/examples/ensembles/tempo-mock-jam.yaml +79 -0
  474. package/examples/ensembles/tempo-review-squad.yaml +32 -0
  475. package/package.json +172 -0
  476. package/packaging/launchd/com.agent.tempo.plist +46 -0
  477. package/packaging/systemd/agent-tempo.service +32 -0
  478. package/packaging/windows/install-task.ps1 +71 -0
  479. package/scenarios/conductor-recruit-mock.yaml +33 -0
  480. package/scenarios/echo-roundtrip.yaml +15 -0
  481. package/scenarios/multi-player-handoff.yaml +38 -0
  482. package/scenarios/recruit-cascade.yaml +38 -0
  483. package/scenarios/two-player-conversation.yaml +33 -0
  484. 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
+ }