@agent-spaces/server 0.3.65 → 0.4.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 (278) hide show
  1. package/dist/adapters/agent-runtime.js +8 -0
  2. package/dist/adapters/codex-function-tool-bridge.js +152 -0
  3. package/dist/adapters/codex-runtime.js +23 -4
  4. package/dist/adapters/git.js +25 -3
  5. package/dist/adapters/hermes-runtime.js +813 -0
  6. package/dist/adapters/langchain-runtime.js +384 -21
  7. package/dist/adapters/oh-my-pi-runtime.js +858 -0
  8. package/dist/adapters/open-agent-sdk-runtime.js +202 -5
  9. package/dist/adapters/open-agent-sdk-runtime.test.js +35 -0
  10. package/dist/agents/agent-message-parts.js +52 -2
  11. package/dist/agents/issue-task-controller.js +71 -2
  12. package/dist/agents/pull-request-agent.js +115 -0
  13. package/dist/agents/scheduler-agent.js +1 -1
  14. package/dist/agents/title-generator-agent.js +120 -0
  15. package/dist/app.js +61 -4
  16. package/dist/middleware/auth.js +1 -1
  17. package/dist/package.json +2 -1
  18. package/dist/routes/agent-sse.js +70 -6
  19. package/dist/routes/channel.js +23 -2
  20. package/dist/routes/chat-run.js +191 -0
  21. package/dist/routes/chat.js +142 -0
  22. package/dist/routes/command.js +16 -0
  23. package/dist/routes/data.js +189 -0
  24. package/dist/routes/git.js +10 -1
  25. package/dist/routes/import.js +199 -0
  26. package/dist/routes/issue.js +17 -5
  27. package/dist/routes/notification.js +10 -0
  28. package/dist/routes/plugin.js +69 -0
  29. package/dist/routes/version.js +95 -0
  30. package/dist/routes/workflow-hook.js +71 -0
  31. package/dist/routes/workflow.js +282 -7
  32. package/dist/routes/workspace.js +13 -4
  33. package/dist/routes/worktree.js +93 -0
  34. package/dist/services/agent.js +123 -36
  35. package/dist/services/ai-text.js +185 -0
  36. package/dist/services/builtin-tools/index.js +1 -0
  37. package/dist/services/builtin-tools/workflow-editor-tools.js +509 -0
  38. package/dist/services/builtin-tools/workflow-exec-tools.js +320 -0
  39. package/dist/services/chat.js +134 -0
  40. package/dist/services/command-process-manager.js +16 -0
  41. package/dist/services/execution-manager.js +1346 -0
  42. package/dist/services/generated-title.js +59 -0
  43. package/dist/services/gitignore.js +22 -18
  44. package/dist/services/interaction-manager.js +114 -0
  45. package/dist/services/issue-retry.js +25 -0
  46. package/dist/services/notification-center.js +9 -0
  47. package/dist/services/output-style.js +8 -1
  48. package/dist/services/plugin.js +257 -0
  49. package/dist/services/prompt-template.js +8 -1
  50. package/dist/services/pty.js +20 -0
  51. package/dist/services/search.js +16 -6
  52. package/dist/services/skill.js +2 -0
  53. package/dist/services/version.js +69 -0
  54. package/dist/services/workflow-command-runner.js +24 -0
  55. package/dist/services/workflow-trigger-service.js +137 -0
  56. package/dist/services/workflow.js +219 -39
  57. package/dist/services/workspace.js +23 -1
  58. package/dist/services/worktree.js +207 -0
  59. package/dist/storage/agent-store.js +8 -0
  60. package/dist/storage/chat-store.js +151 -0
  61. package/dist/storage/database-store.js +6 -0
  62. package/dist/storage/json-store.js +2 -1
  63. package/dist/storage/kanban-store.js +6 -0
  64. package/dist/storage/workflow-store.js +386 -22
  65. package/dist/storage/worktree-store.js +33 -0
  66. package/dist/web/404.html +1 -1
  67. package/dist/web/__next.__PAGE__.txt +4 -4
  68. package/dist/web/__next._full.txt +21 -21
  69. package/dist/web/__next._head.txt +4 -4
  70. package/dist/web/__next._index.txt +13 -13
  71. package/dist/web/__next._tree.txt +2 -2
  72. package/dist/web/_next/static/chunks/{08x85faoz58i7.js → 0-92u-tiuy40a.js} +4 -4
  73. package/dist/web/_next/static/chunks/{0db14450oqzhg.js → 0-yizd8osmas2.js} +1 -1
  74. package/dist/web/_next/static/chunks/0.ln~3mnv8.69.js +1 -0
  75. package/dist/web/_next/static/chunks/{11cchuwoupmbf.js → 01y3e9pemva7b.js} +10 -10
  76. package/dist/web/_next/static/chunks/02l~tmzrgny.~.js +1 -0
  77. package/dist/web/_next/static/chunks/03f1bwuynjdmk.js +1 -0
  78. package/dist/web/_next/static/chunks/04cb4kc9r2pd1.js +1 -0
  79. package/dist/web/_next/static/chunks/04de6ha5_e3pp.js +1 -0
  80. package/dist/web/_next/static/chunks/09he0p79ov1rn.js +1 -0
  81. package/dist/web/_next/static/chunks/09vpocs4zuc8h.js +1 -0
  82. package/dist/web/_next/static/chunks/0_o2exgr0e_nh.js +1 -0
  83. package/dist/web/_next/static/chunks/{0xbaakj0dfc0..js → 0_y9nskc-xt2k.js} +1 -1
  84. package/dist/web/_next/static/chunks/0clnj-1fi6u8j.js +3 -0
  85. package/dist/web/_next/static/chunks/0gv40by0zzxs_.js +1 -0
  86. package/dist/web/_next/static/chunks/0iusr8y88.kjg.js +1 -0
  87. package/dist/web/_next/static/chunks/{0-a.-hl3wv99k.js → 0j-8418fxvkd1.js} +1 -1
  88. package/dist/web/_next/static/chunks/0j.wxxyoclma5.js +1 -0
  89. package/dist/web/_next/static/chunks/{0.6b64sy0g5.3.js → 0jq.16sozqik..js} +1 -1
  90. package/dist/web/_next/static/chunks/0jw8xdk_fpnp0.css +1 -0
  91. package/dist/web/_next/static/chunks/0l1lkd-wcotma.js +1 -0
  92. package/dist/web/_next/static/chunks/{0u6h-hqc_i52e.js → 0ncxf_zr4n839.js} +1 -1
  93. package/dist/web/_next/static/chunks/0ndo7um~v7vco.js +1 -0
  94. package/dist/web/_next/static/chunks/0ngk.~kan8wq_.js +1 -0
  95. package/dist/web/_next/static/chunks/0ppr271hz0xoq.js +1 -0
  96. package/dist/web/_next/static/chunks/0rt9bofnn6qvn.js +1 -0
  97. package/dist/web/_next/static/chunks/0t64.9jd00hio.js +3 -0
  98. package/dist/web/_next/static/chunks/0taui9--5n6-5.js +1 -0
  99. package/dist/web/_next/static/chunks/0ty39169lh~z3.js +1 -0
  100. package/dist/web/_next/static/chunks/0v8nof~waedlu.js +1 -0
  101. package/dist/web/_next/static/chunks/{0-pi.67w.ymsr.js → 0vcfoagnpf.aw.js} +1 -1
  102. package/dist/web/_next/static/chunks/{0vis~lx0nxgn-.js → 0wwwq.gyyz0vg.js} +3 -3
  103. package/dist/web/_next/static/chunks/0x5lak8yz7pm7.js +1 -0
  104. package/dist/web/_next/static/chunks/0xon_.4-o0_zq.js +7 -0
  105. package/dist/web/_next/static/chunks/0zjnrbt7inime.js +1 -0
  106. package/dist/web/_next/static/chunks/0zwebpitvpuv1.js +1 -0
  107. package/dist/web/_next/static/chunks/0~02o4g-o6n~m.js +1 -0
  108. package/dist/web/_next/static/chunks/{0w7.tb29958zj.js → 0~fjezp.k4qk3.js} +1 -1
  109. package/dist/web/_next/static/chunks/0~sh93re1-_rp.js +1 -0
  110. package/dist/web/_next/static/chunks/10_niiq0k6o-p.js +5 -0
  111. package/dist/web/_next/static/chunks/12tjr0_2ccrtq.js +2 -0
  112. package/dist/web/_next/static/chunks/12x5.go_x9mfk.js +1 -0
  113. package/dist/web/_next/static/chunks/{0l2w0qz1ea7ql.js → 12~5dk9i9d16t.js} +2 -2
  114. package/dist/web/_next/static/chunks/13iye4rv5p31b.js +1 -0
  115. package/dist/web/_next/static/chunks/{01~quun6sefgl.js → 13~y-b.b9rrw4.js} +1 -1
  116. package/dist/web/_next/static/chunks/151wzhb5q0lo5.js +1 -0
  117. package/dist/web/_not-found/__next._full.txt +19 -19
  118. package/dist/web/_not-found/__next._head.txt +4 -4
  119. package/dist/web/_not-found/__next._index.txt +13 -13
  120. package/dist/web/_not-found/__next._not-found.__PAGE__.txt +2 -2
  121. package/dist/web/_not-found/__next._not-found.txt +3 -3
  122. package/dist/web/_not-found/__next._tree.txt +2 -2
  123. package/dist/web/_not-found.html +1 -1
  124. package/dist/web/_not-found.txt +19 -19
  125. package/dist/web/index.html +1 -1
  126. package/dist/web/index.txt +21 -21
  127. package/dist/web/login/__next._full.txt +21 -21
  128. package/dist/web/login/__next._head.txt +4 -4
  129. package/dist/web/login/__next._index.txt +13 -13
  130. package/dist/web/login/__next._tree.txt +2 -2
  131. package/dist/web/login/__next.login.__PAGE__.txt +4 -4
  132. package/dist/web/login/__next.login.txt +3 -3
  133. package/dist/web/login.html +1 -1
  134. package/dist/web/login.txt +21 -21
  135. package/dist/web/settings/__next._full.txt +23 -23
  136. package/dist/web/settings/__next._head.txt +4 -4
  137. package/dist/web/settings/__next._index.txt +13 -13
  138. package/dist/web/settings/__next._tree.txt +2 -2
  139. package/dist/web/settings/__next.settings.__PAGE__.txt +4 -4
  140. package/dist/web/settings/__next.settings.txt +5 -5
  141. package/dist/web/settings/agents/__next._full.txt +23 -23
  142. package/dist/web/settings/agents/__next._head.txt +4 -4
  143. package/dist/web/settings/agents/__next._index.txt +13 -13
  144. package/dist/web/settings/agents/__next._tree.txt +2 -2
  145. package/dist/web/settings/agents/__next.settings.agents.__PAGE__.txt +4 -4
  146. package/dist/web/settings/agents/__next.settings.agents.txt +3 -3
  147. package/dist/web/settings/agents/__next.settings.txt +5 -5
  148. package/dist/web/settings/agents.html +1 -1
  149. package/dist/web/settings/agents.txt +23 -23
  150. package/dist/web/settings/mcps/__next._full.txt +23 -23
  151. package/dist/web/settings/mcps/__next._head.txt +4 -4
  152. package/dist/web/settings/mcps/__next._index.txt +13 -13
  153. package/dist/web/settings/mcps/__next._tree.txt +2 -2
  154. package/dist/web/settings/mcps/__next.settings.mcps.__PAGE__.txt +4 -4
  155. package/dist/web/settings/mcps/__next.settings.mcps.txt +3 -3
  156. package/dist/web/settings/mcps/__next.settings.txt +5 -5
  157. package/dist/web/settings/mcps.html +1 -1
  158. package/dist/web/settings/mcps.txt +23 -23
  159. package/dist/web/settings/models/__next._full.txt +23 -23
  160. package/dist/web/settings/models/__next._head.txt +4 -4
  161. package/dist/web/settings/models/__next._index.txt +13 -13
  162. package/dist/web/settings/models/__next._tree.txt +2 -2
  163. package/dist/web/settings/models/__next.settings.models.__PAGE__.txt +4 -4
  164. package/dist/web/settings/models/__next.settings.models.txt +3 -3
  165. package/dist/web/settings/models/__next.settings.txt +5 -5
  166. package/dist/web/settings/models.html +1 -1
  167. package/dist/web/settings/models.txt +23 -23
  168. package/dist/web/settings/output-styles/__next._full.txt +23 -23
  169. package/dist/web/settings/output-styles/__next._head.txt +4 -4
  170. package/dist/web/settings/output-styles/__next._index.txt +13 -13
  171. package/dist/web/settings/output-styles/__next._tree.txt +2 -2
  172. package/dist/web/settings/output-styles/__next.settings.output-styles.__PAGE__.txt +4 -4
  173. package/dist/web/settings/output-styles/__next.settings.output-styles.txt +3 -3
  174. package/dist/web/settings/output-styles/__next.settings.txt +5 -5
  175. package/dist/web/settings/output-styles.html +1 -1
  176. package/dist/web/settings/output-styles.txt +23 -23
  177. package/dist/web/settings/prompts/__next._full.txt +23 -23
  178. package/dist/web/settings/prompts/__next._head.txt +4 -4
  179. package/dist/web/settings/prompts/__next._index.txt +13 -13
  180. package/dist/web/settings/prompts/__next._tree.txt +2 -2
  181. package/dist/web/settings/prompts/__next.settings.prompts.__PAGE__.txt +4 -4
  182. package/dist/web/settings/prompts/__next.settings.prompts.txt +3 -3
  183. package/dist/web/settings/prompts/__next.settings.txt +5 -5
  184. package/dist/web/settings/prompts.html +1 -1
  185. package/dist/web/settings/prompts.txt +23 -23
  186. package/dist/web/settings/providers/__next._full.txt +23 -23
  187. package/dist/web/settings/providers/__next._head.txt +4 -4
  188. package/dist/web/settings/providers/__next._index.txt +13 -13
  189. package/dist/web/settings/providers/__next._tree.txt +2 -2
  190. package/dist/web/settings/providers/__next.settings.providers.__PAGE__.txt +4 -4
  191. package/dist/web/settings/providers/__next.settings.providers.txt +3 -3
  192. package/dist/web/settings/providers/__next.settings.txt +5 -5
  193. package/dist/web/settings/providers.html +1 -1
  194. package/dist/web/settings/providers.txt +23 -23
  195. package/dist/web/settings/skills/__next._full.txt +23 -23
  196. package/dist/web/settings/skills/__next._head.txt +4 -4
  197. package/dist/web/settings/skills/__next._index.txt +13 -13
  198. package/dist/web/settings/skills/__next._tree.txt +2 -2
  199. package/dist/web/settings/skills/__next.settings.skills.__PAGE__.txt +4 -4
  200. package/dist/web/settings/skills/__next.settings.skills.txt +3 -3
  201. package/dist/web/settings/skills/__next.settings.txt +5 -5
  202. package/dist/web/settings/skills.html +1 -1
  203. package/dist/web/settings/skills.txt +23 -23
  204. package/dist/web/settings/tools/__next._full.txt +23 -23
  205. package/dist/web/settings/tools/__next._head.txt +4 -4
  206. package/dist/web/settings/tools/__next._index.txt +13 -13
  207. package/dist/web/settings/tools/__next._tree.txt +2 -2
  208. package/dist/web/settings/tools/__next.settings.tools.__PAGE__.txt +4 -4
  209. package/dist/web/settings/tools/__next.settings.tools.txt +3 -3
  210. package/dist/web/settings/tools/__next.settings.txt +5 -5
  211. package/dist/web/settings/tools.html +1 -1
  212. package/dist/web/settings/tools.txt +23 -23
  213. package/dist/web/settings.html +1 -1
  214. package/dist/web/settings.txt +23 -23
  215. package/dist/web/workflows/__next._full.txt +22 -22
  216. package/dist/web/workflows/__next._head.txt +4 -4
  217. package/dist/web/workflows/__next._index.txt +13 -13
  218. package/dist/web/workflows/__next._tree.txt +2 -2
  219. package/dist/web/workflows/__next.workflows.__PAGE__.txt +4 -4
  220. package/dist/web/workflows/__next.workflows.txt +3 -3
  221. package/dist/web/workflows.html +1 -1
  222. package/dist/web/workflows.txt +22 -22
  223. package/dist/web/workspace/_/__next._full.txt +20 -20
  224. package/dist/web/workspace/_/__next._head.txt +4 -4
  225. package/dist/web/workspace/_/__next._index.txt +13 -13
  226. package/dist/web/workspace/_/__next._tree.txt +2 -2
  227. package/dist/web/workspace/_/__next.workspace.$d$id.__PAGE__.txt +3 -3
  228. package/dist/web/workspace/_/__next.workspace.$d$id.txt +3 -3
  229. package/dist/web/workspace/_/__next.workspace.txt +3 -3
  230. package/dist/web/workspace/_.html +1 -1
  231. package/dist/web/workspace/_.txt +20 -20
  232. package/dist/web/workspaces/__next._full.txt +21 -21
  233. package/dist/web/workspaces/__next._head.txt +4 -4
  234. package/dist/web/workspaces/__next._index.txt +13 -13
  235. package/dist/web/workspaces/__next._tree.txt +2 -2
  236. package/dist/web/workspaces/__next.workspaces.__PAGE__.txt +4 -4
  237. package/dist/web/workspaces/__next.workspaces.txt +3 -3
  238. package/dist/web/workspaces.html +1 -1
  239. package/dist/web/workspaces.txt +21 -21
  240. package/dist/ws/agent-prompt.js +36 -3
  241. package/dist/ws/agent-runner.js +30 -18
  242. package/dist/ws/chat-handler.js +89 -0
  243. package/dist/ws/connection-manager.js +49 -2
  244. package/dist/ws/execution-channels.js +88 -0
  245. package/dist/ws/handler.js +32 -5
  246. package/dist/ws/message-parts.js +130 -12
  247. package/dist/ws/terminal-handler.js +26 -0
  248. package/package.json +13 -1
  249. package/public/avatars/user.jpg +0 -0
  250. package/dist/web/_next/static/chunks/0-v_p6j5u3_9l.js +0 -1
  251. package/dist/web/_next/static/chunks/0.-x29t-8gk0r.js +0 -1
  252. package/dist/web/_next/static/chunks/0.l5e8~r7_0~p.js +0 -1
  253. package/dist/web/_next/static/chunks/0.n~yfwf4~6w-.js +0 -1
  254. package/dist/web/_next/static/chunks/00-syqzi-6zxr.js +0 -1
  255. package/dist/web/_next/static/chunks/02j4sr9psucf-.js +0 -1
  256. package/dist/web/_next/static/chunks/06si8l0doqe8p.js +0 -1
  257. package/dist/web/_next/static/chunks/08kfx8on9oty~.js +0 -1
  258. package/dist/web/_next/static/chunks/08ugbvzr20drp.js +0 -2
  259. package/dist/web/_next/static/chunks/0_cqojbge48kq.js +0 -5
  260. package/dist/web/_next/static/chunks/0d6~~lv0-6kex.js +0 -3
  261. package/dist/web/_next/static/chunks/0gdwop9cnpmtu.js +0 -1
  262. package/dist/web/_next/static/chunks/0ghx3j_.-gdyd.js +0 -1
  263. package/dist/web/_next/static/chunks/0kbvoxla7f751.js +0 -1
  264. package/dist/web/_next/static/chunks/0paaat8y96ayc.js +0 -7
  265. package/dist/web/_next/static/chunks/0pq318f8xgafg.js +0 -1
  266. package/dist/web/_next/static/chunks/0t6s8of0qr6nc.js +0 -1
  267. package/dist/web/_next/static/chunks/0xt-8wk21gs7g.js +0 -1
  268. package/dist/web/_next/static/chunks/0yd.m1afqg.2t.js +0 -3
  269. package/dist/web/_next/static/chunks/0yhbr8hl09b-5.js +0 -1
  270. package/dist/web/_next/static/chunks/0z~vqzbm_q371.js +0 -1
  271. package/dist/web/_next/static/chunks/0~~_ltu6dx_ch.js +0 -1
  272. package/dist/web/_next/static/chunks/12oxcgyhpaxw~.js +0 -1
  273. package/dist/web/_next/static/chunks/1452eibz62gs6.css +0 -1
  274. package/dist/web/_next/static/chunks/16zlreiems5cn.js +0 -1
  275. package/dist/web/_next/static/chunks/18awqjk9-o5m0.js +0 -1
  276. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_buildManifest.js +0 -0
  277. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_clientMiddlewareManifest.js +0 -0
  278. /package/dist/web/_next/static/{x4-CsKe6qUQyFDWVtfvUg → rGXyAK8rgod1NjF3bLZAY}/_ssgManifest.js +0 -0
