@agent-spaces/server 0.3.61 → 0.3.63

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 (327) hide show
  1. package/dist/adapters/agent-runtime-types.js +12 -0
  2. package/dist/adapters/claude-code-runtime/index.js +22 -0
  3. package/dist/adapters/claude-code-runtime/sdk-config.js +34 -10
  4. package/dist/adapters/codex-runtime.js +2 -2
  5. package/dist/adapters/git.js +29 -13
  6. package/dist/adapters/langchain-runtime.js +2 -2
  7. package/dist/adapters/open-agent-sdk-runtime.js +2 -2
  8. package/dist/agents/agent-message-parts.js +27 -3
  9. package/dist/agents/commit-agent.js +1 -0
  10. package/dist/agents/issue-agent-progress.js +4 -2
  11. package/dist/agents/issue-agent-runner.js +5 -5
  12. package/dist/agents/issue-task-controller.js +95 -14
  13. package/dist/app.js +74 -5
  14. package/dist/package.json +1 -1
  15. package/dist/routes/agent-sse.js +4 -2
  16. package/dist/routes/channel.js +21 -4
  17. package/dist/routes/file.js +87 -1
  18. package/dist/routes/hooks.js +88 -0
  19. package/dist/routes/issue.js +59 -1
  20. package/dist/routes/output-style.js +48 -0
  21. package/dist/routes/prompt-template.js +2 -2
  22. package/dist/routes/skill.js +97 -3
  23. package/dist/routes/task.js +13 -1
  24. package/dist/services/agent.js +45 -16
  25. package/dist/services/hook-engine.js +118 -0
  26. package/dist/services/issue.js +6 -0
  27. package/dist/services/notification-hub/bot-agent.js +1 -0
  28. package/dist/services/output-style.js +134 -0
  29. package/dist/services/persistent-agent-context.js +37 -4
  30. package/dist/services/prompt-template.js +2 -2
  31. package/dist/services/skill.js +275 -40
  32. package/dist/storage/hook-store.js +55 -0
  33. package/dist/web/404.html +1 -1
  34. package/dist/web/__next.__PAGE__.txt +4 -4
  35. package/dist/web/__next._full.txt +25 -23
  36. package/dist/web/__next._head.txt +4 -4
  37. package/dist/web/__next._index.txt +13 -13
  38. package/dist/web/__next._tree.txt +2 -2
  39. package/dist/web/_next/static/chunks/0-1bfvfwifh-m.js +1 -0
  40. package/dist/web/_next/static/chunks/0-f~0xmwl__u5.js +1 -0
  41. package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +1 -0
  42. package/dist/web/_next/static/chunks/002_j970b9gdx.js +1 -0
  43. package/dist/web/_next/static/chunks/014x2qbuiszni.js +3 -0
  44. package/dist/web/_next/static/chunks/01iqv28_snwus.js +1 -0
  45. package/dist/web/_next/static/chunks/02c22g~xd-y30.js +19 -0
  46. package/dist/web/_next/static/chunks/02mf61htij8g0.js +31 -0
  47. package/dist/web/_next/static/chunks/03scq1-0ykve3.js +1 -0
  48. package/dist/web/_next/static/chunks/046me66nilkff.js +1 -0
  49. package/dist/web/_next/static/chunks/04v-pqqs-2lvp.css +1 -0
  50. package/dist/web/_next/static/chunks/05f8cnmzqd5wb.js +1 -0
  51. package/dist/web/_next/static/chunks/{0zd1o3u04~qvo.js → 05psi2gtx~bev.js} +7 -7
  52. package/dist/web/_next/static/chunks/{12odetnu-lyrr.js → 06whz1~s_7pva.js} +12 -12
  53. package/dist/web/_next/static/chunks/06w~.izyfylap.js +1 -0
  54. package/dist/web/_next/static/chunks/08kfx8on9oty~.js +1 -0
  55. package/dist/web/_next/static/chunks/09t62a89~jesc.js +3 -0
  56. package/dist/web/_next/static/chunks/09y_3krccdd~u.js +1 -0
  57. package/dist/web/_next/static/chunks/{0njy89g5coeyi.js → 0_8dm7bxp18xo.js} +6 -6
  58. package/dist/web/_next/static/chunks/0_e~hmntdfop9.js +1 -0
  59. package/dist/web/_next/static/chunks/0_udc.6.tueey.js +1 -0
  60. package/dist/web/_next/static/chunks/0_vi1a~g4wx46.js +3 -0
  61. package/dist/web/_next/static/chunks/{05iy4.c-1cfhd.js → 0aa6bcdmtpekn.js} +1 -1
  62. package/dist/web/_next/static/chunks/{0tvd~rxgf0oh_.js → 0bbmx45w_h5np.js} +1 -1
  63. package/dist/web/_next/static/chunks/0cke33d38fnby.js +90 -0
  64. package/dist/web/_next/static/chunks/{0w1iudd1d46sw.js → 0ctg5qmw7uu.1.js} +2 -2
  65. package/dist/web/_next/static/chunks/0cyq~8xb~r5fc.js +1 -0
  66. package/dist/web/_next/static/chunks/0ewceguykiut5.js +1 -0
  67. package/dist/web/_next/static/chunks/0fxa1edqr7dve.js +1 -0
  68. package/dist/web/_next/static/chunks/{0202ia13sijwb.js → 0h18ej5yf974e.js} +1 -1
  69. package/dist/web/_next/static/chunks/0hra80eyuu37u.js +1 -0
  70. package/dist/web/_next/static/chunks/0i39jifmxla7q.js +1 -0
  71. package/dist/web/_next/static/chunks/0ie7y4k2ncvnn.js +1 -0
  72. package/dist/web/_next/static/chunks/0igc~uli58ncw.js +2 -0
  73. package/dist/web/_next/static/chunks/0kof5qdg8c2-k.js +71 -0
  74. package/dist/web/_next/static/chunks/{08o-zmr-40r5g.js → 0m11vvph7q94-.js} +1 -1
  75. package/dist/web/_next/static/chunks/0msb2mi3hiu5_.js +67 -0
  76. package/dist/web/_next/static/chunks/0np.d~hc1-pmh.js +4 -0
  77. package/dist/web/_next/static/chunks/{0qpq0~_58fe9a.js → 0oaq67r7ix.vg.js} +1 -1
  78. package/dist/web/_next/static/chunks/0ogpc3f3keopc.js +3 -0
  79. package/dist/web/_next/static/chunks/{0d_dp_4ofz0l3.css → 0ojcao3hdm36s.css} +0 -1
  80. package/dist/web/_next/static/chunks/0p1n8bvfnpl41.js +1 -0
  81. package/dist/web/_next/static/chunks/{0gig.k.rq8d0x.js → 0ppr271hz0xoq.js} +1 -1
  82. package/dist/web/_next/static/chunks/0s_dva~eh2yhi.js +3 -0
  83. package/dist/web/_next/static/chunks/0typj55ox8d5i.js +1 -0
  84. package/dist/web/_next/static/chunks/0w2p4ldi2aymp.js +1 -0
  85. package/dist/web/_next/static/chunks/{0..g0jl-8qup7.js → 0xoo17yef_b8g.js} +1 -1
  86. package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +1 -0
  87. package/dist/web/_next/static/chunks/0y09i51u6ixq5.js +1 -0
  88. package/dist/web/_next/static/chunks/0za1r07-6az6w.js +1 -0
  89. package/dist/web/_next/static/chunks/0zc0xgp5ourcn.js +1 -0
  90. package/dist/web/_next/static/chunks/0zmhdridea1s5.js +90 -0
  91. package/dist/web/_next/static/chunks/0~u.5r3-t7179.js +1 -0
  92. package/dist/web/_next/static/chunks/10.alq.6stmld.js +1 -0
  93. package/dist/web/_next/static/chunks/11-czmcci_55y.js +1 -0
  94. package/dist/web/_next/static/chunks/11b3zgk1etlkw.js +7 -0
  95. package/dist/web/_next/static/chunks/11k-.49r~1mfm.js +1 -0
  96. package/dist/web/_next/static/chunks/11qp8r5dotf7d.js +1 -0
  97. package/dist/web/_next/static/chunks/128ugqd34tr_-.js +1 -0
  98. package/dist/web/_next/static/chunks/12mxlicp247c8.js +1 -0
  99. package/dist/web/_next/static/chunks/135w27dd8c19s.js +5 -0
  100. package/dist/web/_next/static/chunks/13~y-b.b9rrw4.js +1 -0
  101. package/dist/web/_next/static/chunks/{0jokpgh485~28.js → 14hqmid8q4apf.js} +11 -11
  102. package/dist/web/_next/static/chunks/16sg-lflc8csx.js +1 -0
  103. package/dist/web/_next/static/chunks/176dc-dtokxxi.js +16 -0
  104. package/dist/web/_next/static/chunks/17g-0pqre.anw.js +1 -0
  105. package/dist/web/_next/static/chunks/17oc2l.ekcs8b.css +1 -0
  106. package/dist/web/_next/static/chunks/{turbopack-0lrdxynxb9-1t.js → turbopack-0ndm2zc_pdptk.js} +1 -1
  107. package/dist/web/_not-found/__next._full.txt +29 -30
  108. package/dist/web/_not-found/__next._head.txt +4 -4
  109. package/dist/web/_not-found/__next._index.txt +13 -13
  110. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  111. package/dist/web/_not-found/__next._not-found.txt +3 -3
  112. package/dist/web/_not-found/__next._tree.txt +2 -2
  113. package/dist/web/_not-found.html +1 -1
  114. package/dist/web/_not-found.txt +29 -30
  115. package/dist/web/icons/file_type_audio.svg +3 -1
  116. package/dist/web/icons/file_type_video.svg +10 -1
  117. package/dist/web/index.html +1 -1
  118. package/dist/web/index.txt +25 -23
  119. package/dist/web/login/__next._full.txt +30 -28
  120. package/dist/web/login/__next._head.txt +4 -4
  121. package/dist/web/login/__next._index.txt +13 -13
  122. package/dist/web/login/__next._tree.txt +2 -2
  123. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  124. package/dist/web/login/__next.login.txt +3 -3
  125. package/dist/web/login.html +1 -1
  126. package/dist/web/login.txt +30 -28
  127. package/dist/web/settings/__next._full.txt +32 -31
  128. package/dist/web/settings/__next._head.txt +4 -4
  129. package/dist/web/settings/__next._index.txt +13 -13
  130. package/dist/web/settings/__next._tree.txt +2 -2
  131. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  132. package/dist/web/settings/__next.settings.txt +5 -5
  133. package/dist/web/settings/agents/__next._full.txt +35 -34
  134. package/dist/web/settings/agents/__next._head.txt +4 -4
  135. package/dist/web/settings/agents/__next._index.txt +13 -13
  136. package/dist/web/settings/agents/__next._tree.txt +2 -2
  137. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  138. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  139. package/dist/web/settings/agents/__next.settings.txt +5 -5
  140. package/dist/web/settings/agents.html +1 -1
  141. package/dist/web/settings/agents.txt +35 -34
  142. package/dist/web/settings/mcps/__next._full.txt +35 -34
  143. package/dist/web/settings/mcps/__next._head.txt +4 -4
  144. package/dist/web/settings/mcps/__next._index.txt +13 -13
  145. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  146. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  147. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  148. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  149. package/dist/web/settings/mcps.html +1 -1
  150. package/dist/web/settings/mcps.txt +35 -34
  151. package/dist/web/settings/models/__next._full.txt +35 -34
  152. package/dist/web/settings/models/__next._head.txt +4 -4
  153. package/dist/web/settings/models/__next._index.txt +13 -13
  154. package/dist/web/settings/models/__next._tree.txt +2 -2
  155. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  156. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  157. package/dist/web/settings/models/__next.settings.txt +5 -5
  158. package/dist/web/settings/models.html +1 -1
  159. package/dist/web/settings/models.txt +35 -34
  160. package/dist/web/settings/output-styles/__next._full.txt +43 -0
  161. package/dist/web/settings/output-styles/__next._head.txt +6 -0
  162. package/dist/web/settings/output-styles/__next._index.txt +15 -0
  163. package/dist/web/settings/output-styles/__next._tree.txt +8 -0
  164. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +9 -0
  165. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +5 -0
  166. package/dist/web/settings/output-styles/__next.settings.txt +7 -0
  167. package/dist/web/settings/output-styles.html +1 -0
  168. package/dist/web/settings/output-styles.txt +43 -0
  169. package/dist/web/settings/prompts/__next._full.txt +35 -34
  170. package/dist/web/settings/prompts/__next._head.txt +4 -4
  171. package/dist/web/settings/prompts/__next._index.txt +13 -13
  172. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  173. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  174. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  175. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  176. package/dist/web/settings/prompts.html +1 -1
  177. package/dist/web/settings/prompts.txt +35 -34
  178. package/dist/web/settings/providers/__next._full.txt +35 -34
  179. package/dist/web/settings/providers/__next._head.txt +4 -4
  180. package/dist/web/settings/providers/__next._index.txt +13 -13
  181. package/dist/web/settings/providers/__next._tree.txt +2 -2
  182. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  183. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  184. package/dist/web/settings/providers/__next.settings.txt +5 -5
  185. package/dist/web/settings/providers.html +1 -1
  186. package/dist/web/settings/providers.txt +35 -34
  187. package/dist/web/settings/skills/__next._full.txt +35 -34
  188. package/dist/web/settings/skills/__next._head.txt +4 -4
  189. package/dist/web/settings/skills/__next._index.txt +13 -13
  190. package/dist/web/settings/skills/__next._tree.txt +2 -2
  191. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  192. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  193. package/dist/web/settings/skills/__next.settings.txt +5 -5
  194. package/dist/web/settings/skills.html +1 -1
  195. package/dist/web/settings/skills.txt +35 -34
  196. package/dist/web/settings.html +1 -1
  197. package/dist/web/settings.txt +32 -31
  198. package/dist/web/workflows/__next._full.txt +32 -30
  199. package/dist/web/workflows/__next._head.txt +4 -4
  200. package/dist/web/workflows/__next._index.txt +13 -13
  201. package/dist/web/workflows/__next._tree.txt +2 -2
  202. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  203. package/dist/web/workflows/__next.workflows.txt +3 -3
  204. package/dist/web/workflows.html +1 -1
  205. package/dist/web/workflows.txt +32 -30
  206. package/dist/web/workspace/_/__next._full.txt +30 -70
  207. package/dist/web/workspace/_/__next._head.txt +4 -4
  208. package/dist/web/workspace/_/__next._index.txt +13 -13
  209. package/dist/web/workspace/_/__next._tree.txt +2 -17
  210. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -18
  211. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  212. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  213. package/dist/web/workspace/_.html +1 -1
  214. package/dist/web/workspace/_.txt +30 -70
  215. package/dist/web/workspaces/__next._full.txt +31 -29
  216. package/dist/web/workspaces/__next._head.txt +4 -4
  217. package/dist/web/workspaces/__next._index.txt +13 -13
  218. package/dist/web/workspaces/__next._tree.txt +2 -2
  219. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  220. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  221. package/dist/web/workspaces.html +1 -1
  222. package/dist/web/workspaces.txt +31 -29
  223. package/dist/ws/agent-runner.js +55 -33
  224. package/dist/ws/message-parts.js +61 -2
  225. package/package.json +1 -1
  226. package/public/output-styles/carmack-mode.md +381 -0
  227. package/public/output-styles/codex-rigor-mode.md +114 -0
  228. package/public/output-styles/dhh-mode.md +265 -0
  229. package/public/output-styles/evan-you-mode.md +539 -0
  230. package/public/output-styles/index.json +37 -0
  231. package/public/output-styles/jobs-mode.md +369 -0
  232. package/public/output-styles/linus-mode.md +135 -0
  233. package/public/output-styles/uncle-bob-mode.md +221 -0
  234. package/public/prompt/andrej-karpathy-skills.md +65 -0
  235. package/public/prompt/claude-token-efficient-coding.md +35 -0
  236. package/public/prompt/index.json +12 -0
  237. package/public/skills/index.json +92 -0
  238. package/public/skills/planning-with-files/planning-with-files-zh/SKILL.md +243 -0
  239. package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.ps1 +44 -0
  240. package/public/skills/planning-with-files/planning-with-files-zh/scripts/check-complete.sh +46 -0
  241. package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.ps1 +124 -0
  242. package/public/skills/planning-with-files/planning-with-files-zh/scripts/init-session.sh +124 -0
  243. package/public/skills/planning-with-files/planning-with-files-zh/scripts/session-catchup.py +438 -0
  244. package/public/skills/planning-with-files/planning-with-files-zh/templates/findings.md +29 -0
  245. package/public/skills/planning-with-files/planning-with-files-zh/templates/progress.md +40 -0
  246. package/public/skills/planning-with-files/planning-with-files-zh/templates/task_plan.md +58 -0
  247. package/public/skills/superpowers/brainstorming/SKILL.md +164 -0
  248. package/public/skills/superpowers/brainstorming/scripts/frame-template.html +214 -0
  249. package/public/skills/superpowers/brainstorming/scripts/helper.js +88 -0
  250. package/public/skills/superpowers/brainstorming/scripts/server.cjs +354 -0
  251. package/public/skills/superpowers/brainstorming/scripts/start-server.sh +148 -0
  252. package/public/skills/superpowers/brainstorming/scripts/stop-server.sh +56 -0
  253. package/public/skills/superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
  254. package/public/skills/superpowers/brainstorming/visual-companion.md +287 -0
  255. package/public/skills/superpowers/dispatching-parallel-agents/SKILL.md +182 -0
  256. package/public/skills/superpowers/executing-plans/SKILL.md +70 -0
  257. package/public/skills/superpowers/finishing-a-development-branch/SKILL.md +251 -0
  258. package/public/skills/superpowers/receiving-code-review/SKILL.md +213 -0
  259. package/public/skills/superpowers/requesting-code-review/SKILL.md +103 -0
  260. package/public/skills/superpowers/requesting-code-review/code-reviewer.md +168 -0
  261. package/public/skills/superpowers/subagent-driven-development/SKILL.md +279 -0
  262. package/public/skills/superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +25 -0
  263. package/public/skills/superpowers/subagent-driven-development/implementer-prompt.md +113 -0
  264. package/public/skills/superpowers/subagent-driven-development/spec-reviewer-prompt.md +61 -0
  265. package/public/skills/superpowers/systematic-debugging/CREATION-LOG.md +119 -0
  266. package/public/skills/superpowers/systematic-debugging/SKILL.md +296 -0
  267. package/public/skills/superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
  268. package/public/skills/superpowers/systematic-debugging/condition-based-waiting.md +115 -0
  269. package/public/skills/superpowers/systematic-debugging/defense-in-depth.md +122 -0
  270. package/public/skills/superpowers/systematic-debugging/find-polluter.sh +63 -0
  271. package/public/skills/superpowers/systematic-debugging/root-cause-tracing.md +169 -0
  272. package/public/skills/superpowers/systematic-debugging/test-academic.md +14 -0
  273. package/public/skills/superpowers/systematic-debugging/test-pressure-1.md +58 -0
  274. package/public/skills/superpowers/systematic-debugging/test-pressure-2.md +68 -0
  275. package/public/skills/superpowers/systematic-debugging/test-pressure-3.md +69 -0
  276. package/public/skills/superpowers/test-driven-development/SKILL.md +371 -0
  277. package/public/skills/superpowers/test-driven-development/testing-anti-patterns.md +299 -0
  278. package/public/skills/superpowers/using-git-worktrees/SKILL.md +215 -0
  279. package/public/skills/superpowers/using-superpowers/SKILL.md +117 -0
  280. package/public/skills/superpowers/using-superpowers/references/codex-tools.md +59 -0
  281. package/public/skills/superpowers/using-superpowers/references/copilot-tools.md +42 -0
  282. package/public/skills/superpowers/using-superpowers/references/gemini-tools.md +51 -0
  283. package/public/skills/superpowers/verification-before-completion/SKILL.md +139 -0
  284. package/public/skills/superpowers/writing-plans/SKILL.md +152 -0
  285. package/public/skills/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
  286. package/public/skills/superpowers/writing-skills/SKILL.md +655 -0
  287. package/public/skills/superpowers/writing-skills/anthropic-best-practices.md +1150 -0
  288. package/public/skills/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  289. package/public/skills/superpowers/writing-skills/graphviz-conventions.dot +172 -0
  290. package/public/skills/superpowers/writing-skills/persuasion-principles.md +187 -0
  291. package/public/skills/superpowers/writing-skills/render-graphs.js +168 -0
  292. package/public/skills/superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
  293. package/dist/web/_next/static/chunks/0-1pn22sg~67u.js +0 -2
  294. package/dist/web/_next/static/chunks/01gi~q0c6ez6..js +0 -1
  295. package/dist/web/_next/static/chunks/03me5_ir..2ix.js +0 -1
  296. package/dist/web/_next/static/chunks/05eu2fdixf7c9.js +0 -1
  297. package/dist/web/_next/static/chunks/0_-c.y2qq4ax4.js +0 -270
  298. package/dist/web/_next/static/chunks/0_g3irbniww6d.js +0 -3
  299. package/dist/web/_next/static/chunks/0cs830n97dgjy.js +0 -1
  300. package/dist/web/_next/static/chunks/0g-0n6glz_z6w.js +0 -1
  301. package/dist/web/_next/static/chunks/0gyede80jpy3n.js +0 -4
  302. package/dist/web/_next/static/chunks/0hrbuvzuxrjfl.js +0 -1
  303. package/dist/web/_next/static/chunks/0ju52ko_nh7gi.js +0 -1
  304. package/dist/web/_next/static/chunks/0mny8i81aenx3.js +0 -1
  305. package/dist/web/_next/static/chunks/0nsu~f3voe~0i.js +0 -5
  306. package/dist/web/_next/static/chunks/0sz8_nijfiz0p.js +0 -1
  307. package/dist/web/_next/static/chunks/0u7_qd9hzqwac.js +0 -1
  308. package/dist/web/_next/static/chunks/0w3o.7vi.dlm6.js +0 -1
  309. package/dist/web/_next/static/chunks/0wv0h27jafag4.js +0 -1
  310. package/dist/web/_next/static/chunks/0wwvc4zc2q6bn.js +0 -1
  311. package/dist/web/_next/static/chunks/0x4b-a2ixww6m.js +0 -1
  312. package/dist/web/_next/static/chunks/0xwqf.llmm_u2.js +0 -31
  313. package/dist/web/_next/static/chunks/0~w6ugk9s5p-h.css +0 -1
  314. package/dist/web/_next/static/chunks/125nunjfj2fj0.js +0 -1
  315. package/dist/web/_next/static/chunks/1285yqy_oirb-.js +0 -1
  316. package/dist/web/_next/static/chunks/13ess~4pdmxma.js +0 -1
  317. package/dist/web/_next/static/chunks/14n8i2xz4_y-e.js +0 -1
  318. package/public/avatars/1777813826407-bea0ja-minimax.svg +0 -1
  319. package/public/avatars/1777813967088-3xlvd0-minimax.svg +0 -1
  320. package/public/avatars/1777814041714-yp0qeq-minimax.svg +0 -1
  321. package/public/avatars/1777904986095-rg26je-icon.png +0 -0
  322. package/public/avatars/1778953228323-5je54x-user.jpg +0 -0
  323. package/public/avatars/1778954057880-g01q6y-user.jpg +0 -0
  324. /package/dist/web/_next/static/{HUlYEknPhyW7dRmJ5mFdG → OoXAsPtrUWjC1l898FSvI}/_buildManifest.js +0 -0
  325. /package/dist/web/_next/static/{HUlYEknPhyW7dRmJ5mFdG → OoXAsPtrUWjC1l898FSvI}/_clientMiddlewareManifest.js +0 -0
  326. /package/dist/web/_next/static/{HUlYEknPhyW7dRmJ5mFdG → OoXAsPtrUWjC1l898FSvI}/_ssgManifest.js +0 -0
  327. /package/public/avatars/{1777815865622-08wp9m-user.jpg → user.jpg} +0 -0
