@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,773 @@
1
+ /**
2
+ * Session Routes
3
+ *
4
+ * Agent session management (start, stop, resume, stream).
5
+ */
6
+ import { Hono } from 'hono';
7
+ import { streamSSE } from 'hono/streaming';
8
+ import { createTimestamp, ElementType } from '@stoneforge/core';
9
+ import { loadRolePrompt, getAgentMetadata, generateSessionBranchName, generateSessionWorktreePath, trackListeners } from '../../index.js';
10
+ import { formatSessionRecord } from '../formatters.js';
11
+ import { notifySSEClientsOfNewSession } from './events.js';
12
+ /**
13
+ * Extract and save a session event to the database.
14
+ * This is called immediately when events are emitted to ensure all messages
15
+ * are persisted, even before any SSE client connects.
16
+ */
17
+ function saveSessionEvent(event, sessionId, agentId, sessionMessageService) {
18
+ const msgId = `${event.type}-${sessionId}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
19
+ // Skip saving system and result events (not displayed in UI)
20
+ if (event.type === 'system' || event.type === 'result') {
21
+ return msgId;
22
+ }
23
+ // Extract content from event (match client-side logic)
24
+ // Content can be in: event.message, event.raw?.message, event.raw?.content (if string)
25
+ // IMPORTANT: content MUST be a string or undefined, never an object (SQLite can't bind objects)
26
+ let content = typeof event.message === 'string' ? event.message : undefined;
27
+ if (!content && event.raw) {
28
+ const raw = event.raw;
29
+ // Only use raw.message if it's actually a string
30
+ if (typeof raw.message === 'string') {
31
+ content = raw.message;
32
+ }
33
+ else if (typeof raw.content === 'string') {
34
+ content = raw.content;
35
+ }
36
+ // If raw.content is an array (Claude API format), extract text from text blocks
37
+ if (!content && Array.isArray(raw.content)) {
38
+ const textParts = [];
39
+ for (const block of raw.content) {
40
+ if (typeof block === 'object' && block !== null && 'type' in block) {
41
+ const b = block;
42
+ if (b.type === 'text' && typeof b.text === 'string') {
43
+ textParts.push(b.text);
44
+ }
45
+ }
46
+ }
47
+ if (textParts.length > 0) {
48
+ content = textParts.join('');
49
+ }
50
+ }
51
+ }
52
+ // Extract tool info from event
53
+ // Check multiple locations to match client-side extraction in StreamViewer.tsx
54
+ const eventAny = event;
55
+ const eventData = eventAny.data;
56
+ const rawTool = event.raw?.tool;
57
+ const rawToolInput = event.raw?.tool_input;
58
+ let toolName = event.tool?.name || eventData?.name || eventAny.toolName || rawTool;
59
+ let toolInput = event.tool?.input || eventData?.input || eventAny.toolInput || rawToolInput;
60
+ let toolOutput;
61
+ let actualType = event.type;
62
+ // Check for tool_use/tool_result blocks in content arrays (Claude API format)
63
+ // Content array can be in multiple locations depending on event source
64
+ // Must match the same locations the client checks in StreamViewer.tsx
65
+ const raw = event.raw;
66
+ const rawMessage = raw?.message;
67
+ const eventMessage = event.message;
68
+ const eventContent = event.content;
69
+ const rawContentArray = rawMessage?.content || // raw.message.content
70
+ raw?.content || // raw.content
71
+ (typeof eventMessage === 'object' && eventMessage !== null
72
+ ? eventMessage.content
73
+ : undefined) || // event.message.content
74
+ eventContent; // event.content
75
+ if (Array.isArray(rawContentArray)) {
76
+ for (const block of rawContentArray) {
77
+ if (typeof block === 'object' && block !== null && 'type' in block) {
78
+ const b = block;
79
+ if (b.type === 'tool_use' && b.name) {
80
+ toolName = toolName || b.name;
81
+ toolInput = toolInput || b.input;
82
+ // Override type if we found tool info but type was 'assistant'
83
+ if (actualType === 'assistant') {
84
+ actualType = 'tool_use';
85
+ }
86
+ }
87
+ else if (b.type === 'tool_result') {
88
+ toolOutput = typeof b.content === 'string' ? b.content : undefined;
89
+ if (actualType === 'user') {
90
+ actualType = 'tool_result';
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ // For tool_result, check additional fallback locations (match client-side logic)
97
+ if (!toolOutput) {
98
+ toolOutput =
99
+ eventAny.output ||
100
+ eventData?.output ||
101
+ (actualType === 'tool_result' && typeof raw?.content === 'string' ? raw.content : undefined);
102
+ }
103
+ // For tool_result events, content should be empty (output is in toolOutput)
104
+ const finalContent = (actualType === 'tool_result' && toolOutput) ? undefined : content;
105
+ // Safely stringify tool input (JSON.stringify can return undefined for functions)
106
+ let toolInputStr;
107
+ if (toolInput !== undefined && toolInput !== null) {
108
+ try {
109
+ const str = JSON.stringify(toolInput);
110
+ toolInputStr = typeof str === 'string' ? str : undefined;
111
+ }
112
+ catch {
113
+ toolInputStr = String(toolInput);
114
+ }
115
+ }
116
+ sessionMessageService.saveMessage({
117
+ id: msgId,
118
+ sessionId: sessionId,
119
+ agentId,
120
+ type: actualType,
121
+ content: finalContent,
122
+ toolName: toolName,
123
+ toolInput: toolInputStr,
124
+ toolOutput: toolOutput,
125
+ isError: actualType === 'error',
126
+ });
127
+ return msgId;
128
+ }
129
+ // Track sessions that already have the event saver attached to avoid duplicate listeners
130
+ const sessionsWithEventSaver = new Set();
131
+ /**
132
+ * Clean up the session event saver tracking for a session.
133
+ * Call this when a session ends to allow re-attachment if the session restarts.
134
+ */
135
+ export function cleanupSessionEventSaver(sessionId) {
136
+ sessionsWithEventSaver.delete(sessionId);
137
+ }
138
+ /**
139
+ * Attach an event listener to save session events immediately.
140
+ * This ensures all events are persisted even before any SSE client connects.
141
+ * Uses a Set to track attached sessions and avoid duplicate listeners.
142
+ * Exported for use by the dispatch daemon callback.
143
+ */
144
+ export function attachSessionEventSaver(events, sessionId, agentId, sessionMessageService) {
145
+ // Skip if already attached for this session
146
+ if (sessionsWithEventSaver.has(sessionId)) {
147
+ return;
148
+ }
149
+ sessionsWithEventSaver.add(sessionId);
150
+ const onEvent = (event) => {
151
+ const msgId = saveSessionEvent(event, sessionId, agentId, sessionMessageService);
152
+ // Attach the msgId to the event so SSE handlers can use the same ID for deduplication
153
+ event.msgId = msgId;
154
+ };
155
+ const onError = (error) => {
156
+ const msgId = `error-${sessionId}-${Date.now()}`;
157
+ sessionMessageService.saveMessage({
158
+ id: msgId,
159
+ sessionId: sessionId,
160
+ agentId,
161
+ type: 'error',
162
+ content: error.message,
163
+ isError: true,
164
+ });
165
+ // Attach msgId for SSE deduplication
166
+ error.msgId = msgId;
167
+ };
168
+ trackListeners(events, { 'event': onEvent, 'error': onError });
169
+ }
170
+ export function createSessionRoutes(services, notifyClientsOfNewSession) {
171
+ const { api, orchestratorApi, agentRegistry, sessionManager, spawnerService, sessionInitialPrompts, sessionMessageService } = services;
172
+ const app = new Hono();
173
+ // POST /api/agents/:id/start
174
+ app.post('/api/agents/:id/start', async (c) => {
175
+ try {
176
+ const agentId = c.req.param('id');
177
+ const body = (await c.req.json().catch(() => ({})));
178
+ const agent = await agentRegistry.getAgent(agentId);
179
+ if (!agent) {
180
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Agent not found' } }, 404);
181
+ }
182
+ const existingSession = sessionManager.getActiveSession(agentId);
183
+ if (existingSession) {
184
+ return c.json({
185
+ error: { code: 'SESSION_EXISTS', message: 'Agent already has an active session' },
186
+ existingSession: formatSessionRecord(existingSession),
187
+ }, 409);
188
+ }
189
+ // Get agent metadata to determine role
190
+ const agentMeta = getAgentMetadata(agent);
191
+ const agentRole = agentMeta?.agentRole;
192
+ const workerMode = agentRole === 'worker'
193
+ ? agentMeta?.workerMode
194
+ : undefined;
195
+ const stewardFocus = agentRole === 'steward'
196
+ ? agentMeta?.stewardFocus
197
+ : undefined;
198
+ console.log('[sessions] Agent metadata:', agentMeta ? `role=${agentMeta.agentRole}${workerMode ? ` mode=${workerMode}` : ''}` : 'undefined');
199
+ // Create worktree for persistent workers
200
+ let worktreePath;
201
+ if (workerMode === 'persistent' && services.worktreeManager) {
202
+ try {
203
+ const now = new Date();
204
+ const timestamp = now.getFullYear().toString()
205
+ + String(now.getMonth() + 1).padStart(2, '0')
206
+ + String(now.getDate()).padStart(2, '0')
207
+ + String(now.getHours()).padStart(2, '0')
208
+ + String(now.getMinutes()).padStart(2, '0')
209
+ + String(now.getSeconds()).padStart(2, '0');
210
+ const agentName = agent.name ?? agentId;
211
+ const sessionBranch = generateSessionBranchName(agentName, timestamp);
212
+ const sessionPath = generateSessionWorktreePath(agentName, timestamp);
213
+ const worktreeResult = await services.worktreeManager.createWorktree({
214
+ agentName,
215
+ taskId: `session-${timestamp}`,
216
+ customBranch: sessionBranch,
217
+ customPath: sessionPath,
218
+ });
219
+ worktreePath = worktreeResult.worktree.path;
220
+ console.log(`[sessions] Created persistent worker worktree: ${worktreePath} on branch ${sessionBranch}`);
221
+ }
222
+ catch (err) {
223
+ console.warn('[sessions] Failed to create worktree for persistent worker:', err);
224
+ // Continue without worktree — don't block session start
225
+ }
226
+ }
227
+ // Load role-specific prompt for interactive agents (Director, persistent workers)
228
+ // This is prepended to any other prompt content
229
+ let rolePrompt;
230
+ if (agentRole) {
231
+ const roleResult = loadRolePrompt(agentRole, stewardFocus, { projectRoot: process.cwd(), workerMode });
232
+ console.log('[sessions] Role prompt result:', roleResult ? `${roleResult.prompt.length} chars from ${roleResult.source}` : 'undefined');
233
+ if (roleResult) {
234
+ rolePrompt = roleResult.prompt;
235
+ }
236
+ }
237
+ let effectivePrompt = body.initialPrompt;
238
+ let assignedTask;
239
+ // Get director ID for worker prompts
240
+ let directorId;
241
+ if (agentRole === 'worker') {
242
+ const director = await agentRegistry.getDirector();
243
+ directorId = director?.id ?? 'unknown';
244
+ }
245
+ if (body.taskId) {
246
+ const taskResult = await api.get(body.taskId);
247
+ if (!taskResult || taskResult.type !== ElementType.TASK) {
248
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
249
+ }
250
+ await orchestratorApi.assignTaskToAgent(body.taskId, agentId);
251
+ const taskPrompt = `You have been assigned the following task:
252
+
253
+ **Worker ID:** ${agentId}
254
+ **Director ID:** ${directorId ?? 'unknown'}
255
+ **Task ID**: ${taskResult.id}
256
+ **Title**: ${taskResult.title}
257
+ **Priority**: ${taskResult.priority ?? 'Not set'}
258
+ ${taskResult.acceptanceCriteria ? `**Acceptance Criteria**: ${taskResult.acceptanceCriteria}` : ''}
259
+
260
+ Please begin working on this task. Use \`sf task get ${taskResult.id}\` to see full details if needed.`;
261
+ effectivePrompt = body.initialMessage
262
+ ? `${taskPrompt}\n\n**Additional Instructions**:\n${body.initialMessage}${body.initialPrompt ? `\n\n${body.initialPrompt}` : ''}`
263
+ : body.initialPrompt
264
+ ? `${taskPrompt}\n\n${body.initialPrompt}`
265
+ : taskPrompt;
266
+ assignedTask = { id: taskResult.id, title: taskResult.title };
267
+ }
268
+ else if (body.initialMessage) {
269
+ effectivePrompt = body.initialPrompt
270
+ ? `${body.initialMessage}\n\n${body.initialPrompt}`
271
+ : body.initialMessage;
272
+ }
273
+ // Prepend role prompt if available, wrapped with clear instructions
274
+ // so Claude understands this is its operating instructions, not a file being opened
275
+ if (rolePrompt) {
276
+ const framedRolePrompt = `Please read and internalize the following operating instructions. These define your role and how you should behave in this session:\n\n${rolePrompt}`;
277
+ // Add identity section after the role prompt for directors and workers (when not already in task prompt)
278
+ let idSection = '';
279
+ if (agentRole === 'director') {
280
+ idSection = `\n\n**Director ID:** ${agentId}`;
281
+ }
282
+ else if (agentRole === 'worker' && !body.taskId) {
283
+ // Only add here if no taskId (task prompt already includes IDs)
284
+ idSection = `\n\n**Worker ID:** ${agentId}\n**Director ID:** ${directorId ?? 'unknown'}`;
285
+ }
286
+ effectivePrompt = effectivePrompt
287
+ ? `${framedRolePrompt}${idSection}\n\n---\n\n${effectivePrompt}`
288
+ : `${framedRolePrompt}${idSection}`;
289
+ }
290
+ const { session, events } = await sessionManager.startSession(agentId, {
291
+ workingDirectory: worktreePath ?? body.workingDirectory,
292
+ worktree: body.worktree ?? worktreePath,
293
+ initialPrompt: effectivePrompt,
294
+ interactive: body.interactive,
295
+ cols: body.cols,
296
+ rows: body.rows,
297
+ });
298
+ // Attach event saver immediately to capture all events, including the first assistant response
299
+ // This must happen before any events are emitted to avoid missing early messages
300
+ attachSessionEventSaver(events, session.id, agentId, sessionMessageService);
301
+ if (effectivePrompt) {
302
+ sessionInitialPrompts.set(session.id, effectivePrompt);
303
+ // Save initial prompt to database immediately (don't wait for SSE connection)
304
+ const initialMsgId = `user-${session.id}-initial`;
305
+ sessionMessageService.saveMessage({
306
+ id: initialMsgId,
307
+ sessionId: session.id,
308
+ agentId,
309
+ type: 'user',
310
+ content: effectivePrompt,
311
+ isError: false,
312
+ });
313
+ }
314
+ notifyClientsOfNewSession(agentId, session, events);
315
+ // Notify SSE stream clients so they dynamically subscribe to this session's events
316
+ notifySSEClientsOfNewSession({
317
+ sessionId: session.id,
318
+ agentId,
319
+ agentRole: agentRole || 'worker',
320
+ events,
321
+ });
322
+ return c.json({
323
+ success: true,
324
+ session: formatSessionRecord(session),
325
+ ...(assignedTask && { assignedTask }),
326
+ }, 201);
327
+ }
328
+ catch (error) {
329
+ console.error('[orchestrator] Failed to start session:', error);
330
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
331
+ }
332
+ });
333
+ // POST /api/agents/:id/stop
334
+ app.post('/api/agents/:id/stop', async (c) => {
335
+ try {
336
+ const agentId = c.req.param('id');
337
+ const body = (await c.req.json().catch(() => ({})));
338
+ const activeSession = sessionManager.getActiveSession(agentId);
339
+ if (!activeSession) {
340
+ try {
341
+ await agentRegistry.updateAgentSession(agentId, undefined, 'idle');
342
+ }
343
+ catch {
344
+ // Agent may not exist
345
+ }
346
+ return c.json({ success: true, message: 'No active session to stop' });
347
+ }
348
+ await sessionManager.stopSession(activeSession.id, {
349
+ graceful: body.graceful,
350
+ reason: body.reason,
351
+ });
352
+ // Clean up initial prompt and event saver tracking for this session
353
+ sessionInitialPrompts.delete(activeSession.id);
354
+ sessionsWithEventSaver.delete(activeSession.id);
355
+ return c.json({ success: true, sessionId: activeSession.id });
356
+ }
357
+ catch (error) {
358
+ console.error('[orchestrator] Failed to stop session:', error);
359
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
360
+ }
361
+ });
362
+ // POST /api/sessions/stop-all
363
+ app.post('/api/sessions/stop-all', async (c) => {
364
+ try {
365
+ const body = (await c.req.json().catch(() => ({})));
366
+ // Get all running sessions
367
+ const runningSessions = sessionManager.listSessions({
368
+ status: ['starting', 'running']
369
+ });
370
+ if (runningSessions.length === 0) {
371
+ return c.json({ success: true, stoppedCount: 0, message: 'No running sessions' });
372
+ }
373
+ // Stop each session
374
+ const results = [];
375
+ for (const session of runningSessions) {
376
+ try {
377
+ await sessionManager.stopSession(session.id, {
378
+ graceful: body.graceful,
379
+ reason: body.reason || 'Stopped by user via Stop All',
380
+ });
381
+ sessionInitialPrompts.delete(session.id);
382
+ sessionsWithEventSaver.delete(session.id);
383
+ results.push({ sessionId: session.id, agentId: session.agentId, success: true });
384
+ }
385
+ catch (error) {
386
+ results.push({
387
+ sessionId: session.id,
388
+ agentId: session.agentId,
389
+ success: false,
390
+ error: String(error)
391
+ });
392
+ }
393
+ }
394
+ const stoppedCount = results.filter(r => r.success).length;
395
+ const failedCount = results.filter(r => !r.success).length;
396
+ return c.json({
397
+ success: failedCount === 0,
398
+ stoppedCount,
399
+ failedCount,
400
+ results
401
+ });
402
+ }
403
+ catch (error) {
404
+ console.error('[orchestrator] Failed to stop all sessions:', error);
405
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
406
+ }
407
+ });
408
+ // POST /api/agents/:id/interrupt
409
+ app.post('/api/agents/:id/interrupt', async (c) => {
410
+ try {
411
+ const agentId = c.req.param('id');
412
+ const activeSession = sessionManager.getActiveSession(agentId);
413
+ if (!activeSession) {
414
+ return c.json({ error: { code: 'NO_SESSION', message: 'No active session to interrupt' } }, 404);
415
+ }
416
+ await sessionManager.interruptSession(activeSession.id);
417
+ return c.json({ success: true, sessionId: activeSession.id });
418
+ }
419
+ catch (error) {
420
+ console.error('[orchestrator] Failed to interrupt session:', error);
421
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
422
+ }
423
+ });
424
+ // POST /api/agents/:id/resume
425
+ app.post('/api/agents/:id/resume', async (c) => {
426
+ try {
427
+ const agentId = c.req.param('id');
428
+ const body = (await c.req.json().catch(() => ({})));
429
+ const agent = await agentRegistry.getAgent(agentId);
430
+ if (!agent) {
431
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Agent not found' } }, 404);
432
+ }
433
+ const existingSession = sessionManager.getActiveSession(agentId);
434
+ if (existingSession) {
435
+ return c.json({
436
+ error: { code: 'SESSION_EXISTS', message: 'Agent already has an active session' },
437
+ existingSession: formatSessionRecord(existingSession),
438
+ }, 409);
439
+ }
440
+ let providerSessionId = body.providerSessionId;
441
+ if (!providerSessionId) {
442
+ const resumable = sessionManager.getMostRecentResumableSession(agentId);
443
+ if (!resumable?.providerSessionId) {
444
+ return c.json({ error: { code: 'NO_RESUMABLE_SESSION', message: 'No resumable session found' } }, 404);
445
+ }
446
+ providerSessionId = resumable.providerSessionId;
447
+ }
448
+ const { session, events, uwpCheck } = await sessionManager.resumeSession(agentId, {
449
+ providerSessionId,
450
+ workingDirectory: body.workingDirectory,
451
+ worktree: body.worktree,
452
+ resumePrompt: body.resumePrompt,
453
+ checkReadyQueue: body.checkReadyQueue,
454
+ });
455
+ // Attach event saver immediately to capture all events, including the first assistant response
456
+ attachSessionEventSaver(events, session.id, agentId, sessionMessageService);
457
+ // Save resume prompt to database if provided
458
+ // Use same ID pattern as start session so SSE deduplication works
459
+ if (body.resumePrompt) {
460
+ sessionInitialPrompts.set(session.id, body.resumePrompt);
461
+ const initialMsgId = `user-${session.id}-initial`;
462
+ sessionMessageService.saveMessage({
463
+ id: initialMsgId,
464
+ sessionId: session.id,
465
+ agentId,
466
+ type: 'user',
467
+ content: body.resumePrompt,
468
+ isError: false,
469
+ });
470
+ }
471
+ notifyClientsOfNewSession(agentId, session, events);
472
+ // Notify SSE stream clients so they dynamically subscribe to this session's events
473
+ notifySSEClientsOfNewSession({
474
+ sessionId: session.id,
475
+ agentId,
476
+ agentRole: session.agentRole || 'worker',
477
+ events,
478
+ });
479
+ return c.json({ success: true, session: formatSessionRecord(session), uwpCheck }, 201);
480
+ }
481
+ catch (error) {
482
+ console.error('[orchestrator] Failed to resume session:', error);
483
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
484
+ }
485
+ });
486
+ // GET /api/agents/:id/stream
487
+ app.get('/api/agents/:id/stream', async (c) => {
488
+ const agentId = c.req.param('id');
489
+ const activeSession = sessionManager.getActiveSession(agentId);
490
+ if (!activeSession) {
491
+ return c.json({ error: { code: 'NO_SESSION', message: 'Agent has no active session' } }, 404);
492
+ }
493
+ const events = sessionManager.getEventEmitter(activeSession.id);
494
+ if (!events) {
495
+ return c.json({ error: { code: 'NO_EVENTS', message: 'Session event emitter not available' } }, 404);
496
+ }
497
+ // Ensure event saver is attached for this session (handles existing sessions
498
+ // that were started before this code change, or if the saver wasn't attached)
499
+ attachSessionEventSaver(events, activeSession.id, agentId, sessionMessageService);
500
+ return streamSSE(c, async (stream) => {
501
+ let eventId = 0;
502
+ try {
503
+ await stream.writeSSE({
504
+ id: String(++eventId),
505
+ event: 'connected',
506
+ data: JSON.stringify({
507
+ sessionId: activeSession.id,
508
+ agentId,
509
+ timestamp: createTimestamp(),
510
+ }),
511
+ });
512
+ // Send initial prompt to every connecting client (for real-time display)
513
+ // Note: The initial prompt is already saved to database when session starts
514
+ // We keep the prompt in the map for the session duration so reconnecting clients also get it via SSE
515
+ const initialPrompt = sessionInitialPrompts.get(activeSession.id);
516
+ if (initialPrompt) {
517
+ const initialMsgId = `user-${activeSession.id}-initial`;
518
+ await stream.writeSSE({
519
+ id: initialMsgId,
520
+ event: 'agent_user',
521
+ data: JSON.stringify({
522
+ type: 'user',
523
+ message: initialPrompt,
524
+ msgId: initialMsgId, // Include ID in data for deduplication
525
+ raw: { type: 'user', content: initialPrompt },
526
+ }),
527
+ });
528
+ }
529
+ // Note: Events are already being saved to the database by the attachSessionEventSaver
530
+ // listener that was attached when the session started. The SSE handler only needs to
531
+ // stream events to connected clients for real-time display.
532
+ const onEvent = async (event) => {
533
+ // Use the msgId attached by saveSessionEvent if available, otherwise generate one
534
+ const eventWithMsgId = event;
535
+ const msgId = eventWithMsgId.msgId || `${event.type}-${activeSession.id}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
536
+ await stream.writeSSE({
537
+ id: msgId,
538
+ event: `agent_${event.type}`,
539
+ data: JSON.stringify({ ...event, msgId }), // Include ID in data for deduplication
540
+ });
541
+ };
542
+ const onError = async (error) => {
543
+ // Use msgId from error if attached by saveSessionEvent, otherwise generate one
544
+ const errorWithMsgId = error;
545
+ const msgId = errorWithMsgId.msgId || `error-${activeSession.id}-${Date.now()}`;
546
+ await stream.writeSSE({
547
+ id: msgId,
548
+ event: 'agent_error',
549
+ data: JSON.stringify({ error: error.message, msgId }),
550
+ });
551
+ };
552
+ const onExit = async (code, signal) => {
553
+ await stream.writeSSE({
554
+ id: String(++eventId),
555
+ event: 'agent_exit',
556
+ data: JSON.stringify({ code, signal }),
557
+ });
558
+ };
559
+ const cleanupListeners = trackListeners(events, {
560
+ 'event': onEvent,
561
+ 'error': onError,
562
+ 'exit': onExit,
563
+ });
564
+ const heartbeatInterval = setInterval(async () => {
565
+ try {
566
+ await stream.writeSSE({
567
+ id: String(++eventId),
568
+ event: 'heartbeat',
569
+ data: JSON.stringify({ timestamp: createTimestamp() }),
570
+ });
571
+ }
572
+ catch {
573
+ clearInterval(heartbeatInterval);
574
+ }
575
+ }, 30000);
576
+ stream.onAbort(() => {
577
+ clearInterval(heartbeatInterval);
578
+ cleanupListeners();
579
+ });
580
+ await new Promise(() => { });
581
+ }
582
+ catch (error) {
583
+ console.error(`[orchestrator] SSE: Error in stream:`, error);
584
+ }
585
+ });
586
+ });
587
+ // POST /api/agents/:id/input
588
+ app.post('/api/agents/:id/input', async (c) => {
589
+ try {
590
+ const agentId = c.req.param('id');
591
+ const body = (await c.req.json());
592
+ if (!body.input) {
593
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'Input is required' } }, 400);
594
+ }
595
+ const activeSession = sessionManager.getActiveSession(agentId);
596
+ if (!activeSession) {
597
+ return c.json({ error: { code: 'NO_SESSION', message: 'Agent has no active session' } }, 404);
598
+ }
599
+ await spawnerService.sendInput(activeSession.id, body.input, {
600
+ isUserMessage: body.isUserMessage,
601
+ });
602
+ // Save user input to database if it's a user message
603
+ if (body.isUserMessage) {
604
+ const inputMsgId = `user-${activeSession.id}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
605
+ sessionMessageService.saveMessage({
606
+ id: inputMsgId,
607
+ sessionId: activeSession.id,
608
+ agentId,
609
+ type: 'user',
610
+ content: body.input,
611
+ isError: false,
612
+ });
613
+ }
614
+ return c.json({ success: true, sessionId: activeSession.id }, 202);
615
+ }
616
+ catch (error) {
617
+ console.error('[orchestrator] Failed to send input:', error);
618
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
619
+ }
620
+ });
621
+ // GET /api/sessions
622
+ app.get('/api/sessions', async (c) => {
623
+ try {
624
+ const url = new URL(c.req.url);
625
+ const agentIdParam = url.searchParams.get('agentId');
626
+ const roleParam = url.searchParams.get('role');
627
+ const statusParam = url.searchParams.get('status');
628
+ const resumableParam = url.searchParams.get('resumable');
629
+ const filter = {
630
+ ...(agentIdParam && { agentId: agentIdParam }),
631
+ ...(roleParam && { role: roleParam }),
632
+ ...(statusParam && {
633
+ status: statusParam.includes(',')
634
+ ? statusParam.split(',')
635
+ : statusParam,
636
+ }),
637
+ ...(resumableParam === 'true' && { resumable: true }),
638
+ };
639
+ const sessions = sessionManager.listSessions(filter);
640
+ return c.json({ sessions: sessions.map(formatSessionRecord) });
641
+ }
642
+ catch (error) {
643
+ console.error('[orchestrator] Failed to list sessions:', error);
644
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
645
+ }
646
+ });
647
+ // GET /api/sessions/latest-messages?sessionIds=id1,id2,...
648
+ // Returns the latest displayable message per session for status display on agent cards
649
+ app.get('/api/sessions/latest-messages', async (c) => {
650
+ try {
651
+ const url = new URL(c.req.url);
652
+ const sessionIdsParam = url.searchParams.get('sessionIds');
653
+ if (!sessionIdsParam) {
654
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'sessionIds parameter is required' } }, 400);
655
+ }
656
+ const sessionIds = sessionIdsParam.split(',').filter(Boolean);
657
+ if (sessionIds.length === 0) {
658
+ return c.json({ messages: {} });
659
+ }
660
+ // Cap at 50 sessions to prevent abuse
661
+ const cappedIds = sessionIds.slice(0, 50);
662
+ const latestMessages = sessionMessageService.getLatestDisplayableMessages(cappedIds);
663
+ // Convert map to plain object for JSON serialization
664
+ const messagesObj = {};
665
+ for (const [sid, msg] of latestMessages) {
666
+ // Generate display content: use content if available, otherwise generate from tool info
667
+ let displayContent = msg.content;
668
+ if (!displayContent && msg.type === 'tool_use' && msg.toolName) {
669
+ const displayName = msg.toolName.charAt(0).toUpperCase() + msg.toolName.slice(1);
670
+ displayContent = `Using ${displayName}...`;
671
+ }
672
+ else if (!displayContent && msg.type === 'tool_result') {
673
+ displayContent = 'Tool completed';
674
+ }
675
+ messagesObj[sid] = {
676
+ content: displayContent,
677
+ type: msg.type,
678
+ toolName: msg.toolName,
679
+ timestamp: msg.createdAt,
680
+ agentId: msg.agentId,
681
+ };
682
+ }
683
+ return c.json({ messages: messagesObj });
684
+ }
685
+ catch (error) {
686
+ console.error('[orchestrator] Failed to get latest messages:', error);
687
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
688
+ }
689
+ });
690
+ // GET /api/sessions/:id
691
+ app.get('/api/sessions/:id', async (c) => {
692
+ try {
693
+ const sessionId = c.req.param('id');
694
+ const session = sessionManager.getSession(sessionId);
695
+ if (!session) {
696
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Session not found' } }, 404);
697
+ }
698
+ return c.json({ session: formatSessionRecord(session) });
699
+ }
700
+ catch (error) {
701
+ console.error('[orchestrator] Failed to get session:', error);
702
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
703
+ }
704
+ });
705
+ // GET /api/sessions/:id/messages
706
+ // Retrieve all messages for a session (for transcript restoration)
707
+ // Also loads messages from related sessions (those with the same providerSessionId)
708
+ // to support viewing full history of resumed sessions
709
+ app.get('/api/sessions/:id/messages', async (c) => {
710
+ try {
711
+ const sessionId = c.req.param('id');
712
+ const url = new URL(c.req.url);
713
+ const afterId = url.searchParams.get('after');
714
+ // Get the session to check for providerSessionId
715
+ // First check in-memory sessions, then check persisted session history
716
+ let providerSessionId;
717
+ const inMemorySession = sessionManager.getSession(sessionId);
718
+ if (inMemorySession?.providerSessionId) {
719
+ providerSessionId = inMemorySession.providerSessionId;
720
+ }
721
+ else {
722
+ // Session not in memory - check persisted session history for all agents
723
+ // This is needed when viewing historical sessions after server restart
724
+ const agents = await agentRegistry.listAgents();
725
+ for (const agent of agents) {
726
+ const history = await sessionManager.getSessionHistory(agent.id, 50);
727
+ const historyEntry = history.find(h => h.id === sessionId);
728
+ if (historyEntry?.providerSessionId) {
729
+ providerSessionId = historyEntry.providerSessionId;
730
+ break;
731
+ }
732
+ }
733
+ }
734
+ // If we found a providerSessionId, find all related sessions and load their messages
735
+ if (providerSessionId) {
736
+ // Collect all session IDs with the same providerSessionId
737
+ const relatedSessionIds = new Set();
738
+ relatedSessionIds.add(sessionId); // Always include the requested session
739
+ // Check in-memory sessions
740
+ const allSessions = sessionManager.listSessions({});
741
+ for (const s of allSessions) {
742
+ if (s.providerSessionId === providerSessionId) {
743
+ relatedSessionIds.add(s.id);
744
+ }
745
+ }
746
+ // Also check persisted session history
747
+ const agents = await agentRegistry.listAgents();
748
+ for (const agent of agents) {
749
+ const history = await sessionManager.getSessionHistory(agent.id, 50);
750
+ for (const entry of history) {
751
+ if (entry.providerSessionId === providerSessionId) {
752
+ relatedSessionIds.add(entry.id);
753
+ }
754
+ }
755
+ }
756
+ // Load messages from all related sessions
757
+ const messages = sessionMessageService.getMessagesForSessions(Array.from(relatedSessionIds));
758
+ return c.json({ messages });
759
+ }
760
+ // Fallback to just loading messages for this session
761
+ const messages = afterId
762
+ ? sessionMessageService.getSessionMessagesAfter(sessionId, afterId)
763
+ : sessionMessageService.getSessionMessages(sessionId);
764
+ return c.json({ messages });
765
+ }
766
+ catch (error) {
767
+ console.error('[orchestrator] Failed to get session messages:', error);
768
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
769
+ }
770
+ });
771
+ return app;
772
+ }
773
+ //# sourceMappingURL=sessions.js.map