@@ -1,5 +1,10 @@
1
- import { createAgent } from '@codeany/open-agent-sdk';
1
+ import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { basename, join } from 'node:path';
3
+ import { createAgent, registerSkill, unregisterSkill } from '@codeany/open-agent-sdk';
2
4
  import { appendOutputStyleToPrompt, summarizeResult } from './agent-runtime-types.js';
5
+ import { startCodexFunctionToolBridge } from './codex-function-tool-bridge.js';
6
+ const registeredConfiguredSkills = new Set();
7
+ const DEFAULT_DATA_DIR = join(process.env.HOME || '~', '.agent-spaces-data');
3
8
  /**
4
9
  * Runtime backed by @codeany/open-agent-sdk.
5
10
  * Runs the agent loop in-process with the SDK's built-in tools.
@@ -17,9 +22,13 @@ export class OpenAgentSdkRuntime {
17
22
  const cwd = workingDir || process.cwd();
18
23
  const startTime = Date.now();
19
24
  const d = (msg) => console.log(`[agent] ${msg}`);
20
- d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'default'} model=${this.config.model ?? 'default'} baseURL=${this.config.baseURL ?? 'default'} permissionMode=${this.config.permissionMode ?? 'bypassPermissions'} maxTurns=${options?.maxTurns ?? '∞'} tools=${options?.tools?.join(',') ?? 'all'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
25
+ let functionToolBridge;
26
+ d(`starting | cwd=${cwd} provider=${this.config.provider ?? 'default'} model=${this.config.model ?? 'default'} baseURL=${this.config.baseURL ?? 'default'} permissionMode=${this.config.permissionMode ?? 'bypassPermissions'} maxTurns=${options?.maxTurns ?? '∞'} allowedTools=${options?.tools?.join(',') ?? 'all'} mcpServers=${Object.keys(options?.mcpServers ?? {}).join(',') || '-'} functionTools=${options?.functionTools?.map((tool) => tool.name).join(',') || '-'} sandboxDirs=${options?.sandboxDirs?.join(',') ?? '-'}`);
21
27
  d(`prompt: ${prompt.slice(0, 300)}${prompt.length > 300 ? '...' : ''}`);
22
28
  try {
29
+ functionToolBridge = await startCodexFunctionToolBridge(options?.functionTools, d);
30
+ const mcpServers = withFunctionToolBridge(normalizeOpenAgentMcpServers(options?.mcpServers), functionToolBridge);
31
+ d(`resolved tools | allowedTools=${options?.tools?.join(',') ?? 'all'} mcpServers=${Object.keys(mcpServers ?? {}).join(',') || '-'} functionTools=${options?.functionTools?.map((tool) => tool.name).join(',') || '-'}`);
23
32
  this.agent = createAgent({
24
33
  apiType: normalizeApiType(this.config.provider),
25
34
  model: this.config.model,
@@ -29,13 +38,17 @@ export class OpenAgentSdkRuntime {
29
38
  systemPrompt: options?.systemPrompt,
30
39
  maxTurns: options?.maxTurns,
31
40
  allowedTools: options?.tools,
41
+ mcpServers,
32
42
  additionalDirectories: options?.sandboxDirs,
33
43
  permissionMode: this.config.permissionMode ?? 'bypassPermissions',
34
44
  abortController: this.abortController,
35
45
  });
46
+ const registeredSkills = registerConfiguredSkills(options?.configDir, options?.skills);
47
+ if (options?.skills?.length) {
48
+ d(`skills registered | requested=${options.skills.join(',') || '-'} registered=${registeredSkills.join(',') || '-'}`);
49
+ }
36
50
  d('agent created, sending prompt...');
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(appendOutputStyleToPrompt(prompt, options?.outputStyle));
51
+ const result = await collectQueryResult(this.agent.query(appendOutputStyleToPrompt(prompt, options?.outputStyle)), output, options, d);
39
52
  const elapsed = Date.now() - startTime;
40
53
  const inputTokens = result.usage.input_tokens;
41
54
  const outputTokens = result.usage.output_tokens;
@@ -68,7 +81,18 @@ export class OpenAgentSdkRuntime {
68
81
  return { success: false, summary: 'Agent execution failed', artifacts: [], error: message, output };
69
82
  }
70
83
  finally {
71
- await this.agent?.close();
84
+ try {
85
+ await this.agent?.close();
86
+ }
87
+ finally {
88
+ try {
89
+ await functionToolBridge?.close();
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : String(err);
93
+ d(`function tool bridge close failed | ${message}`);
94
+ }
95
+ }
72
96
  this.agent = null;
73
97
  this.abortController = null;
74
98
  }
@@ -78,10 +102,183 @@ export class OpenAgentSdkRuntime {
78
102
  this.agent?.interrupt();
79
103
  }
80
104
  }
105
+ function withFunctionToolBridge(mcpServers, bridge) {
106
+ if (!bridge)
107
+ return mcpServers;
108
+ return {
109
+ ...(mcpServers ?? {}),
110
+ [bridge.name]: { type: 'http', url: bridge.url },
111
+ };
112
+ }
113
+ export function normalizeOpenAgentMcpServers(mcpServers) {
114
+ if (!mcpServers)
115
+ return undefined;
116
+ return Object.fromEntries(Object.entries(mcpServers).map(([name, server]) => [name, normalizeOpenAgentMcpServer(server)]));
117
+ }
118
+ function normalizeOpenAgentMcpServer(server) {
119
+ if (!isRecord(server))
120
+ return server;
121
+ const args = Array.isArray(server.args) ? server.args.map(String) : undefined;
122
+ const badFetchPackageIndex = args?.indexOf('@modelcontextprotocol/server-fetch') ?? -1;
123
+ if (String(server.command) !== 'npx' || !args || badFetchPackageIndex < 0)
124
+ return server;
125
+ return {
126
+ ...server,
127
+ command: 'uvx',
128
+ args: ['mcp-server-fetch', ...args.slice(badFetchPackageIndex + 1)],
129
+ env: {
130
+ PYTHONIOENCODING: 'utf-8',
131
+ ...(isRecord(server.env) ? server.env : {}),
132
+ },
133
+ };
134
+ }
135
+ function isRecord(value) {
136
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
137
+ }
138
+ async function collectQueryResult(events, output, options, log) {
139
+ const collected = {
140
+ text: '',
141
+ usage: { input_tokens: 0, output_tokens: 0 },
142
+ num_turns: 0,
143
+ };
144
+ for await (const event of events) {
145
+ if (event.type === 'system' && event.subtype === 'init') {
146
+ log(`sdk init | session=${event.session_id} model=${event.model} cwd=${event.cwd} permissionMode=${event.permission_mode} tools=${event.tools.join(',') || '-'} mcpServers=${event.mcp_servers.map((server) => `${server.name}:${server.status}`).join(',') || '-'}`);
147
+ options?.onEvent?.({ type: 'session', sessionId: event.session_id });
148
+ continue;
149
+ }
150
+ if (event.type === 'assistant') {
151
+ const text = event.message.content
152
+ .filter((block) => block.type === 'text')
153
+ .map((block) => block.text)
154
+ .join('');
155
+ if (text)
156
+ collected.text = text;
157
+ for (const block of event.message.content) {
158
+ if (block.type !== 'tool_use')
159
+ continue;
160
+ const line = `Tool: ${block.name} input=${JSON.stringify(block.input)}`;
161
+ log(`tool use | id=${block.id} name=${block.name} input=${JSON.stringify(block.input)}`);
162
+ output.push(line);
163
+ options?.onEvent?.({ type: 'tool_use', id: block.id, name: block.name, input: block.input, line });
164
+ }
165
+ continue;
166
+ }
167
+ if (event.type === 'tool_result') {
168
+ const result = event.result;
169
+ log(`tool result | id=${result.tool_use_id} name=${result.tool_name} output=${truncateForLog(result.output)}`);
170
+ options?.onEvent?.({ type: 'tool_result', toolUseId: result.tool_use_id, result: result.output });
171
+ continue;
172
+ }
173
+ if (event.type === 'result') {
174
+ collected.num_turns = event.num_turns ?? 0;
175
+ collected.usage = {
176
+ input_tokens: event.usage?.input_tokens ?? 0,
177
+ output_tokens: event.usage?.output_tokens ?? 0,
178
+ cache_read_input_tokens: event.usage?.cache_read_input_tokens,
179
+ cache_creation_input_tokens: event.usage?.cache_creation_input_tokens,
180
+ };
181
+ if (event.is_error) {
182
+ log(`sdk result error | subtype=${event.subtype} errors=${event.errors?.join('; ') || '-'}`);
183
+ }
184
+ }
185
+ }
186
+ return collected;
187
+ }
188
+ function truncateForLog(value, max = 1000) {
189
+ return value.length > max ? `${value.slice(0, max)}...` : value;
190
+ }
81
191
  function normalizeApiType(provider) {
82
192
  if (provider === 'anthropic-messages' || provider === 'openai-completions') {
83
193
  return provider;
84
194
  }
85
195
  return undefined;
86
196
  }
197
+ export function registerConfiguredSkills(agentDir, skills) {
198
+ for (const skill of registeredConfiguredSkills)
199
+ unregisterSkill(skill);
200
+ registeredConfiguredSkills.clear();
201
+ if (!agentDir || !Array.isArray(skills))
202
+ return [];
203
+ const registered = [];
204
+ for (const rawSkill of skills) {
205
+ const skillName = sanitizeSkillName(rawSkill);
206
+ if (!skillName)
207
+ continue;
208
+ const skillFile = resolveSkillFile(agentDir, skillName);
209
+ if (!skillFile)
210
+ continue;
211
+ const source = readFileSync(skillFile, 'utf-8');
212
+ const parsed = parseSkillMarkdown(source);
213
+ const name = sanitizeSkillName(parsed.meta.name) || skillName;
214
+ const aliases = new Set(parseListMeta(parsed.meta.aliases).map(sanitizeSkillName).filter(Boolean));
215
+ if (name !== skillName)
216
+ aliases.add(skillName);
217
+ registerSkill({
218
+ name,
219
+ aliases: aliases.size ? [...aliases] : undefined,
220
+ description: parsed.meta.description || summarizeSkillDescription(parsed.body, skillName),
221
+ whenToUse: parsed.meta['when-to-use'] || parsed.meta.whenToUse,
222
+ userInvocable: true,
223
+ async getPrompt() {
224
+ return [{ type: 'text', text: parsed.body || source }];
225
+ },
226
+ });
227
+ registeredConfiguredSkills.add(name);
228
+ registered.push(name);
229
+ }
230
+ return registered;
231
+ }
232
+ function resolveSkillFile(agentDir, skillName) {
233
+ const skillsBase = join(agentDir, 'skills');
234
+ const folderSkillFile = join(skillsBase, skillName, 'SKILL.md');
235
+ if (existsSync(folderSkillFile) && statSync(folderSkillFile).size > 0)
236
+ return folderSkillFile;
237
+ const legacySkillFile = join(skillsBase, `${skillName}.md`);
238
+ if (existsSync(legacySkillFile) && statSync(legacySkillFile).size > 0)
239
+ return legacySkillFile;
240
+ const globalSkillFile = join(getDataDir(), 'skills', skillName, 'SKILL.md');
241
+ if (existsSync(globalSkillFile) && statSync(globalSkillFile).size > 0)
242
+ return globalSkillFile;
243
+ const globalLegacySkillFile = join(getDataDir(), 'skills', `${skillName}.md`);
244
+ if (existsSync(globalLegacySkillFile) && statSync(globalLegacySkillFile).size > 0)
245
+ return globalLegacySkillFile;
246
+ return undefined;
247
+ }
248
+ function getDataDir() {
249
+ return process.env.AGENT_SPACES_DATA_DIR || DEFAULT_DATA_DIR;
250
+ }
251
+ function parseSkillMarkdown(source) {
252
+ const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
253
+ if (!match)
254
+ return { meta: {}, body: source.trim() };
255
+ const meta = {};
256
+ for (const line of match[1].split(/\r?\n/)) {
257
+ const parsed = line.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
258
+ if (!parsed)
259
+ continue;
260
+ meta[parsed[1]] = parsed[2].trim().replace(/^['"]|['"]$/g, '');
261
+ }
262
+ return { meta, body: source.slice(match[0].length).trim() };
263
+ }
264
+ function summarizeSkillDescription(body, skillName) {
265
+ const firstLine = body
266
+ .split(/\r?\n/)
267
+ .map((line) => line.replace(/^#+\s*/, '').trim())
268
+ .find(Boolean);
269
+ return firstLine || `Configured skill ${skillName}`;
270
+ }
271
+ function parseListMeta(value) {
272
+ if (!value)
273
+ return [];
274
+ const trimmed = value.trim();
275
+ if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
276
+ return trimmed.slice(1, -1).split(',').map((item) => item.trim().replace(/^['"]|['"]$/g, ''));
277
+ }
278
+ return trimmed.split(',').map((item) => item.trim());
279
+ }
280
+ function sanitizeSkillName(name) {
281
+ const raw = basename(name ?? '').replace(/\.md$/i, '').trim();
282
+ return raw.replace(/[^a-zA-Z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
283
+ }
87
284
  //# sourceMappingURL=open-agent-sdk-runtime.js.map
@@ -0,0 +1,35 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { clearSkills, getSkill } from '@codeany/open-agent-sdk';
7
+ import { registerConfiguredSkills } from './open-agent-sdk-runtime.js';
8
+ test('registerConfiguredSkills loads folder skills from agent config', async () => {
9
+ clearSkills();
10
+ const agentDir = mkdtempSync(join(tmpdir(), 'agent-skills-'));
11
+ try {
12
+ mkdirSync(join(agentDir, 'skills', 'brainstorming'), { recursive: true });
13
+ writeFileSync(join(agentDir, 'skills', 'brainstorming', 'SKILL.md'), [
14
+ '---',
15
+ 'name: brainstorming',
16
+ 'description: Explore requirements before implementation.',
17
+ '---',
18
+ '',
19
+ 'Ask clarifying questions and produce a design.',
20
+ ].join('\n'), 'utf-8');
21
+ const registered = registerConfiguredSkills(agentDir, ['brainstorming']);
22
+ const skill = getSkill('brainstorming');
23
+ assert.deepEqual(registered, ['brainstorming']);
24
+ assert.ok(skill);
25
+ assert.equal(skill.description, 'Explore requirements before implementation.');
26
+ assert.deepEqual(await skill.getPrompt('', {}), [
27
+ { type: 'text', text: 'Ask clarifying questions and produce a design.' },
28
+ ]);
29
+ }
30
+ finally {
31
+ rmSync(agentDir, { recursive: true, force: true });
32
+ clearSkills();
33
+ }
34
+ });
35
+ //# sourceMappingURL=open-agent-sdk-runtime.test.js.map
@@ -63,7 +63,8 @@ export function createAgentMessagePartsTracker(input) {
63
63
  };
64
64
  }
65
65
  function buildAgentMessageParts(input) {
66
- const lines = normalizeOutputLines(input.output);
66
+ const extractedReasoning = extractInlineReasoning(normalizeOutputLines(input.output));
67
+ const lines = extractedReasoning.lines;
67
68
  const finalTextRange = findFinalTextRange(lines);
68
69
  const finalText = finalTextRange
69
70
  ? collapseRepeatedTextBlock(lines.slice(finalTextRange.start, finalTextRange.end + 1)).join('\n').trim()
@@ -71,7 +72,10 @@ function buildAgentMessageParts(input) {
71
72
  const usage = input.usage ?? extractUsage(lines);
72
73
  const parts = [];
73
74
  const chainItems = buildChainItems(lines, finalTextRange, finalText, input.workspaceRoot, input.toolDetails, input.persistentContext);
74
- const reasoningText = normalizeReasoningText(input.reasoning);
75
+ const reasoningText = normalizeReasoningText([
76
+ ...(input.reasoning ?? []),
77
+ ...extractedReasoning.reasoning.map((text) => ({ text })),
78
+ ]);
75
79
  if (reasoningText) {
76
80
  parts.push({
77
81
  id: `reasoning-${input.sessionId}`,
@@ -124,6 +128,52 @@ function normalizeReasoningText(reasoning) {
124
128
  .map((item) => item.text.trim())
125
129
  .filter(Boolean)).join('\n\n').trim();
126
130
  }
131
+ function extractInlineReasoning(lines) {
132
+ const reasoning = [];
133
+ const outputLines = [];
134
+ let buffer = [];
135
+ let inThinkBlock = false;
136
+ const flushReasoning = () => {
137
+ const text = buffer.join('\n').trim();
138
+ if (text)
139
+ reasoning.push(text);
140
+ buffer = [];
141
+ };
142
+ for (const line of lines) {
143
+ let rest = line;
144
+ let visible = '';
145
+ while (rest) {
146
+ if (inThinkBlock) {
147
+ const closeIndex = rest.search(/<\/think>/i);
148
+ if (closeIndex < 0) {
149
+ buffer.push(rest);
150
+ rest = '';
151
+ continue;
152
+ }
153
+ buffer.push(rest.slice(0, closeIndex));
154
+ flushReasoning();
155
+ rest = rest.slice(closeIndex).replace(/^<\/think>/i, '');
156
+ inThinkBlock = false;
157
+ continue;
158
+ }
159
+ const openIndex = rest.search(/<think>/i);
160
+ if (openIndex < 0) {
161
+ visible += rest;
162
+ rest = '';
163
+ continue;
164
+ }
165
+ visible += rest.slice(0, openIndex);
166
+ rest = rest.slice(openIndex).replace(/^<think>/i, '');
167
+ inThinkBlock = true;
168
+ }
169
+ const cleaned = visible.trimEnd();
170
+ if (cleaned.trim())
171
+ outputLines.push(cleaned);
172
+ }
173
+ if (inThinkBlock)
174
+ flushReasoning();
175
+ return { lines: outputLines, reasoning };
176
+ }
127
177
  function hasTokenUsage(usage) {
128
178
  return Boolean(usage.totalTokens || usage.inputTokens || usage.outputTokens || usage.cachedInputTokens || usage.reasoningTokens);
129
179
  }
@@ -4,12 +4,13 @@ import * as channelService from '../services/channel.js';
4
4
  import * as issueService from '../services/issue.js';
5
5
  import * as taskService from '../services/task.js';
6
6
  import * as workspaceService from '../services/workspace.js';
7
- import { mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
7
+ import { getWorkflow, mapWorkflowToTaskDrafts, validateWorkflowForRun } from '../services/workflow.js';
8
8
  import { createIssueFunctionTools } from '../services/builtin-tools/index.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
12
  import { wrapOnEventWithHooks } from '../services/hook-engine.js';
13
+ import { executeCommandNode } from '../services/workflow-command-runner.js';
13
14
  const ACTIVE_TASK_STATUSES = ['running', 'reviewing', 'retrying', 'waiting_review'];
14
15
  const activeIssueRuntimes = new Map();
15
16
  export async function syncIssueTasksAfterPlanning(workspaceId, issueId, input, ctx) {
@@ -137,6 +138,13 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
137
138
  if (task.status !== 'pending') {
138
139
  return;
139
140
  }
141
+ // Check if this task came from a command workflow node
142
+ const workflow = findWorkflowForIssue(workspaceId, issue);
143
+ const commandNode = findCommandNodeForTask(workflow, task);
144
+ if (commandNode) {
145
+ await runCommandTask(workspaceId, issueId, taskId, commandNode, ctx);
146
+ return;
147
+ }
140
148
  const taskAgentPreset = findAgentForTask(workspaceId, issue, task);
141
149
  if (!taskAgentPreset) {
142
150
  if (issueService.getById(workspaceId, issueId)?.status === 'error')
@@ -274,6 +282,50 @@ export async function runIssueTask(workspaceId, issueId, taskId, ctx) {
274
282
  await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
275
283
  }
276
284
  }
285
+ async function runCommandTask(workspaceId, issueId, taskId, commandNode, ctx) {
286
+ const task = taskService.getById(workspaceId, taskId);
287
+ if (!task || task.status !== 'pending')
288
+ return;
289
+ const runningTask = taskService.updateStatus(workspaceId, taskId, 'running');
290
+ if (!runningTask)
291
+ return;
292
+ broadcastTaskUpdate(ctx, runningTask, 'pending');
293
+ const cmdData = commandNode.data;
294
+ const cmdScript = cmdData.script;
295
+ ctx.broadcast('task.output', { taskId, data: `$ ${cmdScript.split('\n')[0]}${cmdScript.includes('\n') ? ' ...' : ''}` });
296
+ const result = await executeCommandNode(workspaceId, commandNode);
297
+ ctx.broadcast('task.output', { taskId, data: result.stdout });
298
+ if (result.stderr) {
299
+ ctx.broadcast('task.output', { taskId, data: result.stderr });
300
+ }
301
+ if (!result.success) {
302
+ const failedTask = taskService.updateStatus(workspaceId, taskId, 'failed', {
303
+ result: {
304
+ success: false,
305
+ summary: `Command failed with exit code ${result.exitCode}`,
306
+ artifacts: [],
307
+ error: result.stderr || `Exit code: ${result.exitCode}`,
308
+ },
309
+ });
310
+ broadcastTaskUpdate(ctx, failedTask, 'running');
311
+ await handleTaskFailure(workspaceId, issueId, taskId, {
312
+ success: false,
313
+ summary: `Command failed with exit code ${result.exitCode}`,
314
+ artifacts: [],
315
+ error: result.stderr || `Exit code: ${result.exitCode}`,
316
+ }, ctx);
317
+ return;
318
+ }
319
+ const completedTask = taskService.complete(workspaceId, taskId, {
320
+ success: true,
321
+ summary: result.stdout.slice(-500) || 'Command completed',
322
+ artifacts: [],
323
+ });
324
+ broadcastTaskUpdate(ctx, completedTask, 'running');
325
+ if (issueService.getById(workspaceId, issueId)?.continuousRun !== false) {
326
+ await scheduleRunnableIssueTasks(workspaceId, issueId, ctx);
327
+ }
328
+ }
277
329
  export async function continueIssueTaskScheduling(workspaceId, issueId, ctx) {
278
330
  await scheduleRunnableIssueTasks(workspaceId, issueId, ctx, { force: true });
279
331
  }
@@ -402,7 +454,7 @@ export function createTasksFromWorkflow(workspaceId, issueId, template, ctx) {
402
454
  });
403
455
  }
404
456
  function ensureWorkflowAgentsForRun(workspaceId, issue, template, ctx) {
405
- const workflowAgentIds = [...new Set(template.nodes.map((node) => node.data.agentConfigId).filter(Boolean))];
457
+ const workflowAgentIds = [...new Set(template.nodes.filter((n) => n.type === 'agent').map((node) => node.data.agentConfigId).filter(Boolean))];
406
458
  const mergedMembers = [...new Set([...(issue.members ?? []), ...workflowAgentIds])];
407
459
  const membersChanged = mergedMembers.length !== (issue.members ?? []).length
408
460
  || mergedMembers.some((member, index) => member !== issue.members?.[index]);
@@ -502,6 +554,23 @@ function unregisterActiveIssueRuntime(workspaceId, issueId, agentId) {
502
554
  function issueRunKey(workspaceId, issueId) {
503
555
  return `${workspaceId}:${issueId}`;
504
556
  }
557
+ function findWorkflowForIssue(workspaceId, issue) {
558
+ if (!issue.workflowId)
559
+ return null;
560
+ return getWorkflow(issue.workflowId);
561
+ }
562
+ function findCommandNodeForTask(workflow, task) {
563
+ if (!workflow)
564
+ return null;
565
+ if (task.agentConfigId)
566
+ return null;
567
+ for (const node of workflow.nodes) {
568
+ if (node.type === 'command' && (task.title === node.label || task.description?.startsWith('Command:'))) {
569
+ return node;
570
+ }
571
+ }
572
+ return null;
573
+ }
505
574
  function broadcastTaskUpdate(ctx, task, from) {
506
575
  if (!task)
507
576
  return;
@@ -0,0 +1,115 @@
1
+ import * as agentService from '../services/agent.js';
2
+ import { AGENT_GENERATOR_PRESET_ID } from '../services/agent.js';
3
+ import { createAgentRuntime } from '../adapters/agent-runtime.js';
4
+ import { getWorkspace } from '../storage/workspace-store.js';
5
+ import { getThinkingRuntimeConfig } from '../services/llm-model-config.js';
6
+ import { prependPersistentAgentContext } from '../services/persistent-agent-context.js';
7
+ const DEFAULT_SYSTEM_PROMPT = [
8
+ 'You are a pull request description generator.',
9
+ 'Return a concise, useful PR body based only on the provided commits and diff.',
10
+ 'Use Markdown with these sections: Summary and Changes.',
11
+ 'Keep it short. Do not greet, explain your process, ask questions, provide options, or wrap the response in code fences.',
12
+ 'Output only the final PR body text.',
13
+ ].join(' ');
14
+ export async function runPullRequestAgent(workspaceId, input) {
15
+ const ws = getWorkspace(workspaceId);
16
+ if (!ws)
17
+ throw new Error('Workspace not found');
18
+ if (!input.diff.trim() && input.commits.length === 0)
19
+ throw new Error('No changes found');
20
+ const agent = findPullRequestAgent(workspaceId);
21
+ const session = agentService.getOrCreateSessionForConfig(workspaceId, agent);
22
+ agentService.updateStatus(workspaceId, session.id, 'active');
23
+ const runtime = createAgentRuntime({
24
+ kind: agent.runtimeKind,
25
+ provider: agent.modelProvider,
26
+ model: agent.modelId,
27
+ apiKey: agent.apiKey,
28
+ baseURL: agent.apiBase,
29
+ ...getThinkingRuntimeConfig(agent),
30
+ });
31
+ const workingDir = agentService.resolveWorkingDir(workspaceId, agent);
32
+ const systemPrompt = DEFAULT_SYSTEM_PROMPT;
33
+ const truncatedDiff = input.diff.length > 12000
34
+ ? `${input.diff.substring(0, 12000)}\n... (truncated)`
35
+ : input.diff;
36
+ const userPrompt = [
37
+ `Generate a pull request body for branch ${input.worktree.branch} targeting ${input.baseBranch}.`,
38
+ '',
39
+ 'Commits:',
40
+ input.commits.length > 0 ? input.commits.map((commit) => `- ${commit}`).join('\n') : '- No commit messages available',
41
+ '',
42
+ 'Diff:',
43
+ truncatedDiff,
44
+ ].join('\n');
45
+ const result = await runtime.execute(prependPersistentAgentContext(userPrompt, {
46
+ workspaceId,
47
+ workingDir,
48
+ boundDirs: ws.boundDirs,
49
+ includeWorkspacePrompt: false,
50
+ excludeNativeClaudeMd: agent.runtimeKind === 'claude-code',
51
+ }), workingDir, {
52
+ maxTurns: 1,
53
+ userPrompt,
54
+ systemPrompt,
55
+ outputStyle: agent.outputStyle,
56
+ });
57
+ agentService.complete(workspaceId, session.id, result.success ? undefined : result.error, {
58
+ runtime: agent.runtimeKind,
59
+ model: agent.modelId,
60
+ summary: result.summary,
61
+ output: result.output,
62
+ usage: result.usage,
63
+ costUsd: result.costUsd,
64
+ });
65
+ if (!result.success) {
66
+ throw new Error(result.error || 'Pull request agent failed');
67
+ }
68
+ const body = sanitizePullRequestBody(result.output);
69
+ if (!body)
70
+ throw new Error('Empty response from pull request agent');
71
+ return body;
72
+ }
73
+ function findPullRequestAgent(workspaceId) {
74
+ const presets = agentService.listPresets(workspaceId);
75
+ const preset = presets?.find((a) => a.id === AGENT_GENERATOR_PRESET_ID);
76
+ if (preset)
77
+ return preset;
78
+ const template = agentService.readAgentTemplate(AGENT_GENERATOR_PRESET_ID);
79
+ if (template)
80
+ return template;
81
+ return agentService.listTemplates().find((a) => a.id === AGENT_GENERATOR_PRESET_ID) ?? agentService.listTemplates()[0];
82
+ }
83
+ function sanitizePullRequestBody(output) {
84
+ const chunks = (Array.isArray(output) ? output : [output])
85
+ .map((chunk) => cleanOutputChunk(chunk))
86
+ .filter(Boolean);
87
+ const uniqueChunks = dedupeAdjacentChunks(chunks);
88
+ return (uniqueChunks.at(-1) || uniqueChunks.join('\n\n')).trim();
89
+ }
90
+ function cleanOutputChunk(chunk) {
91
+ return chunk
92
+ .split('\n')
93
+ .filter((line) => !isRuntimeNoise(line))
94
+ .join('\n')
95
+ .replace(/```(?:\w+)?\n([\s\S]*?)```/g, '$1')
96
+ .trim();
97
+ }
98
+ function dedupeAdjacentChunks(chunks) {
99
+ const result = [];
100
+ for (const chunk of chunks) {
101
+ if (result.at(-1)?.trim() === chunk.trim())
102
+ continue;
103
+ result.push(chunk);
104
+ }
105
+ return result;
106
+ }
107
+ function isRuntimeNoise(line) {
108
+ const trimmed = line.trim();
109
+ return /^\[Usage\]/.test(trimmed)
110
+ || /^Claude Code initialized\b/i.test(trimmed)
111
+ || /^Codex initialized\b/i.test(trimmed)
112
+ || /^Tool:/i.test(trimmed)
113
+ || /^Todo:/i.test(trimmed);
114
+ }
115
+ //# sourceMappingURL=pull-request-agent.js.map
@@ -16,7 +16,7 @@ export function startScheduler(workspaceId, ctx) {
16
16
  const tick = async () => {
17
17
  // 检查 workspace 是否关闭了自动处理
18
18
  const workspace = workspaceService.getById(workspaceId);
19
- if (workspace && workspace.autoProcessIssues === false) {
19
+ if (workspace && workspace.autoProcessIssues !== true) {
20
20
  console.log(`[scheduler:${workspaceId}] auto-processing disabled for this workspace, skip`);
21
21
  return;
22
22
  }