@@ -1,7 +1,19 @@
1
+ import { resolveOutputStyleContent } from '../services/output-style.js';
1
2
  export function summarizeResult(text) {
2
3
  const normalized = text.trim().replace(/\s+/g, ' ');
3
4
  if (!normalized)
4
5
  return 'Completed agent execution';
5
6
  return normalized.length > 160 ? `${normalized.slice(0, 157)}...` : normalized;
6
7
  }
8
+ export function appendOutputStyleToPrompt(prompt, outputStyle) {
9
+ const style = resolveOutputStyleContent(outputStyle)?.trim();
10
+ if (!style)
11
+ return prompt;
12
+ return [
13
+ prompt.trimEnd(),
14
+ '',
15
+ 'Output style instructions:',
16
+ style,
17
+ ].join('\n');
18
+ }
7
19
  //# sourceMappingURL=agent-runtime-types.js.map
@@ -1,6 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { query } from '@anthropic-ai/claude-agent-sdk';
3
3
  import { summarizeResult } from '../agent-runtime-types.js';
4
+ import { prepareClaudeOutputStyleFile } from '../../services/output-style.js';
4
5
  import { normalizeAdditionalDirectories, normalizePermissionMode, normalizeSkillNames, prepareConfigDir, resolveBundledClaudeExecutable, buildEnv, normalizeMcpServers } from './sdk-config.js';
