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,113 @@
1
+ /**
2
+ * `listHosts` — join layer for #274 host discovery.
3
+ *
4
+ * Three RPCs per invocation (AC6b):
5
+ * 1. `DescribeTaskQueue` on the shared queue with `TASK_QUEUE_TYPE_WORKFLOW`
6
+ * — populates `hasWorkflowWorker` per identity.
7
+ * 2. Same queue, `TASK_QUEUE_TYPE_ACTIVITY` — populates `hasActivityWorker`.
8
+ * 3. `DescribeTaskQueue` on each per-host queue
9
+ * (`agent-tempo-<hostname>`), `TASK_QUEUE_TYPE_ACTIVITY` — populates
10
+ * `hasHostQueueWorker`. Runs in parallel across discovered hostnames.
11
+ *
12
+ * Plus one `hostProfiles` query against the global maestro when it's
13
+ * running (AC6g). Absent → liveness-only with
14
+ * `profileStaleness: 'missing'`.
15
+ *
16
+ * Identity is parsed with dual-format tolerance (AC6d / M6):
17
+ * - `agent-tempo:<hostname>:<pid>:<version>` (set by v1.0+ workers)
18
+ * - `<pid>@<hostname>` (legacy SDK default, pre-#274 daemons)
19
+ * - Anything else is skipped silently as opaque third-party.
20
+ *
21
+ * Results are cached for `CACHE_TTL_MS` (AC6h) to keep rapid-fire CLI/TUI
22
+ * invocations cheap; pass `force: true` to bypass.
23
+ */
24
+ import type { Client } from '@temporalio/client';
25
+ import { temporal } from '@temporalio/proto';
26
+ import type { HostInfo, HostProfile } from '../types';
27
+ /**
28
+ * Freshness threshold — pollers seen in the last minute are `'live'`,
29
+ * older ones are tagged `'stale'` and hidden by default. Temporal
30
+ * server-side LRU keeps pollers for ~5 min; 1-min gives a tighter
31
+ * "currently polling" signal and aligns with the daemon heartbeat
32
+ * cadence. AC6c.
33
+ */
34
+ export declare const HOST_FRESHNESS_THRESHOLD_MS = 60000;
35
+ /** Read-through cache TTL for the join result. AC6h. */
36
+ export declare const CACHE_TTL_MS = 3000;
37
+ /**
38
+ * #281 — concurrency cap for the per-host RPC fan-out.
39
+ *
40
+ * `Promise.all(hostnames.map(describe...))` is unbounded; at >20 hosts that
41
+ * thunder-herds the Temporal frontend's per-namespace RPS budget. 8 keeps
42
+ * the common case (≤8 hosts) at full parallelism while preventing the worst
43
+ * case from spiking the backend.
44
+ */
45
+ export declare const HOST_FANOUT_CONCURRENCY = 8;
46
+ /**
47
+ * Test hook — never call from production code.
48
+ *
49
+ * Convention: `__<verb><Noun>ForTests`, co-located with the module that owns
50
+ * the state. See `docs/adr/0006-test-hooks-naming.md`.
51
+ */
52
+ export declare function __resetHostsCacheForTests(): void;
53
+ export interface ParsedIdentity {
54
+ hostname: string;
55
+ pid: number;
56
+ version: string;
57
+ /** `true` when the identity was in the legacy `<pid>@<hostname>` SDK-default shape. */
58
+ legacy: boolean;
59
+ }
60
+ /**
61
+ * Parse a Temporal poller identity back into the daemon that emitted it.
62
+ *
63
+ * Returns `null` for opaque / third-party identities (e.g. Temporal's own
64
+ * system pollers or an unrelated worker sharing the namespace). Callers
65
+ * skip those silently.
66
+ *
67
+ * The v1.0 format (`agent-tempo:<hostname>:<pid>:<version>`) is
68
+ * guaranteed to have exactly 4 colon-delimited segments because every
69
+ * component has its own validation: hostname passes `PLAYER_NAME_REGEX`
70
+ * (no colons possible), pid is numeric, and version is a semver-ish
71
+ * string (no colons).
72
+ *
73
+ * Pre-v1.0 daemons emitted a `claude-tempo:` prefix. Under the v1.0 hard
74
+ * break those daemons can't reach v1.x task queues anyway, but if one does
75
+ * surface in a host listing we tag it `legacy: true` so operators can
76
+ * distinguish a still-running old daemon from a fresh v1.x worker.
77
+ */
78
+ export declare function parseIdentity(identity: string): ParsedIdentity | null;
79
+ interface RawPoller {
80
+ identity: string;
81
+ lastAccessTimeMs: number;
82
+ lastAccessTimeIso: string;
83
+ }
84
+ /**
85
+ * Dep-injection seam used by tests. Production callers OMIT this entirely —
86
+ * grouping it under `deps` (vs. mixing into the top level of `ListHostsOpts`)
87
+ * keeps the user-facing surface (`force`, `namespace`, `taskQueue`) cleanly
88
+ * separated from internal test wiring. See #283.
89
+ */
90
+ export interface ListHostsDeps {
91
+ /** `Date.now` replacement for deterministic tests. */
92
+ now?: () => number;
93
+ /**
94
+ * Dep-injected poller fetcher — tests stub to return canned rows.
95
+ * Production callers omit and the helper uses
96
+ * `client.workflowService.describeTaskQueue` directly.
97
+ */
98
+ describePollers?: (client: Client, namespace: string, taskQueueName: string, taskQueueType: temporal.api.enums.v1.TaskQueueType) => Promise<RawPoller[]>;
99
+ /** Dep-injected profile fetcher. Tests stub to control profile state. */
100
+ fetchProfiles?: (client: Client) => Promise<Record<string, HostProfile> | null>;
101
+ }
102
+ export interface ListHostsOpts {
103
+ /** Bypass the 3s TTL cache. CLI/TUI refresh handlers pass `true`. */
104
+ force?: boolean;
105
+ /** Default `'default'`. */
106
+ namespace?: string;
107
+ /** Default `'agent-tempo'`. */
108
+ taskQueue?: string;
109
+ /** Test-only dep-injection seam. Production callers omit. */
110
+ deps?: ListHostsDeps;
111
+ }
112
+ export declare function listHosts(client: Client, opts?: ListHostsOpts): Promise<HostInfo[]>;
113
+ export {};
@@ -0,0 +1,265 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.HOST_FANOUT_CONCURRENCY = exports.CACHE_TTL_MS = exports.HOST_FRESHNESS_THRESHOLD_MS = void 0;
7
+ exports.__resetHostsCacheForTests = __resetHostsCacheForTests;
8
+ exports.parseIdentity = parseIdentity;
9
+ exports.listHosts = listHosts;
10
+ const proto_1 = require("@temporalio/proto");
11
+ const p_limit_1 = __importDefault(require("p-limit"));
12
+ const config_1 = require("../config");
13
+ /**
14
+ * Freshness threshold — pollers seen in the last minute are `'live'`,
15
+ * older ones are tagged `'stale'` and hidden by default. Temporal
16
+ * server-side LRU keeps pollers for ~5 min; 1-min gives a tighter
17
+ * "currently polling" signal and aligns with the daemon heartbeat
18
+ * cadence. AC6c.
19
+ */
20
+ exports.HOST_FRESHNESS_THRESHOLD_MS = 60_000;
21
+ /** Read-through cache TTL for the join result. AC6h. */
22
+ exports.CACHE_TTL_MS = 3_000;
23
+ /**
24
+ * #281 — concurrency cap for the per-host RPC fan-out.
25
+ *
26
+ * `Promise.all(hostnames.map(describe...))` is unbounded; at >20 hosts that
27
+ * thunder-herds the Temporal frontend's per-namespace RPS budget. 8 keeps
28
+ * the common case (≤8 hosts) at full parallelism while preventing the worst
29
+ * case from spiking the backend.
30
+ */
31
+ exports.HOST_FANOUT_CONCURRENCY = 8;
32
+ let cache = null;
33
+ /**
34
+ * Test hook — never call from production code.
35
+ *
36
+ * Convention: `__<verb><Noun>ForTests`, co-located with the module that owns
37
+ * the state. See `docs/adr/0006-test-hooks-naming.md`.
38
+ */
39
+ function __resetHostsCacheForTests() {
40
+ cache = null;
41
+ }
42
+ /**
43
+ * Parse a Temporal poller identity back into the daemon that emitted it.
44
+ *
45
+ * Returns `null` for opaque / third-party identities (e.g. Temporal's own
46
+ * system pollers or an unrelated worker sharing the namespace). Callers
47
+ * skip those silently.
48
+ *
49
+ * The v1.0 format (`agent-tempo:<hostname>:<pid>:<version>`) is
50
+ * guaranteed to have exactly 4 colon-delimited segments because every
51
+ * component has its own validation: hostname passes `PLAYER_NAME_REGEX`
52
+ * (no colons possible), pid is numeric, and version is a semver-ish
53
+ * string (no colons).
54
+ *
55
+ * Pre-v1.0 daemons emitted a `claude-tempo:` prefix. Under the v1.0 hard
56
+ * break those daemons can't reach v1.x task queues anyway, but if one does
57
+ * surface in a host listing we tag it `legacy: true` so operators can
58
+ * distinguish a still-running old daemon from a fresh v1.x worker.
59
+ */
60
+ function parseIdentity(identity) {
61
+ if (identity.startsWith('agent-tempo:') || identity.startsWith('claude-tempo:')) {
62
+ const legacy = identity.startsWith('claude-tempo:');
63
+ const parts = identity.split(':');
64
+ if (parts.length === 4) {
65
+ const [, hostname, pidStr, version] = parts;
66
+ const pid = Number(pidStr);
67
+ if (hostname.length > 0 && Number.isFinite(pid) && pid > 0 && version.length > 0) {
68
+ return { hostname, pid, version, legacy };
69
+ }
70
+ }
71
+ return null;
72
+ }
73
+ // Legacy SDK default format: `<pid>@<hostname>`
74
+ const legacyMatch = identity.match(/^(\d+)@(.+)$/);
75
+ if (legacyMatch) {
76
+ const pid = Number(legacyMatch[1]);
77
+ const hostname = legacyMatch[2];
78
+ if (Number.isFinite(pid) && pid > 0 && hostname.length > 0) {
79
+ return { hostname, pid, version: 'unknown', legacy: true };
80
+ }
81
+ }
82
+ return null;
83
+ }
84
+ // ────────────────────────────────────────────────────────────────────────
85
+ // Low-level RPC + timestamp helpers — exposed for dep-injection in tests
86
+ // ────────────────────────────────────────────────────────────────────────
87
+ /** Convert a protobuf Timestamp to ISO 8601. Returns `''` for unset timestamps. */
88
+ function timestampToIso(ts) {
89
+ // Protobuf Timestamp is `{seconds: Long | number | string, nanos: number}`.
90
+ // Protobuf.js may deliver `seconds` as a Long object, a raw number, or a
91
+ // string depending on the runtime — normalise defensively.
92
+ if (!ts || typeof ts !== 'object')
93
+ return '';
94
+ const t = ts;
95
+ const secondsRaw = t.seconds;
96
+ if (secondsRaw === undefined || secondsRaw === null)
97
+ return '';
98
+ const seconds = typeof secondsRaw === 'number' ? secondsRaw : Number(String(secondsRaw));
99
+ if (!Number.isFinite(seconds))
100
+ return '';
101
+ const ms = seconds * 1000 + Math.floor((t.nanos ?? 0) / 1_000_000);
102
+ return new Date(ms).toISOString();
103
+ }
104
+ async function describeQueuePollers(client, namespace, taskQueueName, taskQueueType) {
105
+ const res = await client.workflowService.describeTaskQueue({
106
+ namespace,
107
+ taskQueue: { name: taskQueueName },
108
+ taskQueueType,
109
+ });
110
+ const pollers = res.pollers ?? [];
111
+ const out = [];
112
+ for (const p of pollers) {
113
+ const identity = p.identity ?? '';
114
+ if (!identity)
115
+ continue;
116
+ const iso = timestampToIso(p.lastAccessTime);
117
+ const ms = iso ? Date.parse(iso) : 0;
118
+ out.push({ identity, lastAccessTimeMs: ms, lastAccessTimeIso: iso });
119
+ }
120
+ return out;
121
+ }
122
+ /**
123
+ * #280 — single combined query against the global maestro.
124
+ *
125
+ * Replaces the prior two-RPC dance (`handle.describe()` for liveness
126
+ * then `handle.query('hostProfiles')` for data) with one round trip.
127
+ * Reaching the handler proves the workflow is running, so the caller
128
+ * gets `{ exists: true, profiles }`. Any transport-level failure
129
+ * (workflow not found, terminated, namespace unreachable, query
130
+ * handler not registered on an older maestro) is caught and surfaced
131
+ * as `null`, which the join site treats as `profileStaleness: 'missing'`.
132
+ *
133
+ * Note: we deliberately fall back to `null` on ANY error rather than
134
+ * trying the legacy `hostProfiles` query as a second attempt — the
135
+ * combined query has been on every maestro since this commit, so a
136
+ * mismatched daemon/maestro pair is a deployment bug worth surfacing
137
+ * as missing-profile (which still lets the listing succeed) rather
138
+ * than masking with extra RPCs.
139
+ */
140
+ async function fetchHostProfiles(client) {
141
+ try {
142
+ const handle = client.workflow.getHandle(config_1.GLOBAL_MAESTRO_WORKFLOW_ID);
143
+ const result = (await handle.query('hostProfilesWithExistence'));
144
+ if (!result || !result.exists)
145
+ return null;
146
+ return result.profiles;
147
+ }
148
+ catch {
149
+ return null;
150
+ }
151
+ }
152
+ async function listHosts(client, opts = {}) {
153
+ const deps = opts.deps ?? {};
154
+ const now = deps.now ? deps.now() : Date.now();
155
+ if (!opts.force && cache && now - cache.timestamp < exports.CACHE_TTL_MS) {
156
+ return cache.hosts;
157
+ }
158
+ const namespace = opts.namespace ?? 'default';
159
+ const taskQueue = opts.taskQueue ?? 'agent-tempo';
160
+ const describe = deps.describePollers ?? describeQueuePollers;
161
+ const fetchProfiles = deps.fetchProfiles ?? fetchHostProfiles;
162
+ const { TASK_QUEUE_TYPE_WORKFLOW, TASK_QUEUE_TYPE_ACTIVITY } = proto_1.temporal.api.enums.v1.TaskQueueType;
163
+ // RPCs 1 + 2 — shared queue, both worker types. Parallel.
164
+ const [sharedWorkflow, sharedActivity] = await Promise.all([
165
+ describe(client, namespace, taskQueue, TASK_QUEUE_TYPE_WORKFLOW),
166
+ describe(client, namespace, taskQueue, TASK_QUEUE_TYPE_ACTIVITY),
167
+ ]);
168
+ // ── Build InstanceInfo per (hostname, pid) as we consume each stream ──
169
+ // Each daemon sets the same identity on both the shared-workflow and
170
+ // shared-activity pollers AND on the per-host-activity worker, so the
171
+ // (hostname, pid) key merges naturally across streams. Opaque identities
172
+ // drop out of `parseIdentity` and are skipped silently (AC6d).
173
+ const hostInstances = new Map();
174
+ const markInstance = (hostname, pid, flag, ident, poller) => {
175
+ let byPid = hostInstances.get(hostname);
176
+ if (!byPid) {
177
+ byPid = new Map();
178
+ hostInstances.set(hostname, byPid);
179
+ }
180
+ let inst = byPid.get(pid);
181
+ if (!inst) {
182
+ inst = {
183
+ pid,
184
+ version: ident.version,
185
+ identity: poller.identity,
186
+ lastAccessTime: poller.lastAccessTimeIso,
187
+ hasWorkflowWorker: false,
188
+ hasActivityWorker: false,
189
+ hasHostQueueWorker: false,
190
+ ...(ident.legacy ? { legacy: true } : {}),
191
+ };
192
+ byPid.set(pid, inst);
193
+ }
194
+ inst[flag] = true;
195
+ };
196
+ const consume = (pollers, flag) => {
197
+ for (const p of pollers) {
198
+ const ident = parseIdentity(p.identity);
199
+ if (!ident)
200
+ continue;
201
+ markInstance(ident.hostname, ident.pid, flag, ident, p);
202
+ }
203
+ };
204
+ consume(sharedWorkflow, 'hasWorkflowWorker');
205
+ consume(sharedActivity, 'hasActivityWorker');
206
+ // Hostnames discovered from the shared queues drive the per-host RPC fan-out.
207
+ const hostnames = Array.from(hostInstances.keys()).sort();
208
+ // RPC 3 — per-host activity queue. Parallel with the profile fetch,
209
+ // but capped at HOST_FANOUT_CONCURRENCY so a deployment with many hosts
210
+ // doesn't thunder-herd the Temporal frontend (#281).
211
+ const limit = (0, p_limit_1.default)(exports.HOST_FANOUT_CONCURRENCY);
212
+ const [perHostActivityRows, profiles] = await Promise.all([
213
+ Promise.all(hostnames.map((hostname) => limit(async () => {
214
+ try {
215
+ const rows = await describe(client, namespace, (0, config_1.hostTaskQueue)(taskQueue, hostname), TASK_QUEUE_TYPE_ACTIVITY);
216
+ return [hostname, rows];
217
+ }
218
+ catch {
219
+ // Per-host-queue not registered yet → empty. Don't fail the whole call.
220
+ return [hostname, []];
221
+ }
222
+ }))),
223
+ fetchProfiles(client).catch(() => null),
224
+ ]);
225
+ for (const [, rows] of perHostActivityRows) {
226
+ consume(rows, 'hasHostQueueWorker');
227
+ }
228
+ // ── Build HostInfo per hostname ──
229
+ const hosts = [];
230
+ for (const hostname of hostnames) {
231
+ const byPid = hostInstances.get(hostname);
232
+ if (!byPid || byPid.size === 0)
233
+ continue;
234
+ const instances = Array.from(byPid.values()).sort((a, b) => a.pid - b.pid);
235
+ const isFresh = (iso) => {
236
+ const ts = iso ? Date.parse(iso) : 0;
237
+ return ts > 0 && now - ts <= exports.HOST_FRESHNESS_THRESHOLD_MS;
238
+ };
239
+ const freshness = instances.some((i) => isFresh(i.lastAccessTime)) ? 'live' : 'stale';
240
+ const recruitReady = instances.some((i) => i.hasActivityWorker && i.hasHostQueueWorker && isFresh(i.lastAccessTime));
241
+ const profile = profiles?.[hostname];
242
+ const profileStaleness = determineStaleness(profile, instances, profiles);
243
+ hosts.push({ hostname, instances, recruitReady, freshness, profile, profileStaleness });
244
+ }
245
+ cache = { timestamp: now, hosts };
246
+ return hosts;
247
+ }
248
+ /**
249
+ * M13 — identity-vs-profile reconciliation.
250
+ *
251
+ * - `'missing'` — maestro is absent OR this host has no profile entry
252
+ * - `'fresh'` — profile present AND some live instance's version
253
+ * matches `profile.version`
254
+ * - `'stale'` — profile present but no live instance matches its
255
+ * version (rolling upgrade, GC pending, etc.)
256
+ */
257
+ function determineStaleness(profile, instances, profiles) {
258
+ if (!profiles)
259
+ return 'missing'; // maestro unreachable or absent
260
+ if (!profile)
261
+ return 'missing';
262
+ if (!profile.version)
263
+ return 'fresh'; // legacy profile with no version — trust as-is
264
+ return instances.some((i) => i.version === profile.version) ? 'fresh' : 'stale';
265
+ }
@@ -0,0 +1 @@
1
+ export declare function installParentDeathWatchdog(): void;
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ // Self-exit when the parent process goes away.
3
+ //
4
+ // Stdio-MCP children (and the Copilot bridge subprocess) are spawned by
5
+ // hosts that often die without sending SIGTERM — Claude Code on Windows
6
+ // just closes the pipe, and an abruptly-killed daemon leaves bridge
7
+ // subprocesses orphaned. Without this watchdog, those children stay
8
+ // alive forever holding the Temporal core-bridge `.node` open, which
9
+ // (a) leaks processes across host restarts and (b) blocks
10
+ // `npm install -g` upgrades with EBUSY on Windows.
11
+ //
12
+ // Two complementary signals:
13
+ // 1. stdin 'end'/'close' — fires the instant the parent closes the
14
+ // stdio pipe. Primary signal; immediate.
15
+ // 2. Parent-PID poll — fallback for when the pipe handle is inherited
16
+ // by another process (or stdio was 'ignore'). 30s cadence is a
17
+ // belt-and-braces interval — orphan dies within half a minute,
18
+ // no measurable syscall cost. `process.kill(pid, 0)` is a
19
+ // permission check, not a signal.
20
+ //
21
+ // PID reuse caveat: Windows recycles PIDs quickly. If the parent dies
22
+ // and an unrelated process inherits the same PID before our next poll,
23
+ // we falsely conclude the parent is alive. The stdin EOF path catches
24
+ // that case immediately, so this is purely a fallback.
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.installParentDeathWatchdog = installParentDeathWatchdog;
27
+ const log = (...args) => console.error('[agent-tempo:watchdog]', ...args);
28
+ function installParentDeathWatchdog() {
29
+ const exit = (reason) => {
30
+ log('parent gone (', reason, ') — exiting');
31
+ process.exit(0);
32
+ };
33
+ process.stdin.on('end', () => exit('stdin end'));
34
+ process.stdin.on('close', () => exit('stdin close'));
35
+ const parentPid = process.ppid;
36
+ if (parentPid && parentPid > 1) {
37
+ const timer = setInterval(() => {
38
+ try {
39
+ process.kill(parentPid, 0);
40
+ }
41
+ catch {
42
+ exit(`ppid ${parentPid} dead`);
43
+ }
44
+ }, 30_000);
45
+ timer.unref();
46
+ }
47
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Bounded `WorkflowHandle.query()` wrapper — Issue #433.
3
+ *
4
+ * **Problem.** `@temporalio/client@1.15` doesn't expose an `AbortSignal` or a
5
+ * per-call `deadline` on `WorkflowHandle.query()`. When the workflow is alive
6
+ * (`Running`) but the worker that polls its task queue is dead (orphaned
7
+ * adapter, wedged Copilot bridge, sticky-cache lock-up), `query()` never
8
+ * resolves — it sits on its gRPC stream until the connection is torn down.
9
+ *
10
+ * Until v0.26 this only manifested as background reconcile log noise. After
11
+ * #399 W2 layered `getPlayerWireMeta` (3 session queries × N players) into
12
+ * the snapshot fan-out, a single hung session also wedged the snapshot
13
+ * endpoint and the AggregateRunner's 750ms poll loop — the trigger for this
14
+ * fix.
15
+ *
16
+ * **Strategy.** Race `handle.query()` against a `setTimeout`. When the timeout
17
+ * wins, throw `QueryTimeoutError` so the caller's existing soft-failure path
18
+ * (`Promise.allSettled` / `try-catch` / `.catch(() => …)`) takes over. The
19
+ * underlying RPC stays pending in memory until it eventually resolves or the
20
+ * gRPC connection is closed — see "Leak characteristics" below for why
21
+ * that's bounded.
22
+ *
23
+ * **Leak characteristics.** The pending `handle.query()` Promise can't be
24
+ * cancelled in this SDK version. Instead we **dedupe in-flight queries** by
25
+ * `(workflowId, queryName)` so the AggregateRunner firing every 750ms against
26
+ * the same hung session doesn't accumulate one new dangling promise per tick
27
+ * — it gets the *same* shared promise back. Total memory cost is bounded by
28
+ * `unique-hung-workflows × distinct-queries-per-workflow` (typically <50
29
+ * entries even in degenerate fleets), and every entry is reclaimed when the
30
+ * RPC settles (workflow closes, namespace evicted, daemon restart). When
31
+ * `@temporalio/client` adds AbortSignal support, swap the race for a real
32
+ * cancellation and drop the dedup map.
33
+ *
34
+ * **Configurable.** Default timeout is `DEFAULT_QUERY_TIMEOUT_MS` (2000ms) —
35
+ * Temporal queries against a live worker round-trip in <100ms, so a 2s ceiling
36
+ * is two orders of magnitude larger than a healthy query. Pass a smaller value
37
+ * for tests; pass a larger value if you have a slow query handler that does
38
+ * meaningful work synchronously.
39
+ */
40
+ import type { WorkflowHandle } from '@temporalio/client';
41
+ import type { QueryDefinition } from '@temporalio/common';
42
+ /**
43
+ * Default per-query timeout. 2 seconds — Temporal queries against a healthy
44
+ * worker round-trip in <100ms, so this is two orders of magnitude of
45
+ * headroom while still being short enough that a hung snapshot fan-out
46
+ * (~12 players × 3 queries) still completes well under 10s.
47
+ */
48
+ export declare const DEFAULT_QUERY_TIMEOUT_MS = 2000;
49
+ /**
50
+ * Thrown by {@link queryHandleWithTimeout} when the per-query timeout fires
51
+ * before the underlying RPC settles. Subclasses `Error`; `name` is set so
52
+ * `err.name === 'QueryTimeoutError'` and `err instanceof QueryTimeoutError`
53
+ * both work for callers that switch on either.
54
+ */
55
+ export declare class QueryTimeoutError extends Error {
56
+ readonly workflowId: string;
57
+ readonly queryName: string;
58
+ readonly timeoutMs: number;
59
+ constructor(workflowId: string, queryName: string, timeoutMs: number);
60
+ }
61
+ /**
62
+ * Test-only — clear the in-flight dedup table so each test case starts with
63
+ * an empty cache. Follows the `__<verb><Noun>ForTests` convention from
64
+ * ADR 0006: never call from production code.
65
+ */
66
+ export declare function __resetInflightQueriesForTests(): void;
67
+ /**
68
+ * Run `handle.query(queryDef, …(opts.args))` with a
69
+ * {@link DEFAULT_QUERY_TIMEOUT_MS} (or `opts.timeoutMs`) ceiling. Throws
70
+ * {@link QueryTimeoutError} if the timeout fires first. Multiple callers
71
+ * issuing the same `(workflowId, queryName)` while the prior call is
72
+ * still in flight share one underlying RPC — see the file header for
73
+ * leak characteristics.
74
+ *
75
+ * `queryDef` may be a typed `QueryDefinition` (preferred — caller gets
76
+ * type inference on `Ret`) or a bare string name (legacy call sites that
77
+ * predate the signal-defs file: `getMetadata`, `getPart`, `maestroPlayers`,
78
+ * etc.).
79
+ *
80
+ * `opts.args` forwards extra positional args to the SDK `query()` call.
81
+ * Used by `getEnsembleChat`'s `{ offset, limit }` payload — every other
82
+ * call site in this codebase issues a zero-arg query.
83
+ *
84
+ * **Dedup caveat with args**: the dedup key is `(workflowId, queryName)`
85
+ * only — args are NOT in the key. If two concurrent callers issue the
86
+ * same query name with *different* args, the second caller will receive
87
+ * the first caller's result. Acceptable today because `getEnsembleChat`
88
+ * is called with `(0, SNAPSHOT_CHAT_LIMIT)` from the snapshot fan-out
89
+ * and a wider `(0, POLL_CHAT_LIMIT)` from the aggregate poll — both
90
+ * are zero-offset reads, the wider window is a superset of the
91
+ * narrower, and the bus's §8 chat cap collapses excess. Revisit if a
92
+ * future args-passing caller issues a non-superset shape.
93
+ *
94
+ * **No retry**: caller decides whether to retry or fall back. Most call
95
+ * sites fall back (snapshot returns `null` wireMeta;
96
+ * `queryOrphanedSessions` skips the candidate; `getEnsembleMeta`
97
+ * substitutes sentinels) so a retry loop here would just delay the
98
+ * inevitable fallback.
99
+ */
100
+ export declare function queryHandleWithTimeout<Ret, Args extends unknown[] = []>(handle: WorkflowHandle, queryDef: QueryDefinition<Ret, Args> | string, opts?: {
101
+ timeoutMs?: number;
102
+ args?: Args;
103
+ }): Promise<Ret>;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryTimeoutError = exports.DEFAULT_QUERY_TIMEOUT_MS = void 0;
4
+ exports.__resetInflightQueriesForTests = __resetInflightQueriesForTests;
5
+ exports.queryHandleWithTimeout = queryHandleWithTimeout;
6
+ /**
7
+ * Default per-query timeout. 2 seconds — Temporal queries against a healthy
8
+ * worker round-trip in <100ms, so this is two orders of magnitude of
9
+ * headroom while still being short enough that a hung snapshot fan-out
10
+ * (~12 players × 3 queries) still completes well under 10s.
11
+ */
12
+ exports.DEFAULT_QUERY_TIMEOUT_MS = 2000;
13
+ /**
14
+ * Thrown by {@link queryHandleWithTimeout} when the per-query timeout fires
15
+ * before the underlying RPC settles. Subclasses `Error`; `name` is set so
16
+ * `err.name === 'QueryTimeoutError'` and `err instanceof QueryTimeoutError`
17
+ * both work for callers that switch on either.
18
+ */
19
+ class QueryTimeoutError extends Error {
20
+ workflowId;
21
+ queryName;
22
+ timeoutMs;
23
+ constructor(workflowId, queryName, timeoutMs) {
24
+ super(`Temporal query timed out after ${timeoutMs}ms: ` +
25
+ `workflow="${workflowId}" query="${queryName}" ` +
26
+ `(worker may be down or wedged — see #433)`);
27
+ this.workflowId = workflowId;
28
+ this.queryName = queryName;
29
+ this.timeoutMs = timeoutMs;
30
+ this.name = 'QueryTimeoutError';
31
+ }
32
+ }
33
+ exports.QueryTimeoutError = QueryTimeoutError;
34
+ /** Module-level dedup table keyed by `(workflowId, queryName)`. See JSDoc. */
35
+ const inflightQueries = new Map();
36
+ function inflightKey(workflowId, queryName) {
37
+ // NUL separator — workflow ids follow the project's `claude-{kind}-…`
38
+ // convention and query names are JS identifiers, so neither can contain
39
+ // a literal NUL byte. Guarantees no collision between e.g.
40
+ // workflow `a` + query `b\0foo` vs. workflow `a\0b` + query `foo`.
41
+ return `${workflowId}\x00${queryName}`;
42
+ }
43
+ /**
44
+ * Test-only — clear the in-flight dedup table so each test case starts with
45
+ * an empty cache. Follows the `__<verb><Noun>ForTests` convention from
46
+ * ADR 0006: never call from production code.
47
+ */
48
+ function __resetInflightQueriesForTests() {
49
+ inflightQueries.clear();
50
+ }
51
+ /**
52
+ * Run `handle.query(queryDef, …(opts.args))` with a
53
+ * {@link DEFAULT_QUERY_TIMEOUT_MS} (or `opts.timeoutMs`) ceiling. Throws
54
+ * {@link QueryTimeoutError} if the timeout fires first. Multiple callers
55
+ * issuing the same `(workflowId, queryName)` while the prior call is
56
+ * still in flight share one underlying RPC — see the file header for
57
+ * leak characteristics.
58
+ *
59
+ * `queryDef` may be a typed `QueryDefinition` (preferred — caller gets
60
+ * type inference on `Ret`) or a bare string name (legacy call sites that
61
+ * predate the signal-defs file: `getMetadata`, `getPart`, `maestroPlayers`,
62
+ * etc.).
63
+ *
64
+ * `opts.args` forwards extra positional args to the SDK `query()` call.
65
+ * Used by `getEnsembleChat`'s `{ offset, limit }` payload — every other
66
+ * call site in this codebase issues a zero-arg query.
67
+ *
68
+ * **Dedup caveat with args**: the dedup key is `(workflowId, queryName)`
69
+ * only — args are NOT in the key. If two concurrent callers issue the
70
+ * same query name with *different* args, the second caller will receive
71
+ * the first caller's result. Acceptable today because `getEnsembleChat`
72
+ * is called with `(0, SNAPSHOT_CHAT_LIMIT)` from the snapshot fan-out
73
+ * and a wider `(0, POLL_CHAT_LIMIT)` from the aggregate poll — both
74
+ * are zero-offset reads, the wider window is a superset of the
75
+ * narrower, and the bus's §8 chat cap collapses excess. Revisit if a
76
+ * future args-passing caller issues a non-superset shape.
77
+ *
78
+ * **No retry**: caller decides whether to retry or fall back. Most call
79
+ * sites fall back (snapshot returns `null` wireMeta;
80
+ * `queryOrphanedSessions` skips the candidate; `getEnsembleMeta`
81
+ * substitutes sentinels) so a retry loop here would just delay the
82
+ * inevitable fallback.
83
+ */
84
+ async function queryHandleWithTimeout(handle, queryDef, opts = {}) {
85
+ const timeoutMs = opts.timeoutMs ?? exports.DEFAULT_QUERY_TIMEOUT_MS;
86
+ const queryName = typeof queryDef === 'string' ? queryDef : queryDef.name;
87
+ const key = inflightKey(handle.workflowId, queryName);
88
+ let underlying = inflightQueries.get(key);
89
+ if (!underlying) {
90
+ const args = (opts.args ?? []);
91
+ underlying = handle.query(queryDef, ...args).finally(() => {
92
+ // Free the slot once the RPC settles — success or failure both clear.
93
+ // Multiple racing callers all see the same settled value.
94
+ inflightQueries.delete(key);
95
+ });
96
+ inflightQueries.set(key, underlying);
97
+ }
98
+ let timer;
99
+ const timeoutPromise = new Promise((_, reject) => {
100
+ timer = setTimeout(() => {
101
+ reject(new QueryTimeoutError(handle.workflowId, queryName, timeoutMs));
102
+ }, timeoutMs);
103
+ // Don't let the timeout keep the process alive.
104
+ timer.unref?.();
105
+ });
106
+ try {
107
+ return await Promise.race([underlying, timeoutPromise]);
108
+ }
109
+ finally {
110
+ if (timer)
111
+ clearTimeout(timer);
112
+ }
113
+ }