@stoneforge/smithy 0.1.0

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 (497) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +114 -0
  3. package/dist/api/index.d.ts +7 -0
  4. package/dist/api/index.d.ts.map +1 -0
  5. package/dist/api/index.js +7 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/orchestrator-api.d.ts +153 -0
  8. package/dist/api/orchestrator-api.d.ts.map +1 -0
  9. package/dist/api/orchestrator-api.js +374 -0
  10. package/dist/api/orchestrator-api.js.map +1 -0
  11. package/dist/bin/sf.d.ts +3 -0
  12. package/dist/bin/sf.d.ts.map +1 -0
  13. package/dist/bin/sf.js +10 -0
  14. package/dist/bin/sf.js.map +1 -0
  15. package/dist/cli/commands/agent.d.ts +20 -0
  16. package/dist/cli/commands/agent.d.ts.map +1 -0
  17. package/dist/cli/commands/agent.js +861 -0
  18. package/dist/cli/commands/agent.js.map +1 -0
  19. package/dist/cli/commands/daemon.d.ts +14 -0
  20. package/dist/cli/commands/daemon.d.ts.map +1 -0
  21. package/dist/cli/commands/daemon.js +272 -0
  22. package/dist/cli/commands/daemon.js.map +1 -0
  23. package/dist/cli/commands/dispatch.d.ts +9 -0
  24. package/dist/cli/commands/dispatch.d.ts.map +1 -0
  25. package/dist/cli/commands/dispatch.js +128 -0
  26. package/dist/cli/commands/dispatch.js.map +1 -0
  27. package/dist/cli/commands/merge.d.ts +11 -0
  28. package/dist/cli/commands/merge.d.ts.map +1 -0
  29. package/dist/cli/commands/merge.js +246 -0
  30. package/dist/cli/commands/merge.js.map +1 -0
  31. package/dist/cli/commands/pool.d.ts +21 -0
  32. package/dist/cli/commands/pool.d.ts.map +1 -0
  33. package/dist/cli/commands/pool.js +762 -0
  34. package/dist/cli/commands/pool.js.map +1 -0
  35. package/dist/cli/commands/serve.d.ts +54 -0
  36. package/dist/cli/commands/serve.d.ts.map +1 -0
  37. package/dist/cli/commands/serve.js +57 -0
  38. package/dist/cli/commands/serve.js.map +1 -0
  39. package/dist/cli/commands/task.d.ts +36 -0
  40. package/dist/cli/commands/task.d.ts.map +1 -0
  41. package/dist/cli/commands/task.js +889 -0
  42. package/dist/cli/commands/task.js.map +1 -0
  43. package/dist/cli/commands/test-orchestration.d.ts +32 -0
  44. package/dist/cli/commands/test-orchestration.d.ts.map +1 -0
  45. package/dist/cli/commands/test-orchestration.js +392 -0
  46. package/dist/cli/commands/test-orchestration.js.map +1 -0
  47. package/dist/cli/index.d.ts +13 -0
  48. package/dist/cli/index.d.ts.map +1 -0
  49. package/dist/cli/index.js +15 -0
  50. package/dist/cli/index.js.map +1 -0
  51. package/dist/cli/plugin.d.ts +23 -0
  52. package/dist/cli/plugin.d.ts.map +1 -0
  53. package/dist/cli/plugin.js +36 -0
  54. package/dist/cli/plugin.js.map +1 -0
  55. package/dist/git/index.d.ts +10 -0
  56. package/dist/git/index.d.ts.map +1 -0
  57. package/dist/git/index.js +12 -0
  58. package/dist/git/index.js.map +1 -0
  59. package/dist/git/merge.d.ts +79 -0
  60. package/dist/git/merge.d.ts.map +1 -0
  61. package/dist/git/merge.js +254 -0
  62. package/dist/git/merge.js.map +1 -0
  63. package/dist/git/worktree-manager.d.ts +299 -0
  64. package/dist/git/worktree-manager.d.ts.map +1 -0
  65. package/dist/git/worktree-manager.js +744 -0
  66. package/dist/git/worktree-manager.js.map +1 -0
  67. package/dist/index.d.ts +24 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +31 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/prompts/director.md +272 -0
  72. package/dist/prompts/index.d.ts +100 -0
  73. package/dist/prompts/index.d.ts.map +1 -0
  74. package/dist/prompts/index.js +294 -0
  75. package/dist/prompts/index.js.map +1 -0
  76. package/dist/prompts/message-triage.md +50 -0
  77. package/dist/prompts/persistent-worker.md +240 -0
  78. package/dist/prompts/steward-base.md +64 -0
  79. package/dist/prompts/steward-docs.md +118 -0
  80. package/dist/prompts/steward-health.md +39 -0
  81. package/dist/prompts/steward-merge.md +168 -0
  82. package/dist/prompts/steward-ops.md +28 -0
  83. package/dist/prompts/steward-reminder.md +26 -0
  84. package/dist/prompts/worker.md +282 -0
  85. package/dist/providers/claude/headless.d.ts +18 -0
  86. package/dist/providers/claude/headless.d.ts.map +1 -0
  87. package/dist/providers/claude/headless.js +307 -0
  88. package/dist/providers/claude/headless.js.map +1 -0
  89. package/dist/providers/claude/index.d.ts +24 -0
  90. package/dist/providers/claude/index.d.ts.map +1 -0
  91. package/dist/providers/claude/index.js +80 -0
  92. package/dist/providers/claude/index.js.map +1 -0
  93. package/dist/providers/claude/interactive.d.ts +21 -0
  94. package/dist/providers/claude/interactive.d.ts.map +1 -0
  95. package/dist/providers/claude/interactive.js +142 -0
  96. package/dist/providers/claude/interactive.js.map +1 -0
  97. package/dist/providers/codex/event-mapper.d.ts +91 -0
  98. package/dist/providers/codex/event-mapper.d.ts.map +1 -0
  99. package/dist/providers/codex/event-mapper.js +299 -0
  100. package/dist/providers/codex/event-mapper.js.map +1 -0
  101. package/dist/providers/codex/headless.d.ts +20 -0
  102. package/dist/providers/codex/headless.d.ts.map +1 -0
  103. package/dist/providers/codex/headless.js +174 -0
  104. package/dist/providers/codex/headless.js.map +1 -0
  105. package/dist/providers/codex/index.d.ts +30 -0
  106. package/dist/providers/codex/index.d.ts.map +1 -0
  107. package/dist/providers/codex/index.js +55 -0
  108. package/dist/providers/codex/index.js.map +1 -0
  109. package/dist/providers/codex/interactive.d.ts +21 -0
  110. package/dist/providers/codex/interactive.d.ts.map +1 -0
  111. package/dist/providers/codex/interactive.js +141 -0
  112. package/dist/providers/codex/interactive.js.map +1 -0
  113. package/dist/providers/codex/jsonrpc-client.d.ts +52 -0
  114. package/dist/providers/codex/jsonrpc-client.d.ts.map +1 -0
  115. package/dist/providers/codex/jsonrpc-client.js +141 -0
  116. package/dist/providers/codex/jsonrpc-client.js.map +1 -0
  117. package/dist/providers/codex/server-manager.d.ts +100 -0
  118. package/dist/providers/codex/server-manager.d.ts.map +1 -0
  119. package/dist/providers/codex/server-manager.js +153 -0
  120. package/dist/providers/codex/server-manager.js.map +1 -0
  121. package/dist/providers/index.d.ts +15 -0
  122. package/dist/providers/index.d.ts.map +1 -0
  123. package/dist/providers/index.js +19 -0
  124. package/dist/providers/index.js.map +1 -0
  125. package/dist/providers/opencode/async-queue.d.ts +21 -0
  126. package/dist/providers/opencode/async-queue.d.ts.map +1 -0
  127. package/dist/providers/opencode/async-queue.js +51 -0
  128. package/dist/providers/opencode/async-queue.js.map +1 -0
  129. package/dist/providers/opencode/event-mapper.d.ts +132 -0
  130. package/dist/providers/opencode/event-mapper.d.ts.map +1 -0
  131. package/dist/providers/opencode/event-mapper.js +204 -0
  132. package/dist/providers/opencode/event-mapper.js.map +1 -0
  133. package/dist/providers/opencode/headless.d.ts +25 -0
  134. package/dist/providers/opencode/headless.d.ts.map +1 -0
  135. package/dist/providers/opencode/headless.js +190 -0
  136. package/dist/providers/opencode/headless.js.map +1 -0
  137. package/dist/providers/opencode/index.d.ts +33 -0
  138. package/dist/providers/opencode/index.d.ts.map +1 -0
  139. package/dist/providers/opencode/index.js +42 -0
  140. package/dist/providers/opencode/index.js.map +1 -0
  141. package/dist/providers/opencode/interactive.d.ts +21 -0
  142. package/dist/providers/opencode/interactive.d.ts.map +1 -0
  143. package/dist/providers/opencode/interactive.js +135 -0
  144. package/dist/providers/opencode/interactive.js.map +1 -0
  145. package/dist/providers/opencode/server-manager.d.ts +145 -0
  146. package/dist/providers/opencode/server-manager.d.ts.map +1 -0
  147. package/dist/providers/opencode/server-manager.js +163 -0
  148. package/dist/providers/opencode/server-manager.js.map +1 -0
  149. package/dist/providers/registry.d.ts +38 -0
  150. package/dist/providers/registry.d.ts.map +1 -0
  151. package/dist/providers/registry.js +82 -0
  152. package/dist/providers/registry.js.map +1 -0
  153. package/dist/providers/types.d.ts +144 -0
  154. package/dist/providers/types.d.ts.map +1 -0
  155. package/dist/providers/types.js +25 -0
  156. package/dist/providers/types.js.map +1 -0
  157. package/dist/runtime/event-utils.d.ts +8 -0
  158. package/dist/runtime/event-utils.d.ts.map +1 -0
  159. package/dist/runtime/event-utils.js +23 -0
  160. package/dist/runtime/event-utils.js.map +1 -0
  161. package/dist/runtime/handoff.d.ts +195 -0
  162. package/dist/runtime/handoff.d.ts.map +1 -0
  163. package/dist/runtime/handoff.js +332 -0
  164. package/dist/runtime/handoff.js.map +1 -0
  165. package/dist/runtime/index.d.ts +17 -0
  166. package/dist/runtime/index.d.ts.map +1 -0
  167. package/dist/runtime/index.js +60 -0
  168. package/dist/runtime/index.js.map +1 -0
  169. package/dist/runtime/message-mapper.d.ts +99 -0
  170. package/dist/runtime/message-mapper.d.ts.map +1 -0
  171. package/dist/runtime/message-mapper.js +202 -0
  172. package/dist/runtime/message-mapper.js.map +1 -0
  173. package/dist/runtime/predecessor-query.d.ts +212 -0
  174. package/dist/runtime/predecessor-query.d.ts.map +1 -0
  175. package/dist/runtime/predecessor-query.js +283 -0
  176. package/dist/runtime/predecessor-query.js.map +1 -0
  177. package/dist/runtime/session-manager.d.ts +466 -0
  178. package/dist/runtime/session-manager.d.ts.map +1 -0
  179. package/dist/runtime/session-manager.js +986 -0
  180. package/dist/runtime/session-manager.js.map +1 -0
  181. package/dist/runtime/spawner.d.ts +407 -0
  182. package/dist/runtime/spawner.d.ts.map +1 -0
  183. package/dist/runtime/spawner.js +781 -0
  184. package/dist/runtime/spawner.js.map +1 -0
  185. package/dist/server/config.d.ts +22 -0
  186. package/dist/server/config.d.ts.map +1 -0
  187. package/dist/server/config.js +59 -0
  188. package/dist/server/config.js.map +1 -0
  189. package/dist/server/daemon-state.d.ts +50 -0
  190. package/dist/server/daemon-state.d.ts.map +1 -0
  191. package/dist/server/daemon-state.js +100 -0
  192. package/dist/server/daemon-state.js.map +1 -0
  193. package/dist/server/events-websocket.d.ts +32 -0
  194. package/dist/server/events-websocket.d.ts.map +1 -0
  195. package/dist/server/events-websocket.js +96 -0
  196. package/dist/server/events-websocket.js.map +1 -0
  197. package/dist/server/formatters.d.ts +94 -0
  198. package/dist/server/formatters.d.ts.map +1 -0
  199. package/dist/server/formatters.js +142 -0
  200. package/dist/server/formatters.js.map +1 -0
  201. package/dist/server/index.d.ts +17 -0
  202. package/dist/server/index.d.ts.map +1 -0
  203. package/dist/server/index.js +153 -0
  204. package/dist/server/index.js.map +1 -0
  205. package/dist/server/lsp-websocket.d.ts +33 -0
  206. package/dist/server/lsp-websocket.d.ts.map +1 -0
  207. package/dist/server/lsp-websocket.js +161 -0
  208. package/dist/server/lsp-websocket.js.map +1 -0
  209. package/dist/server/routes/agents.d.ts +9 -0
  210. package/dist/server/routes/agents.d.ts.map +1 -0
  211. package/dist/server/routes/agents.js +369 -0
  212. package/dist/server/routes/agents.js.map +1 -0
  213. package/dist/server/routes/daemon.d.ts +13 -0
  214. package/dist/server/routes/daemon.d.ts.map +1 -0
  215. package/dist/server/routes/daemon.js +187 -0
  216. package/dist/server/routes/daemon.js.map +1 -0
  217. package/dist/server/routes/events.d.ts +23 -0
  218. package/dist/server/routes/events.d.ts.map +1 -0
  219. package/dist/server/routes/events.js +282 -0
  220. package/dist/server/routes/events.js.map +1 -0
  221. package/dist/server/routes/extensions.d.ts +9 -0
  222. package/dist/server/routes/extensions.d.ts.map +1 -0
  223. package/dist/server/routes/extensions.js +202 -0
  224. package/dist/server/routes/extensions.js.map +1 -0
  225. package/dist/server/routes/health.d.ts +7 -0
  226. package/dist/server/routes/health.d.ts.map +1 -0
  227. package/dist/server/routes/health.js +33 -0
  228. package/dist/server/routes/health.js.map +1 -0
  229. package/dist/server/routes/index.d.ts +21 -0
  230. package/dist/server/routes/index.d.ts.map +1 -0
  231. package/dist/server/routes/index.js +21 -0
  232. package/dist/server/routes/index.js.map +1 -0
  233. package/dist/server/routes/lsp.d.ts +9 -0
  234. package/dist/server/routes/lsp.d.ts.map +1 -0
  235. package/dist/server/routes/lsp.js +50 -0
  236. package/dist/server/routes/lsp.js.map +1 -0
  237. package/dist/server/routes/plugins.d.ts +9 -0
  238. package/dist/server/routes/plugins.d.ts.map +1 -0
  239. package/dist/server/routes/plugins.js +109 -0
  240. package/dist/server/routes/plugins.js.map +1 -0
  241. package/dist/server/routes/pools.d.ts +9 -0
  242. package/dist/server/routes/pools.d.ts.map +1 -0
  243. package/dist/server/routes/pools.js +189 -0
  244. package/dist/server/routes/pools.js.map +1 -0
  245. package/dist/server/routes/scheduler.d.ts +9 -0
  246. package/dist/server/routes/scheduler.d.ts.map +1 -0
  247. package/dist/server/routes/scheduler.js +162 -0
  248. package/dist/server/routes/scheduler.js.map +1 -0
  249. package/dist/server/routes/sessions.d.ts +27 -0
  250. package/dist/server/routes/sessions.d.ts.map +1 -0
  251. package/dist/server/routes/sessions.js +773 -0
  252. package/dist/server/routes/sessions.js.map +1 -0
  253. package/dist/server/routes/tasks.d.ts +9 -0
  254. package/dist/server/routes/tasks.d.ts.map +1 -0
  255. package/dist/server/routes/tasks.js +954 -0
  256. package/dist/server/routes/tasks.js.map +1 -0
  257. package/dist/server/routes/upload.d.ts +8 -0
  258. package/dist/server/routes/upload.d.ts.map +1 -0
  259. package/dist/server/routes/upload.js +40 -0
  260. package/dist/server/routes/upload.js.map +1 -0
  261. package/dist/server/routes/workflows.d.ts +9 -0
  262. package/dist/server/routes/workflows.d.ts.map +1 -0
  263. package/dist/server/routes/workflows.js +532 -0
  264. package/dist/server/routes/workflows.js.map +1 -0
  265. package/dist/server/routes/workspace-files.d.ts +12 -0
  266. package/dist/server/routes/workspace-files.d.ts.map +1 -0
  267. package/dist/server/routes/workspace-files.js +520 -0
  268. package/dist/server/routes/workspace-files.js.map +1 -0
  269. package/dist/server/routes/worktrees.d.ts +9 -0
  270. package/dist/server/routes/worktrees.d.ts.map +1 -0
  271. package/dist/server/routes/worktrees.js +94 -0
  272. package/dist/server/routes/worktrees.js.map +1 -0
  273. package/dist/server/server.d.ts +14 -0
  274. package/dist/server/server.d.ts.map +1 -0
  275. package/dist/server/server.js +258 -0
  276. package/dist/server/server.js.map +1 -0
  277. package/dist/server/services/lsp-manager.d.ts +93 -0
  278. package/dist/server/services/lsp-manager.d.ts.map +1 -0
  279. package/dist/server/services/lsp-manager.js +291 -0
  280. package/dist/server/services/lsp-manager.js.map +1 -0
  281. package/dist/server/services/session-messages.d.ts +61 -0
  282. package/dist/server/services/session-messages.d.ts.map +1 -0
  283. package/dist/server/services/session-messages.js +101 -0
  284. package/dist/server/services/session-messages.js.map +1 -0
  285. package/dist/server/services.d.ts +35 -0
  286. package/dist/server/services.d.ts.map +1 -0
  287. package/dist/server/services.js +159 -0
  288. package/dist/server/services.js.map +1 -0
  289. package/dist/server/static.d.ts +18 -0
  290. package/dist/server/static.d.ts.map +1 -0
  291. package/dist/server/static.js +71 -0
  292. package/dist/server/static.js.map +1 -0
  293. package/dist/server/types.d.ts +20 -0
  294. package/dist/server/types.d.ts.map +1 -0
  295. package/dist/server/types.js +7 -0
  296. package/dist/server/types.js.map +1 -0
  297. package/dist/server/websocket.d.ts +16 -0
  298. package/dist/server/websocket.d.ts.map +1 -0
  299. package/dist/server/websocket.js +143 -0
  300. package/dist/server/websocket.js.map +1 -0
  301. package/dist/services/agent-pool-service.d.ts +181 -0
  302. package/dist/services/agent-pool-service.d.ts.map +1 -0
  303. package/dist/services/agent-pool-service.js +590 -0
  304. package/dist/services/agent-pool-service.js.map +1 -0
  305. package/dist/services/agent-registry.d.ts +185 -0
  306. package/dist/services/agent-registry.d.ts.map +1 -0
  307. package/dist/services/agent-registry.js +432 -0
  308. package/dist/services/agent-registry.js.map +1 -0
  309. package/dist/services/dispatch-daemon.d.ts +429 -0
  310. package/dist/services/dispatch-daemon.d.ts.map +1 -0
  311. package/dist/services/dispatch-daemon.js +1833 -0
  312. package/dist/services/dispatch-daemon.js.map +1 -0
  313. package/dist/services/dispatch-service.d.ts +148 -0
  314. package/dist/services/dispatch-service.d.ts.map +1 -0
  315. package/dist/services/dispatch-service.js +170 -0
  316. package/dist/services/dispatch-service.js.map +1 -0
  317. package/dist/services/docs-steward-service.d.ts +199 -0
  318. package/dist/services/docs-steward-service.d.ts.map +1 -0
  319. package/dist/services/docs-steward-service.js +599 -0
  320. package/dist/services/docs-steward-service.js.map +1 -0
  321. package/dist/services/health-steward-service.d.ts +446 -0
  322. package/dist/services/health-steward-service.d.ts.map +1 -0
  323. package/dist/services/health-steward-service.js +866 -0
  324. package/dist/services/health-steward-service.js.map +1 -0
  325. package/dist/services/index.d.ts +26 -0
  326. package/dist/services/index.d.ts.map +1 -0
  327. package/dist/services/index.js +111 -0
  328. package/dist/services/index.js.map +1 -0
  329. package/dist/services/merge-request-provider.d.ts +59 -0
  330. package/dist/services/merge-request-provider.d.ts.map +1 -0
  331. package/dist/services/merge-request-provider.js +89 -0
  332. package/dist/services/merge-request-provider.js.map +1 -0
  333. package/dist/services/merge-steward-service.d.ts +268 -0
  334. package/dist/services/merge-steward-service.d.ts.map +1 -0
  335. package/dist/services/merge-steward-service.js +568 -0
  336. package/dist/services/merge-steward-service.js.map +1 -0
  337. package/dist/services/plugin-executor.d.ts +247 -0
  338. package/dist/services/plugin-executor.d.ts.map +1 -0
  339. package/dist/services/plugin-executor.js +451 -0
  340. package/dist/services/plugin-executor.js.map +1 -0
  341. package/dist/services/role-definition-service.d.ts +117 -0
  342. package/dist/services/role-definition-service.d.ts.map +1 -0
  343. package/dist/services/role-definition-service.js +289 -0
  344. package/dist/services/role-definition-service.js.map +1 -0
  345. package/dist/services/steward-scheduler.d.ts +336 -0
  346. package/dist/services/steward-scheduler.d.ts.map +1 -0
  347. package/dist/services/steward-scheduler.js +732 -0
  348. package/dist/services/steward-scheduler.js.map +1 -0
  349. package/dist/services/task-assignment-service.d.ts +291 -0
  350. package/dist/services/task-assignment-service.d.ts.map +1 -0
  351. package/dist/services/task-assignment-service.js +454 -0
  352. package/dist/services/task-assignment-service.js.map +1 -0
  353. package/dist/services/worker-task-service.d.ts +202 -0
  354. package/dist/services/worker-task-service.d.ts.map +1 -0
  355. package/dist/services/worker-task-service.js +228 -0
  356. package/dist/services/worker-task-service.js.map +1 -0
  357. package/dist/testing/index.d.ts +13 -0
  358. package/dist/testing/index.d.ts.map +1 -0
  359. package/dist/testing/index.js +17 -0
  360. package/dist/testing/index.js.map +1 -0
  361. package/dist/testing/orchestration-tests.d.ts +62 -0
  362. package/dist/testing/orchestration-tests.d.ts.map +1 -0
  363. package/dist/testing/orchestration-tests.js +1115 -0
  364. package/dist/testing/orchestration-tests.js.map +1 -0
  365. package/dist/testing/test-context.d.ts +171 -0
  366. package/dist/testing/test-context.d.ts.map +1 -0
  367. package/dist/testing/test-context.js +665 -0
  368. package/dist/testing/test-context.js.map +1 -0
  369. package/dist/testing/test-prompts.d.ts +46 -0
  370. package/dist/testing/test-prompts.d.ts.map +1 -0
  371. package/dist/testing/test-prompts.js +140 -0
  372. package/dist/testing/test-prompts.js.map +1 -0
  373. package/dist/testing/test-utils.d.ts +200 -0
  374. package/dist/testing/test-utils.d.ts.map +1 -0
  375. package/dist/testing/test-utils.js +378 -0
  376. package/dist/testing/test-utils.js.map +1 -0
  377. package/dist/types/agent-pool.d.ts +215 -0
  378. package/dist/types/agent-pool.d.ts.map +1 -0
  379. package/dist/types/agent-pool.js +143 -0
  380. package/dist/types/agent-pool.js.map +1 -0
  381. package/dist/types/agent.d.ts +265 -0
  382. package/dist/types/agent.d.ts.map +1 -0
  383. package/dist/types/agent.js +127 -0
  384. package/dist/types/agent.js.map +1 -0
  385. package/dist/types/index.d.ts +11 -0
  386. package/dist/types/index.d.ts.map +1 -0
  387. package/dist/types/index.js +40 -0
  388. package/dist/types/index.js.map +1 -0
  389. package/dist/types/message-types.d.ts +294 -0
  390. package/dist/types/message-types.d.ts.map +1 -0
  391. package/dist/types/message-types.js +354 -0
  392. package/dist/types/message-types.js.map +1 -0
  393. package/dist/types/role-definition.d.ts +272 -0
  394. package/dist/types/role-definition.d.ts.map +1 -0
  395. package/dist/types/role-definition.js +144 -0
  396. package/dist/types/role-definition.js.map +1 -0
  397. package/dist/types/task-meta.d.ts +248 -0
  398. package/dist/types/task-meta.d.ts.map +1 -0
  399. package/dist/types/task-meta.js +213 -0
  400. package/dist/types/task-meta.js.map +1 -0
  401. package/package.json +120 -0
  402. package/web/assets/abap-BrgZPUOV.js +6 -0
  403. package/web/assets/apex-DyP6w7ZV.js +6 -0
  404. package/web/assets/azcli-BaLxmfj-.js +6 -0
  405. package/web/assets/bat-CFOPXBzS.js +6 -0
  406. package/web/assets/bicep-BfEKNvv3.js +7 -0
  407. package/web/assets/cameligo-BFG1Mk7z.js +6 -0
  408. package/web/assets/clojure-DTECt2xU.js +6 -0
  409. package/web/assets/codicon-DCmgc-ay.ttf +0 -0
  410. package/web/assets/coffee-CDGzqUPQ.js +6 -0
  411. package/web/assets/cpp-CLLBncYj.js +6 -0
  412. package/web/assets/csharp-dUCx_-0o.js +6 -0
  413. package/web/assets/csp-5Rap-vPy.js +6 -0
  414. package/web/assets/css-D3h14YRZ.js +8 -0
  415. package/web/assets/cssMode-DMo-5YLA.js +9 -0
  416. package/web/assets/cypher-DrQuvNYM.js +6 -0
  417. package/web/assets/dart-CFKIUWau.js +6 -0
  418. package/web/assets/dockerfile-Zznr-cwX.js +6 -0
  419. package/web/assets/ecl-Ce3n6wWz.js +6 -0
  420. package/web/assets/elixir-deUWdS0T.js +6 -0
  421. package/web/assets/flow9-i9-g7ZhI.js +6 -0
  422. package/web/assets/freemarker2-D4qgkQzN.js +8 -0
  423. package/web/assets/fsharp-CzKuDChf.js +6 -0
  424. package/web/assets/go-Cphgjts3.js +6 -0
  425. package/web/assets/graphql-Cg7bfA9N.js +6 -0
  426. package/web/assets/handlebars-CXFvNjQC.js +6 -0
  427. package/web/assets/hcl-0cvrggvQ.js +6 -0
  428. package/web/assets/html-oyuB_D-B.js +6 -0
  429. package/web/assets/htmlMode-iWuZ24-r.js +9 -0
  430. package/web/assets/index-DqP-_E4F.css +32 -0
  431. package/web/assets/index-R1cylSgw.js +1665 -0
  432. package/web/assets/ini-Drc7WvVn.js +6 -0
  433. package/web/assets/java-B_fMsGYe.js +6 -0
  434. package/web/assets/javascript-CRIkN2Pg.js +6 -0
  435. package/web/assets/jsonMode-DVDkDgex.js +15 -0
  436. package/web/assets/julia-Bqgm2twL.js +6 -0
  437. package/web/assets/kotlin-BSkB5QuD.js +6 -0
  438. package/web/assets/less-BsTHnhdd.js +7 -0
  439. package/web/assets/lexon-YWi4-JPR.js +6 -0
  440. package/web/assets/liquid-CSfldbB5.js +6 -0
  441. package/web/assets/lua-nf6ki56Z.js +6 -0
  442. package/web/assets/m3-Cpb6xl2v.js +6 -0
  443. package/web/assets/markdown-DSZPf7rp.js +6 -0
  444. package/web/assets/mdx-Dd58iymR.js +6 -0
  445. package/web/assets/mips-B_c3zf-v.js +6 -0
  446. package/web/assets/monaco-editor-B4lwqA13.js +751 -0
  447. package/web/assets/monaco-editor-CQpyCxOA.css +1 -0
  448. package/web/assets/msdax-rUNN04Wq.js +6 -0
  449. package/web/assets/mysql-DDwshQtU.js +6 -0
  450. package/web/assets/objective-c-B5zXfXm9.js +6 -0
  451. package/web/assets/pascal-CXOwvkN_.js +6 -0
  452. package/web/assets/pascaligo-Bc-ZgV77.js +6 -0
  453. package/web/assets/perl-CwNk8-XU.js +6 -0
  454. package/web/assets/pgsql-tGk8EFnU.js +6 -0
  455. package/web/assets/php-CpIb_Oan.js +6 -0
  456. package/web/assets/pla-B03wrqEc.js +6 -0
  457. package/web/assets/postiats-BKlk5iyT.js +6 -0
  458. package/web/assets/powerquery-Bhzvs7bI.js +6 -0
  459. package/web/assets/powershell-Dd3NCNK9.js +6 -0
  460. package/web/assets/protobuf-COyEY5Pt.js +7 -0
  461. package/web/assets/pug-BaJupSGV.js +6 -0
  462. package/web/assets/python-XWrMqdhO.js +6 -0
  463. package/web/assets/qsharp-DXyYeYxl.js +6 -0
  464. package/web/assets/r-CdQndTaG.js +6 -0
  465. package/web/assets/razor-DPlhCpIs.js +6 -0
  466. package/web/assets/redis-CVwtpugi.js +6 -0
  467. package/web/assets/redshift-25W9uPmb.js +6 -0
  468. package/web/assets/restructuredtext-DfzH4Xui.js +6 -0
  469. package/web/assets/router-vendor-DHlGizSU.js +41 -0
  470. package/web/assets/ruby-Cp1zYvxS.js +6 -0
  471. package/web/assets/rust-D5C2fndG.js +6 -0
  472. package/web/assets/sb-CDntyWJ8.js +6 -0
  473. package/web/assets/scala-BoFRg7Ot.js +6 -0
  474. package/web/assets/scheme-Bio4gycK.js +6 -0
  475. package/web/assets/scss-4Ik7cdeQ.js +8 -0
  476. package/web/assets/shell-CX-rkNHf.js +6 -0
  477. package/web/assets/solidity-Tw7wswEv.js +6 -0
  478. package/web/assets/sophia-C5WLch3f.js +6 -0
  479. package/web/assets/sparql-DHaeiCBh.js +6 -0
  480. package/web/assets/sql-CCSDG5nI.js +6 -0
  481. package/web/assets/st-pnP8ivHi.js +6 -0
  482. package/web/assets/swift-DwJ7jVG9.js +8 -0
  483. package/web/assets/systemverilog-B9Xyijhd.js +6 -0
  484. package/web/assets/tcl-DnHyzjbg.js +6 -0
  485. package/web/assets/tsMode-BbA1Jbf3.js +16 -0
  486. package/web/assets/twig-CPajHgWi.js +6 -0
  487. package/web/assets/typescript-DcLHYzvH.js +6 -0
  488. package/web/assets/typespec-D-MeaMDU.js +6 -0
  489. package/web/assets/ui-vendor-BSco96uv.js +51 -0
  490. package/web/assets/utils-vendor-DaJ2Dubl.js +911 -0
  491. package/web/assets/vb-DgyLZaXg.js +6 -0
  492. package/web/assets/wgsl-DYQUnd45.js +303 -0
  493. package/web/assets/xml-xKYS3dO6.js +6 -0
  494. package/web/assets/yaml-CNmlXqzH.js +6 -0
  495. package/web/favicon.ico +0 -0
  496. package/web/index.html +22 -0
  497. package/web/logo.png +0 -0