5
6
  import { startClaudeAdapterIfNeeded, getClaudeCodeModel } from './adapter-pool.js';
6
7
  import { extractThinkingEvents, extractToolUseEvents, extractToolResultEvent, logToolDebug, formatMessage, isAskUserQuestionAutoResult, countUsageTokens, formatUsageLine, normalizeUsage } from './message-format.js';
@@ -25,6 +26,7 @@ export class ClaudeCodeRuntime {
25
26
  if (configDir)
26
27
  prepareConfigDir(configDir, agentDir);
27
28
  const skillNames = normalizeSkillNames(options?.skills, configDir);
29
+ const outputStyleFile = configDir ? prepareClaudeOutputStyleFile(configDir, options?.outputStyle) : undefined;
28
30
  const claudeExecutable = resolveBundledClaudeExecutable();
29
31
  this.adapterRun = await startClaudeAdapterIfNeeded(this.config);
30
32
  const baseURL = this.adapterRun?.url ?? this.config.baseURL;
@@ -46,6 +48,7 @@ export class ClaudeCodeRuntime {
46
48
  tools: { type: 'preset', preset: 'claude_code' },
47
49
  mcpServers: sdkMcpServers,
48
50
  skills: skillNames,
51
+ outputStyle: outputStyleFile,
49
52
  managedSettings: {
50
53
  strictPluginOnlyCustomization: ['mcp'],
51
54
  },
@@ -84,9 +87,12 @@ export class ClaudeCodeRuntime {
84
87
  options?.onEvent?.({ type: 'session', sessionId });
85
88
  }
86
89
  const toolUses = extractToolUseEvents(message);
90
+ let sawAskUserQuestion = false;
87
91
  for (const toolUse of toolUses) {
88
92
  if (toolUse.name === 'AskUserQuestion') {
89
93
  pendingAskUserQuestionToolIds.add(toolUse.id);
94
+ waitingForUserAnswer = true;
95
+ sawAskUserQuestion = true;
90
96
  }
91
97
  }
92
98
  const toolResult = extractToolResultEvent(message);
@@ -133,8 +139,24 @@ export class ClaudeCodeRuntime {
133
139
  error = message.errors.join('\n') || message.subtype;
134
140
  }
135
141
  }
142
+ if (sawAskUserQuestion)
143
+ break;
136
144
  }
