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,598 @@
1
+ "use strict";
2
+ /**
3
+ * `TempoClient.subscribe` — AsyncIterable wrapper over the daemon's
4
+ * `/v1/events*` SSE endpoints. PR-3 of #94/#95.
5
+ *
6
+ * **Reference**: [`docs/SSE-PROTOCOL.md`](../../docs/SSE-PROTOCOL.md),
7
+ * [`docs/adr/0010-drop-caller-controllable-event-cursor.md`](../../docs/adr/0010-drop-caller-controllable-event-cursor.md),
8
+ * and the wire types in [`src/http/event-types.ts`](../http/event-types.ts).
9
+ *
10
+ * ## Design
11
+ *
12
+ * - **Dual transport** — the wrapper picks the best transport for the
13
+ * environment + auth context:
14
+ * - **Native `EventSource`** when available AND no bearer token is set
15
+ * (loopback browser dashboard). Native auto-reconnect handles
16
+ * `Last-Event-ID` for free.
17
+ * - **`fetch().body`** otherwise (Node 20+, or any path that needs the
18
+ * `Authorization` header). Manual reconnect + backoff + `Last-Event-ID`
19
+ * tracking implemented here.
20
+ *
21
+ * Native `EventSource` cannot set custom request headers on initial
22
+ * connect, so any path that requires `Authorization: Bearer …` must use
23
+ * fetch. The transport choice is hidden from the consumer — the
24
+ * `AsyncIterable<TempoEvent>` shape is identical on both paths.
25
+ *
26
+ * - **Caller-controllable cursor resume is not supported (ADR 0010).**
27
+ * `SubscribeOptions` exposes `signal` and `topics` only. Snapshot-then-
28
+ * stream (server emits `event: snapshot` on fresh connect; SSE-PROTOCOL.md
29
+ * §7.2) covers every realistic case. In-session reconnects use
30
+ * `Last-Event-ID` under the hood — auto-managed by `EventSource`,
31
+ * tracked manually by the fetch wrapper.
32
+ *
33
+ * - **Transparent reconnect on the fetch path (§7.5).** TCP drops,
34
+ * premature stream ends, and 5xx responses are retried with exponential
35
+ * backoff (`100 ms × 2^attempt`, cap 30 s, reset on first event of a
36
+ * new connection). The most-recently-seen event id is replayed on each
37
+ * retry so consumers see seamless replay/`gap` behaviour. Native
38
+ * `EventSource` does the equivalent automatically.
39
+ *
40
+ * - **Gap events surface to consumer (§7.5/§7.6).** The wrapper does NOT
41
+ * re-fetch `/v1/state/:ensemble` on `gap` or `chat.compressed`; those
42
+ * are application-level concerns that the consumer (TUI / web dashboard)
43
+ * handles. The wrapper just relays events faithfully.
44
+ *
45
+ * - **Cancellation contract (§7.4).** Two paths:
46
+ * - `subscribe(..., { signal })` — when the external `AbortSignal`
47
+ * aborts, the wrapper closes the underlying transport and ends the
48
+ * iterator.
49
+ * - `for await … break` — the JavaScript runtime invokes the iterator's
50
+ * `return()` method, which in turn calls `controller.abort('iterator.return')`
51
+ * and tears down the transport. Without this, an idle SSE stream would
52
+ * leak the underlying socket.
53
+ */
54
+ Object.defineProperty(exports, "__esModule", { value: true });
55
+ exports.SubscribeHttpError = void 0;
56
+ exports.createSubscribe = createSubscribe;
57
+ const port_file_1 = require("../http/port-file");
58
+ const event_types_1 = require("../http/event-types");
59
+ // ── Constants ──────────────────────────────────────────────────────────────
60
+ const DEFAULT_HOST = '127.0.0.1';
61
+ const DEFAULT_PORT = 8473;
62
+ const RECONNECT_INITIAL_MS = 100;
63
+ const RECONNECT_MAX_MS = 30_000;
64
+ const SSE_KIND_SET = new Set(event_types_1.SSE_EVENT_KINDS);
65
+ /**
66
+ * Build the `subscribe` method for a TempoClient instance. Returns a
67
+ * function with two overloads — per-ensemble (`subscribe(ensemble, opts)`)
68
+ * and global (`subscribe(opts)`).
69
+ */
70
+ function createSubscribe(deps = {}) {
71
+ const sleepFn = deps.sleep ?? defaultSleep;
72
+ function subscribe(arg1, arg2) {
73
+ const scope = typeof arg1 === 'string'
74
+ ? { type: 'ensemble', ensemble: arg1 }
75
+ : { type: 'global' };
76
+ const opts = (typeof arg1 === 'string' ? arg2 : arg1) ?? {};
77
+ return makeIterable({ scope, opts, deps, sleep: sleepFn });
78
+ }
79
+ return subscribe;
80
+ }
81
+ // ── AsyncIterable plumbing ────────────────────────────────────────────────
82
+ function makeIterable(args) {
83
+ return {
84
+ [Symbol.asyncIterator]() {
85
+ return makeIterator(args);
86
+ },
87
+ };
88
+ }
89
+ /**
90
+ * Build the AsyncIterator. Bridges the run loop (which pushes events) to
91
+ * the consumer (which pulls via `next()`) through a single-element queue
92
+ * with promise-based backpressure.
93
+ */
94
+ function makeIterator(args) {
95
+ const externalSignal = args.opts.signal;
96
+ const wrapperController = new AbortController();
97
+ const wrapperSignal = wrapperController.signal;
98
+ // Forward external abort onto the wrapper controller.
99
+ let externalAbortListener = null;
100
+ if (externalSignal) {
101
+ if (externalSignal.aborted) {
102
+ wrapperController.abort(externalSignal.reason);
103
+ }
104
+ else {
105
+ externalAbortListener = () => {
106
+ wrapperController.abort(externalSignal.reason);
107
+ };
108
+ externalSignal.addEventListener('abort', externalAbortListener, {
109
+ once: true,
110
+ });
111
+ }
112
+ }
113
+ function detachExternal() {
114
+ if (externalSignal && externalAbortListener) {
115
+ externalSignal.removeEventListener('abort', externalAbortListener);
116
+ externalAbortListener = null;
117
+ }
118
+ }
119
+ // Buffered events ready for the consumer.
120
+ const buffer = [];
121
+ // Promise-based handoff for the next() call awaiting an event.
122
+ let pendingResolve = null;
123
+ let pendingReject = null;
124
+ // Run loop terminal state.
125
+ let done = false;
126
+ let runError = null;
127
+ function tryDrainPending() {
128
+ if (!pendingResolve)
129
+ return;
130
+ if (buffer.length > 0) {
131
+ const ev = buffer.shift();
132
+ const resolve = pendingResolve;
133
+ pendingResolve = null;
134
+ pendingReject = null;
135
+ resolve({ value: ev, done: false });
136
+ return;
137
+ }
138
+ if (done) {
139
+ const resolve = pendingResolve;
140
+ const reject = pendingReject;
141
+ pendingResolve = null;
142
+ pendingReject = null;
143
+ if (runError)
144
+ reject(runError);
145
+ else
146
+ resolve({ value: undefined, done: true });
147
+ }
148
+ }
149
+ const transport = canUseEventSource(args.deps) ? runEventSourceLoop : runFetchLoop;
150
+ const runPromise = transport({
151
+ ...args,
152
+ signal: wrapperSignal,
153
+ onEvent(ev) {
154
+ buffer.push(ev);
155
+ tryDrainPending();
156
+ },
157
+ })
158
+ .catch((err) => {
159
+ runError = err;
160
+ })
161
+ .finally(() => {
162
+ done = true;
163
+ detachExternal();
164
+ tryDrainPending();
165
+ });
166
+ return {
167
+ next() {
168
+ if (buffer.length > 0) {
169
+ return Promise.resolve({ value: buffer.shift(), done: false });
170
+ }
171
+ if (done) {
172
+ if (runError)
173
+ return Promise.reject(runError);
174
+ return Promise.resolve({ value: undefined, done: true });
175
+ }
176
+ return new Promise((resolve, reject) => {
177
+ pendingResolve = resolve;
178
+ pendingReject = reject;
179
+ });
180
+ },
181
+ async return() {
182
+ // Consumer is exiting — `for await … break` lands here. Abort the
183
+ // run loop, drain it, ensure the underlying transport tore down.
184
+ // Without this the SSE socket would leak (§7.4).
185
+ if (!wrapperSignal.aborted)
186
+ wrapperController.abort('iterator.return');
187
+ detachExternal();
188
+ try {
189
+ await runPromise;
190
+ }
191
+ catch {
192
+ // Swallow — the consumer has explicitly chosen to stop.
193
+ }
194
+ return { value: undefined, done: true };
195
+ },
196
+ async throw(err) {
197
+ if (!wrapperSignal.aborted)
198
+ wrapperController.abort(err);
199
+ detachExternal();
200
+ try {
201
+ await runPromise;
202
+ }
203
+ catch {
204
+ // Swallow — caller chose to throw.
205
+ }
206
+ throw err;
207
+ },
208
+ };
209
+ }
210
+ /**
211
+ * Native `EventSource` is the preferred transport when:
212
+ * - it's available in the runtime (browsers; Node 22+ optionally), AND
213
+ * - no bearer token is set (loopback / local dev).
214
+ *
215
+ * Otherwise we fall back to fetch — which gives us full header control
216
+ * for `Authorization: Bearer …` and is the only option in Node 20.
217
+ */
218
+ function canUseEventSource(deps) {
219
+ if (deps.token)
220
+ return false;
221
+ return resolveEventSource(deps) !== undefined;
222
+ }
223
+ function resolveEventSource(deps) {
224
+ if (deps.EventSourceImpl)
225
+ return deps.EventSourceImpl;
226
+ return globalThis.EventSource;
227
+ }
228
+ // ── EventSource transport ──────────────────────────────────────────────────
229
+ /**
230
+ * Open a native `EventSource` and pipe each parsed event to `onEvent`.
231
+ * Native auto-reconnect handles `Last-Event-ID` for in-session drops, so
232
+ * this loop is just: open, listen, close on abort. Errors that the
233
+ * `EventSource` cannot recover from (e.g. 404) bubble up via the silent
234
+ * reconnect cycle — for hard-error visibility callers should set a token
235
+ * (which forces the fetch path) or use `signal` with their own timeout.
236
+ */
237
+ async function runEventSourceLoop(args) {
238
+ const { scope, opts, deps, signal, onEvent } = args;
239
+ const ES = resolveEventSource(deps);
240
+ if (!ES)
241
+ throw new Error('[agent-tempo:subscribe] EventSource not available');
242
+ const url = buildUrl(scope, opts.topics, deps.baseUrl);
243
+ const es = new ES(url);
244
+ const listeners = [];
245
+ for (const kind of event_types_1.SSE_EVENT_KINDS) {
246
+ const listener = (ev) => {
247
+ const tempoEvent = parseEventSourceMessage(kind, ev);
248
+ if (tempoEvent)
249
+ onEvent(tempoEvent);
250
+ };
251
+ es.addEventListener(kind, listener);
252
+ listeners.push([kind, listener]);
253
+ }
254
+ return new Promise((resolve) => {
255
+ const teardown = () => {
256
+ // Inline call preserves `this` binding on the EventSource — extracting
257
+ // `es.removeEventListener` to a const would lose it under strict mode.
258
+ for (const [k, l] of listeners) {
259
+ es.removeEventListener(k, l);
260
+ }
261
+ es.close();
262
+ resolve();
263
+ };
264
+ if (signal.aborted) {
265
+ teardown();
266
+ return;
267
+ }
268
+ signal.addEventListener('abort', teardown, { once: true });
269
+ });
270
+ }
271
+ function parseEventSourceMessage(kind, ev) {
272
+ let envelope;
273
+ try {
274
+ envelope = JSON.parse(ev.data);
275
+ }
276
+ catch {
277
+ return null;
278
+ }
279
+ return liftEnvelope(envelope, kind, ev.lastEventId);
280
+ }
281
+ // ── Fetch transport ────────────────────────────────────────────────────────
282
+ /**
283
+ * The fetch reconnect loop. Opens a `fetch`, parses SSE frames, yields
284
+ * events via `onEvent`, and on transport failure backs off and reopens
285
+ * with the latest tracked `Last-Event-ID`. Returns when `signal.aborted`
286
+ * flips.
287
+ */
288
+ async function runFetchLoop(args) {
289
+ const { scope, opts, deps, sleep, signal, onEvent } = args;
290
+ const fetchImpl = deps.fetchImpl ?? globalFetch();
291
+ // In-session reconnect cursor — populated on each event the wrapper
292
+ // sees, replayed via `Last-Event-ID` on every retry. Caller-controllable
293
+ // resume via `opts.lastEventId` was deliberately dropped (ADR 0010).
294
+ let lastEventId = undefined;
295
+ let attempt = 0;
296
+ while (!signal.aborted) {
297
+ let connectionYieldedAny = false;
298
+ try {
299
+ const url = buildUrl(scope, opts.topics, deps.baseUrl);
300
+ const headers = buildHeaders(lastEventId, deps.token);
301
+ const response = await fetchImpl(url, { headers, signal });
302
+ if (signal.aborted)
303
+ break;
304
+ if (!response.ok) {
305
+ if (isPermanentHttpStatus(response.status)) {
306
+ // 401/404 — surface a hard error; do not retry.
307
+ const body = await safeReadBodyText(response);
308
+ throw new SubscribeHttpError(response.status, body);
309
+ }
310
+ if (response.status === 503) {
311
+ // Connection-cap-exceeded (§9). Honor `Retry-After` if present.
312
+ const retryAfter = parseRetryAfter(response.headers.get('Retry-After'));
313
+ await sleep(retryAfter ?? backoffMs(attempt), signal);
314
+ attempt++;
315
+ continue;
316
+ }
317
+ // Other 4xx/5xx — backoff retry. Drain body so the connection can close.
318
+ await safeReadBodyText(response);
319
+ await sleep(backoffMs(attempt), signal);
320
+ attempt++;
321
+ continue;
322
+ }
323
+ if (!response.body) {
324
+ // Server returned no body — treat as transient, back off and retry.
325
+ await sleep(backoffMs(attempt), signal);
326
+ attempt++;
327
+ continue;
328
+ }
329
+ // Stream the body. parseSseStream throws on signal abort.
330
+ for await (const raw of parseSseStream(response.body, signal)) {
331
+ if (raw.id)
332
+ lastEventId = raw.id;
333
+ const tempoEvent = parseTempoEvent(raw);
334
+ if (!tempoEvent)
335
+ continue; // malformed / unknown — skip
336
+ if (!connectionYieldedAny) {
337
+ // First event on a fresh connection — reset the backoff so a
338
+ // brief network blip doesn't poison the retry cadence (§7.5).
339
+ attempt = 0;
340
+ connectionYieldedAny = true;
341
+ }
342
+ onEvent(tempoEvent);
343
+ }
344
+ // Stream ended without abort — server closed the connection or the
345
+ // body iterator finished. Reconnect after backoff with the latest
346
+ // `Last-Event-ID`. A connection that yielded no events at all is
347
+ // treated as a transient failure (count it against the backoff
348
+ // schedule) — otherwise an empty-body server bug would tight-loop.
349
+ if (signal.aborted)
350
+ break;
351
+ await sleep(connectionYieldedAny ? RECONNECT_INITIAL_MS : backoffMs(attempt), signal);
352
+ if (!connectionYieldedAny)
353
+ attempt++;
354
+ }
355
+ catch (err) {
356
+ if (signal.aborted)
357
+ break;
358
+ if (err instanceof SubscribeHttpError) {
359
+ // Permanent — surface to consumer.
360
+ throw err;
361
+ }
362
+ // Network error / parse error / abort during sleep → backoff retry.
363
+ try {
364
+ await sleep(backoffMs(attempt), signal);
365
+ }
366
+ catch {
367
+ // sleep aborted — outer loop check will exit.
368
+ }
369
+ attempt++;
370
+ }
371
+ }
372
+ }
373
+ // ── Errors ─────────────────────────────────────────────────────────────────
374
+ /**
375
+ * Surfaced to the consumer when the daemon returned a permanent HTTP
376
+ * status (401 unauthorized, 404 ensemble-not-found). Consumers should not
377
+ * retry on these.
378
+ */
379
+ class SubscribeHttpError extends Error {
380
+ status;
381
+ body;
382
+ constructor(status, body) {
383
+ super(`SSE subscribe failed: HTTP ${status}${body ? ` — ${body}` : ''}`);
384
+ this.status = status;
385
+ this.body = body;
386
+ this.name = 'SubscribeHttpError';
387
+ }
388
+ }
389
+ exports.SubscribeHttpError = SubscribeHttpError;
390
+ function isPermanentHttpStatus(status) {
391
+ return status === 401 || status === 404;
392
+ }
393
+ // ── URL + header construction ──────────────────────────────────────────────
394
+ function buildUrl(scope, topics, baseUrl) {
395
+ const base = resolveBaseUrl(baseUrl);
396
+ const path = scope.type === 'ensemble'
397
+ ? `/v1/events/${encodeURIComponent(scope.ensemble)}`
398
+ : '/v1/events';
399
+ if (!topics || topics.length === 0)
400
+ return `${base}${path}`;
401
+ const query = topics.map((t) => encodeURIComponent(t)).join(',');
402
+ return `${base}${path}?topics=${query}`;
403
+ }
404
+ function resolveBaseUrl(override) {
405
+ if (override)
406
+ return override.replace(/\/$/, '');
407
+ const port = (0, port_file_1.readPortFile)() ?? DEFAULT_PORT;
408
+ return `http://${DEFAULT_HOST}:${port}`;
409
+ }
410
+ function buildHeaders(lastEventId, token) {
411
+ const h = {
412
+ Accept: 'text/event-stream',
413
+ };
414
+ if (lastEventId)
415
+ h['Last-Event-ID'] = lastEventId;
416
+ if (token)
417
+ h.Authorization = `Bearer ${token}`;
418
+ return h;
419
+ }
420
+ // ── SSE parser ─────────────────────────────────────────────────────────────
421
+ /**
422
+ * Parse an SSE response body into raw frames. Each yielded `RawSseEvent`
423
+ * corresponds to one `\n\n`-delimited block. Comments (lines starting with
424
+ * `:`) and unrecognised fields (e.g. `retry:`) are skipped per the SSE
425
+ * spec. Throws on abort.
426
+ */
427
+ async function* parseSseStream(body, signal) {
428
+ const decoder = new TextDecoder('utf-8');
429
+ const reader = body.getReader();
430
+ let buffer = '';
431
+ // Resume scanning at this index instead of `0`. After we've scanned a
432
+ // chunk and found no `\n`, we record the buffer length so the next
433
+ // chunk's scan starts at the boundary — avoids O(n²) on pathologically
434
+ // large `data:` payloads (spec §5: payloads MAY exceed typical SSE
435
+ // samples; consumers MUST NOT cap line length).
436
+ let scanStart = 0;
437
+ let id;
438
+ let event;
439
+ let data = [];
440
+ const onAbort = () => {
441
+ try {
442
+ reader.cancel().catch(() => { });
443
+ }
444
+ catch {
445
+ // Reader already released.
446
+ }
447
+ };
448
+ if (signal.aborted)
449
+ onAbort();
450
+ signal.addEventListener('abort', onAbort, { once: true });
451
+ try {
452
+ while (!signal.aborted) {
453
+ const { value, done } = await reader.read();
454
+ if (done)
455
+ break;
456
+ buffer += decoder.decode(value, { stream: true });
457
+ let nlIdx;
458
+ while ((nlIdx = buffer.indexOf('\n', scanStart)) !== -1) {
459
+ const line = buffer.slice(0, nlIdx).replace(/\r$/, '');
460
+ buffer = buffer.slice(nlIdx + 1);
461
+ scanStart = 0;
462
+ if (line === '') {
463
+ // Frame complete.
464
+ if (data.length > 0 || event !== undefined || id !== undefined) {
465
+ yield { id, event, data: data.join('\n') };
466
+ }
467
+ id = undefined;
468
+ event = undefined;
469
+ data = [];
470
+ continue;
471
+ }
472
+ if (line.startsWith(':'))
473
+ continue; // comment
474
+ const colonIdx = line.indexOf(':');
475
+ const field = colonIdx === -1 ? line : line.slice(0, colonIdx);
476
+ let val = colonIdx === -1 ? '' : line.slice(colonIdx + 1);
477
+ if (val.startsWith(' '))
478
+ val = val.slice(1);
479
+ if (field === 'id')
480
+ id = val;
481
+ else if (field === 'event')
482
+ event = val;
483
+ else if (field === 'data')
484
+ data.push(val);
485
+ // `retry:` and unknown fields ignored per SSE spec.
486
+ }
487
+ // No `\n` left in the buffer — record the boundary so the next
488
+ // decoded chunk's scan starts here instead of re-scanning the
489
+ // already-checked prefix.
490
+ scanStart = buffer.length;
491
+ }
492
+ }
493
+ finally {
494
+ signal.removeEventListener('abort', onAbort);
495
+ try {
496
+ reader.releaseLock();
497
+ }
498
+ catch {
499
+ // Already released.
500
+ }
501
+ }
502
+ }
503
+ /**
504
+ * Lift a raw SSE frame into a typed `TempoEvent`. Returns `null` for
505
+ * unknown event kinds (forward compatibility per §6) or invalid JSON
506
+ * payloads — both are skipped silently per the spec.
507
+ */
508
+ function parseTempoEvent(raw) {
509
+ if (!raw.event || !SSE_KIND_SET.has(raw.event))
510
+ return null;
511
+ let envelope;
512
+ try {
513
+ envelope = JSON.parse(raw.data);
514
+ }
515
+ catch {
516
+ return null;
517
+ }
518
+ return liftEnvelope(envelope, raw.event, raw.id);
519
+ }
520
+ /**
521
+ * Unwrap the on-wire envelope `{ v, eventId, payload }` produced by
522
+ * `frameSseEvent` in `src/http/sse-handler.ts`. Returns `null` for
523
+ * structurally-invalid frames so a single bad event can't kill the
524
+ * stream (§6 spec — consumers MUST skip).
525
+ */
526
+ function liftEnvelope(envelope, type, rawId) {
527
+ if (typeof envelope !== 'object' || envelope === null)
528
+ return null;
529
+ const env = envelope;
530
+ if (typeof env.payload !== 'object' || env.payload === null)
531
+ return null;
532
+ // `||` (not `??`) — `EventSource.lastEventId` is always a `string` and
533
+ // is `''` when no `id:` line was sent, so we have to coerce the empty
534
+ // string to the envelope's `eventId` like the pre-#351 EventSource
535
+ // parser did. The fetch parser produces `string | undefined`; both
536
+ // paths share this fallback chain.
537
+ const fromEnv = typeof env.eventId === 'string' ? env.eventId : '';
538
+ const eventId = rawId || fromEnv || '0:0';
539
+ return {
540
+ v: 1,
541
+ eventId,
542
+ type,
543
+ payload: env.payload,
544
+ };
545
+ }
546
+ // ── Helpers ────────────────────────────────────────────────────────────────
547
+ function backoffMs(attempt) {
548
+ // 100, 200, 400, 800, … capped at 30 000 ms per §7.5.
549
+ return Math.min(RECONNECT_INITIAL_MS * Math.pow(2, attempt), RECONNECT_MAX_MS);
550
+ }
551
+ function parseRetryAfter(header) {
552
+ if (!header)
553
+ return null;
554
+ const seconds = Number(header);
555
+ if (!Number.isFinite(seconds) || seconds < 0)
556
+ return null;
557
+ return Math.round(seconds * 1000);
558
+ }
559
+ async function safeReadBodyText(response) {
560
+ try {
561
+ return await response.text();
562
+ }
563
+ catch {
564
+ return '';
565
+ }
566
+ }
567
+ function defaultSleep(ms, signal) {
568
+ return new Promise((resolve, reject) => {
569
+ if (signal?.aborted) {
570
+ reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
571
+ return;
572
+ }
573
+ const timer = setTimeout(() => {
574
+ cleanup();
575
+ resolve();
576
+ }, ms);
577
+ const onAbort = () => {
578
+ clearTimeout(timer);
579
+ cleanup();
580
+ reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));
581
+ };
582
+ const cleanup = () => {
583
+ signal?.removeEventListener('abort', onAbort);
584
+ };
585
+ if (signal)
586
+ signal.addEventListener('abort', onAbort, { once: true });
587
+ });
588
+ }
589
+ function globalFetch() {
590
+ // Node 20+ ships fetch as a global; modern browsers do too. We resolve
591
+ // it lazily so the import doesn't fail at module-load on environments
592
+ // that polyfill late (e.g. tests injecting `fetchImpl`).
593
+ const f = globalThis.fetch;
594
+ if (!f) {
595
+ throw new Error('[agent-tempo:subscribe] global fetch is not available; pass deps.fetchImpl');
596
+ }
597
+ return f;
598
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * `TempoClientWithSpawn` — TTY-bound superset of {@link TempoClientCore}.
3
+ *
4
+ * Composes {@link createTempoClientCore} with the two methods that shell
5
+ * out to a local `agent-tempo up …` invocation. **Never import this from
6
+ * the daemon, MCP tools, or the SSE event source** — those contexts have
7
+ * no TTY and a stray spawn would launch a terminal nothing can render.
8
+ *
9
+ * The boundary is enforced by the type system: headless callers depend on
10
+ * `TempoClientCore`; only the TUI (and its `ensure-conductor-spawned`
11
+ * helper) opts into the spawn surface.
12
+ *
13
+ * See `docs/adr/0007-tempoclient-core-withspawn-split.md`.
14
+ */
15
+ import type { Client } from '@temporalio/client';
16
+ import { type CreateTempoClientOpts } from './core';
17
+ import type { TempoClientWithSpawn } from './interface';
18
+ export type { CreateTempoClientOpts } from './core';
19
+ /**
20
+ * Build a `TempoClientWithSpawn` over a configured Temporal `Client`.
21
+ * Composes {@link createTempoClientCore} and adds the two TTY-bound
22
+ * spawn methods. Use this for TUI-side consumers; headless callers
23
+ * should use {@link createTempoClientCore} directly.
24
+ *
25
+ * `opts` (e.g. `subscribeDeps`) is forwarded to the Core factory.
26
+ */
27
+ export declare function createTempoClientWithSpawn(client: Client, opts?: CreateTempoClientOpts): TempoClientWithSpawn;