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,710 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ClaudeCodeHeadlessAttachment = exports.CLAUDE_CODE_PERMISSION_MODES = exports.claudeCodeHeadlessDescriptor = void 0;
37
+ exports.encodeCwd = encodeCwd;
38
+ /**
39
+ * Headless Claude Code adapter — SDK class.
40
+ *
41
+ * Issue #520. Drives the host's installed `claude` CLI (the official Claude
42
+ * Code binary) as a per-turn `claude -p` subprocess. The whole point: tap
43
+ * subscription extra-usage credits via the host's existing OAuth login —
44
+ * the only ToS-clean way for a third-party tool to reach that pool per
45
+ * Anthropic's authentication policy.
46
+ *
47
+ * Mirrors the claude-api / opencode SDK-class adapters: detached Node
48
+ * subprocess, dual-purpose entry point (`require.main === module`),
49
+ * `claimAttachment` + heartbeat lifecycle inherited from `SdkAttachment`.
50
+ *
51
+ * Class hierarchy: `ClaudeCodeHeadlessAttachment extends SdkAttachment extends BaseAttachment`.
52
+ *
53
+ * **Commit progression for #520**:
54
+ * - PR-1: scaffold — directory layout, descriptor, class skeleton, recruit
55
+ * pre-flight, AgentType extension, registry registration.
56
+ * - **PR-2 (this commit)**: lifecycle + subprocess spawn — `run()` connects
57
+ * Temporal, claims attachment, hydrates session UUID via the shared
58
+ * `sessionId` metadata field (architect-ratified post-spike — see PR-3
59
+ * §16 spike-findings appendix), drives the poll loop. `onSuperseded()`
60
+ * scaffold for the in-flight `claude` subprocess SIGTERM (target hooks
61
+ * in PR-3). `invokeSdk()` still a stub — calling it throws so the
62
+ * adapter exits loudly on the first message rather than silently
63
+ * dropping it.
64
+ * - PR-3: tool-use loop — stream-json frame parser, --mcp-config inline
65
+ * JSON synthesis, error-mapper translating subprocess fail modes into
66
+ * the shared `ApiErrorCategory` classifier (per architect's
67
+ * ratification of Delta #3).
68
+ * - PR-4: tests + docs + example lineup.
69
+ *
70
+ * Design reference: `docs/design/520-claude-code-headless-adapter.md` —
71
+ * §0 (TL;DR), §2 (adapter precedents), §3 (spawn integration), §5
72
+ * (streaming + state), §6 (wire-protocol), §7 (engineer-facing skeleton).
73
+ */
74
+ const fs = __importStar(require("fs"));
75
+ const os = __importStar(require("os"));
76
+ const path = __importStar(require("path"));
77
+ const crypto = __importStar(require("crypto"));
78
+ const child_process_1 = require("child_process");
79
+ const client_1 = require("@temporalio/client");
80
+ const base_1 = require("../sdk/base");
81
+ const config_1 = require("../../config");
82
+ const connection_1 = require("../../connection");
83
+ const signals_1 = require("../../workflows/signals");
84
+ const stream_json_1 = require("./stream-json");
85
+ const error_mapper_1 = require("./error-mapper");
86
+ // #536 — pure prompt-build helpers (system-prompt injection +
87
+ // MAESTRO_ACK augmentation). Extracted so the per-turn driver below
88
+ // stays focused on subprocess I/O.
89
+ const prompt_1 = require("./prompt");
90
+ /**
91
+ * Descriptor for the claude-code-headless adapter. Colocated with the
92
+ * class so `adapter.ts` has no import dependency on `index.ts` (avoids
93
+ * the circular module-graph cycle QA flagged on copilot's PR-B).
94
+ *
95
+ * Design reference: `docs/design/520-claude-code-headless-adapter.md` §2.
96
+ */
97
+ exports.claudeCodeHeadlessDescriptor = {
98
+ adapterId: 'claude-code-headless',
99
+ adapterClass: 'sdk',
100
+ // `claude -p` blocks until the result frame — processingStart/End pairing
101
+ // is mandatory and provided by SdkAttachment.deliver().
102
+ blocksOnLLMTurn: true,
103
+ // SDK class — 30s cadence per design § / lifecycle-rebuild-v2 §4.3.
104
+ // Inherited from BaseAttachment's heartbeat loop via the descriptor.
105
+ heartbeatMs: 30_000,
106
+ };
107
+ // Re-export the canonical permission-mode type from `./types` so existing
108
+ // consumers that import it from `./adapter` (or from the barrel) keep
109
+ // working unchanged. Single source of truth lives in `./types.ts` —
110
+ // addresses QA Nit 3 from PR-1's review.
111
+ var types_1 = require("./types");
112
+ Object.defineProperty(exports, "CLAUDE_CODE_PERMISSION_MODES", { enumerable: true, get: function () { return types_1.CLAUDE_CODE_PERMISSION_MODES; } });
113
+ /**
114
+ * Unbuffered stderr logger. `fs.writeSync(2, ...)` bypasses Node.js stream
115
+ * buffering so log lines appear immediately even when stderr is redirected
116
+ * to a file. Same pattern claude-api / opencode use.
117
+ */
118
+ const log = (...args) => {
119
+ const msg = `[agent-tempo:claude-code-headless] ${args.map((a) => {
120
+ if (typeof a === 'string')
121
+ return a;
122
+ if (a instanceof Error)
123
+ return a.stack ? `${a.message}\n${a.stack}` : a.message;
124
+ try {
125
+ return JSON.stringify(a);
126
+ }
127
+ catch {
128
+ return String(a);
129
+ }
130
+ }).join(' ')}\n`;
131
+ fs.writeSync(2, msg);
132
+ };
133
+ /** Idle poll cadence — short enough for snappy cue delivery, loose on Temporal. */
134
+ const POLL_INTERVAL_MS = 2000;
135
+ /** Workflow-register poll bounds — same as claude-api/opencode. */
136
+ const WORKFLOW_REGISTER_ATTEMPTS = 30;
137
+ const WORKFLOW_REGISTER_INTERVAL_MS = 1000;
138
+ /** Workflow status check cadence (every N polls). */
139
+ const WORKFLOW_STATUS_CHECK_INTERVAL = 15;
140
+ /** Per-turn timeout — `claude -p` tool-use chains can run minutes. */
141
+ const TURN_TIMEOUT_MS = 5 * 60 * 1000;
142
+ /** SIGTERM grace before SIGKILL escalation on the in-flight `claude` subprocess. */
143
+ const SIGTERM_GRACE_MS = 5_000;
144
+ /**
145
+ * SDK-class adapter that drives `claude -p` as a per-turn subprocess.
146
+ *
147
+ * **PR-2 status**: lifecycle + spawn scaffold complete. `run()` boots the
148
+ * full V2 lifecycle, hydrates the per-cwd session UUID via the shared
149
+ * `sessionId` metadata field, and drives the poll loop. `onSuperseded()`
150
+ * SIGTERMs `this.childProcess` (which PR-3 populates inside `invokeSdk`).
151
+ * `invokeSdk()` still throws — calling it errors the turn loudly until
152
+ * PR-3 wires the per-turn `claude -p` invocation.
153
+ *
154
+ * Lifecycle inherited from `SdkAttachment` / `BaseAttachment`: claim,
155
+ * heartbeat, phase watcher, `processingStart`/`End` pairing,
156
+ * `markDelivered`. No reconnect opt-in (matches claude-api / opencode —
157
+ * the daemon's `reconcile-on-boot` recovers from lease loss).
158
+ */
159
+ class ClaudeCodeHeadlessAttachment extends base_1.SdkAttachment {
160
+ descriptor = exports.claudeCodeHeadlessDescriptor;
161
+ /** `--permission-mode` flag value. Resolved at construction; ENV fallback. */
162
+ permissionMode;
163
+ /** Whether to use `--dangerously-skip-permissions` instead of permissionMode. */
164
+ dangerouslySkipPermissions;
165
+ /**
166
+ * In-flight per-turn `claude -p` subprocess. Set by PR-3's `invokeSdk`
167
+ * before each turn; cleared after exit. `onSuperseded` reads this to
168
+ * SIGTERM the child on lease revocation.
169
+ */
170
+ childProcess = null;
171
+ /**
172
+ * Per-cwd Claude Code session UUID. Used as `--session-id` on the first
173
+ * turn (to PIN the UUID) and `--resume` alone on subsequent turns
174
+ * (mutually exclusive per CLI v2.1.126 — see §16.9). Hydrated from
175
+ * `SessionMetadata.sessionId` on `run()` — generated fresh + stashed if
176
+ * absent. The same field is shared with the interactive Claude Code
177
+ * adapter (architect-ratified Option (a) post-spike — see PR-3 §16).
178
+ */
179
+ sessionId = null;
180
+ /** Cached for the per-turn telemetry log. Populated in `run()`. */
181
+ playerName = '';
182
+ constructor(opts = {}) {
183
+ super();
184
+ this.permissionMode = opts.permissionMode
185
+ ?? process.env[config_1.ENV.PERMISSION_MODE]
186
+ ?? 'acceptEdits';
187
+ this.dangerouslySkipPermissions = opts.dangerouslySkipPermissions === true;
188
+ }
189
+ /**
190
+ * Lease-revocation hook — fired by `SdkAttachment` when the base-class
191
+ * phase watcher detects another claimant stole the attachment.
192
+ *
193
+ * Two-step graceful → forced fallback per design §5.7: SIGTERM first
194
+ * (lets `claude` flush any in-flight stream-json frames + tear down its
195
+ * MCP child cleanly); SIGKILL after {@link SIGTERM_GRACE_MS} grace if it
196
+ * doesn't exit. Identical pattern to opencode/adapter.ts:160-173.
197
+ *
198
+ * Idempotent — racing signals (lease revoke + cleanup) are safe.
199
+ */
200
+ onSuperseded() {
201
+ const child = this.childProcess;
202
+ this.childProcess = null;
203
+ if (!child) {
204
+ // No in-flight subprocess to kill — common case when superseded
205
+ // fires while the adapter is idle in the poll loop.
206
+ log('lease revoked — no in-flight claude subprocess');
207
+ return;
208
+ }
209
+ log('lease revoked — SIGTERM in-flight claude subprocess');
210
+ try {
211
+ child.kill('SIGTERM');
212
+ }
213
+ catch (err) {
214
+ log('SIGTERM threw:', err?.message ?? err);
215
+ }
216
+ setTimeout(() => {
217
+ if (child.exitCode === null && !child.killed) {
218
+ log(`SIGTERM grace (${SIGTERM_GRACE_MS}ms) expired — escalating to SIGKILL`);
219
+ try {
220
+ child.kill('SIGKILL');
221
+ }
222
+ catch {
223
+ // Already gone — fine.
224
+ }
225
+ }
226
+ }, SIGTERM_GRACE_MS).unref();
227
+ }
228
+ /**
229
+ * Subprocess entry point. Connects Temporal, waits for the session
230
+ * workflow to register, hydrates the per-cwd session UUID via metadata,
231
+ * claims the attachment via V2 lifecycle, and drives the poll loop.
232
+ *
233
+ * Design reference: §3.6 (env hygiene), §5.1 (session continuity), §7
234
+ * (engineer-facing skeleton).
235
+ */
236
+ async run() {
237
+ const config = (0, config_1.getConfig)();
238
+ const isConductor = process.env[config_1.ENV.CONDUCTOR] === 'true';
239
+ const requestedName = process.env[config_1.ENV.PLAYER_NAME] || '';
240
+ const playerIdForWorkflow = isConductor
241
+ ? 'conductor'
242
+ : (requestedName && requestedName !== 'conductor' ? requestedName : '') || `claude-code-headless-${Date.now()}`;
243
+ const expectedWorkflowId = `agent-session-${config.ensemble}-${playerIdForWorkflow}`;
244
+ const workDir = process.cwd();
245
+ this.playerName = playerIdForWorkflow;
246
+ // §3.6 env-hygiene early warning. ANTHROPIC_API_KEY in the parent env
247
+ // would defeat the whole point of this adapter (subscription billing).
248
+ // The actual env-strip happens in PR-3's invokeSdk before each `claude
249
+ // -p` spawn; this is just a heads-up so operators don't burn an hour
250
+ // wondering why their Console workspace is being charged. The strip is
251
+ // load-bearing — log it loudly.
252
+ if (process.env.ANTHROPIC_API_KEY) {
253
+ log('WARNING: ANTHROPIC_API_KEY is set in the adapter env. ' +
254
+ 'The per-turn `claude` child will have it stripped (PR-3) so OAuth ' +
255
+ 'subscription billing wins. If you intended Console billing, recruit ' +
256
+ 'with `agent: "claude-api"` instead.');
257
+ }
258
+ if (process.env.CLAUDE_CODE_OAUTH_TOKEN) {
259
+ log('WARNING: CLAUDE_CODE_OAUTH_TOKEN is set in the adapter env. ' +
260
+ 'The per-turn `claude` child will have it stripped (PR-3) — the ' +
261
+ 'host\'s keychain OAuth wins. Set `setup-token` only if you want ' +
262
+ 'long-lived non-keychain auth and recruit accordingly.');
263
+ }
264
+ log(`Starting claude-code-headless adapter in ${workDir} (ensemble: ${config.ensemble}, player: ${playerIdForWorkflow}, permissionMode: ${this.dangerouslySkipPermissions ? 'dangerously-skip-permissions' : this.permissionMode})`);
265
+ const connection = await (0, connection_1.createTemporalConnection)(config);
266
+ const client = new client_1.Client({
267
+ connection,
268
+ namespace: config.temporalNamespace,
269
+ });
270
+ this.configureV2(client, os.hostname());
271
+ // Wait for the session workflow to register in Temporal (the spawn
272
+ // activity is responsible for starting it; we know the ID deterministically).
273
+ log(`Waiting for workflow ${expectedWorkflowId} to register...`);
274
+ let handle = client.workflow.getHandle(expectedWorkflowId);
275
+ let pinnedRunId;
276
+ let workflowReady = false;
277
+ for (let attempt = 0; attempt < WORKFLOW_REGISTER_ATTEMPTS; attempt++) {
278
+ try {
279
+ const desc = await handle.describe();
280
+ if (desc.status.name === 'RUNNING') {
281
+ workflowReady = true;
282
+ pinnedRunId = desc.runId;
283
+ break;
284
+ }
285
+ }
286
+ catch { /* not yet started */ }
287
+ await new Promise((r) => setTimeout(r, WORKFLOW_REGISTER_INTERVAL_MS));
288
+ if (attempt % 5 === 4)
289
+ log(`Still waiting for workflow... attempt ${attempt + 1}/${WORKFLOW_REGISTER_ATTEMPTS}`);
290
+ }
291
+ if (!workflowReady) {
292
+ log(`ERROR: Workflow ${expectedWorkflowId} did not register within ${WORKFLOW_REGISTER_ATTEMPTS}s — exiting`);
293
+ process.exit(1);
294
+ }
295
+ handle = client.workflow.getHandle(expectedWorkflowId, pinnedRunId);
296
+ log(`Workflow ready: ${expectedWorkflowId} (pinned runId ${pinnedRunId})`);
297
+ // Hydrate the per-cwd Claude Code session UUID. Architect-ratified
298
+ // Option (a) post-spike: reuse the existing `sessionId` metadata field
299
+ // (already typed for shared use across Copilot + Claude Code per
300
+ // types.ts JSDoc). Same UUID survives encore / restart / migrate-within-cwd
301
+ // for free since Claude Code's session JSONL is per-cwd, not per-adapter.
302
+ try {
303
+ const meta = await handle.query(signals_1.getMetadataQuery);
304
+ if (meta.sessionId) {
305
+ this.sessionId = meta.sessionId;
306
+ log(`Resuming Claude Code session ${this.sessionId} from workflow metadata`);
307
+ }
308
+ else {
309
+ this.sessionId = crypto.randomUUID();
310
+ await handle.signal(signals_1.updateMetadataSignal, { sessionId: this.sessionId });
311
+ log(`Created new Claude Code session ${this.sessionId}; stashed on workflow metadata`);
312
+ }
313
+ }
314
+ catch (err) {
315
+ log(`ERROR: session-UUID hydration failed: ${err?.message ?? err} — exiting`);
316
+ process.exit(1);
317
+ }
318
+ // Wire terminal-cleanup hook BEFORE claiming so a race between claim +
319
+ // lease loss can't drop the event.
320
+ let shuttingDown = false;
321
+ const cleanup = async () => {
322
+ if (shuttingDown)
323
+ return;
324
+ shuttingDown = true;
325
+ log('Cleanup running...');
326
+ // SIGTERM any in-flight subprocess via onSuperseded's machinery.
327
+ this.onSuperseded();
328
+ // Graceful detach — fires `adapterExited` so the workflow collapses
329
+ // draining → detached immediately. Same pattern claude-api uses.
330
+ try {
331
+ await this.detachGracefully('user-stop');
332
+ }
333
+ catch (err) {
334
+ log('detachGracefully error:', err?.message ?? err);
335
+ }
336
+ };
337
+ this.onTerminal((reason) => {
338
+ log(`V2 terminal (${reason}) — triggering cleanup`);
339
+ cleanup().catch((err) => log('terminal cleanup error:', err?.message ?? err));
340
+ });
341
+ // V2 path: claim the attachment + start the base-class heartbeat &
342
+ // phase watcher loops. `startV2Lifecycle` returns its own pinned
343
+ // handle; we use that going forward so the heartbeat and delivery
344
+ // share a consistent handle.
345
+ try {
346
+ const expectedAttachmentId = process.env[config_1.ENV.ATTACHMENT_ID] || undefined;
347
+ handle = await this.startV2Lifecycle(expectedWorkflowId, expectedAttachmentId);
348
+ log(`V2 attachment claimed (attachmentId=${this.token?.attachmentId}${expectedAttachmentId ? ', renewed' : ''})`);
349
+ }
350
+ catch (err) {
351
+ log(`ERROR: V2 claimAttachment failed: ${err?.message ?? err}`);
352
+ await cleanup();
353
+ process.exit(1);
354
+ }
355
+ // PID file so callers can find / kill orphaned adapter processes.
356
+ const pidDir = path.join(workDir, 'logs');
357
+ const pidFile = path.join(pidDir, `${playerIdForWorkflow}.pid`);
358
+ try {
359
+ fs.mkdirSync(pidDir, { recursive: true });
360
+ fs.writeFileSync(pidFile, String(process.pid));
361
+ }
362
+ catch (err) {
363
+ log(`Warning: PID file write failed: ${err?.message ?? err}`);
364
+ }
365
+ // Graceful shutdown handlers. The cleanup fn is idempotent so racing
366
+ // signals are safe.
367
+ const shutdown = async () => {
368
+ log('Shutting down (signal received)...');
369
+ await cleanup();
370
+ try {
371
+ fs.unlinkSync(pidFile);
372
+ }
373
+ catch { /* already gone */ }
374
+ process.exit(0);
375
+ };
376
+ process.on('SIGINT', shutdown);
377
+ process.on('SIGTERM', shutdown);
378
+ // Drive the poll loop until cleanup is requested.
379
+ await this.pollLoop(handle);
380
+ try {
381
+ fs.unlinkSync(pidFile);
382
+ }
383
+ catch { /* already gone */ }
384
+ }
385
+ /**
386
+ * Poll the workflow for pending messages, drive each one through
387
+ * `SdkAttachment.deliver()`. Mirrors claude-api's loop shape; the LLM
388
+ * call lives in `invokeSdk` (PR-3).
389
+ *
390
+ * Periodic workflow-status checks detect external termination /
391
+ * destroy and exit cleanly so the daemon doesn't accumulate zombie
392
+ * adapter processes after a `destroy` from another player.
393
+ */
394
+ async pollLoop(handle) {
395
+ let polling = true;
396
+ let processing = false;
397
+ let pollCount = 0;
398
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
399
+ while (polling && !this.shouldStop()) {
400
+ pollCount++;
401
+ // Periodic workflow-status check — detect external destroy / completion.
402
+ if (pollCount % WORKFLOW_STATUS_CHECK_INTERVAL === 0) {
403
+ try {
404
+ const desc = await handle.describe();
405
+ if (desc.status.name !== 'RUNNING') {
406
+ log(`Workflow status is ${desc.status.name} — exiting cleanly`);
407
+ polling = false;
408
+ break;
409
+ }
410
+ try {
411
+ const isDestroyed = await handle.query(signals_1.isDestroyedQuery);
412
+ if (isDestroyed) {
413
+ log('Workflow destroyed — exiting cleanly');
414
+ polling = false;
415
+ break;
416
+ }
417
+ }
418
+ catch { /* isDestroyed query unavailable on pre-upgrade workflows — safe to skip */ }
419
+ }
420
+ catch (err) {
421
+ log(`Workflow describe failed: ${err?.message ?? err} — treating as terminated`);
422
+ polling = false;
423
+ break;
424
+ }
425
+ }
426
+ if (processing) {
427
+ await sleep(POLL_INTERVAL_MS);
428
+ continue;
429
+ }
430
+ let messages = [];
431
+ try {
432
+ messages = await handle.query(signals_1.pendingMessagesQuery);
433
+ }
434
+ catch (err) {
435
+ log(`pendingMessages query failed: ${err?.message ?? err}`);
436
+ await sleep(POLL_INTERVAL_MS);
437
+ continue;
438
+ }
439
+ if (messages.length === 0) {
440
+ await sleep(POLL_INTERVAL_MS);
441
+ continue;
442
+ }
443
+ processing = true;
444
+ try {
445
+ const ackIds = messages.map((m) => m.id);
446
+ // The whole batch is delivered as one `claude -p` invocation — the
447
+ // representative `messages[0]` only drives processingStart/End.
448
+ // QA flag from PR-2 review: closure-wrap so `invokeSdkWithBatch`
449
+ // sees `messages` via the captured argument (NOT via instance
450
+ // state — the prior comment claimed "via closure" but the method
451
+ // can't actually read closure-only vars). Mirrors opencode's
452
+ // pattern at `src/adapters/opencode/adapter.ts:443`.
453
+ await this.deliver(handle, messages[0],
454
+ /* prompt unused — invokeSdkWithBatch reads `messages` from the closure-captured arg */ '', TURN_TIMEOUT_MS, (timeoutPrompt, timeoutMs) => this.invokeSdkWithBatch(messages, timeoutPrompt, timeoutMs), ackIds);
455
+ }
456
+ catch (err) {
457
+ log(`deliver error: ${err?.message ?? err}`);
458
+ // Don't exit the loop — transient failures leave messages PENDING
459
+ // for the next poll. Only terminal events (lease loss, destroy)
460
+ // exit via onTerminal / status-check above.
461
+ }
462
+ finally {
463
+ processing = false;
464
+ }
465
+ }
466
+ log('Poll loop exiting');
467
+ }
468
+ /** True once `onTerminal` fired and cleanup tore down the V2 token. */
469
+ shouldStop() {
470
+ return this.token === null;
471
+ }
472
+ /**
473
+ * Per-turn LLM dispatch. Spawns `claude -p` with the synthesized argv
474
+ * + inline `--mcp-config`, streams stdout through `StreamJsonReader`,
475
+ * and returns the closing `result` frame's assembled text + usage.
476
+ *
477
+ * Closure-captured `messages[]` carries the multi-cue batch (PR-2 QA
478
+ * flag — see `pollLoop` for the closure-wrapping rationale). The
479
+ * representative `_prompt` arg from `SdkAttachment.deliver()` is
480
+ * unused; we build the prompt argv from `messages` directly.
481
+ *
482
+ * `_timeoutMs` is passed through to a Promise.race that SIGTERMs the
483
+ * subprocess on timeout. The base `SdkAttachment.deliver()` doesn't
484
+ * enforce its own timeout — we do it here.
485
+ *
486
+ * Subprocess failures (exit != 0, no result frame, or
487
+ * `result.is_error`) flow through the architect-ratified classifier
488
+ * in `./error-mapper.ts`. The classifier output is logged but the
489
+ * adapter does NOT call `markDelivered` on failure — the message
490
+ * stays PENDING so the next poll picks it up. The adapter's own
491
+ * retry-budget logic (PR-3 follow-up; mirrors #521's claude-api fix)
492
+ * tracks consecutive failures and escalates to detach when N=10
493
+ * `retriable-with-backoff` failures pile up.
494
+ */
495
+ async invokeSdkWithBatch(messages, _prompt, timeoutMs) {
496
+ if (!this.sessionId) {
497
+ throw new Error('invokeSdkWithBatch called before run() initialized sessionId');
498
+ }
499
+ const sessionId = this.sessionId;
500
+ const t0 = Date.now();
501
+ // Build the prompt — concatenate every queued cue with attribution.
502
+ // Mirrors opencode's `[from ${m.from}]: ${m.text}` shape so operators
503
+ // who switch between adapters see consistent transcript framing.
504
+ // #536 — `buildPromptText` additionally appends MAESTRO_ACK to any
505
+ // message with `isMaestro: true`, mirroring copilot's poll-loop
506
+ // pattern (see `src/adapters/copilot/adapter.ts:639-645`).
507
+ const promptText = (0, prompt_1.buildPromptText)(messages);
508
+ // Synthesize the inline --mcp-config JSON. Per design §4 the adapter
509
+ // does NOT translate tool schemas itself; instead `claude` spawns
510
+ // `node dist/server.js` as a stdio MCP child of THE child (a separate
511
+ // process from the adapter). The MCP server picks up the env vars
512
+ // and registers all tempo tools natively.
513
+ const mcpServerPath = path.resolve(__dirname, '..', '..', 'server.js');
514
+ const config = (0, config_1.getConfig)();
515
+ const mcpConfig = JSON.stringify({
516
+ mcpServers: {
517
+ 'agent-tempo': {
518
+ type: 'stdio',
519
+ command: 'node',
520
+ args: [mcpServerPath],
521
+ env: {
522
+ [config_1.ENV.ENSEMBLE]: config.ensemble,
523
+ [config_1.ENV.PLAYER_NAME]: this.playerName,
524
+ [config_1.ENV.TEMPORAL_ADDRESS]: config.temporalAddress,
525
+ [config_1.ENV.TEMPORAL_NAMESPACE]: config.temporalNamespace,
526
+ },
527
+ },
528
+ },
529
+ });
530
+ // Build the per-turn argv. `--resume` only on subsequent turns —
531
+ // detect by checking the per-cwd JSONL session file (see design
532
+ // §5.1 and the §11.2 cwd-encoding spike check).
533
+ const sessionFile = path.join(os.homedir(), '.claude', 'projects', encodeCwd(process.cwd()), `${sessionId}.jsonl`);
534
+ const isResume = fs.existsSync(sessionFile);
535
+ // #536 — per-turn system-prompt injection. The shared template
536
+ // (also used by copilot via `sessionConfig.systemMessage`) tells
537
+ // the model to use MCP tools — including `cue` — to reply. Without
538
+ // this the model produced English-prose responses to stdout that
539
+ // the adapter captured and discarded; no cue-back ever surfaced.
540
+ const systemPrompt = (0, prompt_1.buildSdkSystemPrompt)({ ensemble: config.ensemble });
541
+ const args = (0, prompt_1.buildClaudeArgs)({
542
+ sessionId,
543
+ isResume,
544
+ mcpConfig,
545
+ systemPrompt,
546
+ promptText,
547
+ permissionMode: this.permissionMode,
548
+ dangerouslySkipPermissions: this.dangerouslySkipPermissions,
549
+ });
550
+ // Env hygiene per design §3.6. ANTHROPIC_API_KEY would defeat the
551
+ // adapter's whole point (subscription billing); CLAUDE_CODE_OAUTH_TOKEN
552
+ // would force long-lived OAuth instead of the host's keychain. Strip
553
+ // both. Also strip AGENT_TEMPO_* (adapter-internal — the MCP server
554
+ // child gets its own env block via --mcp-config).
555
+ const childEnv = { ...process.env };
556
+ delete childEnv.ANTHROPIC_API_KEY;
557
+ delete childEnv.CLAUDE_CODE_OAUTH_TOKEN;
558
+ for (const k of Object.keys(childEnv)) {
559
+ if (k.startsWith('AGENT_TEMPO_'))
560
+ delete childEnv[k];
561
+ }
562
+ // Windows: per architect's PR-3 reminder + the established pattern
563
+ // from spawnInTerminal / spawnOpenCodeAdapter, npm-installed binaries
564
+ // land as `.cmd` shims that Node's `CreateProcess` won't run directly
565
+ // and `shell: true` trips DEP0190. Wrap via `cmd.exe /c claude ...`
566
+ // explicitly. Non-Windows hosts spawn `claude` directly.
567
+ const isWindows = process.platform === 'win32';
568
+ const spawnCmd = isWindows ? 'cmd.exe' : 'claude';
569
+ const spawnArgs = isWindows ? ['/c', 'claude', ...args] : args;
570
+ log(`spawning claude -p (sessionId=${sessionId}, resume=${isResume}, permissionMode=${this.dangerouslySkipPermissions ? 'dangerously-skip-permissions' : this.permissionMode}, batch=${messages.length})`);
571
+ const child = (0, child_process_1.spawn)(spawnCmd, spawnArgs, {
572
+ stdio: ['ignore', 'pipe', 'pipe'],
573
+ env: childEnv,
574
+ // Don't inherit a controlling TTY — adapter runs headless.
575
+ detached: false,
576
+ });
577
+ this.childProcess = child;
578
+ const reader = new stream_json_1.StreamJsonReader({
579
+ onParseError: (line, err) => {
580
+ log(`malformed stream-json frame skipped: ${err.message} — first 120 bytes: ${line.slice(0, 120)}`);
581
+ },
582
+ });
583
+ child.stdout.on('data', (chunk) => reader.feed(chunk));
584
+ const stderrChunks = [];
585
+ let stderrBytes = 0;
586
+ const STDERR_CAP = 4096;
587
+ child.stderr.on('data', (chunk) => {
588
+ const s = chunk.toString('utf8');
589
+ if (stderrBytes < STDERR_CAP) {
590
+ stderrChunks.push(s);
591
+ stderrBytes += s.length;
592
+ }
593
+ });
594
+ // Wait for subprocess exit OR per-turn timeout. The timeout SIGTERMs
595
+ // the child via `onSuperseded`'s machinery (childProcess pointer is
596
+ // set above) — same path the lease-loss abort uses.
597
+ let timedOut = false;
598
+ let timer = null;
599
+ const exitCode = await new Promise((resolve) => {
600
+ timer = setTimeout(() => {
601
+ timedOut = true;
602
+ log(`turn timeout (${timeoutMs}ms) — SIGTERMing claude subprocess`);
603
+ try {
604
+ child.kill('SIGTERM');
605
+ }
606
+ catch (err) {
607
+ log('SIGTERM threw:', err?.message ?? err);
608
+ }
609
+ }, timeoutMs);
610
+ child.on('exit', (code) => resolve(code));
611
+ });
612
+ if (timer)
613
+ clearTimeout(timer);
614
+ // Flush any trailing stdout line that arrived without a newline.
615
+ reader.flush();
616
+ this.childProcess = null;
617
+ const turn = reader.snapshot();
618
+ const stderr = stderrChunks.join('');
619
+ // Telemetry — log `apiKeySource: 'none'` from init so operators can
620
+ // confirm OAuth subscription billing is in effect. Architect note
621
+ // from spike comment.
622
+ if (turn.initApiKeySource !== null) {
623
+ log(`init apiKeySource=${turn.initApiKeySource} model=${turn.initModel ?? 'unknown'} (apiKeySource='none' confirms OAuth subscription billing)`);
624
+ }
625
+ if (turn.pluginErrors.length > 0) {
626
+ log(`WARNING: plugin_errors observed: ${JSON.stringify(turn.pluginErrors)}`);
627
+ }
628
+ // Surface informational rate-limit transitions for ops visibility.
629
+ // Architect Constraint #1: status='allowed' is informational — log
630
+ // only the action-required transitions to keep the log signal clean.
631
+ for (const evt of turn.rateLimitEvents) {
632
+ const status = evt.rate_limit_info?.status;
633
+ const overage = evt.rate_limit_info?.overageStatus;
634
+ if (status === 'blocked') {
635
+ log(`WARNING: rate_limit_event status=${status} overageStatus=${overage} — see error-mapper for classifier dispatch`);
636
+ }
637
+ }
638
+ // Per-turn telemetry log (design §5.6). `total_cost_usd` is reported
639
+ // even on subscription billing — it reflects equivalent API cost,
640
+ // not actual subscription burn. Operators should know this.
641
+ log(`turn-usage adapter=claude-code-headless model=${turn.initModel ?? 'unknown'} ` +
642
+ `input=${turn.usage?.['input_tokens'] ?? 0} ` +
643
+ `output=${turn.usage?.['output_tokens'] ?? 0} ` +
644
+ `cache_read=${turn.usage?.['cache_read_input_tokens'] ?? 0} ` +
645
+ `cache_create=${turn.usage?.['cache_creation_input_tokens'] ?? 0} ` +
646
+ `cost_usd=${turn.totalCostUsd ?? 0} ` +
647
+ `elapsed_ms=${Date.now() - t0} ` +
648
+ `player=${this.playerName} ` +
649
+ `stop_reason=${turn.stopReason ?? 'none'}`);
650
+ // ── Success path ──
651
+ if (turn.resultFrameSeen &&
652
+ turn.resultIsError === false &&
653
+ exitCode === 0) {
654
+ return {
655
+ sdkResult: {
656
+ assistantText: turn.assembledText,
657
+ stopReason: turn.stopReason,
658
+ usage: turn.usage,
659
+ totalCostUsd: turn.totalCostUsd,
660
+ },
661
+ elapsedMs: Date.now() - t0,
662
+ };
663
+ }
664
+ // ── Failure path ── classify and surface a useful error.
665
+ const ctx = { exitCode: timedOut ? null : exitCode, stderr, turn };
666
+ const category = (0, error_mapper_1.mapSubprocessFailure)(ctx);
667
+ const description = (0, error_mapper_1.describeFailure)(ctx);
668
+ log(`classifier=${category}: ${description}`);
669
+ // Throwing here surfaces to `SdkAttachment.deliver()`'s try/finally
670
+ // so `processingEnd` still fires and the message stays PENDING (no
671
+ // markDelivered on failure). The classifier category is logged for
672
+ // future PR-3 follow-up: when #521's shared classifier lands, the
673
+ // adapter's retry-budget logic will read this category and escalate
674
+ // to detach after N=10 consecutive `retriable-with-backoff` failures.
675
+ throw new Error(`claude -p ${category}: ${description}`);
676
+ }
677
+ }
678
+ exports.ClaudeCodeHeadlessAttachment = ClaudeCodeHeadlessAttachment;
679
+ /**
680
+ * Encode a cwd into Claude Code's per-cwd project-dir naming scheme.
681
+ * Confirmed empirically in the §11.2 spike check (issue #520) — every
682
+ * `:`, `/`, and `\` becomes `-`. Drive prefixes like `C:\` produce a
683
+ * double-dash (`C--`).
684
+ *
685
+ * Pinned by `tests/adapters/claude-code-headless/cwd-encoding.test.ts`
686
+ * against the captured fixture so a future Claude Code minor bump that
687
+ * changes the scheme breaks loudly here, not silently in resume.
688
+ *
689
+ * Exported for tests only — production callers go through the on-disk
690
+ * sessionFile-exists check in `invokeSdkWithBatch`.
691
+ */
692
+ function encodeCwd(cwd) {
693
+ return cwd.replace(/[\/\\:]/g, '-');
694
+ }
695
+ // Self-exec entry point — same pattern as claude-api / opencode. When this
696
+ // file is launched as `node dist/adapters/claude-code-headless/adapter.js`
697
+ // (per the spawn helper in src/spawn.ts), boot the adapter. When imported
698
+ // by the registry during normal MCP-server startup, no-op.
699
+ if (require.main === module) {
700
+ const opts = {};
701
+ const pmode = process.env[config_1.ENV.PERMISSION_MODE];
702
+ if (pmode)
703
+ opts.permissionMode = pmode;
704
+ if (process.env[config_1.ENV.DANGEROUSLY_SKIP_PERMISSIONS] === '1')
705
+ opts.dangerouslySkipPermissions = true;
706
+ new ClaudeCodeHeadlessAttachment(opts).run().catch((err) => {
707
+ log('Fatal error:', err);
708
+ process.exit(1);
709
+ });
710
+ }