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,565 @@
1
+ "use strict";
2
+ /**
3
+ * Copilot Bridge — allows GitHub Copilot CLI sessions to participate
4
+ * as players in a claude-tempo ensemble.
5
+ *
6
+ * The bridge:
7
+ * 1. Spawns a Copilot CLI session via the Copilot SDK
8
+ * 2. Configures it with claude-tempo as an MCP server (so it gets all tools)
9
+ * 3. Polls the Temporal workflow for pending messages
10
+ * 4. Injects messages as prompts via the SDK
11
+ *
12
+ * Usage:
13
+ * npx ts-node src/copilot-bridge.ts
14
+ *
15
+ * Environment variables:
16
+ * CLAUDE_TEMPO_ENSEMBLE — ensemble name (default: "default")
17
+ * CLAUDE_TEMPO_PLAYER_NAME — player ID for workflow registration (set by spawner for deterministic workflow IDs)
18
+ * COPILOT_BRIDGE_NAME — player name for set_name (optional)
19
+ * COPILOT_BRIDGE_MODEL — model to use (optional)
20
+ * COPILOT_BRIDGE_SESSION_ID — deterministic session ID for resumable sessions (optional)
21
+ * GITHUB_TOKEN — GitHub auth token (optional, uses logged-in user by default)
22
+ */
23
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ var desc = Object.getOwnPropertyDescriptor(m, k);
26
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
27
+ desc = { enumerable: true, get: function() { return m[k]; } };
28
+ }
29
+ Object.defineProperty(o, k2, desc);
30
+ }) : (function(o, m, k, k2) {
31
+ if (k2 === undefined) k2 = k;
32
+ o[k2] = m[k];
33
+ }));
34
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
35
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
36
+ }) : function(o, v) {
37
+ o["default"] = v;
38
+ });
39
+ var __importStar = (this && this.__importStar) || (function () {
40
+ var ownKeys = function(o) {
41
+ ownKeys = Object.getOwnPropertyNames || function (o) {
42
+ var ar = [];
43
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
44
+ return ar;
45
+ };
46
+ return ownKeys(o);
47
+ };
48
+ return function (mod) {
49
+ if (mod && mod.__esModule) return mod;
50
+ var result = {};
51
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
52
+ __setModuleDefault(result, mod);
53
+ return result;
54
+ };
55
+ })();
56
+ Object.defineProperty(exports, "__esModule", { value: true });
57
+ const fs = __importStar(require("fs"));
58
+ const path = __importStar(require("path"));
59
+ const client_1 = require("@temporalio/client");
60
+ const config_1 = require("./config");
61
+ const connection_1 = require("./connection");
62
+ // Optional dependency — must be installed separately: npm install @github/copilot-sdk
63
+ let CopilotClient;
64
+ let approveAll;
65
+ try {
66
+ const sdk = require('@github/copilot-sdk');
67
+ CopilotClient = sdk.CopilotClient;
68
+ approveAll = sdk.approveAll;
69
+ }
70
+ catch {
71
+ console.error('Error: @github/copilot-sdk is not installed.\n' +
72
+ 'Install it with: npm install @github/copilot-sdk\n' +
73
+ 'See the Copilot CLI integration section in the README.');
74
+ process.exit(1);
75
+ }
76
+ // Unbuffered logging — fs.writeSync(2, ...) bypasses Node.js stream buffering,
77
+ // ensuring log output appears immediately even when stderr is redirected to a file.
78
+ const log = (...args) => {
79
+ const msg = `[copilot-bridge] ${args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')}\n`;
80
+ fs.writeSync(2, msg);
81
+ };
82
+ /** Filter process.env to exclude undefined values (safe to spread as Record<string, string>). */
83
+ const cleanEnv = () => Object.fromEntries(Object.entries(process.env).filter((e) => e[1] !== undefined));
84
+ const POLL_INTERVAL_MS = 2000;
85
+ const CREATE_SESSION_TIMEOUT_MS = 45_000;
86
+ const MAX_CONSECUTIVE_FAILURES = 3;
87
+ const MAX_SESSION_RECREATIONS = 2;
88
+ /** Check workflow status every N polls (~30s at 2s interval). */
89
+ const WORKFLOW_STATUS_CHECK_INTERVAL = 15;
90
+ /** Proactively recreate the Copilot session after this idle period (ms). Default 60 min. */
91
+ const SESSION_MAX_IDLE_MS = 60 * 60 * 1000;
92
+ /** Wrap createSession with a timeout so auth/network hangs don't block forever. */
93
+ async function createSessionWithTimeout(copilotClient, sessionConfig, timeoutMs = CREATE_SESSION_TIMEOUT_MS) {
94
+ let timer;
95
+ const timeout = new Promise((_, reject) => {
96
+ timer = setTimeout(() => reject(new Error(`createSession timed out after ${timeoutMs / 1000}s — check Copilot auth and network connectivity`)), timeoutMs);
97
+ });
98
+ try {
99
+ return await Promise.race([
100
+ copilotClient.createSession(sessionConfig),
101
+ timeout,
102
+ ]);
103
+ }
104
+ finally {
105
+ clearTimeout(timer);
106
+ }
107
+ }
108
+ async function main() {
109
+ const config = (0, config_1.getConfig)();
110
+ const playerName = process.env[config_1.ENV.BRIDGE_NAME];
111
+ const model = process.env[config_1.ENV.BRIDGE_MODEL];
112
+ const copilotSessionId = process.env[config_1.ENV.BRIDGE_SESSION_ID] || `tempo-${config.ensemble}-${playerName || 'unknown'}-${Date.now()}-${process.pid}`;
113
+ const workDir = process.cwd();
114
+ log(`Starting Copilot bridge in ${workDir} (ensemble: ${config.ensemble})`);
115
+ // Connect Temporal client (for polling only — the MCP server child process runs its own worker)
116
+ const connection = await (0, connection_1.createTemporalConnection)(config);
117
+ const client = new client_1.Client({
118
+ connection,
119
+ namespace: config.temporalNamespace,
120
+ });
121
+ // Determine the expected workflow ID. The MCP server uses the pattern
122
+ // `claude-session-{ensemble}-{playerId}`, where playerId comes from
123
+ // CLAUDE_TEMPO_PLAYER_NAME or a random hex. We pass CLAUDE_TEMPO_PLAYER_NAME
124
+ // to the MCP server env so both sides agree on the ID.
125
+ const isConductor = process.env[config_1.ENV.CONDUCTOR] === 'true';
126
+ const requestedName = process.env[config_1.ENV.PLAYER_NAME] || playerName || '';
127
+ const playerIdForWorkflow = isConductor
128
+ ? 'conductor'
129
+ : (requestedName && requestedName !== 'conductor' ? requestedName : '') || `copilot-${Date.now()}`;
130
+ const expectedWorkflowId = `claude-session-${config.ensemble}-${playerIdForWorkflow}`;
131
+ // Build the MCP server command — always use the compiled dist/server.js
132
+ // Run `npm run build` (or `pnpm build`) before using the bridge.
133
+ const serverJsPath = path.resolve(__dirname, '..', 'dist', 'server.js');
134
+ if (!fs.existsSync(serverJsPath)) {
135
+ log(`ERROR: ${serverJsPath} not found. Run 'pnpm build' first.`);
136
+ process.exit(1);
137
+ }
138
+ log(`MCP server path: ${serverJsPath}`);
139
+ const serverCommand = 'node';
140
+ const serverArgs = [serverJsPath];
141
+ const mcpEnv = {
142
+ ...cleanEnv(),
143
+ [config_1.ENV.ENSEMBLE]: config.ensemble,
144
+ [config_1.ENV.TEMPORAL_ADDRESS]: config.temporalAddress,
145
+ [config_1.ENV.TEMPORAL_NAMESPACE]: config.temporalNamespace,
146
+ [config_1.ENV.TASK_QUEUE]: config.taskQueue,
147
+ [config_1.ENV.CONDUCTOR]: isConductor ? 'true' : '',
148
+ [config_1.ENV.BRIDGE_MODE]: '1', // disable MCP server's message poller — bridge handles delivery
149
+ [config_1.ENV.PLAYER_NAME]: playerIdForWorkflow, // ensures MCP server uses same workflow ID
150
+ ...(config.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: config.temporalApiKey } : {}),
151
+ ...(config.temporalTlsCertPath ? { [config_1.ENV.TEMPORAL_TLS_CERT_PATH]: config.temporalTlsCertPath } : {}),
152
+ ...(config.temporalTlsKeyPath ? { [config_1.ENV.TEMPORAL_TLS_KEY_PATH]: config.temporalTlsKeyPath } : {}),
153
+ };
154
+ // Spawn Copilot SDK client and session
155
+ const copilotClient = new CopilotClient({
156
+ logLevel: 'debug',
157
+ env: {
158
+ ...cleanEnv(),
159
+ ...(process.env.GITHUB_TOKEN ? { GITHUB_TOKEN: process.env.GITHUB_TOKEN } : {}),
160
+ },
161
+ });
162
+ const sessionConfig = {
163
+ sessionId: copilotSessionId,
164
+ // approveAll is intentional: Copilot bridge sessions run headless with no
165
+ // interactive terminal, so there is no way to prompt for permission approval.
166
+ // All tool calls are auto-approved by design — the bridge operator accepts
167
+ // this when launching the bridge process.
168
+ onPermissionRequest: approveAll,
169
+ workingDirectory: workDir,
170
+ mcpServers: {
171
+ 'claude-tempo': {
172
+ command: serverCommand,
173
+ args: serverArgs,
174
+ env: mcpEnv,
175
+ tools: ['*'],
176
+ },
177
+ },
178
+ systemMessage: {
179
+ mode: 'append',
180
+ content: `You are part of the "${config.ensemble}" ensemble coordinated via Temporal. ` +
181
+ `You have MCP tools available — ALWAYS use these tools directly, NEVER try to run them as shell commands:\n` +
182
+ `- set_name: Set your player name (call this FIRST if instructed)\n` +
183
+ `- ensemble: List active sessions\n` +
184
+ `- cue: Send a message to another player\n` +
185
+ `- set_part: Update your status/description\n` +
186
+ `- listen: Check for pending messages\n` +
187
+ `- recruit: Spawn a new player session\n` +
188
+ `- report: Report to the conductor\n` +
189
+ `- stop: Stop a session\n\n` +
190
+ `When you receive a message from another session, treat it like a coworker asking for help — respond promptly using your MCP tools.`,
191
+ },
192
+ excludedTools: ['write_powershell', 'read_powershell', 'list_powershell'],
193
+ ...(model ? { model } : {}),
194
+ };
195
+ log('Creating Copilot session...');
196
+ let session = await createSessionWithTimeout(copilotClient, sessionConfig);
197
+ log(`Copilot session created: ${session.sessionId}`);
198
+ // Track session health — resets to true on any successful interaction
199
+ let sessionAlive = true;
200
+ let lastEventTime = Date.now();
201
+ let lastEventType = 'session.created';
202
+ function attachEventLogger(s) {
203
+ s.on((event) => {
204
+ lastEventTime = Date.now();
205
+ lastEventType = event.type;
206
+ // Log tool calls and completions fully, truncate verbose events
207
+ if (event.type === 'tool.execution_start' || event.type === 'tool.execution_complete') {
208
+ log(`[event:${event.type}]`, JSON.stringify(event.data ?? event).substring(0, 800));
209
+ }
210
+ else if (event.type === 'assistant.message') {
211
+ const data = event.data ?? event;
212
+ const tools = data.toolRequests?.map((t) => t.name).join(', ') || 'none';
213
+ log(`[event:${event.type}] content="${(data.content || '').substring(0, 200)}" tools=[${tools}]`);
214
+ }
215
+ else if (event.type === 'session.info') {
216
+ log(`[session.info] ${JSON.stringify(event.data)}`);
217
+ }
218
+ else if (event.type === 'session.warning') {
219
+ log(`[session.warning] ${JSON.stringify(event.data)}`);
220
+ }
221
+ else if (event.type === 'session.mcp_servers_loaded') {
222
+ log(`[mcp_servers_loaded] ${JSON.stringify(event.data)}`);
223
+ }
224
+ else if (event.type === 'session.mcp_server_status_changed') {
225
+ log(`[mcp_server_status_changed] ${JSON.stringify(event.data)}`);
226
+ }
227
+ else if (event.type === 'session.idle') {
228
+ log(`[event:session.idle] Session is idle`);
229
+ }
230
+ else if (event.type?.includes('error') || event.type?.includes('disconnect')) {
231
+ log(`[event:${event.type}]`, JSON.stringify(event.data ?? event).substring(0, 500));
232
+ sessionAlive = false;
233
+ }
234
+ else {
235
+ log(`[event:${event.type}]`);
236
+ }
237
+ });
238
+ }
239
+ attachEventLogger(session);
240
+ // Send an initial prompt to trigger MCP server initialization.
241
+ // The Copilot SDK doesn't start MCP server subprocesses until the session
242
+ // processes a message that could use tools. We await this so the workflow
243
+ // registers before we try to find it, and so subsequent sendAndWait calls
244
+ // don't collide with this one.
245
+ log('Sending initial prompt to trigger MCP server startup...');
246
+ try {
247
+ const t0 = Date.now();
248
+ const initResult = await session.sendAndWait({ prompt: 'Call the ensemble tool to list active sessions. Respond in one short sentence.' }, 120_000);
249
+ log(`Initial prompt completed in ${Date.now() - t0}ms, result:`, JSON.stringify(initResult)?.substring(0, 300));
250
+ // Dump available tools for diagnostics
251
+ try {
252
+ const toolList = await session.rpc.tools.list({});
253
+ log('Available tools:', JSON.stringify(toolList.tools?.map((t) => t.name || t.namespacedName)));
254
+ }
255
+ catch (toolErr) {
256
+ log('Failed to list tools:', toolErr?.message);
257
+ }
258
+ }
259
+ catch (err) {
260
+ log(`Initial prompt error after ${Date.now()}ms:`, err?.message, err?.stack?.substring(0, 300));
261
+ }
262
+ // PID file paths — computed early so early-exit paths can clean up
263
+ const pidDir = path.join(workDir, 'logs');
264
+ const pidFile = path.join(pidDir, `${playerName || playerIdForWorkflow}.pid`);
265
+ // Wait for the MCP server's workflow to register in Temporal.
266
+ // We know the exact workflow ID because we pass CLAUDE_TEMPO_PLAYER_NAME to the
267
+ // MCP server — no need for a time-window heuristic that could misidentify workflows.
268
+ log(`Waiting for workflow ${expectedWorkflowId} to register...`);
269
+ let handle = client.workflow.getHandle(expectedWorkflowId);
270
+ let workflowReady = false;
271
+ let pinnedRunId;
272
+ for (let attempt = 0; attempt < 30; attempt++) {
273
+ try {
274
+ const desc = await handle.describe();
275
+ if (desc.status.name === 'RUNNING') {
276
+ workflowReady = true;
277
+ pinnedRunId = desc.runId;
278
+ break;
279
+ }
280
+ }
281
+ catch {
282
+ // Workflow not yet started
283
+ }
284
+ await new Promise((r) => setTimeout(r, 1000));
285
+ if (attempt % 5 === 4)
286
+ log(`Still waiting... attempt ${attempt + 1}/30`);
287
+ }
288
+ if (!workflowReady) {
289
+ log(`ERROR: Workflow ${expectedWorkflowId} did not register within 30 seconds`);
290
+ await session.disconnect();
291
+ await copilotClient.stop();
292
+ // Clean up PID file to avoid stale entries in `claude-tempo status`
293
+ try {
294
+ fs.unlinkSync(pidFile);
295
+ }
296
+ catch { /* may not exist yet */ }
297
+ process.exit(1);
298
+ }
299
+ // Pin all subsequent interactions to the runId we observed — prevents the
300
+ // zombie-resurrection hazard from #102. If this run completes (e.g. destroy),
301
+ // a later USE_EXISTING start would spawn a new run with the same workflow ID;
302
+ // an unpinned handle would silently attach to the new run, but a pinned handle
303
+ // returns WorkflowNotFound and lets the bridge exit cleanly.
304
+ handle = client.workflow.getHandle(expectedWorkflowId, pinnedRunId);
305
+ log(`Workflow ready: ${expectedWorkflowId} (pinned runId ${pinnedRunId})`);
306
+ // Store sessionId in workflow metadata for future encore/resume
307
+ try {
308
+ await handle.signal('updateMetadata', { sessionId: copilotSessionId });
309
+ }
310
+ catch { /* workflow may not be ready yet */ }
311
+ // If a name was requested, send the set_name instruction
312
+ if (playerName) {
313
+ log(`Sending set_name instruction for "${playerName}"...`);
314
+ const t0 = Date.now();
315
+ await session.sendAndWait({ prompt: `Call set_name("${playerName}") immediately. Respond in one short sentence.` }, 120_000);
316
+ log(`set_name completed in ${Date.now() - t0}ms`);
317
+ }
318
+ const MAESTRO_ACK = '\n\n[IMPORTANT: This message is from a human (Maestro). Immediately cue the sender back with a brief acknowledgment and your planned next step before doing the work.]';
319
+ // Write PID file so callers can find/kill orphaned bridge processes
320
+ try {
321
+ fs.mkdirSync(pidDir, { recursive: true });
322
+ fs.writeFileSync(pidFile, String(process.pid));
323
+ log(`PID file written: ${pidFile}`);
324
+ }
325
+ catch (err) {
326
+ log(`Warning: could not write PID file: ${err?.message}`);
327
+ }
328
+ // Start message poller — inject messages into the Copilot session.
329
+ // Tracks consecutive failures and attempts session recreation before giving up.
330
+ let polling = true;
331
+ let processing = false;
332
+ let pollCount = 0;
333
+ let consecutiveFailures = 0;
334
+ let sessionRecreations = 0;
335
+ let proactiveRecreations = 0;
336
+ let lastActivityTime = Date.now();
337
+ // interval declared here, assigned after poll is defined
338
+ let interval;
339
+ // Shared cleanup — disconnects session, removes PID file, stops client.
340
+ // `signalTermination` controls whether we also signal the workflow to terminate
341
+ // (skip if the workflow is already gone).
342
+ let shuttingDown = false;
343
+ const cleanup = async (signalTermination) => {
344
+ if (shuttingDown)
345
+ return;
346
+ shuttingDown = true;
347
+ polling = false;
348
+ clearInterval(interval);
349
+ if (signalTermination) {
350
+ try {
351
+ await handle.signal('updateMetadata', { status: 'terminated', terminatedBy: 'system' });
352
+ }
353
+ catch {
354
+ // workflow may already be gone
355
+ }
356
+ }
357
+ try {
358
+ await session.disconnect();
359
+ }
360
+ catch { /* already disconnected */ }
361
+ try {
362
+ fs.unlinkSync(pidFile);
363
+ }
364
+ catch { /* already gone */ }
365
+ await copilotClient.stop();
366
+ };
367
+ /** Attempt to recreate the Copilot session after repeated failures. */
368
+ async function recreateSession() {
369
+ // Fixes #102: before recreating, check whether the pinned-runId workflow has
370
+ // been destroyed. If so, the user explicitly stopped this session — do NOT
371
+ // bring it back as a zombie. This also covers WorkflowNotFound (the pinned
372
+ // run has completed/terminated) since the query throws cleanly.
373
+ try {
374
+ const isDestroyed = await handle.query('isDestroyed');
375
+ if (isDestroyed) {
376
+ log('Workflow is destroyed — not reconnecting (session was intentionally stopped)');
377
+ return false;
378
+ }
379
+ }
380
+ catch (err) {
381
+ const errName = err?.name || '';
382
+ const errMsg = err?.message || '';
383
+ if (errName.includes('WorkflowNotFound') || errMsg.includes('NOT_FOUND')) {
384
+ log('Workflow not found (likely terminated) — not reconnecting');
385
+ return false;
386
+ }
387
+ // Query failed for another reason — log and continue with recreation.
388
+ log(`isDestroyed query failed (${errMsg}), continuing with recreation`);
389
+ }
390
+ sessionRecreations++;
391
+ if (sessionRecreations > MAX_SESSION_RECREATIONS) {
392
+ log(`ERROR: Exceeded max session recreations (${MAX_SESSION_RECREATIONS}). Giving up.`);
393
+ return false;
394
+ }
395
+ log(`Attempting session recovery (${sessionRecreations}/${MAX_SESSION_RECREATIONS})...`);
396
+ try {
397
+ await session.disconnect().catch(() => { });
398
+ // Try resumeSession first to preserve conversation history
399
+ try {
400
+ const { sessionId: _discard, ...resumeConfig } = sessionConfig;
401
+ session = await copilotClient.resumeSession(copilotSessionId, resumeConfig);
402
+ attachEventLogger(session);
403
+ sessionAlive = true;
404
+ consecutiveFailures = 0;
405
+ lastActivityTime = Date.now();
406
+ log(`Session resumed successfully: ${session.sessionId}`);
407
+ return true;
408
+ }
409
+ catch (resumeErr) {
410
+ log(`resumeSession failed (${resumeErr?.message}), falling back to createSession`);
411
+ session = await createSessionWithTimeout(copilotClient, sessionConfig);
412
+ attachEventLogger(session);
413
+ sessionAlive = true;
414
+ consecutiveFailures = 0;
415
+ lastActivityTime = Date.now();
416
+ log(`Session recreated (fresh) successfully: ${session.sessionId}`);
417
+ return true;
418
+ }
419
+ }
420
+ catch (err) {
421
+ log(`Session recovery failed: ${err?.message}`);
422
+ return false;
423
+ }
424
+ }
425
+ const poll = async () => {
426
+ if (!polling || processing)
427
+ return;
428
+ pollCount++;
429
+ // Periodic health check
430
+ if (pollCount % 30 === 0) { // every ~60 seconds
431
+ const silenceSec = ((Date.now() - lastEventTime) / 1000).toFixed(0);
432
+ log(`[health] poll #${pollCount}, sessionAlive=${sessionAlive}, lastEvent=${lastEventType} ${silenceSec}s ago`);
433
+ }
434
+ // Periodic workflow status check — detect external termination/completion
435
+ if (pollCount % WORKFLOW_STATUS_CHECK_INTERVAL === 0) {
436
+ try {
437
+ const desc = await handle.describe();
438
+ const wfStatus = desc.status.name;
439
+ if (wfStatus !== 'RUNNING') {
440
+ log(`Workflow status is ${wfStatus} — exiting cleanly`);
441
+ await cleanup(false); // workflow already gone, don't signal
442
+ process.exit(0);
443
+ }
444
+ // Also check the in-workflow destroy flag — the workflow may still be RUNNING
445
+ // (draining) but has been destroyed. Exit cleanly without attempting signals
446
+ // that would be rejected.
447
+ try {
448
+ const isDestroyed = await handle.query('isDestroyed');
449
+ if (isDestroyed) {
450
+ log('Workflow destroyed — exiting cleanly');
451
+ await cleanup(false);
452
+ process.exit(0);
453
+ }
454
+ }
455
+ catch { /* isDestroyed query unavailable pre-upgrade — safe to ignore */ }
456
+ }
457
+ catch (err) {
458
+ // If we can't describe (e.g., workflow not found), it was likely terminated
459
+ log(`Workflow describe failed: ${err?.message} — treating as terminated`);
460
+ await cleanup(false);
461
+ process.exit(0);
462
+ }
463
+ }
464
+ // Proactive stale-session detection — recreate before the SDK server GCs the session
465
+ const idleMs = Date.now() - lastActivityTime;
466
+ if (idleMs > SESSION_MAX_IDLE_MS && !processing) {
467
+ try {
468
+ processing = true; // guard against overlapping polls during async recreation
469
+ log(`Session idle for ${(idleMs / 1000 / 60).toFixed(0)}min — proactively recreating`);
470
+ proactiveRecreations++;
471
+ const recovered = await recreateSession();
472
+ if (recovered) {
473
+ // Proactive recreation is lifecycle management, not failure recovery — restore failure budget
474
+ // but don't reset to 0: use proactiveRecreations to cap total lifecycle recreations
475
+ sessionRecreations = Math.max(0, sessionRecreations - 1);
476
+ }
477
+ else {
478
+ // Session is almost certainly dead server-side — force immediate recovery on next message
479
+ // Use MAX - 1 so the next poll error increments to the threshold and triggers recovery
480
+ consecutiveFailures = MAX_CONSECUTIVE_FAILURES - 1;
481
+ sessionAlive = false;
482
+ log('ERROR: Proactive session recreation failed — will force recovery on next message');
483
+ }
484
+ }
485
+ finally {
486
+ processing = false;
487
+ }
488
+ }
489
+ try {
490
+ const messages = await handle.query('pendingMessages');
491
+ if (messages.length === 0)
492
+ return;
493
+ processing = true;
494
+ const ids = messages.map((m) => m.id);
495
+ // Format messages into a single prompt, appending ack instruction for Maestro messages
496
+ const prompt = messages
497
+ .map((m) => {
498
+ const line = `[Message from ${m.from}]: ${m.text}`;
499
+ return m.isMaestro ? line + MAESTRO_ACK : line;
500
+ })
501
+ .join('\n\n');
502
+ log(`Injecting ${messages.length} message(s) into Copilot session`);
503
+ log(`Prompt: ${prompt.substring(0, 300)}`);
504
+ if (!sessionAlive) {
505
+ log('WARNING: session appears dead, sendAndWait may hang');
506
+ }
507
+ // Fixes #99: mark these messages as in-flight before the blocking LLM call so
508
+ // the workflow's stale detection doesn't misclassify a long tool call as dead.
509
+ // Fire-and-logged — don't block sendAndWait on the update roundtrip.
510
+ const processingMarkerId = ids[0]; // representative for the batch
511
+ handle.executeUpdate('processingStart', { args: [{ messageId: processingMarkerId }] })
512
+ .catch((err) => log(`processingStart update failed (non-fatal): ${err?.message}`));
513
+ const t0 = Date.now();
514
+ let result;
515
+ try {
516
+ result = await session.sendAndWait({ prompt }, 300_000); // 5 min timeout
517
+ }
518
+ finally {
519
+ // Always release the in-flight marker — a thrown error from sendAndWait
520
+ // must not leave the workflow pinned as "processing".
521
+ handle.executeUpdate('processingEnd', { args: [{ messageId: processingMarkerId }] })
522
+ .catch((err) => log(`processingEnd update failed (non-fatal): ${err?.message}`));
523
+ }
524
+ const elapsed = Date.now() - t0;
525
+ log(`sendAndWait completed in ${elapsed}ms`);
526
+ log(`Response: ${JSON.stringify(result)?.substring(0, 500)}`);
527
+ // Mark delivered only after successful send — failed messages stay in pending queue for retry
528
+ await handle.signal('markDelivered', ids);
529
+ // Success — reset failure tracking
530
+ consecutiveFailures = 0;
531
+ lastActivityTime = Date.now();
532
+ sessionAlive = true;
533
+ processing = false;
534
+ }
535
+ catch (err) {
536
+ processing = false;
537
+ consecutiveFailures++;
538
+ log(`Poll error (${consecutiveFailures}/${MAX_CONSECUTIVE_FAILURES}): ${err?.message}`);
539
+ log(`Error stack: ${err?.stack?.substring(0, 300)}`);
540
+ if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
541
+ log('Consecutive failure threshold reached — attempting session recovery');
542
+ const recovered = await recreateSession();
543
+ if (!recovered) {
544
+ log('ERROR: Session recovery failed. Shutting down bridge.');
545
+ await cleanup(true);
546
+ process.exit(2);
547
+ }
548
+ }
549
+ }
550
+ };
551
+ interval = setInterval(poll, POLL_INTERVAL_MS);
552
+ log('Message poller started. Bridge is running.');
553
+ // Graceful shutdown on SIGINT/SIGTERM — signal the workflow before exiting
554
+ const shutdown = async () => {
555
+ log('Shutting down (signal received)...');
556
+ await cleanup(true);
557
+ process.exit(0);
558
+ };
559
+ process.on('SIGINT', shutdown);
560
+ process.on('SIGTERM', shutdown);
561
+ }
562
+ main().catch((err) => {
563
+ log('Fatal error:', err);
564
+ process.exit(1);
565
+ });
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Test seam — production code passes nothing. Tests inject stubs to
3
+ * avoid spawning real processes / reading real package.json files.
4
+ */
5
+ export interface ProbeAdapterVersionsDeps {
6
+ /**
7
+ * Run a CLI and return its stdout. Default: real `execFile` with a
8
+ * timeout. Tests pass a stub that returns canned output.
9
+ */
10
+ runCommand?: (cmd: string, args: string[]) => Promise<string>;
11
+ /**
12
+ * Resolve the Copilot SDK's package version. Default: dynamic
13
+ * `require('@github/copilot-sdk/package.json').version`. Tests stub
14
+ * to control "installed vs. not installed" + version values.
15
+ * Returns `undefined` when the dep isn't installed.
16
+ */
17
+ resolveCopilotSdkVersion?: () => Promise<string | undefined>;
18
+ /** Optional log sink. Tests pass a no-op to silence output. */
19
+ log?: (...args: unknown[]) => void;
20
+ }
21
+ /**
22
+ * Probe the upstream tool versions for every adapter this daemon ships.
23
+ *
24
+ * @returns A map keyed by adapter name. Adapters whose probe failed or
25
+ * whose output couldn't be parsed are omitted entirely. The map may be
26
+ * empty in environments without any of the supported adapters available.
27
+ */
28
+ export declare function probeAdapterVersions(deps?: ProbeAdapterVersionsDeps): Promise<Record<string, string>>;
29
+ /**
30
+ * Synchronous companion to {@link defaultResolveCopilotSdkVersion}.
31
+ * Returns the Copilot SDK's `package.json#version` if the optional
32
+ * dependency is installed, or `undefined` when the require fails.
33
+ *
34
+ * Exported for #532 PR-2 — `src/daemon.ts` `computeHostProfile()` is
35
+ * synchronous (matches the existing `claude-code-headless` block at
36
+ * `daemon.ts:278-288`, which uses synchronous probes from
37
+ * `src/adapters/claude-code-headless/pre-flight.ts`). Awaiting an
38
+ * async helper from a sync function isn't possible without cascading
39
+ * the change to all `computeHostProfile` call sites; instead, this
40
+ * sync helper is the single require-source-of-truth, and the async
41
+ * `defaultResolveCopilotSdkVersion` (kept for the
42
+ * `ProbeAdapterVersionsDeps['resolveCopilotSdkVersion']` contract)
43
+ * delegates to it.
44
+ *
45
+ * Tests stub this directly via the `resolveCopilotSdkVersionSync` dep
46
+ * on `computeHostProfile` (see `test/daemon-boot.test.ts`).
47
+ *
48
+ * Never throws — a `try/catch` swallows MODULE_NOT_FOUND and any
49
+ * other failure mode to `undefined`. A throw here would crash the
50
+ * daemon-boot critical path.
51
+ */
52
+ export declare function resolveCopilotSdkVersionSync(): string | undefined;