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,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { WorkflowHandle } from '@temporalio/client';
3
+ export declare function registerRecallTool(server: McpServer, handle: WorkflowHandle, getPlayerId: () => string): void;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerRecallTool = registerRecallTool;
4
+ const zod_1 = require("zod");
5
+ const helpers_1 = require("./helpers");
6
+ const recall_format_1 = require("../utils/recall-format");
7
+ function registerRecallTool(server, handle, getPlayerId) {
8
+ (0, helpers_1.defineTool)(server, 'recall', 'Read your own message history. Shows received messages by default; use includeSent to also see outgoing messages. `offset` pages the timeline; `previewLength` truncates bodies (unset = full text).', {
9
+ limit: zod_1.z.number().min(1).max(100).optional().describe('Max messages to return (default 20, max 100)'),
10
+ offset: zod_1.z.number().int().min(0).optional().describe('Skip N messages for paging (default 0)'),
11
+ previewLength: zod_1.z.number().int().min(1).optional().describe('Truncate each body to N chars + "…"; omit for full text'),
12
+ since: zod_1.z.string().optional().describe('Only show messages at or after this ISO timestamp'),
13
+ from: zod_1.z.string().optional().describe('Filter received messages by sender name'),
14
+ includeSent: zod_1.z.boolean().optional().describe('Include sent messages in the timeline (default: false)'),
15
+ }, async (args) => {
16
+ const { limit, offset, previewLength, since, from: fromFilter, includeSent } = args;
17
+ // Validate `since` up-front so the formatter never sees a garbage date.
18
+ if (since && Number.isNaN(Date.parse(since))) {
19
+ return (0, helpers_1.fail)(`Invalid ISO timestamp for "since": ${since}`);
20
+ }
21
+ try {
22
+ const received = await handle.query('allMessages');
23
+ const sent = includeSent ? await handle.query('allSentMessages') : [];
24
+ const timeline = (0, recall_format_1.buildTimeline)(received, sent, Boolean(includeSent));
25
+ const rendered = (0, recall_format_1.formatRecall)(timeline, { limit, offset, previewLength, since, from: fromFilter });
26
+ return (0, helpers_1.ok)(rendered.text);
27
+ }
28
+ catch (err) {
29
+ return (0, helpers_1.fail)(`Failed to recall messages: ${(0, helpers_1.formatError)(err)}`);
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,38 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Client, WorkflowHandle } from '@temporalio/client';
3
+ import { Config } from '../config';
4
+ import { AgentType } from '../types';
5
+ import type { HostInfo } from '../types';
6
+ /**
7
+ * #274 M15 — dep-injection surface on the recruit tool registrar.
8
+ *
9
+ * `listHostsFn` lets tests stub host discovery without touching
10
+ * `TestWorkflowEnvironment` internals. Production callers (`src/server.ts`)
11
+ * omit this; the default pulls in `src/utils/hosts.ts` lazily.
12
+ */
13
+ export interface RegisterRecruitToolDeps {
14
+ listHostsFn?: (client: Client) => Promise<HostInfo[]>;
15
+ }
16
+ export declare function registerRecruitTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle, ownAgentType?: AgentType, deps?: RegisterRecruitToolDeps): void;
17
+ /**
18
+ * Given a host liveness+profile snapshot, validate that `targetHost` is
19
+ * (a) known, (b) recruit-ready, and (c) advertises support for
20
+ * `requestedAgent`. Returns a user-facing error string when validation
21
+ * fails, or `null` when all checks pass.
22
+ *
23
+ * Profile-missing is treated as a soft pass (the daemon is alive but
24
+ * didn't signal a profile, e.g. rolling-upgrade scenario or global
25
+ * maestro was unreachable at its boot). We fall through rather than
26
+ * fail — M10 / AC11c says operational UX beats strict enforcement when
27
+ * information is genuinely absent.
28
+ *
29
+ * Exported for unit testing per the M15 dep-injection pattern.
30
+ */
31
+ export declare function checkHostPreflight(hosts: HostInfo[], targetHost: string, requestedAgent: AgentType): string | null;
32
+ /**
33
+ * Pick the closest hostname to `target` by Levenshtein distance. Returns
34
+ * `null` when `candidates` is empty OR the nearest match is so distant
35
+ * (edit distance ≥ half the longer string's length) that suggesting it
36
+ * would be misleading. Exported for tests.
37
+ */
38
+ export declare function nearestHostname(target: string, candidates: string[]): string | null;
@@ -0,0 +1,447 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerRecruitTool = registerRecruitTool;
37
+ exports.checkHostPreflight = checkHostPreflight;
38
+ exports.nearestHostname = nearestHostname;
39
+ const zod_1 = require("zod");
40
+ const child_process_1 = require("child_process");
41
+ const config_1 = require("../config");
42
+ const types_1 = require("../types");
43
+ const resolve_1 = require("./resolve");
44
+ const signals_1 = require("../workflows/signals");
45
+ const helpers_1 = require("./helpers");
46
+ const agent_types_1 = require("../ensemble/agent-types");
47
+ const validation_1 = require("../utils/validation");
48
+ const sdk_probe_1 = require("../utils/sdk-probe");
49
+ const pre_flight_1 = require("../adapters/claude-code-headless/pre-flight");
50
+ const types_2 = require("../adapters/claude-code-headless/types");
51
+ const toolLog = (...args) => console.error('[agent-tempo:recruit]', ...args);
52
+ /**
53
+ * #449 Phase C — check whether the `opencode` binary is on PATH. Used by
54
+ * the recruit pre-flight to fail fast with an actionable error before the
55
+ * adapter spawn activity runs and hits ENOENT mid-flight.
56
+ *
57
+ * Cross-platform: `where` on Windows, `command -v` on POSIX. Both exit 0
58
+ * when found, non-zero otherwise; stdout/stderr captured silently.
59
+ */
60
+ function hasOpencodeOnPath() {
61
+ try {
62
+ const cmd = process.platform === 'win32' ? 'where' : 'command';
63
+ const args = process.platform === 'win32' ? ['opencode'] : ['-v', 'opencode'];
64
+ const result = (0, child_process_1.spawnSync)(cmd, args, {
65
+ stdio: ['ignore', 'ignore', 'ignore'],
66
+ shell: process.platform !== 'win32', // POSIX needs the shell built-in
67
+ });
68
+ return result.status === 0;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ function registerRecruitTool(server, client, config, getPlayerId, handle, ownAgentType = 'claude', deps = {}) {
75
+ // Lazy default — only imports utils/hosts when actually called, so the
76
+ // MCP server's module load graph doesn't drag the whole join layer
77
+ // into every consumer at import time.
78
+ const listHostsFn = deps.listHostsFn ?? (async (c) => {
79
+ const { listHosts } = await Promise.resolve().then(() => __importStar(require('../utils/hosts')));
80
+ return listHosts(c, {
81
+ force: true, // pre-flight wants "right now" — no cached snapshot
82
+ namespace: config.temporalNamespace,
83
+ taskQueue: config.taskQueue,
84
+ });
85
+ });
86
+ (0, helpers_1.defineTool)(server, 'recruit', `Start a new named session in a directory. Rejects if the name is already active. Supports Claude Code or Copilot CLI agents. Defaults to "${ownAgentType}" (same as this session).`, {
87
+ workDir: zod_1.z.string().max(validation_1.PATH_MAX).describe('The working directory for the new session'),
88
+ name: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).describe('Name for the new session'),
89
+ conductor: zod_1.z.boolean().optional()
90
+ .describe('Whether this session is a conductor (default: false)'),
91
+ initialMessage: zod_1.z.string().max(validation_1.MESSAGE_MAX).optional()
92
+ .describe('Optional task or message for the new session (sent after it sets its name)'),
93
+ agent: zod_1.z.enum(types_1.AGENT_TYPES).optional()
94
+ .describe(`Which agent to use (default: "${ownAgentType}", same as this session). "mock" requires dev mode (--dev). "claude-api" runs headless via the Anthropic Messages API — requires ANTHROPIC_API_KEY env var and the @anthropic-ai/sdk optional dependency installed; has access to agent-tempo MCP tools (cue, report, recall, ensemble, …) but NOT file-edit or shell tools (use "claude" for those). "opencode" runs headless via a local opencode serve subprocess; multi-provider (Anthropic, OpenAI, Bedrock, Ollama, …) — requires the @opencode-ai/sdk optional dep and an opencode binary on PATH. opencode players ARE file-op-capable (file edits / shell / web search via OpenCode's built-in tools). "claude-code-headless" runs the official Claude Code CLI as a headless per-turn \`claude -p\` subprocess — requires the \`claude\` binary on PATH AND a logged-in Claude Code session (\`claude auth login\`); turns bill against the host's existing subscription extra-usage credits, NOT a Console API key. claude-code-headless players have full Claude Code tool access (Bash, Read, Write, Edit, Glob, Grep, WebSearch, WebFetch).`),
95
+ model: zod_1.z.string().regex(/^[a-z0-9][a-z0-9-/.:_]*$/).optional()
96
+ .describe('Model id. For "claude-api": bare Anthropic id (e.g. "claude-opus-4-7"). For "opencode": combined "provider/model" (e.g. "anthropic/claude-opus-4-7", "openai/gpt-4o", "ollama/llama3"). Falls back to AGENT_TEMPO_API_MODEL (claude-api) or AGENT_TEMPO_OPENCODE_MODEL (opencode), then a constants-pinned default. Ignored for claude / copilot / mock adapters.'),
97
+ type: zod_1.z.string().optional()
98
+ .describe('Agent type name — references a Claude Code agent definition (e.g., "tempo-soloist")'),
99
+ systemPrompt: zod_1.z.string().optional()
100
+ .describe('Path to a .md file to use as custom agent system prompt (--system-prompt)'),
101
+ host: zod_1.z.string().optional()
102
+ .describe('Target hostname for cross-machine recruiting. Omit for local spawn.'),
103
+ force: zod_1.z.boolean().optional()
104
+ .describe('Force-terminate any existing session with this name before recruiting. Use when a previous session is orphaned or stuck.'),
105
+ mockMode: zod_1.z.enum(types_1.MOCK_MODES).optional()
106
+ .describe('Dev-mode only (agent: "mock"). Mock adapter mode. Default: "echo". "silent" never replies (heartbeat-stale validation); "chaos" probabilistic fail/crash injection (env-tuned).'),
107
+ mockScenario: zod_1.z.string().optional()
108
+ .describe('Dev-mode only (agent: "mock", mockMode: "scripted"). Bare scenario name (resolved against shipped scenarios/) or absolute path to a scenario YAML.'),
109
+ // #520 — claude-code-headless adapter knobs. Both ignored for other adapters.
110
+ permissionMode: zod_1.z.enum(types_2.CLAUDE_CODE_PERMISSION_MODES).optional()
111
+ .describe('claude-code-headless only. Permission mode forwarded to `claude -p --permission-mode`. Default "acceptEdits" auto-approves writes + common fs commands. "bypassPermissions" / "dangerouslySkipPermissions" trades safety for speed in trusted contexts. "plan" plans without executing — not useful for headless players. Mutually exclusive with `dangerouslySkipPermissions`.'),
112
+ dangerouslySkipPermissions: zod_1.z.boolean().optional()
113
+ .describe('claude-code-headless only. When true, passes `--dangerously-skip-permissions` to `claude -p` instead of `--permission-mode`. Use only in sandboxed/trusted contexts. Mutually exclusive with `permissionMode`.'),
114
+ }, async (args) => {
115
+ const { workDir, name, initialMessage } = args;
116
+ const isConductor = args.conductor === true;
117
+ const agent = args.agent || ownAgentType;
118
+ const model = args.model;
119
+ const agentTypeName = args.type;
120
+ const systemPrompt = args.systemPrompt;
121
+ const host = args.host;
122
+ const force = args.force === true;
123
+ const mockMode = args.mockMode;
124
+ const mockScenario = args.mockScenario;
125
+ const permissionMode = args.permissionMode;
126
+ const dangerouslySkipPermissions = args.dangerouslySkipPermissions === true;
127
+ // ADR 0014 §7 gate 3 — recruit-time rejection of `agent: 'mock'`
128
+ // outside dev mode. Defense-in-depth: even if a hand-edited install
129
+ // had `dist/adapters/mock/` present (gate 1 bypassed) AND somehow
130
+ // got the registry to register the descriptor (gate 2 bypassed),
131
+ // this rejects the request with a clear, actionable error.
132
+ if (agent === 'mock' && !(0, config_1.isDevMode)()) {
133
+ return (0, helpers_1.fail)(`agent: "mock" is only available in dev mode. Restart agent-tempo with --dev (or set AGENT_TEMPO_DEV_MODE=1) to enable.`);
134
+ }
135
+ // mockMode / mockScenario are only meaningful with the mock adapter —
136
+ // reject silently-ignored params so users learn the right flag shape.
137
+ if (mockMode != null && agent !== 'mock') {
138
+ return (0, helpers_1.fail)(`mockMode is only valid when agent: "mock" (got agent: "${agent}").`);
139
+ }
140
+ if (mockScenario != null && agent !== 'mock') {
141
+ return (0, helpers_1.fail)(`mockScenario is only valid when agent: "mock" (got agent: "${agent}").`);
142
+ }
143
+ if (agent === 'mock' && mockMode === 'scripted' && !mockScenario) {
144
+ return (0, helpers_1.fail)(`mockMode: "scripted" requires mockScenario (a bare scenario name or path to a YAML file).`);
145
+ }
146
+ // PR-3: silent + chaos modes don't consult the scenario file. Reject
147
+ // explicitly rather than silently ignore so users learn the right
148
+ // shape and don't sit wondering why their scenario wasn't applied.
149
+ if (mockScenario && (mockMode === 'silent' || mockMode === 'chaos')) {
150
+ return (0, helpers_1.fail)(`mockMode: "${mockMode}" does not use a scenario. Drop mockScenario or switch to mockMode: "scripted".`);
151
+ }
152
+ // #131 / #449 Phase C — model knob is meaningful for claude-api AND
153
+ // opencode (different shapes — bare vs `provider/model` — both flow
154
+ // through the same recruit field). Reject silently-ignored params for
155
+ // the other adapters so users learn the right shape.
156
+ // Local-spawn pre-flight checks (env vars + SDK install + binaries)
157
+ // run only when `host` is unset; cross-host recruits delegate to the
158
+ // target daemon's `availableAgentTypes` advertisement (the existing
159
+ // `checkHostPreflight` path), which already gates on whether the
160
+ // remote daemon resolved the SDK at boot.
161
+ if (model != null && agent !== 'claude-api' && agent !== 'opencode') {
162
+ return (0, helpers_1.fail)(`model is only valid when agent: "claude-api" or agent: "opencode" (got agent: "${agent}").`);
163
+ }
164
+ // #520 — claude-code-headless permission knobs are mutually exclusive
165
+ // and only meaningful for that adapter. Reject silently-ignored
166
+ // params so users learn the right shape.
167
+ if (permissionMode != null && agent !== 'claude-code-headless') {
168
+ return (0, helpers_1.fail)(`permissionMode is only valid when agent: "claude-code-headless" (got agent: "${agent}").`);
169
+ }
170
+ if (dangerouslySkipPermissions && agent !== 'claude-code-headless') {
171
+ return (0, helpers_1.fail)(`dangerouslySkipPermissions is only valid when agent: "claude-code-headless" (got agent: "${agent}").`);
172
+ }
173
+ if (permissionMode != null && dangerouslySkipPermissions) {
174
+ return (0, helpers_1.fail)(`permissionMode and dangerouslySkipPermissions are mutually exclusive — pass at most one.`);
175
+ }
176
+ if (agent === 'claude-api' && !host && !force) {
177
+ if (!process.env.ANTHROPIC_API_KEY) {
178
+ return (0, helpers_1.fail)(`agent: "claude-api" requires the ANTHROPIC_API_KEY environment variable on the spawn host. Set it before recruiting (export ANTHROPIC_API_KEY=sk-...) or use \`force: true\` to bypass this check.`);
179
+ }
180
+ try {
181
+ require.resolve('@anthropic-ai/sdk');
182
+ }
183
+ catch {
184
+ return (0, helpers_1.fail)(`agent: "claude-api" requires the @anthropic-ai/sdk optional dependency. Install with \`npm install @anthropic-ai/sdk\` and retry, or use \`force: true\` to bypass this check.`);
185
+ }
186
+ }
187
+ // #449 Phase C — opencode pre-flight. Two checks: the optional SDK
188
+ // (signal that opencode integration is intended on this host) AND
189
+ // the `opencode` binary on PATH (the adapter spawns `opencode serve`
190
+ // as a subprocess). Cross-host recruits skip both — the target
191
+ // daemon's `availableAgentTypes` is the gate there.
192
+ if (agent === 'opencode' && !host && !force) {
193
+ if (!(0, sdk_probe_1.probeSdkInstall)('@opencode-ai/sdk')) {
194
+ return (0, helpers_1.fail)(`agent: "opencode" requires the @opencode-ai/sdk optional dependency. Install with \`npm install @opencode-ai/sdk\` and retry, or use \`force: true\` to bypass this check.`);
195
+ }
196
+ if (!hasOpencodeOnPath()) {
197
+ return (0, helpers_1.fail)(`agent: "opencode" requires the \`opencode\` binary on PATH. Install with \`npm install -g opencode-ai\` and retry, or use \`force: true\` to bypass this check.`);
198
+ }
199
+ }
200
+ // #520 — claude-code-headless pre-flight. Two checks, both bounded by
201
+ // short timeouts (3s + 5s). The auth probe uses the official `claude
202
+ // auth status` subcommand — no billed API call. Cross-host recruits
203
+ // skip both — the target daemon's `availableAgentTypes` is the gate
204
+ // there (PR-2 wires the daemon-side probe).
205
+ if (agent === 'claude-code-headless' && !host && !force) {
206
+ const claudeBin = config.claudeBin ?? 'claude';
207
+ const binProbe = (0, pre_flight_1.probeClaudeBinary)(claudeBin);
208
+ if (!binProbe.ok) {
209
+ return (0, helpers_1.fail)(`agent: "claude-code-headless" pre-flight failed: ${binProbe.error} Use \`force: true\` to bypass.`);
210
+ }
211
+ const authProbe = (0, pre_flight_1.probeClaudeAuth)(claudeBin);
212
+ if (!authProbe.loggedIn) {
213
+ return (0, helpers_1.fail)(`agent: "claude-code-headless" pre-flight failed: ${authProbe.error} Use \`force: true\` to bypass.`);
214
+ }
215
+ }
216
+ // #532 — copilot pre-flight. SDK-only probe (mirrors claude-api's
217
+ // pattern; copilot has no subprocess CLI on PATH). The bridge
218
+ // subprocess hard-requires `@github/copilot-sdk` at module load
219
+ // (`src/adapters/copilot/adapter.ts:71`), so without this gate the
220
+ // user only learns of the missing dep AFTER bridge spawn —
221
+ // adapter crashes with `process.exit(1)` and the player sits in
222
+ // `booting` until lease timeout. We use `probeSdkInstall` (FS walk)
223
+ // rather than `require.resolve` because pnpm layouts without a
224
+ // top-level hoisted link otherwise false-negative; see issue #532
225
+ // investigation footnote. GITHUB_TOKEN / Copilot CLI login are
226
+ // intentionally NOT checked: the SDK falls through to the
227
+ // logged-in user (`adapter.ts:31, :263`), so token presence is not
228
+ // a hard requirement. Cross-host recruits skip — the target
229
+ // daemon's `availableAgentTypes` is the gate there.
230
+ if (agent === 'copilot' && !host && !force) {
231
+ if (!(0, sdk_probe_1.probeSdkInstall)('@github/copilot-sdk')) {
232
+ return (0, helpers_1.fail)(`agent: "copilot" requires the @github/copilot-sdk optional dependency. Install with \`npm install @github/copilot-sdk\` and retry, or use \`force: true\` to bypass this check.`);
233
+ }
234
+ }
235
+ // Resolve agent type if provided
236
+ let agentDefinition;
237
+ let agentDefinitionPath;
238
+ let agentDefinitionDescription;
239
+ let nativeResolvable;
240
+ let allowedTools;
241
+ if (agentTypeName) {
242
+ const info = (0, agent_types_1.resolveAgentType)(agentTypeName);
243
+ if (!info) {
244
+ const available = (0, agent_types_1.listAgentTypes)().map(t => t.name);
245
+ return (0, helpers_1.fail)(`Unknown agent type "${agentTypeName}". Available types: ${available.length ? available.join(', ') : '(none)'}`);
246
+ }
247
+ agentDefinition = info.name;
248
+ agentDefinitionPath = info.path;
249
+ agentDefinitionDescription = info.description;
250
+ nativeResolvable = info.nativeResolvable;
251
+ allowedTools = info.allowedTools;
252
+ }
253
+ // Validate name
254
+ const nameError = (0, validation_1.validatePlayerName)(name);
255
+ if (nameError) {
256
+ return (0, helpers_1.fail)(nameError);
257
+ }
258
+ if (name === 'conductor' && !isConductor) {
259
+ return (0, helpers_1.fail)(`The name "conductor" is reserved for conductor sessions. Use a different name, or set conductor: true.`);
260
+ }
261
+ // ── #274 AC12 — cross-host recruit pre-flight ──
262
+ //
263
+ // When `host` is specified, verify that the target is live + has
264
+ // the requested agent runtime available BEFORE submitting to the
265
+ // outbox. Otherwise `recruit --host foo` could queue forever on a
266
+ // per-host task queue nobody's listening on (the exact failure
267
+ // mode #274 set out to close).
268
+ //
269
+ // `force: true` bypasses pre-flight entirely (AC12d) — covers the
270
+ // scripted-recruit-on-about-to-boot-daemon case plus any other
271
+ // "I know what I'm doing" override. RPC failure during pre-flight
272
+ // falls through with a warning (AC12e) so today's recruit
273
+ // availability characteristics are preserved.
274
+ if (host && !force) {
275
+ try {
276
+ const hosts = await listHostsFn(client);
277
+ const preflightError = checkHostPreflight(hosts, host, agent);
278
+ if (preflightError)
279
+ return (0, helpers_1.fail)(preflightError);
280
+ }
281
+ catch (err) {
282
+ toolLog(`Host pre-flight RPC failed for "${host}"; proceeding without validation (use --force to silence this warning): ${err instanceof Error ? err.message : err}`);
283
+ }
284
+ }
285
+ try {
286
+ // Check if a conductor already exists when recruiting a conductor
287
+ if (isConductor) {
288
+ try {
289
+ const conductorWfId = (0, config_1.conductorWorkflowId)(config.ensemble);
290
+ const conductorHandle = client.workflow.getHandle(conductorWfId);
291
+ const desc = await conductorHandle.describe();
292
+ if (desc.status.name === 'RUNNING') {
293
+ if (force) {
294
+ await conductorHandle.terminate(`Force-terminated for re-recruit by ${getPlayerId()}`);
295
+ }
296
+ else {
297
+ return (0, helpers_1.fail)(`A conductor is already running in ensemble "${config.ensemble}". Use \`agent-tempo conduct --replace\` from the CLI to replace it, \`stop\` it first, or use \`force: true\` to replace it.`);
298
+ }
299
+ }
300
+ }
301
+ catch {
302
+ // No existing conductor — proceed
303
+ }
304
+ }
305
+ // Check if a session with this name is already active
306
+ const existing = await (0, resolve_1.resolveSession)(client, config.ensemble, name);
307
+ if (existing) {
308
+ if (force) {
309
+ // Force-terminate the existing session before recruiting
310
+ await existing.terminate(`Force-terminated for re-recruit by ${getPlayerId()}`);
311
+ // Best-effort notify conductor
312
+ try {
313
+ const condId = (0, config_1.conductorWorkflowId)(config.ensemble);
314
+ const condHandle = client.workflow.getHandle(condId);
315
+ await condHandle.signal('receiveMessage', {
316
+ from: 'system',
317
+ text: `Session "${name}" was force-terminated for re-recruit by ${getPlayerId()}.`,
318
+ responseRequested: false,
319
+ });
320
+ }
321
+ catch {
322
+ // Conductor may not exist — that's fine
323
+ }
324
+ }
325
+ else {
326
+ return (0, helpers_1.fail)(`Session **${name}** is already active. Use \`cue\` to send it a message, \`stop\` it first, or use \`force: true\` to replace it.`);
327
+ }
328
+ }
329
+ const entry = {
330
+ type: 'recruit',
331
+ targetName: name,
332
+ workDir,
333
+ isConductor,
334
+ initialMessage,
335
+ agent,
336
+ systemPrompt: agentDefinition ? undefined : systemPrompt,
337
+ targetHostname: host,
338
+ agentDefinition,
339
+ agentDefinitionPath,
340
+ agentDefinitionDescription,
341
+ nativeResolvable,
342
+ allowedTools,
343
+ claudeBin: config.claudeBin,
344
+ ...(agent === 'mock' ? { mockMode: mockMode ?? 'echo' } : {}),
345
+ ...(agent === 'mock' && mockScenario ? { mockScenario } : {}),
346
+ ...(agent === 'claude-api' && model ? { model } : {}),
347
+ // #520 — claude-code-headless permission knobs flow through the
348
+ // outbox so PR-2's spawn helper picks them up via env. Fields
349
+ // are only present on the entry when actually set; the
350
+ // OutboxEntryInput shape will gain typed `permissionMode` /
351
+ // `dangerouslySkipPermissions` fields in PR-2.
352
+ ...(agent === 'claude-code-headless' && permissionMode ? { permissionMode } : {}),
353
+ ...(agent === 'claude-code-headless' && dangerouslySkipPermissions ? { dangerouslySkipPermissions: true } : {}),
354
+ };
355
+ const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
356
+ return (0, helpers_1.ok)(`Recruit request submitted for **${name}** in ${workDir}. The session will be spawned shortly. (outbox: ${entryId})`);
357
+ }
358
+ catch (err) {
359
+ return (0, helpers_1.fail)(`Failed to recruit: ${(0, helpers_1.formatError)(err)}`);
360
+ }
361
+ });
362
+ }
363
+ // ────────────────────────────────────────────────────────────────────────
364
+ // #274 AC12 — host pre-flight validation (exported for unit tests)
365
+ // ────────────────────────────────────────────────────────────────────────
366
+ /**
367
+ * Given a host liveness+profile snapshot, validate that `targetHost` is
368
+ * (a) known, (b) recruit-ready, and (c) advertises support for
369
+ * `requestedAgent`. Returns a user-facing error string when validation
370
+ * fails, or `null` when all checks pass.
371
+ *
372
+ * Profile-missing is treated as a soft pass (the daemon is alive but
373
+ * didn't signal a profile, e.g. rolling-upgrade scenario or global
374
+ * maestro was unreachable at its boot). We fall through rather than
375
+ * fail — M10 / AC11c says operational UX beats strict enforcement when
376
+ * information is genuinely absent.
377
+ *
378
+ * Exported for unit testing per the M15 dep-injection pattern.
379
+ */
380
+ function checkHostPreflight(hosts, targetHost, requestedAgent) {
381
+ const match = hosts.find((h) => h.hostname === targetHost);
382
+ if (!match) {
383
+ const knownNames = hosts.map((h) => h.hostname);
384
+ const suggestion = nearestHostname(targetHost, knownNames);
385
+ const available = knownNames.length > 0 ? knownNames.join(', ') : '(none — no daemons currently polling)';
386
+ const hint = suggestion && suggestion !== targetHost ? ` Did you mean "${suggestion}"?` : '';
387
+ return `Unknown host "${targetHost}".${hint} Available: ${available}. Use \`force: true\` to bypass this check.`;
388
+ }
389
+ if (match.freshness !== 'live' || !match.recruitReady) {
390
+ return `Host "${targetHost}" is not recruit-ready right now (freshness=${match.freshness}, recruitReady=${match.recruitReady}). Wait for the daemon to signal again, or use \`force: true\` to bypass.`;
391
+ }
392
+ // Profile-missing → soft pass. Daemon is alive + recruit-ready; we
393
+ // just don't have capability info. Log-level guidance could be added
394
+ // later; for now we accept to avoid false-negative rejection.
395
+ if (match.profile && match.profile.availableAgentTypes) {
396
+ if (!match.profile.availableAgentTypes.includes(requestedAgent)) {
397
+ const supports = match.profile.availableAgentTypes.length > 0
398
+ ? match.profile.availableAgentTypes.join(', ')
399
+ : '(none advertised)';
400
+ return `Host "${targetHost}" cannot run \`${requestedAgent}\` (supports: ${supports}). Use \`force: true\` to bypass this check.`;
401
+ }
402
+ }
403
+ return null;
404
+ }
405
+ /**
406
+ * Pick the closest hostname to `target` by Levenshtein distance. Returns
407
+ * `null` when `candidates` is empty OR the nearest match is so distant
408
+ * (edit distance ≥ half the longer string's length) that suggesting it
409
+ * would be misleading. Exported for tests.
410
+ */
411
+ function nearestHostname(target, candidates) {
412
+ if (candidates.length === 0)
413
+ return null;
414
+ let best = null;
415
+ for (const cand of candidates) {
416
+ const dist = levenshtein(target, cand);
417
+ if (best === null || dist < best.dist)
418
+ best = { name: cand, dist };
419
+ }
420
+ if (!best)
421
+ return null;
422
+ const ratioThreshold = Math.max(target.length, best.name.length) / 2;
423
+ return best.dist < ratioThreshold ? best.name : null;
424
+ }
425
+ /** Iterative O(n·m) Levenshtein distance. 15 lines, no dep. */
426
+ function levenshtein(a, b) {
427
+ if (a === b)
428
+ return 0;
429
+ if (a.length === 0)
430
+ return b.length;
431
+ if (b.length === 0)
432
+ return a.length;
433
+ // previous row of distances
434
+ let prev = new Array(b.length + 1);
435
+ for (let j = 0; j <= b.length; j++)
436
+ prev[j] = j;
437
+ for (let i = 1; i <= a.length; i++) {
438
+ const curr = new Array(b.length + 1);
439
+ curr[0] = i;
440
+ for (let j = 1; j <= b.length; j++) {
441
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
442
+ curr[j] = Math.min(prev[j] + 1, curr[j - 1] + 1, prev[j - 1] + cost);
443
+ }
444
+ prev = curr;
445
+ }
446
+ return prev[b.length];
447
+ }
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Client, WorkflowHandle } from '@temporalio/client';
3
+ import { Config } from '../config';
4
+ export declare function registerReleaseTool(server: McpServer, client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle): void;
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerReleaseTool = registerReleaseTool;
4
+ const zod_1 = require("zod");
5
+ const resolve_1 = require("../activities/resolve");
6
+ const signals_1 = require("../workflows/signals");
7
+ const helpers_1 = require("./helpers");
8
+ const validation_1 = require("../utils/validation");
9
+ function registerReleaseTool(server, client, config, getPlayerId, handle) {
10
+ (0, helpers_1.defineTool)(server, 'release', 'Release held player sessions — unlocks their outboxes and delivers deferred task messages. Without a player name, releases all held sessions.', {
11
+ player: zod_1.z.string().max(validation_1.PLAYER_NAME_MAX).optional()
12
+ .describe('Name of a specific held player to release. Omit to release all held players.'),
13
+ }, async (args) => {
14
+ const { player } = args;
15
+ if (player) {
16
+ const nameError = (0, validation_1.validatePlayerName)(player);
17
+ if (nameError)
18
+ return (0, helpers_1.fail)(nameError);
19
+ }
20
+ try {
21
+ if (player) {
22
+ // Release a specific player
23
+ const sessions = await (0, resolve_1.scanEnsembleSessions)(client, config.ensemble);
24
+ const target = sessions.find((s) => s.playerId === player);
25
+ if (!target) {
26
+ return (0, helpers_1.fail)(`No session found with name "${player}".`);
27
+ }
28
+ // Check if the session's outbox is actually locked
29
+ const targetHandle = client.workflow.getHandle(target.workflowId);
30
+ let isLocked = false;
31
+ try {
32
+ isLocked = await targetHandle.query(signals_1.outboxLockedQuery);
33
+ }
34
+ catch {
35
+ // Query may fail for old workflows without the handler — not held
36
+ }
37
+ if (!isLocked) {
38
+ return (0, helpers_1.fail)(`Session "${player}" is not held (outbox not locked). Only held sessions can be released.`);
39
+ }
40
+ const entry = {
41
+ type: 'release',
42
+ targetPlayerId: player,
43
+ };
44
+ const entryId = await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
45
+ return (0, helpers_1.ok)(`Release request submitted for **${player}**. Task assignment will be delivered shortly. (outbox: ${entryId})`);
46
+ }
47
+ else {
48
+ // Release all held players — scan ensemble and check outboxLocked on each
49
+ const sessions = await (0, resolve_1.scanEnsembleSessions)(client, config.ensemble);
50
+ const heldSessions = [];
51
+ for (const session of sessions) {
52
+ // Skip self and conductors (conductor outbox is never locked)
53
+ if (session.playerId === getPlayerId())
54
+ continue;
55
+ try {
56
+ const sessionHandle = client.workflow.getHandle(session.workflowId);
57
+ const isLocked = await sessionHandle.query(signals_1.outboxLockedQuery);
58
+ if (isLocked) {
59
+ heldSessions.push(session);
60
+ }
61
+ }
62
+ catch {
63
+ // Skip sessions where query fails (old workflows, terminated, etc.)
64
+ }
65
+ }
66
+ if (heldSessions.length === 0) {
67
+ return (0, helpers_1.ok)('No held sessions found. Nothing to release.');
68
+ }
69
+ const released = [];
70
+ const errors = [];
71
+ for (const session of heldSessions) {
72
+ try {
73
+ const entry = {
74
+ type: 'release',
75
+ targetPlayerId: session.playerId,
76
+ };
77
+ await handle.executeUpdate(signals_1.submitOutboxUpdate, { args: [entry] });
78
+ released.push(session.playerId);
79
+ }
80
+ catch (err) {
81
+ errors.push(`${session.playerId}: ${(0, helpers_1.formatError)(err)}`);
82
+ }
83
+ }
84
+ const lines = [];
85
+ if (released.length > 0) {
86
+ lines.push(`Released ${released.length} player(s): ${released.join(', ')}`);
87
+ }
88
+ if (errors.length > 0) {
89
+ lines.push(`Errors:\n${errors.map((e) => ` - ${e}`).join('\n')}`);
90
+ }
91
+ return (0, helpers_1.ok)(lines.join('\n'));
92
+ }
93
+ }
94
+ catch (err) {
95
+ return (0, helpers_1.fail)(`Failed to release: ${(0, helpers_1.formatError)(err)}`);
96
+ }
97
+ });
98
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { WorkflowHandle } from '@temporalio/client';
3
+ export declare function registerReportTool(server: McpServer, handle: WorkflowHandle): void;