@@ -0,0 +1,781 @@
1
+ /**
2
+ * Spawner Service
3
+ *
4
+ * This service manages spawning and lifecycle of agent processes
5
+ * for AI agents in the orchestration system.
6
+ *
7
+ * Key features:
8
+ * - Spawn headless agents (ephemeral workers, stewards) via provider abstraction
9
+ * - Spawn interactive agents (Director, persistent workers) via provider abstraction
10
+ * - Resume existing sessions with provider session IDs
11
+ * - Parse stream-json events (assistant, tool_use, tool_result, error)
12
+ * - Track session metadata for cross-restart resumption
13
+ * - Provider-agnostic: supports Claude Code, OpenCode, and future providers
14
+ *
15
+ * @module
16
+ */
17
+ import { EventEmitter } from 'node:events';
18
+ import { createTimestamp } from '@stoneforge/core';
19
+ import { ClaudeAgentProvider } from '../providers/claude/index.js';
20
+ /**
21
+ * Shell-quotes a string for safe inclusion in a bash command.
22
+ * Wraps in single quotes and escapes internal single quotes.
23
+ */
24
+ export function shellQuote(s) {
25
+ return "'" + s.replace(/'/g, "'\\''") + "'";
26
+ }
27
+ /**
28
+ * Session state machine transitions
29
+ */
30
+ export const SessionStatusTransitions = {
31
+ starting: ['running', 'terminated'],
32
+ running: ['suspended', 'terminating', 'terminated'],
33
+ suspended: ['running', 'terminated'],
34
+ terminating: ['terminated'],
35
+ terminated: [],
36
+ };
37
+ /**
38
+ * Builds the CLI arguments for headless (non-interactive) Claude Code spawning.
39
+ *
40
+ * This function is exported to allow unit testing of the argument construction,
41
+ * which is critical for ensuring Claude Code is invoked correctly.
42
+ *
43
+ * @param options - Options affecting argument construction
44
+ * @returns Array of CLI arguments
45
+ */
46
+ export function buildHeadlessArgs(options) {
47
+ const args = [
48
+ '-p', // Print mode (non-interactive)
49
+ '--verbose', // Required for stream-json output in print mode
50
+ '--dangerously-skip-permissions',
51
+ '--output-format', 'stream-json',
52
+ '--input-format', 'stream-json',
53
+ ];
54
+ if (options?.resumeSessionId) {
55
+ args.push('--resume', options.resumeSessionId);
56
+ }
57
+ // Note: initialPrompt is NOT added as a CLI argument when using --input-format stream-json.
58
+ // Instead, it must be sent via stdin in JSON format after the process starts.
59
+ // This is handled in spawnHeadless().
60
+ return args;
61
+ }
62
+ // ============================================================================
63
+ // Spawner Service Implementation
64
+ // ============================================================================
65
+ /**
66
+ * Implementation of the Spawner Service.
67
+ * Uses the provider abstraction to spawn and manage agent processes.
68
+ */
69
+ export class SpawnerServiceImpl {
70
+ sessions = new Map();
71
+ defaultConfig;
72
+ provider;
73
+ sessionCounter = 0;
74
+ constructor(config) {
75
+ this.defaultConfig = {
76
+ claudePath: config?.claudePath ?? 'claude',
77
+ workingDirectory: config?.workingDirectory ?? process.cwd(),
78
+ timeout: config?.timeout ?? 120000, // 2 minutes default
79
+ stoneforgeRoot: config?.stoneforgeRoot,
80
+ environmentVariables: config?.environmentVariables,
81
+ };
82
+ // Use injected provider or default to Claude
83
+ this.provider = config?.provider ?? new ClaudeAgentProvider(config?.claudePath ?? 'claude');
84
+ }
85
+ // ----------------------------------------
86
+ // Session Lifecycle
87
+ // ----------------------------------------
88
+ async spawn(agentId, agentRole, options) {
89
+ const mode = this.determineSpawnMode(agentRole, options?.mode);
90
+ const now = createTimestamp();
91
+ const sessionId = this.generateSessionId();
92
+ // Create session record
93
+ const session = {
94
+ id: sessionId,
95
+ agentId,
96
+ agentRole,
97
+ workerMode: this.getWorkerMode(agentRole, options?.mode),
98
+ mode,
99
+ status: 'starting',
100
+ workingDirectory: options?.workingDirectory ?? this.defaultConfig.workingDirectory,
101
+ createdAt: now,
102
+ lastActivityAt: now,
103
+ events: new EventEmitter(),
104
+ jsonBuffer: '',
105
+ };
106
+ this.sessions.set(sessionId, session);
107
+ try {
108
+ if (mode === 'headless') {
109
+ await this.spawnHeadless(session, options);
110
+ }
111
+ else {
112
+ await this.spawnInteractive(session, options);
113
+ }
114
+ return {
115
+ session: this.toPublicSession(session),
116
+ events: session.events,
117
+ };
118
+ }
119
+ catch (error) {
120
+ // Clean up on failure
121
+ session.status = 'terminated';
122
+ session.endedAt = createTimestamp();
123
+ throw error;
124
+ }
125
+ }
126
+ async terminate(sessionId, graceful = true) {
127
+ const session = this.sessions.get(sessionId);
128
+ if (!session) {
129
+ throw new Error(`Session not found: ${sessionId}`);
130
+ }
131
+ if (session.status === 'terminated' || session.status === 'terminating') {
132
+ return;
133
+ }
134
+ this.transitionStatus(session, 'terminating');
135
+ // Handle provider interactive sessions
136
+ if (session.interactiveSession) {
137
+ if (graceful) {
138
+ session.interactiveSession.write('exit\r');
139
+ await new Promise((resolve) => {
140
+ const timeout = setTimeout(() => {
141
+ clearInterval(checkInterval);
142
+ if (session.interactiveSession) {
143
+ session.interactiveSession.kill();
144
+ }
145
+ resolve();
146
+ }, 5000);
147
+ const checkInterval = setInterval(() => {
148
+ if (session.status === 'terminated') {
149
+ clearInterval(checkInterval);
150
+ clearTimeout(timeout);
151
+ resolve();
152
+ }
153
+ }, 100);
154
+ });
155
+ }
156
+ else {
157
+ session.interactiveSession.kill();
158
+ }
159
+ }
160
+ // Handle provider headless sessions
161
+ if (session.headlessSession) {
162
+ session.headlessSession.close();
163
+ }
164
+ // Handle headless process sessions (legacy)
165
+ if (session.process) {
166
+ if (graceful) {
167
+ session.process.kill('SIGTERM');
168
+ await new Promise((resolve) => {
169
+ const timeout = setTimeout(() => {
170
+ if (session.process && !session.process.killed) {
171
+ session.process.kill('SIGKILL');
172
+ }
173
+ resolve();
174
+ }, 5000);
175
+ session.process?.once('exit', () => {
176
+ clearTimeout(timeout);
177
+ resolve();
178
+ });
179
+ });
180
+ }
181
+ else {
182
+ session.process.kill('SIGKILL');
183
+ }
184
+ }
185
+ // Only transition if not already terminated
186
+ if (session.status !== 'terminated') {
187
+ this.transitionStatus(session, 'terminated');
188
+ session.endedAt = createTimestamp();
189
+ }
190
+ this.scheduleTerminatedSessionCleanup(sessionId);
191
+ }
192
+ async suspend(sessionId) {
193
+ const session = this.sessions.get(sessionId);
194
+ if (!session) {
195
+ throw new Error(`Session not found: ${sessionId}`);
196
+ }
197
+ if (session.status !== 'running') {
198
+ throw new Error(`Cannot suspend session in status: ${session.status}`);
199
+ }
200
+ // For headless, close the session but mark as suspended
201
+ // The providerSessionId can be used to resume later
202
+ if (session.headlessSession) {
203
+ session.headlessSession.close();
204
+ }
205
+ if (session.process) {
206
+ session.process.kill('SIGTERM');
207
+ }
208
+ // For interactive, kill the PTY but mark as suspended
209
+ if (session.interactiveSession) {
210
+ session.interactiveSession.kill();
211
+ }
212
+ this.transitionStatus(session, 'suspended');
213
+ session.lastActivityAt = createTimestamp();
214
+ }
215
+ async interrupt(sessionId) {
216
+ const session = this.sessions.get(sessionId);
217
+ if (!session) {
218
+ throw new Error(`Session not found: ${sessionId}`);
219
+ }
220
+ if (session.status !== 'running') {
221
+ throw new Error(`Cannot interrupt session in status: ${session.status}`);
222
+ }
223
+ // For provider headless sessions, use interrupt
224
+ if (session.headlessSession) {
225
+ await session.headlessSession.interrupt();
226
+ }
227
+ // For legacy process-based headless sessions, send SIGINT
228
+ else if (session.process) {
229
+ session.process.kill('SIGINT');
230
+ }
231
+ // For interactive PTY sessions, send Escape key
232
+ if (session.interactiveSession) {
233
+ session.interactiveSession.write('\x1b');
234
+ }
235
+ session.lastActivityAt = createTimestamp();
236
+ }
237
+ // ----------------------------------------
238
+ // Session Queries
239
+ // ----------------------------------------
240
+ getSession(sessionId) {
241
+ const session = this.sessions.get(sessionId);
242
+ return session ? this.toPublicSession(session) : undefined;
243
+ }
244
+ listActiveSessions(agentId) {
245
+ const sessions = Array.from(this.sessions.values())
246
+ .filter((s) => s.status !== 'terminated');
247
+ return this.filterAndMapSessions(sessions, agentId);
248
+ }
249
+ listAllSessions(agentId) {
250
+ const sessions = Array.from(this.sessions.values());
251
+ return this.filterAndMapSessions(sessions, agentId);
252
+ }
253
+ getMostRecentSession(agentId) {
254
+ const agentSessions = Array.from(this.sessions.values())
255
+ .filter((s) => s.agentId === agentId)
256
+ .sort((a, b) => {
257
+ const aTime = typeof a.createdAt === 'number' ? a.createdAt : new Date(a.createdAt).getTime();
258
+ const bTime = typeof b.createdAt === 'number' ? b.createdAt : new Date(b.createdAt).getTime();
259
+ return bTime - aTime;
260
+ });
261
+ return agentSessions.length > 0 ? this.toPublicSession(agentSessions[0]) : undefined;
262
+ }
263
+ // ----------------------------------------
264
+ // Headless Agent Communication
265
+ // ----------------------------------------
266
+ async sendInput(sessionId, input, _options) {
267
+ const session = this.sessions.get(sessionId);
268
+ if (!session) {
269
+ throw new Error(`Session not found: ${sessionId}`);
270
+ }
271
+ if (session.mode !== 'headless') {
272
+ throw new Error('sendInput is only supported for headless sessions');
273
+ }
274
+ if (session.status !== 'running') {
275
+ throw new Error(`Cannot send input to session in status: ${session.status}`);
276
+ }
277
+ // For provider headless sessions, use sendMessage
278
+ if (session.headlessSession) {
279
+ session.headlessSession.sendMessage(input);
280
+ session.lastActivityAt = createTimestamp();
281
+ return;
282
+ }
283
+ // For legacy process-based sessions, write to stdin
284
+ if (!session.process?.stdin?.writable) {
285
+ throw new Error('Session stdin is not writable');
286
+ }
287
+ const message = {
288
+ type: 'user',
289
+ message: {
290
+ role: 'user',
291
+ content: input,
292
+ },
293
+ };
294
+ session.process.stdin.write(JSON.stringify(message) + '\n');
295
+ session.lastActivityAt = createTimestamp();
296
+ }
297
+ // ----------------------------------------
298
+ // Interactive PTY Communication
299
+ // ----------------------------------------
300
+ async writeToPty(sessionId, data) {
301
+ const session = this.sessions.get(sessionId);
302
+ if (!session) {
303
+ throw new Error(`Session not found: ${sessionId}`);
304
+ }
305
+ if (session.mode !== 'interactive') {
306
+ throw new Error('writeToPty is only supported for interactive sessions');
307
+ }
308
+ if (session.status !== 'running') {
309
+ throw new Error(`Cannot write to PTY in status: ${session.status}`);
310
+ }
311
+ if (!session.interactiveSession) {
312
+ throw new Error('Session PTY is not available');
313
+ }
314
+ session.interactiveSession.write(data);
315
+ session.lastActivityAt = createTimestamp();
316
+ }
317
+ async resize(sessionId, cols, rows) {
318
+ const session = this.sessions.get(sessionId);
319
+ if (!session) {
320
+ throw new Error(`Session not found: ${sessionId}`);
321
+ }
322
+ if (session.mode !== 'interactive') {
323
+ throw new Error('resize is only supported for interactive sessions');
324
+ }
325
+ if (!session.interactiveSession) {
326
+ throw new Error('Session PTY is not available');
327
+ }
328
+ if (session.status !== 'running') {
329
+ throw new Error(`Cannot resize session in ${session.status} state`);
330
+ }
331
+ try {
332
+ session.interactiveSession.resize(cols, rows);
333
+ session.cols = cols;
334
+ session.rows = rows;
335
+ session.lastActivityAt = createTimestamp();
336
+ }
337
+ catch (error) {
338
+ const errorMessage = error instanceof Error ? error.message : String(error);
339
+ if (errorMessage.includes('EBADF') || errorMessage.includes('ioctl')) {
340
+ console.warn(`[spawner] Resize failed for session ${sessionId} (PTY may be closed): ${errorMessage}`);
341
+ return;
342
+ }
343
+ throw error;
344
+ }
345
+ }
346
+ // ----------------------------------------
347
+ // Event Subscription
348
+ // ----------------------------------------
349
+ getEventEmitter(sessionId) {
350
+ const session = this.sessions.get(sessionId);
351
+ return session?.events;
352
+ }
353
+ // ----------------------------------------
354
+ // Universal Work Principle (UWP)
355
+ // ----------------------------------------
356
+ async checkReadyQueue(agentId, options) {
357
+ const limit = options?.limit ?? 1;
358
+ if (!options?.getReadyTasks) {
359
+ return {
360
+ hasReadyTask: false,
361
+ autoStarted: false,
362
+ };
363
+ }
364
+ const readyTasks = await options.getReadyTasks(agentId, limit);
365
+ if (readyTasks.length === 0) {
366
+ return {
367
+ hasReadyTask: false,
368
+ autoStarted: false,
369
+ };
370
+ }
371
+ const task = readyTasks[0];
372
+ const result = {
373
+ hasReadyTask: true,
374
+ taskId: task.id,
375
+ taskTitle: task.title,
376
+ taskPriority: task.priority,
377
+ autoStarted: false,
378
+ };
379
+ if (options.autoStart) {
380
+ result.autoStarted = true;
381
+ }
382
+ return result;
383
+ }
384
+ // ----------------------------------------
385
+ // Private Helpers - Spawning
386
+ // ----------------------------------------
387
+ async spawnHeadless(session, options) {
388
+ const headlessProvider = options?.provider?.headless ?? this.provider.headless;
389
+ try {
390
+ const headlessSession = await headlessProvider.spawn({
391
+ workingDirectory: session.workingDirectory,
392
+ initialPrompt: options?.initialPrompt,
393
+ resumeSessionId: options?.resumeSessionId,
394
+ environmentVariables: {
395
+ ...this.defaultConfig.environmentVariables,
396
+ ...options?.environmentVariables,
397
+ },
398
+ stoneforgeRoot: options?.stoneforgeRoot ?? this.defaultConfig.stoneforgeRoot,
399
+ timeout: options?.timeout ?? this.defaultConfig.timeout,
400
+ model: options?.model,
401
+ });
402
+ session.headlessSession = headlessSession;
403
+ // Process messages from the provider in the background
404
+ this.processProviderMessages(session, headlessSession);
405
+ // Wait for the init event to get the provider session ID
406
+ await this.waitForInit(session, options?.timeout ?? this.defaultConfig.timeout);
407
+ }
408
+ catch (error) {
409
+ this.transitionStatus(session, 'terminated');
410
+ session.endedAt = createTimestamp();
411
+ throw error;
412
+ }
413
+ }
414
+ /**
415
+ * Process provider messages and emit them as session events.
416
+ */
417
+ async processProviderMessages(session, headlessSession) {
418
+ let resumeErrorDetected = false;
419
+ try {
420
+ for await (const message of headlessSession) {
421
+ if (session.status === 'terminated') {
422
+ break;
423
+ }
424
+ // Check for resume failure
425
+ if (message.type === 'result' && message.subtype === 'error_during_execution') {
426
+ const raw = message.raw;
427
+ const errors = raw?.errors || [];
428
+ const sessionNotFoundError = errors.find((e) => e.includes('No conversation found with session ID'));
429
+ if (sessionNotFoundError) {
430
+ resumeErrorDetected = true;
431
+ session.events.emit('resume_failed', {
432
+ reason: 'session_not_found',
433
+ message: sessionNotFoundError,
434
+ });
435
+ }
436
+ }
437
+ // Convert AgentMessage to SpawnedSessionEvent
438
+ const event = this.convertAgentMessageToEvent(message);
439
+ if (event) {
440
+ session.events.emit('event', event);
441
+ // Extract provider session ID from system init message
442
+ if (message.type === 'system' && message.subtype === 'init' && message.sessionId) {
443
+ session.providerSessionId = message.sessionId;
444
+ session.events.emit('provider-session-id', message.sessionId);
445
+ }
446
+ }
447
+ }
448
+ }
449
+ catch (error) {
450
+ if (error instanceof Error && error.name === 'AbortError') {
451
+ session.events.emit('interrupt');
452
+ return;
453
+ }
454
+ if (!resumeErrorDetected) {
455
+ session.events.emit('error', error);
456
+ }
457
+ }
458
+ finally {
459
+ if (session.status !== 'suspended' && session.status !== 'terminated') {
460
+ this.transitionStatus(session, 'terminated');
461
+ }
462
+ if (!session.endedAt) {
463
+ session.endedAt = createTimestamp();
464
+ }
465
+ session.events.emit('exit', resumeErrorDetected ? 1 : 0, null);
466
+ if (session.status === 'terminated') {
467
+ this.scheduleTerminatedSessionCleanup(session.id);
468
+ }
469
+ }
470
+ }
471
+ /**
472
+ * Convert a provider AgentMessage to a SpawnedSessionEvent.
473
+ */
474
+ convertAgentMessageToEvent(message) {
475
+ const receivedAt = createTimestamp();
476
+ // Build a raw event from the AgentMessage
477
+ const raw = {
478
+ type: message.type,
479
+ subtype: message.subtype,
480
+ session_id: message.sessionId,
481
+ message: message.content,
482
+ ...(message.tool ? {
483
+ tool: message.tool.name,
484
+ tool_use_id: message.tool.id,
485
+ tool_input: message.tool.input,
486
+ } : {}),
487
+ };
488
+ return {
489
+ type: message.type,
490
+ subtype: message.subtype,
491
+ receivedAt,
492
+ raw,
493
+ message: message.content,
494
+ tool: message.tool,
495
+ };
496
+ }
497
+ async spawnInteractive(session, options) {
498
+ const interactiveProvider = options?.provider?.interactive ?? this.provider.interactive;
499
+ try {
500
+ const interactiveSession = await interactiveProvider.spawn({
501
+ workingDirectory: session.workingDirectory,
502
+ initialPrompt: options?.initialPrompt,
503
+ resumeSessionId: options?.resumeSessionId,
504
+ environmentVariables: {
505
+ ...this.defaultConfig.environmentVariables,
506
+ ...options?.environmentVariables,
507
+ },
508
+ stoneforgeRoot: options?.stoneforgeRoot ?? this.defaultConfig.stoneforgeRoot,
509
+ cols: options?.cols,
510
+ rows: options?.rows,
511
+ model: options?.model,
512
+ });
513
+ session.interactiveSession = interactiveSession;
514
+ session.cols = options?.cols ?? 80;
515
+ session.rows = options?.rows ?? 24;
516
+ session.pid = interactiveSession.pid;
517
+ // Set providerSessionId immediately if the provider knows it upfront
518
+ // (e.g., Claude provider generates a UUID before spawning)
519
+ const knownSessionId = interactiveSession.getSessionId();
520
+ if (knownSessionId) {
521
+ session.providerSessionId = knownSessionId;
522
+ }
523
+ // Handle data output
524
+ interactiveSession.onData((data) => {
525
+ session.lastActivityAt = createTimestamp();
526
+ session.events.emit('pty-data', data);
527
+ // Check for provider session ID in output
528
+ if (!session.providerSessionId) {
529
+ const detectedId = interactiveSession.getSessionId();
530
+ if (detectedId) {
531
+ session.providerSessionId = detectedId;
532
+ session.events.emit('provider-session-id', detectedId);
533
+ }
534
+ }
535
+ });
536
+ // Handle exit
537
+ interactiveSession.onExit((code, signal) => {
538
+ if (session.status !== 'suspended' && session.status !== 'terminated') {
539
+ this.transitionStatus(session, 'terminated');
540
+ }
541
+ if (!session.endedAt) {
542
+ session.endedAt = createTimestamp();
543
+ }
544
+ session.events.emit('exit', code, signal);
545
+ if (session.status === 'terminated') {
546
+ this.scheduleTerminatedSessionCleanup(session.id);
547
+ }
548
+ });
549
+ // Transition to running state
550
+ this.transitionStatus(session, 'running');
551
+ session.startedAt = createTimestamp();
552
+ }
553
+ catch (error) {
554
+ this.transitionStatus(session, 'terminated');
555
+ session.endedAt = createTimestamp();
556
+ throw error;
557
+ }
558
+ }
559
+ async waitForInit(session, timeout) {
560
+ return new Promise((resolve, reject) => {
561
+ const timer = setTimeout(() => {
562
+ reject(new Error(`Timeout waiting for agent init (${timeout}ms)`));
563
+ }, timeout);
564
+ const onEvent = (event) => {
565
+ if (event.type === 'system' && event.subtype === 'init') {
566
+ clearTimeout(timer);
567
+ session.events.off('event', onEvent);
568
+ // Extract provider session ID from init event
569
+ if (event.raw.session_id) {
570
+ session.providerSessionId = event.raw.session_id;
571
+ session.events.emit('provider-session-id', event.raw.session_id);
572
+ }
573
+ this.transitionStatus(session, 'running');
574
+ session.startedAt = createTimestamp();
575
+ resolve();
576
+ }
577
+ };
578
+ session.events.on('event', onEvent);
579
+ });
580
+ }
581
+ // ----------------------------------------
582
+ // Private Helpers - Output Parsing (legacy)
583
+ // ----------------------------------------
584
+ handleHeadlessOutput(session, data) {
585
+ const text = data.toString();
586
+ session.jsonBuffer += text;
587
+ session.lastActivityAt = createTimestamp();
588
+ const lines = session.jsonBuffer.split('\n');
589
+ session.jsonBuffer = lines.pop() ?? '';
590
+ for (const line of lines) {
591
+ if (line.trim()) {
592
+ this.parseAndEmitEvent(session, line);
593
+ }
594
+ }
595
+ }
596
+ parseAndEmitEvent(session, line) {
597
+ try {
598
+ const parsed = JSON.parse(line);
599
+ const rawEvent = parsed;
600
+ const parsedMessage = parsed.message;
601
+ let message;
602
+ let toolFromContent;
603
+ let effectiveType = rawEvent.type;
604
+ const rawContent = parsedMessage?.content ?? parsed.message ?? parsed.content ?? parsed.result;
605
+ if (typeof rawContent === 'string') {
606
+ message = rawContent;
607
+ }
608
+ else if (Array.isArray(rawContent)) {
609
+ const textBlocks = [];
610
+ for (const item of rawContent) {
611
+ if (typeof item === 'object' && item !== null && 'type' in item) {
612
+ const block = item;
613
+ if (block.type === 'text' && typeof block.text === 'string') {
614
+ textBlocks.push(block.text);
615
+ }
616
+ else if (block.type === 'tool_use' && block.name) {
617
+ toolFromContent = {
618
+ name: block.name,
619
+ id: block.id,
620
+ input: block.input,
621
+ };
622
+ effectiveType = 'tool_use';
623
+ }
624
+ else if (block.type === 'tool_result') {
625
+ toolFromContent = {
626
+ id: block.tool_use_id,
627
+ };
628
+ message = typeof block.content === 'string' ? block.content : undefined;
629
+ effectiveType = 'tool_result';
630
+ }
631
+ }
632
+ }
633
+ if (textBlocks.length > 0 && !message) {
634
+ message = textBlocks.join('\n');
635
+ }
636
+ }
637
+ else if (typeof rawContent === 'object' && rawContent !== null) {
638
+ const contentObj = rawContent;
639
+ if ('content' in contentObj && typeof contentObj.content === 'string') {
640
+ message = contentObj.content;
641
+ }
642
+ else if ('text' in contentObj && typeof contentObj.text === 'string') {
643
+ message = contentObj.text;
644
+ }
645
+ }
646
+ const tool = rawEvent.tool
647
+ ? {
648
+ name: rawEvent.tool,
649
+ id: rawEvent.tool_use_id,
650
+ input: rawEvent.tool_input,
651
+ }
652
+ : toolFromContent;
653
+ const event = {
654
+ type: effectiveType,
655
+ subtype: rawEvent.subtype,
656
+ receivedAt: createTimestamp(),
657
+ raw: rawEvent,
658
+ message,
659
+ tool,
660
+ };
661
+ session.events.emit('event', event);
662
+ }
663
+ catch (error) {
664
+ session.events.emit('raw', line);
665
+ }
666
+ }
667
+ // ----------------------------------------
668
+ // Private Helpers - State Management
669
+ // ----------------------------------------
670
+ generateSessionId() {
671
+ this.sessionCounter++;
672
+ const timestamp = Date.now().toString(36);
673
+ const counter = this.sessionCounter.toString(36).padStart(4, '0');
674
+ const random = Math.random().toString(36).slice(2, 6);
675
+ return `session-${timestamp}-${counter}-${random}`;
676
+ }
677
+ determineSpawnMode(agentRole, requestedMode) {
678
+ if (requestedMode) {
679
+ return requestedMode;
680
+ }
681
+ switch (agentRole) {
682
+ case 'director':
683
+ return 'interactive';
684
+ case 'worker':
685
+ return 'headless';
686
+ case 'steward':
687
+ return 'headless';
688
+ default:
689
+ return 'headless';
690
+ }
691
+ }
692
+ getWorkerMode(agentRole, requestedMode) {
693
+ if (agentRole !== 'worker') {
694
+ return undefined;
695
+ }
696
+ const mode = requestedMode ?? this.determineSpawnMode(agentRole);
697
+ return mode === 'interactive' ? 'persistent' : 'ephemeral';
698
+ }
699
+ scheduleTerminatedSessionCleanup(sessionId) {
700
+ setTimeout(() => {
701
+ const session = this.sessions.get(sessionId);
702
+ if (session && session.status === 'terminated') {
703
+ this.sessions.delete(sessionId);
704
+ }
705
+ }, 5000);
706
+ }
707
+ transitionStatus(session, newStatus) {
708
+ const allowedTransitions = SessionStatusTransitions[session.status];
709
+ if (!allowedTransitions.includes(newStatus)) {
710
+ throw new Error(`Invalid status transition: ${session.status} -> ${newStatus}`);
711
+ }
712
+ session.status = newStatus;
713
+ }
714
+ filterAndMapSessions(sessions, agentId) {
715
+ const filtered = agentId
716
+ ? sessions.filter((s) => s.agentId === agentId)
717
+ : sessions;
718
+ return filtered.map((s) => this.toPublicSession(s));
719
+ }
720
+ toPublicSession(session) {
721
+ return {
722
+ id: session.id,
723
+ providerSessionId: session.providerSessionId,
724
+ agentId: session.agentId,
725
+ agentRole: session.agentRole,
726
+ workerMode: session.workerMode,
727
+ mode: session.mode,
728
+ pid: session.pid,
729
+ status: session.status,
730
+ workingDirectory: session.workingDirectory,
731
+ createdAt: session.createdAt,
732
+ lastActivityAt: session.lastActivityAt,
733
+ startedAt: session.startedAt,
734
+ endedAt: session.endedAt,
735
+ };
736
+ }
737
+ }
738
+ // ============================================================================
739
+ // Factory Function
740
+ // ============================================================================
741
+ /**
742
+ * Creates a SpawnerService instance
743
+ */
744
+ export function createSpawnerService(config) {
745
+ return new SpawnerServiceImpl(config);
746
+ }
747
+ // ============================================================================
748
+ // Utility Functions
749
+ // ============================================================================
750
+ /**
751
+ * Checks if a status allows the session to receive input
752
+ */
753
+ export function canReceiveInput(status) {
754
+ return status === 'running';
755
+ }
756
+ /**
757
+ * Checks if a status is a terminal state
758
+ */
759
+ export function isTerminalStatus(status) {
760
+ return status === 'terminated';
761
+ }
762
+ /**
763
+ * Gets human-readable description of a session status
764
+ */
765
+ export function getStatusDescription(status) {
766
+ switch (status) {
767
+ case 'starting':
768
+ return 'Starting up';
769
+ case 'running':
770
+ return 'Running';
771
+ case 'suspended':
772
+ return 'Suspended (can be resumed)';
773
+ case 'terminating':
774
+ return 'Shutting down';
775
+ case 'terminated':
776
+ return 'Terminated';
777
+ default:
778
+ return 'Unknown';
779
+ }
780
+ }
781
+ //# sourceMappingURL=spawner.js.map