137
145
  const elapsed = Date.now() - startTime;
146
+ if (waitingForUserAnswer) {
147
+ d(`waiting for user answer ${elapsed}ms | turns=${turns} tokens=${tokenCount}`);
148
+ if (usageLine)
149
+ output.push(usageLine);
150
+ return {
151
+ success: true,
152
+ summary: 'Waiting for user answer',
153
+ artifacts: [],
154
+ output,
155
+ usage,
156
+ costUsd,
157
+ sessionId,
158
+ };
159
+ }
138
160
  if (!sawResult) {
139
161
  const runtimeError = extractRuntimeError([...stderrLines, ...output])
140
162
  || 'Claude Code execution stopped before reporting a final result';
@@ -1,6 +1,6 @@
1
- import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, statSync } from 'node:fs';
1
+ import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { createRequire } from 'node:module';
3
- import { extname, isAbsolute, resolve } from 'node:path';
3
+ import { basename, extname, isAbsolute, resolve } from 'node:path';
4
4
  import { join } from 'node:path';
5
5
  import { createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
6
6
  import { isAnthropicBridgeProvider } from './types.js';
@@ -125,7 +125,7 @@ export function normalizeSkillNames(skills, configDir) {
125
125
  if (!Array.isArray(skills))
126
126
  return [];
127
127
  return skills
128
- .map((skill) => skill.trim().replace(/\.md$/i, ''))
128
+ .map(sanitizeSkillName)
129
129
  .filter((skill) => Boolean(skill) && hasSkillContent(skill, configDir));
130
130
  }
131
131
  export function prepareConfigDir(configDir, agentDir) {
@@ -138,19 +138,43 @@ export function prepareConfigDir(configDir, agentDir) {
138
138
  mkdirSync(targetSkillsDir, { recursive: true });
139
139
  if (!existsSync(sourceSkillsDir))
140
140
  return;
141
- for (const file of readdirSync(sourceSkillsDir)) {
142
- if (extname(file).toLowerCase() !== '.md')
141
+ for (const entry of readdirSync(sourceSkillsDir)) {
142
+ const source = join(sourceSkillsDir, entry);
143
+ const sourceStat = statSync(source);
144
+ if (sourceStat.isDirectory()) {
145
+ const skillName = sanitizeSkillName(entry);
146
+ if (!skillName)
147
+ continue;
148
+ const sourceSkillFile = join(source, 'SKILL.md');
149
+ const targetSkillDir = join(targetSkillsDir, skillName);
150
+ if (existsSync(sourceSkillFile) && statSync(sourceSkillFile).size > 0) {
151
+ cpSync(source, targetSkillDir, { recursive: true, force: true });
152
+ }
143
153
  continue;
144
- copyFileSync(join(sourceSkillsDir, file), join(targetSkillsDir, file));
154
+ }
155
+ if (!sourceStat.isFile() || extname(entry).toLowerCase() !== '.md')
156
+ continue;
157
+ const skillName = sanitizeSkillName(entry);
158
+ if (!skillName || sourceStat.size === 0)
159
+ continue;
160
+ const targetSkillDir = join(targetSkillsDir, skillName);
161
+ mkdirSync(targetSkillDir, { recursive: true });
162
+ writeFileSync(join(targetSkillDir, 'SKILL.md'), readFileSync(source, 'utf-8'), 'utf-8');
163
+ copyFileSync(source, join(targetSkillsDir, `${skillName}.md`));
145
164
  }
146
165
  }
147
166
  function hasSkillContent(skill, configDir) {
148
167
  if (!configDir)
149
168
  return true;
150
- const skillFile = join(configDir, 'skills', `${skill}.md`);
151
- if (!existsSync(skillFile))
152
- return false;
153
- return statSync(skillFile).size > 0;
169
+ const skillFile = join(configDir, 'skills', skill, 'SKILL.md');
170
+ if (existsSync(skillFile) && statSync(skillFile).size > 0)
171
+ return true;
172
+ const legacySkillFile = join(configDir, 'skills', `${skill}.md`);
173
+ return existsSync(legacySkillFile) && statSync(legacySkillFile).size > 0;
174
+ }
175
+ function sanitizeSkillName(name) {
176
+ const raw = basename(name).replace(/\.md$/i, '').trim();
177
+ return raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
154
178
  }
155
179
  export function normalizePermissionMode(permissionMode) {
156
180
  if (permissionMode === 'default' ||
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { basename, extname, join } from 'node:path';
3
3
  import { Codex } from '@openai/codex-sdk';
4
- import { summarizeResult } from './agent-runtime-types.js';
4
+ import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
5
5
  /**
6
6
  * Runtime backed by OpenAI's Codex SDK.
7
7
  * Spawns the bundled Codex CLI and consumes its structured JSONL event stream.
@@ -48,7 +48,7 @@ export class CodexRuntime {
48
48
  const thread = options?.resumeSessionId
49
49
  ? codex.resumeThread(options.resumeSessionId, threadOptions)
50
50
  : codex.startThread(threadOptions);
51
- const { events } = await thread.runStreamed(prompt, {
51
+ const { events } = await thread.runStreamed(appendOutputStyleToPrompt(prompt, options?.outputStyle), {
52
52
  signal: this.abortController.signal,
53
53
  });
54
54
  let resultText = '';
@@ -3,6 +3,19 @@ import { getWorkspace } from '../storage/workspace-store.js';
3
3
  import { writeFile, readFile, mkdir } from 'node:fs/promises';
4
4
  import { existsSync } from 'node:fs';
5
5
  import { join, basename } from 'node:path';
6
+ const BINARY_EXTENSIONS = new Set([
7
+ 'png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'webp', 'svg', 'tiff', 'tif',
8
+ 'mp3', 'mp4', 'wav', 'avi', 'mov', 'mkv', 'flv', 'wmv', 'webm',
9
+ 'zip', 'tar', 'gz', 'bz2', 'xz', '7z', 'rar', 'dmg', 'iso',
10
+ 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
11
+ 'woff', 'woff2', 'ttf', 'otf', 'eot',
12
+ 'exe', 'dll', 'so', 'dylib', 'bin', 'dat',
13
+ 'sqlite', 'db', 'pyc', 'o', 'obj', 'class', 'jar', 'wasm',
14
+ ]);
15
+ function isBinaryPath(filePath) {
16
+ const ext = filePath.split('.').pop()?.toLowerCase();
17
+ return ext ? BINARY_EXTENSIONS.has(ext) : true;
18
+ }
6
19
  const gitInstances = new Map();
7
20
  function getGitOptions() {
8
21
  const proxy = process.env.HTTPS_PROXY || process.env.https_proxy
@@ -115,9 +128,9 @@ export async function gitDiff(workspaceId, filePath) {
115
128
  const newContent = await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${filePath}`, 'utf-8')).catch(() => '');
116
129
  diffs.push({
117
130
  path: filePath,
118
- oldContent,
119
- newContent,
120
- isBinary: false,
131
+ oldContent: isBinaryPath(filePath) ? '' : oldContent,
132
+ newContent: isBinaryPath(filePath) ? '' : newContent,
133
+ isBinary: isBinaryPath(filePath),
121
134
  isNew: !oldContent,
122
135
  isDeleted: !newContent,
123
136
  });
@@ -126,13 +139,14 @@ export async function gitDiff(workspaceId, filePath) {
126
139
  const raw = await git.diff(['--name-only']);
127
140
  const files = raw.split('\n').filter(Boolean);
128
141
  for (const f of files) {
129
- const oldContent = await git.show([`HEAD:${f}`]).catch(() => '');
130
- const newContent = await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
142
+ const binary = isBinaryPath(f);
143
+ const oldContent = binary ? '' : await git.show([`HEAD:${f}`]).catch(() => '');
144
+ const newContent = binary ? '' : await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
131
145
  diffs.push({
132
146
  path: f,
133
147
  oldContent,
134
148
  newContent,
135
- isBinary: false,
149
+ isBinary: binary,
136
150
  isNew: !oldContent,
137
151
  isDeleted: !newContent,
138
152
  });
@@ -140,13 +154,14 @@ export async function gitDiff(workspaceId, filePath) {
140
154
  // Also check untracked (staged new files)
141
155
  const status = await git.status();
142
156
  for (const f of status.not_added) {
143
- const newContent = await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
144
- if (newContent) {
157
+ const binary = isBinaryPath(f);
158
+ const newContent = binary ? '' : await import('node:fs/promises').then(fs => fs.readFile(`${ws.boundDirs[0]}/${f}`, 'utf-8')).catch(() => '');
159
+ if (newContent || binary) {
145
160
  diffs.push({
146
161
  path: f,
147
162
  oldContent: '',
148
- newContent,
149
- isBinary: false,
163
+ newContent: binary ? '' : newContent,
164
+ isBinary: binary,
150
165
  isNew: true,
151
166
  isDeleted: false,
152
167
  });
@@ -337,9 +352,10 @@ export async function gitCommitDiff(workspaceId, commitHash) {
337
352
  const files = nameOnlyRaw.split('\n').filter(Boolean);
338
353
  const diffs = [];
339
354
  for (const f of files) {
340
- const oldContent = await git.show([`${commitHash}^:${f}`]).catch(() => '');
341
- const newContent = await git.show([`${commitHash}:${f}`]).catch(() => '');
342
- diffs.push({ path: f, oldContent, newContent, isBinary: false, isNew: !oldContent, isDeleted: !newContent });
355
+ const binary = isBinaryPath(f);
356
+ const oldContent = binary ? '' : await git.show([`${commitHash}^:${f}`]).catch(() => '');
357
+ const newContent = binary ? '' : await git.show([`${commitHash}:${f}`]).catch(() => '');
358
+ diffs.push({ path: f, oldContent, newContent, isBinary: binary, isNew: !oldContent, isDeleted: !newContent });
343
359
  }
344
360
  return diffs;
345
361
  }
@@ -1,6 +1,6 @@
1
1
  import { createAgent, initChatModel, tool } from 'langchain';
2
2
  import { z } from 'zod';
3
- import { summarizeResult } from './agent-runtime-types.js';
3
+ import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
4
4
  /**
5
5
  * Runtime backed by LangChain.js.
6
6
  * Uses LangChain's provider-neutral createAgent API and adapts Agent Spaces tools.
@@ -27,7 +27,7 @@ export class LangChainRuntime {
27
27
  tools: buildLangChainTools(options?.functionTools, output, options),
28
28
  systemPrompt: options?.systemPrompt,
29
29
  });
30
- const result = await agent.invoke({ messages: [{ role: 'user', content: prompt }] }, {
30
+ const result = await agent.invoke({ messages: [{ role: 'user', content: appendOutputStyleToPrompt(prompt, options?.outputStyle) }] }, {
31
31
  signal: this.abortController?.signal,
32
32
  recursionLimit: options?.maxTurns ? Math.max(2, options.maxTurns * 2 + 1) : undefined,
33
33
  });
@@ -1,5 +1,5 @@
1
1
  import { createAgent } from '@codeany/open-agent-sdk';
2
- import { summarizeResult } from './agent-runtime-types.js';
2
+ import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
3
3
  /**
4
4
  * Runtime backed by @codeany/open-agent-sdk.
5
5
  * Runs the agent loop in-process with the SDK's built-in tools.
@@ -35,7 +35,7 @@ export class OpenAgentSdkRuntime {
35
35
  });
36
36
  d('agent created, sending prompt...');
37
37
  d('tool debug | open-agent-sdk runtime does not expose per-tool stream events through prompt(); only final text/usage is available here');
38
- const result = await this.agent.prompt(prompt);
38
+ const result = await this.agent.prompt(appendOutputStyleToPrompt(prompt, options?.outputStyle));
39
39
  const elapsed = Date.now() - startTime;
40
40
  const inputTokens = result.usage.input_tokens;
41
41
  const outputTokens = result.usage.output_tokens;
@@ -46,7 +46,7 @@ export function createAgentMessagePartsTracker(input) {
46
46
  output.push(event.line);
47
47
  input.onOutput?.(event.line);
48
48
  },
49
- buildParts({ sessionId, workspaceRoot, model, usage, success, error }) {
49
+ buildParts({ sessionId, workspaceRoot, model, usage, persistentContext, success, error }) {
50
50
  return buildAgentMessageParts({
51
51
  sessionId,
52
52
  workspaceRoot,
@@ -55,6 +55,7 @@ export function createAgentMessagePartsTracker(input) {
55
55
  output,
56
56
  reasoning,
57
57
  toolDetails,
58
+ persistentContext,
58
59
  success,
59
60
  error,
60
61
  });
@@ -69,7 +70,7 @@ function buildAgentMessageParts(input) {
69
70
  : '';
70
71
  const usage = input.usage ?? extractUsage(lines);
71
72
  const parts = [];
72
- const chainItems = buildChainItems(lines, finalTextRange, finalText, input.workspaceRoot, input.toolDetails);
73
+ const chainItems = buildChainItems(lines, finalTextRange, finalText, input.workspaceRoot, input.toolDetails, input.persistentContext);
73
74
  const reasoningText = normalizeReasoningText(input.reasoning);
74
75
  if (reasoningText) {
75
76
  parts.push({
@@ -181,12 +182,17 @@ function findFinalTextRange(lines) {
181
182
  }
182
183
  return { start, end };
183
184
  }
184
- function buildChainItems(lines, finalTextRange, finalText, workspaceRoot, toolDetails) {
185
+ function buildChainItems(lines, finalTextRange, finalText, workspaceRoot, toolDetails, persistentContext) {
185
186
  let toolIndex = 0;
186
187
  let messageIndex = 0;
187
188
  const items = [];
188
189
  const toolDetailMatchCounts = new Map();
189
190
  let messageBuffer = [];
191
+ const persistentContextItem = buildPersistentContextToolItem(persistentContext);
192
+ if (persistentContextItem) {
193
+ items.push(persistentContextItem);
194
+ toolIndex += 1;
195
+ }
190
196
  const flushMessageBuffer = () => {
191
197
  if (messageBuffer.length === 0)
192
198
  return;
@@ -229,6 +235,7 @@ function buildToolTodo(line, index, workspaceRoot, toolDetails, toolDetailMatchC
229
235
  const detailId = findToolDetailId(line, toolDetails, toolDetailMatchCounts);
230
236
  return {
231
237
  id: `tool-${index}`,
238
+ kind: 'tool',
232
239
  title: summary.title,
233
240
  description: summary.description,
234
241
  status: 'completed',
@@ -238,6 +245,23 @@ function buildToolTodo(line, index, workspaceRoot, toolDetails, toolDetailMatchC
238
245
  detailId,
239
246
  };
240
247
  }
248
+ function buildPersistentContextToolItem(persistentContext) {
249
+ const counts = persistentContext?.counts;
250
+ if (!counts?.total)
251
+ return null;
252
+ const description = [
253
+ counts.claudeMd ? `${counts.claudeMd} claude.md` : null,
254
+ counts.agentsMd ? `${counts.agentsMd} AGENTS.md` : null,
255
+ ].filter(Boolean).join(', ') || `${counts.total} instruction files`;
256
+ return {
257
+ id: 'tool-persistent-context',
258
+ kind: 'tool',
259
+ title: 'Load claude.md',
260
+ description,
261
+ status: 'completed',
262
+ toolName: 'LoadClaudeMd',
263
+ };
264
+ }
241
265
  function summarizeToolLine(line, workspaceRoot) {
242
266
  const trimmed = line.trim();
243
267
  const toolName = extractToolName(trimmed);
@@ -42,6 +42,7 @@ export async function runCommitAgent(workspaceId) {
42
42
  }), workingDir, {
43
43
  maxTurns: 1,
44
44
  systemPrompt,
45
+ outputStyle: commitAgent.outputStyle,
45
46
  });
46
47
  agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
47
48
  runtime: commitAgent.runtimeKind,
@@ -74,7 +74,9 @@ export function createIssueAgentProgress(workspaceId, issue, preset, agentSessio
74
74
  return { message, comment };
75
75
  }
76
76
  export function completeIssueAgentProgress(workspaceId, issue, progress, summary, output, metadata) {
77
- const content = output.join('\n').trim() || summary;
77
+ const parts = metadata.parts;
78
+ const finalText = [...(parts ?? [])].reverse().find((part) => part.type === 'text')?.text.trim();
79
+ const content = finalText || summary.trim() || output.join('\n').trim();
78
80
  if (!content.trim())
79
81
  return;
80
82
  const message = messageService.updateMessage(workspaceId, issue.channelId, progress.message.id, {
@@ -88,7 +90,7 @@ export function completeIssueAgentProgress(workspaceId, issue, progress, summary
88
90
  summary,
89
91
  duration: metadata.duration,
90
92
  },
91
- parts: metadata.parts,
93
+ parts,
92
94
  });
93
95
  if (message)
94
96
  broadcastToWorkspace(workspaceId, 'channel.message.updated', message);
@@ -9,6 +9,11 @@ export async function runIssueAutomation(workspaceId, issueId, ctx) {
9
9
  console.warn(`[issue-runner] issue not found workspaceId=${workspaceId} issueId=${issueId}`);
10
10
  return;
11
11
  }
12
+ const tasks = taskService.list(workspaceId, issueId);
13
+ if (tasks.length > 0) {
14
+ await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
15
+ return;
16
+ }
12
17
  // Workflow template branch
13
18
  if (issue.workflowId) {
14
19
  const template = workflowService.getWorkflow(issue.workflowId);
@@ -29,11 +34,6 @@ export async function runIssueAutomation(workspaceId, issueId, ctx) {
29
34
  return;
30
35
  }
31
36
  }
32
- const tasks = taskService.list(workspaceId, issueId);
33
- if (tasks.length > 0) {
34
- await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
35
- return;
36
- }
37
37
  console.warn(`[issue-runner] no workflow configured for issue workspaceId=${workspaceId} issueId=${issueId}`);
38
38
  }
39
39
  export function hasActiveIssueAutomation(workspaceId) {
@@ -9,7 +9,9 @@ import { createIssueFunctionTools } from '../services/builtin-tools.js';
9
9
  import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
10
10
  import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
11
11
  import { completeIssueAgentProgress, createIssueAgentProgress, createIssueAgentProgressTracker } from './issue-agent-progress.js';
12
+ import { wrapOnEventWithHooks } from '../services/hook-engine.js';
12
13
  const ACTIVE_TASK_STATUSES = ['running', 'reviewing', 'retrying', 'waiting_review'];
14
+ const activeIssueRuntimes = new Map();
13
15
  export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, ctx) {
14
16
  const issue = issueService.getById(workspaceId, issueId);
15
17
  if (!issue) {
@@ -31,6 +33,7 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
31
33
  ctx.broadcast('agent.output', { agentId: taskSyncAgent.id, data: `Syncing issue tasks: ${issueId}` });
32
34
  const startTime = Date.now();
33
35
  const taskSyncWorkingDir = resolveIssueWorkspaceRoot(workspaceId);
36
+ const workspace = workspaceService.getById(workspaceId);
34
37
  const progress = createIssueAgentProgress(workspaceId, issue, taskSyncPreset, taskSyncAgent.id, {
35
38
  runtime: taskSyncPreset.runtimeKind,
36
39
  model: taskSyncPreset.modelId,
@@ -53,7 +56,8 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
53
56
  skills: [],
54
57
  configDir: agentService.getAgentConfigDir(workspaceId, taskSyncPreset),
55
58
  sandboxDirs: taskSyncPreset.sandboxDirs,
56
- onEvent: taskSyncTracker.handleEvent,
59
+ outputStyle: taskSyncPreset.outputStyle,
60
+ onEvent: wrapOnEventWithHooks(taskSyncTracker.handleEvent.bind(taskSyncTracker), workspaceId, workspace?.hooksEnabled),
57
61
  });
58
62
  if (taskSyncTracker.output.length === 0) {
59
63
  for (const line of result.output) {
@@ -101,7 +105,10 @@ export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, c
101
105
  updateIssueStatus(workspaceId, issueId, 'in_progress', ctx);
102
106
  await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
103
107
  }
104
- export async function scheduleRunnableIssueTasks(workspaceId, issueId, ctx) {
108
+ export async function scheduleRunnableIssueTasks(workspaceId, issueId, ctx, _options = {}) {
109
+ const issue = issueService.getById(workspaceId, issueId);
110
+ if (!issue)
111
+ return;
105
112
  const tasks = taskService.list(workspaceId, issueId);
106
113
  const doneIds = new Set(tasks.filter((task) => task.status === 'done').map((task) => task.id));
107
114
  const hasActive = tasks.some((task) => ACTIVE_TASK_STATUSES.includes(task.status));
@@ -113,7 +120,8 @@ export async function scheduleRunnableIssueTasks(workspaceId, issueId, ctx) {
113
120
  }
114
121
  return;
115
122
  }
116
- for (const task of runnable) {
123
+ const tasksToRun = issue.continuousRun === false ? runnable.slice(0, 1) : runnable;
124
+ for (const task of tasksToRun) {
117
125
  await runIssueTask(workspaceId, issueId, task.id, ctx);
118
126
  }
119
127
  }
@@ -129,6 +137,8 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
129
137
  }
130
138
  const taskAgentPreset = findAgentForTask(workspaceId, issue, task);
131
139
  if (!taskAgentPreset) {
140
+ if (issueService.getById(workspaceId, issueId)?.status === 'error')
141
+ return;
132
142
  const missingExecutorResult = {
133
143
  success: false,
134
144
  summary: 'No runnable agent configured in issue channel members',
@@ -161,6 +171,7 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
161
171
  broadcastTaskUpdate(ctx, runningTask, task.status);
162
172
  agentService.assignTask(workspaceId, taskAgent.id, taskId);
163
173
  const runtime = createRuntimeForPreset(taskAgentPreset);
174
+ registerActiveIssueRuntime(workspaceId, issueId, taskAgent.id, runtime);
164
175
  const agentWorkingDir = resolveIssueWorkspaceRoot(workspaceId);
165
176
  const startTime = Date.now();
166
177
  const progress = createIssueAgentProgress(workspaceId, issue, taskAgentPreset, taskAgent.id, {
@@ -180,20 +191,40 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
180
191
  ctx.broadcast('task.output', { taskId, data: line });
181
192
  },
182
193
  });
194
+ const workspace = workspaceService.getById(workspaceId);
183
195
  ctx.broadcast('agent.output', { agentId: taskAgent.id, data: `Executing task: ${runningTask.title}` });
184
196
  ctx.broadcast('agent.output', {
185
197
  agentId: taskAgent.id,
186
198
  data: `[debug] workflow agentConfigId=${taskAgentPreset.id} role=${taskAgentPreset.role} taskAgentConfigId=${runningTask.agentConfigId || '(fallback)'} runtime=${taskAgentPreset.runtimeKind ?? 'open-agent-sdk'}`,
187
199
  });
188
- const result = await runtime.execute(buildIssueAgentPrompt(workspaceId, buildTaskAgentPrompt(issue, runningTask, taskAgentPreset, agentWorkingDir), agentWorkingDir, taskAgentPreset), agentWorkingDir, {
189
- maxTurns: 100,
190
- mcpServers: agentService.getMcpServers(taskAgentPreset.mcps),
191
- functionTools: createCurrentIssueTools(workspaceId, issue, taskAgentPreset),
192
- skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, taskAgentPreset), taskAgentPreset.skills),
193
- configDir: agentService.getAgentConfigDir(workspaceId, taskAgentPreset),
194
- sandboxDirs: runningTask.sandboxDirs ?? taskAgentPreset.sandboxDirs,
195
- onEvent: agentTracker.handleEvent,
196
- });
200
+ let result;
201
+ try {
202
+ result = await runtime.execute(buildIssueAgentPrompt(workspaceId, buildTaskAgentPrompt(issue, runningTask, taskAgentPreset, agentWorkingDir), agentWorkingDir, taskAgentPreset), agentWorkingDir, {
203
+ maxTurns: 100,
204
+ mcpServers: agentService.getMcpServers(taskAgentPreset.mcps),
205
+ functionTools: createCurrentIssueTools(workspaceId, issue, taskAgentPreset),
206
+ skills: agentService.getAvailableSkillNames(agentService.getAgentConfigDir(workspaceId, taskAgentPreset), taskAgentPreset.skills),
207
+ configDir: agentService.getAgentConfigDir(workspaceId, taskAgentPreset),
208
+ sandboxDirs: runningTask.sandboxDirs ?? taskAgentPreset.sandboxDirs,
209
+ outputStyle: taskAgentPreset.outputStyle,
210
+ onEvent: wrapOnEventWithHooks(agentTracker.handleEvent.bind(agentTracker), workspaceId, workspace?.hooksEnabled),
211
+ });
212
+ }
213
+ finally {
214
+ unregisterActiveIssueRuntime(workspaceId, issueId, taskAgent.id);
215
+ }
216
+ if (issueService.getById(workspaceId, issueId)?.status === 'error') {
217
+ const failedTask = taskService.updateStatus(workspaceId, taskId, 'failed', {
218
+ result: {
219
+ success: false,
220
+ summary: 'Interrupted by user',
221
+ artifacts: [],
222
+ error: 'Interrupted by user',
223
+ },
224
+ });
225
+ broadcastTaskUpdate(ctx, failedTask, 'running');
226
+ return;
227
+ }
197
228
  if (agentTracker.output.length === 0) {
198
229
  for (const line of result.output) {
199
230
  agentTracker.output.push(line);
@@ -235,10 +266,42 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
235
266
  }
236
267
  const completedTask = taskService.complete(workspaceId, taskId, result);
237
268
  broadcastTaskUpdate(ctx, completedTask, 'running');
238
- await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
269
+ if (issueService.getById(workspaceId, issueId)?.continuousRun !== false) {
270
+ await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
271
+ }
239
272
  }
240
273
  export async function continueIssueTaskScheduling(workspaceId, issueId, ctx) {
241
- await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
274
+ await scheduleRunnableIssueTasks(workspaceId, issueId, ctx, { force: true });
275
+ }
276
+ export function stopIssueAutomation(workspaceId, issueId, reason = 'Interrupted by user') {
277
+ const key = issueRunKey(workspaceId, issueId);
278
+ const runtimes = activeIssueRuntimes.get(key);
279
+ if (runtimes) {
280
+ for (const [agentId, runtime] of runtimes.entries()) {
281
+ try {
282
+ runtime.stop();
283
+ }
284
+ catch {
285
+ // Runtime may already be unwinding.
286
+ }
287
+ agentService.complete(workspaceId, agentId, reason);
288
+ }
289
+ activeIssueRuntimes.delete(key);
290
+ }
291
+ const activeTasks = taskService.list(workspaceId, issueId)
292
+ .filter((task) => ACTIVE_TASK_STATUSES.includes(task.status));
293
+ return activeTasks.map((task) => {
294
+ const from = task.status;
295
+ const failed = taskService.updateStatus(workspaceId, task.id, 'failed', {
296
+ result: {
297
+ success: false,
298
+ summary: reason,
299
+ artifacts: [],
300
+ error: reason,
301
+ },
302
+ });
303
+ return failed ? { task: failed, from } : null;
304
+ }).filter((item) => Boolean(item));
242
305
  }
243
306
  function createTaskSyncTools(workspaceId, issue, preset, ctx) {
244
307
  return [
@@ -417,6 +480,24 @@ async function handleTaskFailure(workspaceId, issueId, taskId, result, ctx) {
417
480
  ctx.broadcast('issue.status_changed', { issueId, from: issue?.status ?? 'in_progress', to: 'error' });
418
481
  ctx.broadcast('issue.updated', updated);
419
482
  }
483
+ function registerActiveIssueRuntime(workspaceId, issueId, agentId, runtime) {
484
+ const key = issueRunKey(workspaceId, issueId);
485
+ const runtimes = activeIssueRuntimes.get(key) ?? new Map();
486
+ runtimes.set(agentId, runtime);
487
+ activeIssueRuntimes.set(key, runtimes);
488
+ }
489
+ function unregisterActiveIssueRuntime(workspaceId, issueId, agentId) {
490
+ const key = issueRunKey(workspaceId, issueId);
491
+ const runtimes = activeIssueRuntimes.get(key);
492
+ if (!runtimes)
493
+ return;
494
+ runtimes.delete(agentId);
495
+ if (runtimes.size === 0)
496
+ activeIssueRuntimes.delete(key);
497
+ }
498
+ function issueRunKey(workspaceId, issueId) {
499
+ return `${workspaceId}:${issueId}`;
500
+ }
420
501
  function broadcastTaskUpdate(ctx, task, from) {
421
502
  if (!task)
422
503
  return;