@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,954 @@
1
+ /**
2
+ * Task Routes
3
+ *
4
+ * CRUD and dispatch operations for tasks.
5
+ */
6
+ import { Hono } from 'hono';
7
+ import { createTask, createDocument, TaskStatus, ElementType, Priority, Complexity, ContentType, updateTaskStatus } from '@stoneforge/core';
8
+ import { updateOrchestratorTaskMeta } from '../../index.js';
9
+ import { formatTaskResponse } from '../formatters.js';
10
+ /**
11
+ * Hydrate the description from descriptionRef if it exists
12
+ */
13
+ async function hydrateTaskDescription(task, api) {
14
+ if (task.descriptionRef) {
15
+ try {
16
+ const doc = await api.get(task.descriptionRef);
17
+ if (doc && doc.type === 'document') {
18
+ return doc.content;
19
+ }
20
+ }
21
+ catch {
22
+ // Document not found or error - fall back to null
23
+ }
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Format a task response with hydrated description
29
+ */
30
+ async function formatTaskWithDescription(task, api) {
31
+ const hydratedDescription = await hydrateTaskDescription(task, api);
32
+ return formatTaskResponse(task, hydratedDescription);
33
+ }
34
+ export function createTaskRoutes(services) {
35
+ const { api, agentRegistry, taskAssignmentService, dispatchService, workerTaskService } = services;
36
+ const app = new Hono();
37
+ // GET /api/tasks - List tasks
38
+ app.get('/api/tasks', async (c) => {
39
+ try {
40
+ const url = new URL(c.req.url);
41
+ const statusParam = url.searchParams.get('status');
42
+ const assigneeParam = url.searchParams.get('assignee');
43
+ const unassignedParam = url.searchParams.get('unassigned');
44
+ const searchParam = url.searchParams.get('search');
45
+ // Helper to filter tasks by search query (case-insensitive title match)
46
+ const filterBySearch = (taskList) => {
47
+ if (!searchParam)
48
+ return taskList;
49
+ const query = searchParam.toLowerCase();
50
+ return taskList.filter((t) => t.title.toLowerCase().includes(query));
51
+ };
52
+ if (unassignedParam === 'true') {
53
+ const unassignedTasks = await taskAssignmentService.getUnassignedTasks();
54
+ const filtered = filterBySearch(unassignedTasks);
55
+ return c.json({ tasks: filtered.map((t) => formatTaskResponse(t)) });
56
+ }
57
+ if (assigneeParam) {
58
+ const agentAssignments = await taskAssignmentService.getAgentTasks(assigneeParam);
59
+ const agentTasks = agentAssignments.map((a) => a.task);
60
+ let filtered = statusParam
61
+ ? agentTasks.filter((t) => t.status === TaskStatus[statusParam.toUpperCase()])
62
+ : agentTasks;
63
+ filtered = filterBySearch(filtered);
64
+ return c.json({ tasks: filtered.map((t) => formatTaskResponse(t)) });
65
+ }
66
+ const allElements = await api.list({ type: ElementType.TASK, limit: 10000 });
67
+ const tasks = allElements.filter((e) => e.type === ElementType.TASK);
68
+ // Filter out tombstoned (deleted) tasks
69
+ let filtered = tasks.filter((t) => t.status !== TaskStatus.TOMBSTONE);
70
+ // Apply status filter if provided
71
+ if (statusParam) {
72
+ filtered = filtered.filter((t) => t.status === TaskStatus[statusParam.toUpperCase()]);
73
+ }
74
+ filtered = filterBySearch(filtered);
75
+ return c.json({ tasks: filtered.map((t) => formatTaskResponse(t)) });
76
+ }
77
+ catch (error) {
78
+ console.error('[orchestrator] Failed to list tasks:', error);
79
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
80
+ }
81
+ });
82
+ // GET /api/tasks/unassigned
83
+ app.get('/api/tasks/unassigned', async (c) => {
84
+ try {
85
+ const tasks = await taskAssignmentService.getUnassignedTasks();
86
+ return c.json({ tasks: tasks.map((t) => formatTaskResponse(t)) });
87
+ }
88
+ catch (error) {
89
+ console.error('[orchestrator] Failed to list unassigned tasks:', error);
90
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
91
+ }
92
+ });
93
+ // POST /api/tasks - Create task
94
+ app.post('/api/tasks', async (c) => {
95
+ try {
96
+ const body = (await c.req.json());
97
+ if (!body.title) {
98
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'title is required' } }, 400);
99
+ }
100
+ // Handle priority as either number (1-5) or string
101
+ const priorityStringMap = {
102
+ critical: Priority.CRITICAL,
103
+ high: Priority.HIGH,
104
+ medium: Priority.MEDIUM,
105
+ low: Priority.LOW,
106
+ };
107
+ let priority;
108
+ if (typeof body.priority === 'number') {
109
+ priority = body.priority;
110
+ }
111
+ else if (body.priority) {
112
+ priority = priorityStringMap[body.priority];
113
+ }
114
+ // Handle complexity as either number (1-5) or string
115
+ const complexityStringMap = {
116
+ trivial: Complexity.TRIVIAL,
117
+ simple: Complexity.SIMPLE,
118
+ medium: Complexity.MEDIUM,
119
+ complex: Complexity.COMPLEX,
120
+ very_complex: Complexity.VERY_COMPLEX,
121
+ };
122
+ let complexity;
123
+ if (typeof body.complexity === 'number') {
124
+ complexity = body.complexity;
125
+ }
126
+ else if (body.complexity) {
127
+ complexity = complexityStringMap[body.complexity];
128
+ }
129
+ const metadata = {};
130
+ if (body.description) {
131
+ metadata.description = body.description;
132
+ }
133
+ const createdBy = (body.createdBy ?? 'el-0000');
134
+ // Map status string to TaskStatus enum
135
+ let status;
136
+ if (body.status) {
137
+ const statusMap = {
138
+ backlog: TaskStatus.BACKLOG,
139
+ open: TaskStatus.OPEN,
140
+ in_progress: TaskStatus.IN_PROGRESS,
141
+ blocked: TaskStatus.BLOCKED,
142
+ review: TaskStatus.REVIEW,
143
+ deferred: TaskStatus.DEFERRED,
144
+ closed: TaskStatus.CLOSED,
145
+ };
146
+ status = statusMap[body.status];
147
+ if (!status) {
148
+ return c.json({ error: { code: 'INVALID_INPUT', message: `Invalid status: ${body.status}` } }, 400);
149
+ }
150
+ }
151
+ const taskData = await createTask({
152
+ title: body.title,
153
+ status,
154
+ priority,
155
+ complexity,
156
+ taskType: body.taskType,
157
+ assignee: body.assignee ? body.assignee : undefined,
158
+ tags: body.tags,
159
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
160
+ createdBy,
161
+ });
162
+ const savedTask = await api.create(taskData);
163
+ return c.json({ task: formatTaskResponse(savedTask) }, 201);
164
+ }
165
+ catch (error) {
166
+ console.error('[orchestrator] Failed to create task:', error);
167
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
168
+ }
169
+ });
170
+ // PATCH /api/tasks/:id - Update task
171
+ app.patch('/api/tasks/:id', async (c) => {
172
+ try {
173
+ const taskId = c.req.param('id');
174
+ const body = (await c.req.json());
175
+ const task = await api.get(taskId);
176
+ if (!task || task.type !== ElementType.TASK) {
177
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
178
+ }
179
+ const updates = {};
180
+ if (body.title !== undefined)
181
+ updates.title = body.title;
182
+ if (body.description !== undefined) {
183
+ // Description is stored in metadata
184
+ const existingMeta = (task.metadata ?? {});
185
+ updates.metadata = { ...existingMeta, description: body.description };
186
+ }
187
+ if (body.status !== undefined) {
188
+ const statusMap = {
189
+ backlog: TaskStatus.BACKLOG,
190
+ open: TaskStatus.OPEN,
191
+ in_progress: TaskStatus.IN_PROGRESS,
192
+ blocked: TaskStatus.BLOCKED,
193
+ review: TaskStatus.REVIEW,
194
+ deferred: TaskStatus.DEFERRED,
195
+ closed: TaskStatus.CLOSED,
196
+ };
197
+ const mappedStatus = statusMap[body.status];
198
+ if (!mappedStatus) {
199
+ return c.json({ error: { code: 'INVALID_INPUT', message: `Invalid status: ${body.status}` } }, 400);
200
+ }
201
+ updates.status = mappedStatus;
202
+ }
203
+ if (body.priority !== undefined)
204
+ updates.priority = body.priority;
205
+ if (body.complexity !== undefined)
206
+ updates.complexity = body.complexity;
207
+ if (body.assignee !== undefined)
208
+ updates.assignee = body.assignee || null;
209
+ if (body.owner !== undefined)
210
+ updates.owner = body.owner || null;
211
+ if (body.deadline !== undefined)
212
+ updates.deadline = body.deadline || null;
213
+ if (body.tags !== undefined)
214
+ updates.tags = body.tags;
215
+ // Handle mergeStatus update (nested in metadata.orchestrator)
216
+ if (body.mergeStatus !== undefined) {
217
+ const validMergeStatuses = ['pending', 'testing', 'merging', 'merged', 'conflict', 'test_failed', 'failed', 'not_applicable'];
218
+ if (!validMergeStatuses.includes(body.mergeStatus)) {
219
+ return c.json({ error: { code: 'INVALID_INPUT', message: `Invalid mergeStatus: ${body.mergeStatus}` } }, 400);
220
+ }
221
+ const existingMeta = (task.metadata ?? {});
222
+ const existingOrchestrator = (existingMeta.orchestrator ?? {});
223
+ updates.metadata = {
224
+ ...existingMeta,
225
+ orchestrator: {
226
+ ...existingOrchestrator,
227
+ mergeStatus: body.mergeStatus,
228
+ },
229
+ };
230
+ }
231
+ if (Object.keys(updates).length === 0) {
232
+ return c.json({ task: formatTaskResponse(task) });
233
+ }
234
+ const updatedTask = await api.update(taskId, updates);
235
+ return c.json({ task: formatTaskResponse(updatedTask) });
236
+ }
237
+ catch (error) {
238
+ console.error('[orchestrator] Failed to update task:', error);
239
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
240
+ }
241
+ });
242
+ // GET /api/tasks/:id
243
+ app.get('/api/tasks/:id', async (c) => {
244
+ try {
245
+ const taskId = c.req.param('id');
246
+ const task = await api.get(taskId);
247
+ if (!task || task.type !== ElementType.TASK) {
248
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
249
+ }
250
+ let assignmentInfo = null;
251
+ if (task.assignee) {
252
+ const agent = await agentRegistry.getAgent(task.assignee);
253
+ if (agent) {
254
+ const meta = task.metadata?.orchestrator;
255
+ assignmentInfo = {
256
+ agent: {
257
+ id: agent.id,
258
+ name: agent.name,
259
+ role: agent.metadata?.agent?.agentRole,
260
+ },
261
+ branch: meta?.branch,
262
+ worktree: meta?.worktree,
263
+ sessionId: meta?.sessionId,
264
+ mergeStatus: meta?.mergeStatus,
265
+ startedAt: meta?.startedAt,
266
+ completedAt: meta?.completedAt,
267
+ };
268
+ }
269
+ }
270
+ // Hydrate description from descriptionRef if it exists
271
+ const formattedTask = await formatTaskWithDescription(task, api);
272
+ return c.json({ task: formattedTask, assignment: assignmentInfo });
273
+ }
274
+ catch (error) {
275
+ console.error('[orchestrator] Failed to get task:', error);
276
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
277
+ }
278
+ });
279
+ // DELETE /api/tasks/:id - Soft delete task
280
+ app.delete('/api/tasks/:id', async (c) => {
281
+ try {
282
+ const taskId = c.req.param('id');
283
+ const body = (await c.req.json().catch(() => ({})));
284
+ const task = await api.get(taskId);
285
+ if (!task || task.type !== ElementType.TASK) {
286
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
287
+ }
288
+ // Soft delete by setting status to tombstone
289
+ await api.update(taskId, {
290
+ status: TaskStatus.TOMBSTONE,
291
+ deletedAt: new Date().toISOString(),
292
+ deleteReason: body.reason,
293
+ });
294
+ return c.json({ success: true });
295
+ }
296
+ catch (error) {
297
+ console.error('[orchestrator] Failed to delete task:', error);
298
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
299
+ }
300
+ });
301
+ // POST /api/tasks/bulk-delete - Bulk soft delete tasks
302
+ app.post('/api/tasks/bulk-delete', async (c) => {
303
+ try {
304
+ const body = (await c.req.json());
305
+ if (!Array.isArray(body.ids) || body.ids.length === 0) {
306
+ return c.json({ error: { code: 'BAD_REQUEST', message: 'ids must be a non-empty array' } }, 400);
307
+ }
308
+ const results = [];
309
+ for (const id of body.ids) {
310
+ try {
311
+ const task = await api.get(id);
312
+ if (!task || task.type !== ElementType.TASK) {
313
+ results.push({ id, success: false, error: 'Task not found' });
314
+ continue;
315
+ }
316
+ await api.update(id, {
317
+ status: TaskStatus.TOMBSTONE,
318
+ deletedAt: new Date().toISOString(),
319
+ });
320
+ results.push({ id, success: true });
321
+ }
322
+ catch (err) {
323
+ results.push({ id, success: false, error: String(err) });
324
+ }
325
+ }
326
+ return c.json({ success: true, results });
327
+ }
328
+ catch (error) {
329
+ console.error('[orchestrator] Failed to bulk delete tasks:', error);
330
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
331
+ }
332
+ });
333
+ // POST /api/tasks/:id/start - Start task (set to in_progress)
334
+ app.post('/api/tasks/:id/start', async (c) => {
335
+ try {
336
+ const taskId = c.req.param('id');
337
+ const task = await api.get(taskId);
338
+ if (!task || task.type !== ElementType.TASK) {
339
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
340
+ }
341
+ if (task.status !== TaskStatus.OPEN) {
342
+ return c.json({ error: { code: 'INVALID_STATE', message: 'Task must be in open status to start' } }, 400);
343
+ }
344
+ const updatedTask = await api.update(taskId, {
345
+ status: TaskStatus.IN_PROGRESS,
346
+ });
347
+ return c.json({ task: formatTaskResponse(updatedTask) });
348
+ }
349
+ catch (error) {
350
+ console.error('[orchestrator] Failed to start task:', error);
351
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
352
+ }
353
+ });
354
+ // POST /api/tasks/:id/dispatch
355
+ app.post('/api/tasks/:id/dispatch', async (c) => {
356
+ try {
357
+ const taskId = c.req.param('id');
358
+ const body = (await c.req.json());
359
+ if (!body.agentId) {
360
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'agentId is required' } }, 400);
361
+ }
362
+ const task = await api.get(taskId);
363
+ if (!task || task.type !== ElementType.TASK) {
364
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
365
+ }
366
+ const agent = await agentRegistry.getAgent(body.agentId);
367
+ if (!agent) {
368
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Agent not found' } }, 404);
369
+ }
370
+ const result = await dispatchService.dispatch(taskId, body.agentId, {
371
+ priority: body.priority,
372
+ restart: body.restart,
373
+ markAsStarted: body.markAsStarted,
374
+ branch: body.branch,
375
+ worktree: body.worktree,
376
+ notificationMessage: body.notificationMessage,
377
+ dispatchedBy: body.dispatchedBy,
378
+ });
379
+ return c.json({
380
+ success: true,
381
+ task: formatTaskResponse(result.task),
382
+ agent: { id: result.agent.id, name: result.agent.name },
383
+ notification: { id: result.notification.id, channelId: result.channel.id },
384
+ isNewAssignment: result.isNewAssignment,
385
+ dispatchedAt: result.dispatchedAt,
386
+ });
387
+ }
388
+ catch (error) {
389
+ console.error('[orchestrator] Failed to dispatch task:', error);
390
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
391
+ }
392
+ });
393
+ // POST /api/tasks/:id/start-worker
394
+ app.post('/api/tasks/:id/start-worker', async (c) => {
395
+ try {
396
+ const taskId = c.req.param('id');
397
+ const body = (await c.req.json());
398
+ if (!body.agentId) {
399
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'agentId is required' } }, 400);
400
+ }
401
+ const task = await api.get(taskId);
402
+ if (!task || task.type !== ElementType.TASK) {
403
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
404
+ }
405
+ const agent = await agentRegistry.getAgent(body.agentId);
406
+ if (!agent) {
407
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Agent not found' } }, 404);
408
+ }
409
+ const result = await workerTaskService.startWorkerOnTask(taskId, body.agentId, {
410
+ branch: body.branch,
411
+ worktreePath: body.worktreePath,
412
+ baseBranch: body.baseBranch,
413
+ additionalPrompt: body.additionalPrompt,
414
+ skipWorktree: body.skipWorktree,
415
+ workingDirectory: body.workingDirectory,
416
+ priority: body.priority,
417
+ performedBy: body.performedBy,
418
+ });
419
+ return c.json({
420
+ success: true,
421
+ task: formatTaskResponse(result.task),
422
+ agent: { id: result.agent.id, name: result.agent.name },
423
+ session: {
424
+ id: result.session.id,
425
+ providerSessionId: result.session.providerSessionId,
426
+ status: result.session.status,
427
+ workingDirectory: result.session.workingDirectory,
428
+ },
429
+ worktree: result.worktree
430
+ ? { path: result.worktree.path, branch: result.worktree.branch, branchCreated: result.worktree.branchCreated }
431
+ : null,
432
+ dispatch: {
433
+ notificationId: result.dispatch.notification.id,
434
+ channelId: result.dispatch.channel.id,
435
+ isNewAssignment: result.dispatch.isNewAssignment,
436
+ },
437
+ startedAt: result.startedAt,
438
+ }, 201);
439
+ }
440
+ catch (error) {
441
+ const errorMessage = String(error);
442
+ if (errorMessage.includes('not a worker')) {
443
+ return c.json({ error: { code: 'INVALID_AGENT', message: 'Agent is not a worker' } }, 400);
444
+ }
445
+ console.error('[orchestrator] Failed to start worker on task:', error);
446
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: errorMessage } }, 500);
447
+ }
448
+ });
449
+ // POST /api/tasks/:id/complete
450
+ app.post('/api/tasks/:id/complete', async (c) => {
451
+ try {
452
+ const taskId = c.req.param('id');
453
+ const body = (await c.req.json().catch(() => ({})));
454
+ const task = await api.get(taskId);
455
+ if (!task || task.type !== ElementType.TASK) {
456
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
457
+ }
458
+ const result = await workerTaskService.completeTask(taskId, {
459
+ summary: body.summary,
460
+ commitHash: body.commitHash,
461
+ performedBy: body.performedBy,
462
+ });
463
+ return c.json({
464
+ success: true,
465
+ task: formatTaskResponse(result.task),
466
+ worktree: result.worktree
467
+ ? { path: result.worktree.path, branch: result.worktree.branch, state: result.worktree.state }
468
+ : null,
469
+ readyForMerge: result.readyForMerge,
470
+ completedAt: result.completedAt,
471
+ });
472
+ }
473
+ catch (error) {
474
+ console.error('[orchestrator] Failed to complete task:', error);
475
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
476
+ }
477
+ });
478
+ // POST /api/tasks/:id/reset - Reset task to open, clearing all work-in-progress data
479
+ app.post('/api/tasks/:id/reset', async (c) => {
480
+ try {
481
+ const taskId = c.req.param('id');
482
+ const task = await api.get(taskId);
483
+ if (!task || task.type !== ElementType.TASK) {
484
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
485
+ }
486
+ // Only allow reset if task has an assignee or is in_progress/review/closed
487
+ const canReset = task.assignee ||
488
+ task.status === TaskStatus.IN_PROGRESS ||
489
+ task.status === TaskStatus.REVIEW ||
490
+ task.status === TaskStatus.CLOSED;
491
+ if (!canReset) {
492
+ return c.json({ error: { code: 'BAD_REQUEST', message: 'Task cannot be reset - no assignee or work-in-progress state' } }, 400);
493
+ }
494
+ // Update status to OPEN (clears closedAt)
495
+ const updated = updateTaskStatus(task, { status: TaskStatus.OPEN });
496
+ // Clear assignee
497
+ updated.assignee = undefined;
498
+ updated.closeReason = undefined;
499
+ // Clear all orchestrator metadata
500
+ const existingMeta = updated.metadata?.orchestrator;
501
+ if (existingMeta) {
502
+ const reconciliationCount = existingMeta.reconciliationCount ?? 0;
503
+ updated.metadata = updateOrchestratorTaskMeta(updated.metadata, {
504
+ // Clear all orchestrator fields
505
+ branch: undefined,
506
+ worktree: undefined,
507
+ mergeStatus: undefined,
508
+ mergedAt: undefined,
509
+ mergeFailureReason: undefined,
510
+ assignedAgent: undefined,
511
+ sessionId: undefined,
512
+ startedAt: undefined,
513
+ completedAt: undefined,
514
+ completionSummary: undefined,
515
+ lastCommitHash: undefined,
516
+ testRunCount: undefined,
517
+ lastTestResult: undefined,
518
+ lastSyncResult: undefined,
519
+ reconciliationCount: reconciliationCount + 1,
520
+ });
521
+ }
522
+ // Save the update
523
+ await api.update(taskId, updated, {
524
+ expectedUpdatedAt: task.updatedAt,
525
+ });
526
+ // Re-fetch the task to get the latest state
527
+ const finalTask = await api.get(taskId);
528
+ const hydratedDescription = await hydrateTaskDescription(finalTask, api);
529
+ return c.json({
530
+ success: true,
531
+ task: formatTaskResponse(finalTask, hydratedDescription),
532
+ resetAt: finalTask.updatedAt,
533
+ });
534
+ }
535
+ catch (error) {
536
+ console.error('[orchestrator] Failed to reset task:', error);
537
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
538
+ }
539
+ });
540
+ // POST /api/tasks/:id/reopen
541
+ app.post('/api/tasks/:id/reopen', async (c) => {
542
+ try {
543
+ const taskId = c.req.param('id');
544
+ const body = (await c.req.json().catch(() => ({})));
545
+ const task = await api.get(taskId);
546
+ if (!task || task.type !== ElementType.TASK) {
547
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
548
+ }
549
+ if (task.status !== TaskStatus.CLOSED) {
550
+ return c.json({ error: { code: 'BAD_REQUEST', message: `Task is not closed (status: ${task.status})` } }, 400);
551
+ }
552
+ // Update status to OPEN (clears closedAt)
553
+ const updated = updateTaskStatus(task, { status: TaskStatus.OPEN });
554
+ // Clear assignee and closeReason
555
+ updated.assignee = undefined;
556
+ updated.closeReason = undefined;
557
+ // Clear orchestrator metadata fields while preserving branch/worktree/handoff info
558
+ const existingMeta = updated.metadata?.orchestrator;
559
+ if (existingMeta) {
560
+ const reconciliationCount = existingMeta.reconciliationCount ?? 0;
561
+ updated.metadata = updateOrchestratorTaskMeta(updated.metadata, {
562
+ mergeStatus: undefined,
563
+ mergedAt: undefined,
564
+ mergeFailureReason: undefined,
565
+ assignedAgent: undefined,
566
+ sessionId: undefined,
567
+ startedAt: undefined,
568
+ completedAt: undefined,
569
+ completionSummary: undefined,
570
+ lastCommitHash: undefined,
571
+ testRunCount: undefined,
572
+ lastTestResult: undefined,
573
+ lastSyncResult: undefined,
574
+ reconciliationCount: reconciliationCount + 1,
575
+ });
576
+ }
577
+ // Save the update
578
+ await api.update(taskId, updated, {
579
+ expectedUpdatedAt: task.updatedAt,
580
+ });
581
+ // If message provided, append to or create description document
582
+ if (body.message) {
583
+ const reopenLine = `**Re-opened** — Task was closed but incomplete. Message: ${body.message}`;
584
+ if (task.descriptionRef) {
585
+ const doc = await api.get(task.descriptionRef);
586
+ if (doc) {
587
+ await api.update(task.descriptionRef, {
588
+ content: doc.content + '\n\n' + reopenLine,
589
+ });
590
+ }
591
+ }
592
+ else {
593
+ const newDoc = await createDocument({
594
+ content: reopenLine,
595
+ contentType: ContentType.MARKDOWN,
596
+ createdBy: 'orchestrator',
597
+ });
598
+ const created = await api.create(newDoc);
599
+ await api.update(taskId, { descriptionRef: created.id });
600
+ }
601
+ }
602
+ // Re-fetch the task to get the latest state (including any descriptionRef changes)
603
+ const finalTask = await api.get(taskId);
604
+ const hydratedDescription = await hydrateTaskDescription(finalTask, api);
605
+ return c.json({
606
+ success: true,
607
+ task: formatTaskResponse(finalTask, hydratedDescription),
608
+ reopenedAt: finalTask.updatedAt,
609
+ });
610
+ }
611
+ catch (error) {
612
+ console.error('[orchestrator] Failed to reopen task:', error);
613
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
614
+ }
615
+ });
616
+ // GET /api/tasks/:id/context
617
+ app.get('/api/tasks/:id/context', async (c) => {
618
+ try {
619
+ const taskId = c.req.param('id');
620
+ const url = new URL(c.req.url);
621
+ const workerIdParam = url.searchParams.get('workerId');
622
+ const additionalInstructions = url.searchParams.get('additionalInstructions') ?? undefined;
623
+ const task = await api.get(taskId);
624
+ if (!task || task.type !== ElementType.TASK) {
625
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
626
+ }
627
+ // Use workerId from query param, or fall back to task assignee
628
+ const workerId = workerIdParam || task.assignee;
629
+ if (!workerId) {
630
+ return c.json({ error: { code: 'BAD_REQUEST', message: 'workerId query parameter required when task has no assignee' } }, 400);
631
+ }
632
+ const context = await workerTaskService.getTaskContext(taskId);
633
+ const prompt = await workerTaskService.buildTaskContextPrompt(taskId, workerId, additionalInstructions);
634
+ return c.json({ task: { id: task.id, title: task.title }, context, prompt });
635
+ }
636
+ catch (error) {
637
+ console.error('[orchestrator] Failed to get task context:', error);
638
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
639
+ }
640
+ });
641
+ // POST /api/tasks/:id/cleanup
642
+ app.post('/api/tasks/:id/cleanup', async (c) => {
643
+ try {
644
+ const taskId = c.req.param('id');
645
+ const body = (await c.req.json().catch(() => ({})));
646
+ const task = await api.get(taskId);
647
+ if (!task || task.type !== ElementType.TASK) {
648
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
649
+ }
650
+ const success = await workerTaskService.cleanupTask(taskId, body.deleteBranch ?? false);
651
+ return c.json({ success, taskId, deletedBranch: success && (body.deleteBranch ?? false) });
652
+ }
653
+ catch (error) {
654
+ console.error('[orchestrator] Failed to cleanup task:', error);
655
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
656
+ }
657
+ });
658
+ // ============================================================================
659
+ // Task Attachments Endpoints
660
+ // ============================================================================
661
+ // GET /api/tasks/:id/attachments - Get all documents attached to a task
662
+ app.get('/api/tasks/:id/attachments', async (c) => {
663
+ try {
664
+ const taskId = c.req.param('id');
665
+ // Verify task exists
666
+ const task = await api.get(taskId);
667
+ if (!task || task.type !== ElementType.TASK) {
668
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
669
+ }
670
+ // Get all dependencies where this task references a document
671
+ const dependencies = await api.getDependencies(taskId);
672
+ const attachmentDeps = dependencies.filter((dep) => dep.blockedId === taskId && dep.type === 'references');
673
+ // Get the document details for each attachment
674
+ const attachments = await Promise.all(attachmentDeps.map(async (dep) => {
675
+ const doc = await api.get(dep.blockerId);
676
+ if (doc && doc.type === 'document') {
677
+ return doc;
678
+ }
679
+ return null;
680
+ }));
681
+ // Filter out nulls (in case documents were deleted)
682
+ return c.json(attachments.filter(Boolean));
683
+ }
684
+ catch (error) {
685
+ console.error('[orchestrator] Failed to get task attachments:', error);
686
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
687
+ }
688
+ });
689
+ // POST /api/tasks/:id/attachments - Attach a document to a task
690
+ app.post('/api/tasks/:id/attachments', async (c) => {
691
+ try {
692
+ const taskId = c.req.param('id');
693
+ const body = (await c.req.json());
694
+ // Validate document ID
695
+ if (!body.documentId || typeof body.documentId !== 'string') {
696
+ return c.json({ error: { code: 'VALIDATION_ERROR', message: 'documentId is required' } }, 400);
697
+ }
698
+ // Verify task exists
699
+ const task = await api.get(taskId);
700
+ if (!task || task.type !== ElementType.TASK) {
701
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
702
+ }
703
+ // Verify document exists
704
+ const doc = await api.get(body.documentId);
705
+ if (!doc || doc.type !== 'document') {
706
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Document not found' } }, 404);
707
+ }
708
+ // Check if already attached
709
+ const existingDeps = await api.getDependencies(taskId);
710
+ const alreadyAttached = existingDeps.some((dep) => dep.blockedId === taskId && dep.blockerId === body.documentId && dep.type === 'references');
711
+ if (alreadyAttached) {
712
+ return c.json({ error: { code: 'VALIDATION_ERROR', message: 'Document is already attached to this task' } }, 400);
713
+ }
714
+ // Create the references dependency
715
+ await api.addDependency({
716
+ blockedId: taskId,
717
+ blockerId: body.documentId,
718
+ type: 'references',
719
+ actor: body.actor || 'el-0000',
720
+ });
721
+ return c.json(doc, 201);
722
+ }
723
+ catch (error) {
724
+ console.error('[orchestrator] Failed to attach document to task:', error);
725
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
726
+ }
727
+ });
728
+ // DELETE /api/tasks/:id/attachments/:docId - Remove a document attachment
729
+ app.delete('/api/tasks/:id/attachments/:docId', async (c) => {
730
+ try {
731
+ const taskId = c.req.param('id');
732
+ const docId = c.req.param('docId');
733
+ // Verify task exists
734
+ const task = await api.get(taskId);
735
+ if (!task || task.type !== ElementType.TASK) {
736
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
737
+ }
738
+ // Find the attachment dependency
739
+ const dependencies = await api.getDependencies(taskId);
740
+ const attachmentDep = dependencies.find((dep) => dep.blockedId === taskId && dep.blockerId === docId && dep.type === 'references');
741
+ if (!attachmentDep) {
742
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Attachment not found' } }, 404);
743
+ }
744
+ // Remove the dependency
745
+ await api.removeDependency(taskId, docId, 'references');
746
+ return c.json({ success: true, taskId, documentId: docId });
747
+ }
748
+ catch (error) {
749
+ console.error('[orchestrator] Failed to remove task attachment:', error);
750
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
751
+ }
752
+ });
753
+ // ============================================================================
754
+ // Task Dependencies Endpoints
755
+ // ============================================================================
756
+ // Blocking dependency types that prevent a task from proceeding
757
+ const BLOCKING_DEPENDENCY_TYPES = ['blocks', 'parent-child', 'awaits'];
758
+ // GET /api/tasks/:id/dependency-tasks - Get hydrated dependency info for UI display
759
+ app.get('/api/tasks/:id/dependency-tasks', async (c) => {
760
+ try {
761
+ const taskId = c.req.param('id');
762
+ // Verify task exists
763
+ const task = await api.get(taskId);
764
+ if (!task || task.type !== ElementType.TASK) {
765
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
766
+ }
767
+ // Get tasks that block this task (blockedBy)
768
+ const dependencies = await api.getDependencies(taskId);
769
+ const blockingDeps = dependencies.filter((dep) => dep.blockedId === taskId && BLOCKING_DEPENDENCY_TYPES.includes(dep.type));
770
+ // Get tasks that this task blocks (blocks)
771
+ const dependents = await api.getDependents(taskId);
772
+ const blockedDeps = dependents.filter((dep) => dep.blockerId === taskId && BLOCKING_DEPENDENCY_TYPES.includes(dep.type));
773
+ // Hydrate task details for blockedBy
774
+ const blockedBy = await Promise.all(blockingDeps.map(async (dep) => {
775
+ const blockerTask = await api.get(dep.blockerId);
776
+ if (blockerTask && blockerTask.type === ElementType.TASK) {
777
+ return {
778
+ dependencyType: dep.type,
779
+ task: {
780
+ id: blockerTask.id,
781
+ title: blockerTask.title,
782
+ status: blockerTask.status,
783
+ priority: blockerTask.priority,
784
+ },
785
+ };
786
+ }
787
+ return null;
788
+ }));
789
+ // Hydrate task details for blocks
790
+ const blocks = await Promise.all(blockedDeps.map(async (dep) => {
791
+ const blockedTask = await api.get(dep.blockedId);
792
+ if (blockedTask && blockedTask.type === ElementType.TASK) {
793
+ return {
794
+ dependencyType: dep.type,
795
+ task: {
796
+ id: blockedTask.id,
797
+ title: blockedTask.title,
798
+ status: blockedTask.status,
799
+ priority: blockedTask.priority,
800
+ },
801
+ };
802
+ }
803
+ return null;
804
+ }));
805
+ // Filter out nulls
806
+ const filteredBlockedBy = blockedBy.filter(Boolean);
807
+ const filteredBlocks = blocks.filter(Boolean);
808
+ // Calculate progress (resolved = closed blockers)
809
+ const resolvedCount = filteredBlockedBy.filter((dep) => {
810
+ return dep?.task?.status === TaskStatus.CLOSED;
811
+ }).length;
812
+ return c.json({
813
+ blockedBy: filteredBlockedBy,
814
+ blocks: filteredBlocks,
815
+ progress: {
816
+ resolved: resolvedCount,
817
+ total: filteredBlockedBy.length,
818
+ },
819
+ });
820
+ }
821
+ catch (error) {
822
+ console.error('[orchestrator] Failed to get task dependencies:', error);
823
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
824
+ }
825
+ });
826
+ // POST /api/tasks/:id/dependencies - Add a blocking dependency to a task
827
+ app.post('/api/tasks/:id/dependencies', async (c) => {
828
+ try {
829
+ const taskId = c.req.param('id');
830
+ const body = (await c.req.json());
831
+ if (!body.blockerId) {
832
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'blockerId is required' } }, 400);
833
+ }
834
+ const blockerId = body.blockerId;
835
+ const dependencyType = body.type || 'blocks';
836
+ // Verify both tasks exist
837
+ const task = await api.get(taskId);
838
+ if (!task || task.type !== ElementType.TASK) {
839
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
840
+ }
841
+ const blockerTask = await api.get(blockerId);
842
+ if (!blockerTask || blockerTask.type !== ElementType.TASK) {
843
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Blocker task not found' } }, 404);
844
+ }
845
+ // Prevent self-reference
846
+ if (taskId === blockerId) {
847
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'A task cannot block itself' } }, 400);
848
+ }
849
+ // Add the dependency (blocker blocks this task)
850
+ await api.addDependency({
851
+ blockedId: taskId,
852
+ blockerId: blockerId,
853
+ type: dependencyType,
854
+ actor: 'el-0000', // System actor for UI operations
855
+ });
856
+ return c.json({
857
+ success: true,
858
+ dependency: {
859
+ blockedId: taskId,
860
+ blockerId: blockerId,
861
+ type: dependencyType,
862
+ },
863
+ });
864
+ }
865
+ catch (error) {
866
+ console.error('[orchestrator] Failed to add dependency:', error);
867
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
868
+ }
869
+ });
870
+ // DELETE /api/tasks/:id/dependencies/:blockerId - Remove a blocking dependency from a task
871
+ app.delete('/api/tasks/:id/dependencies/:blockerId', async (c) => {
872
+ try {
873
+ const taskId = c.req.param('id');
874
+ const blockerId = c.req.param('blockerId');
875
+ // Verify task exists
876
+ const task = await api.get(taskId);
877
+ if (!task || task.type !== ElementType.TASK) {
878
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
879
+ }
880
+ // Find the dependency to get its type
881
+ const dependencies = await api.getDependencies(taskId);
882
+ const dependency = dependencies.find((dep) => dep.blockerId === blockerId && BLOCKING_DEPENDENCY_TYPES.includes(dep.type));
883
+ if (!dependency) {
884
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Dependency not found' } }, 404);
885
+ }
886
+ // Remove the dependency with the correct type
887
+ await api.removeDependency(taskId, blockerId, dependency.type, 'el-0000');
888
+ return c.json({
889
+ success: true,
890
+ taskId,
891
+ blockerId,
892
+ });
893
+ }
894
+ catch (error) {
895
+ console.error('[orchestrator] Failed to remove dependency:', error);
896
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
897
+ }
898
+ });
899
+ // ============================================================================
900
+ // Merge Status Endpoints
901
+ // ============================================================================
902
+ // Valid merge status values
903
+ const VALID_MERGE_STATUSES = [
904
+ 'pending',
905
+ 'testing',
906
+ 'merging',
907
+ 'merged',
908
+ 'conflict',
909
+ 'test_failed',
910
+ 'failed',
911
+ 'not_applicable',
912
+ ];
913
+ // PATCH /api/tasks/:id/merge-status - Update merge status
914
+ app.patch('/api/tasks/:id/merge-status', async (c) => {
915
+ try {
916
+ const taskId = c.req.param('id');
917
+ const body = (await c.req.json());
918
+ if (!body.mergeStatus) {
919
+ return c.json({ error: { code: 'INVALID_INPUT', message: 'mergeStatus is required' } }, 400);
920
+ }
921
+ if (!VALID_MERGE_STATUSES.includes(body.mergeStatus)) {
922
+ return c.json({
923
+ error: {
924
+ code: 'INVALID_INPUT',
925
+ message: `Invalid mergeStatus. Must be one of: ${VALID_MERGE_STATUSES.join(', ')}`,
926
+ },
927
+ }, 400);
928
+ }
929
+ const task = await api.get(taskId);
930
+ if (!task || task.type !== ElementType.TASK) {
931
+ return c.json({ error: { code: 'NOT_FOUND', message: 'Task not found' } }, 404);
932
+ }
933
+ // Update orchestrator metadata with new merge status
934
+ const existingMeta = (task.metadata ?? {});
935
+ const updatedMeta = updateOrchestratorTaskMeta(existingMeta, {
936
+ mergeStatus: body.mergeStatus,
937
+ // If setting to merged, also set mergedAt timestamp
938
+ ...(body.mergeStatus === 'merged' ? { mergedAt: new Date().toISOString() } : {}),
939
+ // Clear failure reason if status is no longer a failure state
940
+ ...(body.mergeStatus !== 'conflict' && body.mergeStatus !== 'test_failed' && body.mergeStatus !== 'failed'
941
+ ? { mergeFailureReason: undefined }
942
+ : {}),
943
+ });
944
+ const updatedTask = await api.update(taskId, { metadata: updatedMeta });
945
+ return c.json({ task: formatTaskResponse(updatedTask) });
946
+ }
947
+ catch (error) {
948
+ console.error('[orchestrator] Failed to update merge status:', error);
949
+ return c.json({ error: { code: 'INTERNAL_ERROR', message: String(error) } }, 500);
950
+ }
951
+ });
952
+ return app;
953
+ }
954
+ //# sourceMappingURL=tasks.js.map