@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
@@ -0,0 +1,509 @@
1
+ import { v4 as uuid } from 'uuid';
2
+ import * as workflowService from '../workflow.js';
3
+ const WORKFLOW_AGENT_SYSTEM_PROMPT = `你是 Agent Spaces 的工作流编辑助手。你的职责是帮助用户创建、修改、排查和优化当前可视化工作流。
4
+
5
+ 回复规则:
6
+ - 回复使用中文。
7
+ - 优先通过工具直接完成工作流编辑,而不是只给口头建议。
8
+ - 只有在本轮实际调用了对应编辑工具并看到 success=true 的工具结果后,才能说“已创建”“已连接”“已更新”。
9
+ - 不要臆测节点字段结构、变量写法或连线方式。
10
+
11
+ 工作流编辑硬规则:
12
+ 1. 准备使用、创建、插入或更新某个节点类型前,必须先调用 search_node_usage 查看节点定义。
13
+ 2. 如果用户只描述用途但没给出节点类型,先用 list_node_types 找候选,再用 search_node_usage 看具体字段、句柄和使用说明。
14
+ 3. 编辑现有工作流前,优先调用 get_current_workflow;需要完整 data 时用 summarize=false。
15
+ 4. 节点参数里的字符串值支持变量引用,优先使用 {{ __data__["节点ID"].字段路径 }} 和 {{ context.some.path }}。
16
+ 5. 结束节点返回结果来自 data.outputs,设置时使用 data: { outputs: [{ key, type, value }] }。
17
+ 6. 需要数据整形、字段映射或结构转换时,优先插入 run_code 节点;代码中不要写 {{ }},必须定义 async function main({ params, context })。
18
+ 7. 复杂、多步、批量或破坏性改动前先调用 create_workflow_version。
19
+ 8. 修改后通常调用 auto_layout 整理画布。
20
+
21
+ 约束:
22
+ - 只能使用本次 Agent Spaces runtime 暴露的工作流编辑工具。
23
+ - 不要编造节点类型、参数或执行结果;工具结果不足时明确说明需要补充信息。`;
24
+ export function buildWorkflowEditorSystemPrompt(workflow, selectedNodes) {
25
+ const summary = summarizeWorkflow(workflow, true);
26
+ const selected = selectedNodes?.length
27
+ ? `\n\n## 当前选中节点\n\n\`\`\`json\n${JSON.stringify(selectedNodes, null, 2)}\n\`\`\``
28
+ : '';
29
+ return `${WORKFLOW_AGENT_SYSTEM_PROMPT}
30
+
31
+ ---
32
+
33
+ ## 当前工作流
34
+
35
+ 当前 workflow_id: ${workflow.id}
36
+
37
+ \`\`\`json
38
+ ${JSON.stringify(summary, null, 2)}
39
+ \`\`\`${selected}`;
40
+ }
41
+ export function createWorkflowEditorFunctionTools(ctx) {
42
+ const versions = new Map();
43
+ let draft = cloneWorkflow(ctx.workflow);
44
+ const commit = (next) => {
45
+ draft = {
46
+ ...next,
47
+ nodes: clone(next.nodes),
48
+ edges: clone(next.edges),
49
+ updatedAt: Date.now(),
50
+ };
51
+ return workflowResult(true, 'updated', draft);
52
+ };
53
+ const definitionByType = new Map(ctx.nodeDefinitions.map((definition) => [definition.type, definition]));
54
+ const searchDefinitions = (input) => {
55
+ const keyword = stringInput(input, 'keyword')?.toLowerCase();
56
+ const type = stringInput(input, 'type')?.toLowerCase();
57
+ const label = stringInput(input, 'label')?.toLowerCase();
58
+ const category = stringInput(input, 'category')?.toLowerCase();
59
+ const description = stringInput(input, 'description')?.toLowerCase();
60
+ return ctx.nodeDefinitions.filter((definition) => {
61
+ const checks = [
62
+ keyword ? searchableDefinitionText(definition).includes(keyword) : true,
63
+ type ? definition.type.toLowerCase().includes(type) : true,
64
+ label ? definition.label.toLowerCase().includes(label) : true,
65
+ category ? definition.category.toLowerCase().includes(category) : true,
66
+ description ? definition.description.toLowerCase().includes(description) : true,
67
+ ];
68
+ return checks.every(Boolean);
69
+ });
70
+ };
71
+ const tools = [
72
+ {
73
+ name: 'get_workflow',
74
+ description: '按 workflow_id 读取指定工作流的最新已保存文件数据。默认返回摘要,summarize=false 返回完整数据。',
75
+ inputSchema: schema({
76
+ workflow_id: { type: 'string', description: '要读取的工作流 ID。' },
77
+ summarize: { type: 'boolean', description: '是否返回摘要,默认 true。' },
78
+ }, ['workflow_id']),
79
+ annotations: { readOnly: true },
80
+ execute: async (input) => {
81
+ const workflowId = stringInput(asRecord(input), 'workflow_id');
82
+ if (!workflowId)
83
+ return { success: false, message: 'workflow_id is required' };
84
+ const workflow = workflowService.getWorkflow(workflowId);
85
+ if (!workflow)
86
+ return { success: false, message: `Workflow not found: ${workflowId}` };
87
+ return { success: true, workflow: summarizeWorkflow(workflow, booleanInput(asRecord(input), 'summarize', true)) };
88
+ },
89
+ },
90
+ {
91
+ name: 'get_current_workflow',
92
+ description: '读取当前编辑器中的工作流草稿,包含尚未保存的编辑状态。默认返回摘要,summarize=false 返回完整数据。',
93
+ inputSchema: schema({ summarize: { type: 'boolean', description: '是否返回摘要,默认 true。' } }),
94
+ annotations: { readOnly: true },
95
+ execute: async (input) => ({
96
+ success: true,
97
+ workflow: summarizeWorkflow(draft, booleanInput(asRecord(input), 'summarize', true)),
98
+ }),
99
+ },
100
+ {
101
+ name: 'create_workflow_version',
102
+ description: '为当前工作流草稿创建会话内版本快照,适合复杂或破坏性编辑前备份。',
103
+ inputSchema: schema({ name: { type: 'string', description: '版本名称。' } }),
104
+ execute: async (input) => {
105
+ const id = `workflow-agent-version-${uuid()}`;
106
+ versions.set(id, { nodes: clone(draft.nodes), edges: clone(draft.edges) });
107
+ return { success: true, version_id: id, name: stringInput(asRecord(input), 'name') ?? 'AI 修改前备份' };
108
+ },
109
+ },
110
+ {
111
+ name: 'restore_workflow_version',
112
+ description: '恢复本次会话内 create_workflow_version 创建的版本快照。',
113
+ inputSchema: schema({ version_id: { type: 'string', description: '要恢复的版本 ID。' } }, ['version_id']),
114
+ execute: async (input) => {
115
+ const versionId = stringInput(asRecord(input), 'version_id');
116
+ const version = versionId ? versions.get(versionId) : undefined;
117
+ if (!version)
118
+ return { success: false, message: `Version not found: ${versionId ?? ''}` };
119
+ return commit({ ...draft, nodes: clone(version.nodes), edges: clone(version.edges) });
120
+ },
121
+ },
122
+ {
123
+ name: 'search_nodes',
124
+ description: '在当前工作流中搜索节点,支持 keyword/type/label/category/description 模糊匹配。',
125
+ inputSchema: workflowSearchSchema(),
126
+ annotations: { readOnly: true },
127
+ execute: async (input) => {
128
+ const record = asRecord(input);
129
+ const defs = new Map(ctx.nodeDefinitions.map((definition) => [definition.type, definition]));
130
+ const keyword = stringInput(record, 'keyword')?.toLowerCase();
131
+ const type = stringInput(record, 'type')?.toLowerCase();
132
+ const label = stringInput(record, 'label')?.toLowerCase();
133
+ const category = stringInput(record, 'category')?.toLowerCase();
134
+ const description = stringInput(record, 'description')?.toLowerCase();
135
+ const nodes = draft.nodes.filter((node) => {
136
+ const definition = defs.get(node.type);
137
+ const checks = [
138
+ keyword ? [node.id, node.type, node.label, definition?.label, definition?.category, definition?.description].filter(Boolean).join(' ').toLowerCase().includes(keyword) : true,
139
+ type ? node.type.toLowerCase().includes(type) : true,
140
+ label ? node.label.toLowerCase().includes(label) : true,
141
+ category ? (definition?.category ?? '').toLowerCase().includes(category) : true,
142
+ description ? (definition?.description ?? '').toLowerCase().includes(description) : true,
143
+ ];
144
+ return checks.every(Boolean);
145
+ });
146
+ return { success: true, nodes: nodes.map((node) => ({ ...node, definition: defs.get(node.type) })) };
147
+ },
148
+ },
149
+ {
150
+ name: 'list_node_types',
151
+ description: '分页查询可用节点类型列表。默认返回精简摘要;includeDetails=true 返回完整定义。',
152
+ inputSchema: schema({
153
+ category: { type: 'string', description: '按分类筛选。' },
154
+ page: { type: 'number', description: '页码,从 1 开始,默认 1。' },
155
+ pageSize: { type: 'number', description: '每页数量,默认 20,最大 50。' },
156
+ page_size: { type: 'number', description: '每页数量,兼容蛇形命名。' },
157
+ includeDetails: { type: 'boolean', description: '是否返回完整节点定义。' },
158
+ }),
159
+ annotations: { readOnly: true },
160
+ execute: async (input) => {
161
+ const record = asRecord(input);
162
+ const category = stringInput(record, 'category')?.toLowerCase();
163
+ const filtered = category
164
+ ? ctx.nodeDefinitions.filter((definition) => definition.category.toLowerCase().includes(category))
165
+ : ctx.nodeDefinitions;
166
+ const page = Math.max(1, numberInput(record, 'page', 1));
167
+ const pageSize = Math.min(50, Math.max(1, numberInput(record, 'pageSize', numberInput(record, 'page_size', 20))));
168
+ const includeDetails = booleanInput(record, 'includeDetails', false);
169
+ const items = filtered.slice((page - 1) * pageSize, page * pageSize);
170
+ return {
171
+ success: true,
172
+ page,
173
+ page_size: pageSize,
174
+ total: filtered.length,
175
+ nodes: includeDetails ? items : items.map(summarizeNodeDefinition),
176
+ };
177
+ },
178
+ },
179
+ {
180
+ name: 'search_node_usage',
181
+ description: '查询节点类型的具体用法,返回字段说明、句柄、输出和示例 data。准备使用陌生节点前必须调用。',
182
+ inputSchema: workflowSearchSchema(),
183
+ annotations: { readOnly: true },
184
+ execute: async (input) => ({ success: true, nodes: searchDefinitions(asRecord(input)).map(describeNodeUsage) }),
185
+ },
186
+ {
187
+ name: 'create_node',
188
+ description: '在工作流中创建新节点。需要指定有效节点 type,可选 label、data。',
189
+ inputSchema: schema({
190
+ type: { type: 'string', description: '节点类型标识。' },
191
+ label: { type: 'string', description: '节点显示名称。' },
192
+ data: { type: 'object', description: '节点参数数据。', properties: {} },
193
+ }, ['type']),
194
+ execute: async (input) => {
195
+ const record = asRecord(input);
196
+ const type = stringInput(record, 'type');
197
+ if (!type)
198
+ return { success: false, message: 'type is required' };
199
+ const definition = definitionByType.get(type);
200
+ if (!definition)
201
+ return { success: false, message: `Unknown node type: ${type}` };
202
+ const node = {
203
+ id: `node_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
204
+ type,
205
+ label: stringInput(record, 'label') ?? definition.label,
206
+ position: nextNodePosition(draft.nodes),
207
+ data: { ...defaultData(definition), ...objectInput(record, 'data') },
208
+ };
209
+ return commit({ ...draft, nodes: [...draft.nodes, node] });
210
+ },
211
+ },
212
+ {
213
+ name: 'update_node',
214
+ description: '更新指定节点的 label 或 data。data 会与现有 data 浅合并。',
215
+ inputSchema: schema({
216
+ nodeId: { type: 'string', description: '要更新的节点 ID。' },
217
+ label: { type: 'string', description: '可选,节点显示名称。' },
218
+ data: { type: 'object', description: '要合并的节点参数。', properties: {} },
219
+ }, ['nodeId']),
220
+ execute: async (input) => {
221
+ const record = asRecord(input);
222
+ const nodeId = stringInput(record, 'nodeId');
223
+ if (!nodeId)
224
+ return { success: false, message: 'nodeId is required' };
225
+ let found = false;
226
+ const nodes = draft.nodes.map((node) => {
227
+ if (node.id !== nodeId)
228
+ return node;
229
+ found = true;
230
+ return {
231
+ ...node,
232
+ label: stringInput(record, 'label') ?? node.label,
233
+ data: { ...node.data, ...objectInput(record, 'data') },
234
+ };
235
+ });
236
+ return found ? commit({ ...draft, nodes }) : { success: false, message: `Node not found: ${nodeId}` };
237
+ },
238
+ },
239
+ {
240
+ name: 'delete_node',
241
+ description: '删除指定节点及其相关连线。',
242
+ inputSchema: schema({ nodeId: { type: 'string', description: '要删除的节点 ID。' } }, ['nodeId']),
243
+ annotations: { destructive: true },
244
+ execute: async (input) => {
245
+ const nodeId = stringInput(asRecord(input), 'nodeId');
246
+ if (!nodeId)
247
+ return { success: false, message: 'nodeId is required' };
248
+ if (!draft.nodes.some((node) => node.id === nodeId))
249
+ return { success: false, message: `Node not found: ${nodeId}` };
250
+ return commit({
251
+ ...draft,
252
+ nodes: draft.nodes.filter((node) => node.id !== nodeId),
253
+ edges: draft.edges.filter((edge) => edge.source !== nodeId && edge.target !== nodeId),
254
+ });
255
+ },
256
+ },
257
+ {
258
+ name: 'create_edge',
259
+ description: '创建连线。source/target 是节点 ID;多输出节点应传 sourceHandle。',
260
+ inputSchema: schema({
261
+ source: { type: 'string', description: '起始节点 ID。' },
262
+ target: { type: 'string', description: '目标节点 ID。' },
263
+ sourceHandle: { type: 'string', description: '起始连接点。' },
264
+ targetHandle: { type: 'string', description: '目标连接点。' },
265
+ }, ['source', 'target']),
266
+ execute: async (input) => {
267
+ const record = asRecord(input);
268
+ const source = stringInput(record, 'source');
269
+ const target = stringInput(record, 'target');
270
+ if (!source || !target)
271
+ return { success: false, message: 'source and target are required' };
272
+ if (!draft.nodes.some((node) => node.id === source))
273
+ return { success: false, message: `Source node not found: ${source}` };
274
+ if (!draft.nodes.some((node) => node.id === target))
275
+ return { success: false, message: `Target node not found: ${target}` };
276
+ const edge = {
277
+ id: `e-${source}-${target}-${Date.now().toString(36)}`,
278
+ source,
279
+ target,
280
+ sourceHandle: stringInput(record, 'sourceHandle') ?? undefined,
281
+ targetHandle: stringInput(record, 'targetHandle') ?? undefined,
282
+ };
283
+ return commit({ ...draft, edges: [...draft.edges, edge] });
284
+ },
285
+ },
286
+ {
287
+ name: 'delete_edge',
288
+ description: '删除指定连线。',
289
+ inputSchema: schema({ edgeId: { type: 'string', description: '要删除的连线 ID。' } }, ['edgeId']),
290
+ annotations: { destructive: true },
291
+ execute: async (input) => {
292
+ const edgeId = stringInput(asRecord(input), 'edgeId');
293
+ if (!edgeId)
294
+ return { success: false, message: 'edgeId is required' };
295
+ if (!draft.edges.some((edge) => edge.id === edgeId))
296
+ return { success: false, message: `Edge not found: ${edgeId}` };
297
+ return commit({ ...draft, edges: draft.edges.filter((edge) => edge.id !== edgeId) });
298
+ },
299
+ },
300
+ {
301
+ name: 'insert_node',
302
+ description: '在已有连线中插入新节点,替换为 source -> 新节点 -> target 两条边。',
303
+ inputSchema: schema({
304
+ edgeId: { type: 'string', description: '要插入的边 ID。' },
305
+ type: { type: 'string', description: '新节点类型。' },
306
+ label: { type: 'string', description: '新节点显示名称。' },
307
+ data: { type: 'object', description: '新节点参数。', properties: {} },
308
+ }, ['edgeId', 'type']),
309
+ execute: async (input) => {
310
+ const record = asRecord(input);
311
+ const edgeId = stringInput(record, 'edgeId');
312
+ const type = stringInput(record, 'type');
313
+ const edge = draft.edges.find((item) => item.id === edgeId);
314
+ if (!edge)
315
+ return { success: false, message: `Edge not found: ${edgeId ?? ''}` };
316
+ if (!type)
317
+ return { success: false, message: 'type is required' };
318
+ const definition = definitionByType.get(type);
319
+ if (!definition)
320
+ return { success: false, message: `Unknown node type: ${type}` };
321
+ const sourceNode = draft.nodes.find((node) => node.id === edge.source);
322
+ const targetNode = draft.nodes.find((node) => node.id === edge.target);
323
+ const nodeId = `node_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
324
+ const node = {
325
+ id: nodeId,
326
+ type,
327
+ label: stringInput(record, 'label') ?? definition.label,
328
+ position: {
329
+ x: ((sourceNode?.position.x ?? 0) + (targetNode?.position.x ?? 260)) / 2,
330
+ y: ((sourceNode?.position.y ?? 0) + (targetNode?.position.y ?? 0)) / 2,
331
+ },
332
+ data: { ...defaultData(definition), ...objectInput(record, 'data') },
333
+ };
334
+ return commit({
335
+ ...draft,
336
+ nodes: [...draft.nodes, node],
337
+ edges: [
338
+ ...draft.edges.filter((item) => item.id !== edgeId),
339
+ { id: `e-${edge.source}-${nodeId}`, source: edge.source, target: nodeId, sourceHandle: edge.sourceHandle },
340
+ { id: `e-${nodeId}-${edge.target}`, source: nodeId, target: edge.target, targetHandle: edge.targetHandle },
341
+ ],
342
+ });
343
+ },
344
+ },
345
+ {
346
+ name: 'batch_update',
347
+ description: '批量执行 create_node/update_node/delete_node/create_edge/delete_edge 操作。',
348
+ inputSchema: schema({
349
+ operations: {
350
+ type: 'array',
351
+ description: '每项为 { tool, args }。',
352
+ items: { type: 'object', properties: { tool: { type: 'string' }, args: { type: 'object', properties: {} } } },
353
+ },
354
+ }, ['operations']),
355
+ execute: async (input) => {
356
+ const operationsInput = asRecord(input).operations;
357
+ const operations = Array.isArray(operationsInput) ? operationsInput : [];
358
+ const toolMap = new Map(tools.map((tool) => [tool.name, tool]));
359
+ const results = [];
360
+ for (const operation of operations) {
361
+ const record = asRecord(operation);
362
+ const toolName = stringInput(record, 'tool');
363
+ if (!toolName || toolName === 'batch_update')
364
+ return { success: false, message: `Invalid batch tool: ${toolName ?? ''}` };
365
+ const tool = toolMap.get(toolName);
366
+ if (!tool)
367
+ return { success: false, message: `Unknown batch tool: ${toolName}` };
368
+ const result = await tool.execute(record.args);
369
+ results.push(result);
370
+ if (asRecord(result).success === false)
371
+ return { success: false, results };
372
+ }
373
+ return workflowResult(true, 'batch updated', draft, results);
374
+ },
375
+ },
376
+ {
377
+ name: 'auto_layout',
378
+ description: '自动整理当前工作流节点位置。direction 可选 LR 或 TB。',
379
+ inputSchema: schema({ direction: { type: 'string', description: '布局方向,LR 或 TB,默认 LR。' } }),
380
+ execute: async (input) => {
381
+ const direction = stringInput(asRecord(input), 'direction') === 'TB' ? 'TB' : 'LR';
382
+ return commit({ ...draft, nodes: layoutNodes(draft.nodes, direction) });
383
+ },
384
+ },
385
+ ];
386
+ return tools;
387
+ }
388
+ function workflowResult(success, message, workflow, results) {
389
+ return {
390
+ success,
391
+ message,
392
+ workflow,
393
+ workflow_patch: {
394
+ workflow_id: workflow.id,
395
+ nodes: workflow.nodes,
396
+ edges: workflow.edges,
397
+ updatedAt: workflow.updatedAt,
398
+ },
399
+ results,
400
+ };
401
+ }
402
+ function summarizeWorkflow(workflow, summarize) {
403
+ if (!summarize)
404
+ return workflow;
405
+ return {
406
+ id: workflow.id,
407
+ name: workflow.name,
408
+ description: workflow.description,
409
+ nodes: workflow.nodes.map((node) => ({ id: node.id, type: node.type, label: node.label, dataKeys: Object.keys(node.data ?? {}) })),
410
+ edges: workflow.edges.map((edge) => ({ id: edge.id, source: edge.source, target: edge.target, sourceHandle: edge.sourceHandle, targetHandle: edge.targetHandle })),
411
+ };
412
+ }
413
+ function summarizeNodeDefinition(definition) {
414
+ return {
415
+ type: definition.type,
416
+ label: definition.label,
417
+ category: definition.category,
418
+ description: definition.description,
419
+ handles: definition.handles,
420
+ properties: definition.properties.map((property) => ({
421
+ key: property.key,
422
+ label: property.label,
423
+ type: property.type,
424
+ required: property.required,
425
+ default: property.default,
426
+ })),
427
+ outputs: definition.outputs,
428
+ };
429
+ }
430
+ function describeNodeUsage(definition) {
431
+ return {
432
+ ...definition,
433
+ exampleData: defaultData(definition),
434
+ usage: {
435
+ variables: '字符串字段支持 {{ __data__["节点ID"].字段路径 }} 和 {{ context.some.path }}。',
436
+ handles: definition.handles ?? {},
437
+ },
438
+ };
439
+ }
440
+ function defaultData(definition) {
441
+ const data = {};
442
+ for (const property of definition.properties ?? []) {
443
+ if (property.default !== undefined)
444
+ data[property.key] = clone(property.default);
445
+ }
446
+ return data;
447
+ }
448
+ function nextNodePosition(nodes) {
449
+ if (!nodes.length)
450
+ return { x: 120, y: 120 };
451
+ const maxX = Math.max(...nodes.map((node) => node.position.x));
452
+ const avgY = nodes.reduce((sum, node) => sum + node.position.y, 0) / nodes.length;
453
+ return { x: maxX + 260, y: Math.round(avgY) };
454
+ }
455
+ function layoutNodes(nodes, direction) {
456
+ return nodes.map((node, index) => ({
457
+ ...node,
458
+ position: direction === 'TB'
459
+ ? { x: 160 + (index % 4) * 260, y: 100 + Math.floor(index / 4) * 180 }
460
+ : { x: 120 + index * 260, y: 120 + (index % 3) * 150 },
461
+ }));
462
+ }
463
+ function searchableDefinitionText(definition) {
464
+ return [
465
+ definition.type,
466
+ definition.label,
467
+ definition.category,
468
+ definition.description,
469
+ ...definition.properties.map((property) => `${property.key} ${property.label} ${property.tooltip ?? ''}`),
470
+ ].join(' ').toLowerCase();
471
+ }
472
+ function workflowSearchSchema() {
473
+ return schema({
474
+ keyword: { type: 'string', description: '模糊搜索关键词。' },
475
+ type: { type: 'string', description: '按节点类型筛选。' },
476
+ label: { type: 'string', description: '按节点标签筛选。' },
477
+ category: { type: 'string', description: '按分类筛选。' },
478
+ description: { type: 'string', description: '按描述筛选。' },
479
+ });
480
+ }
481
+ function schema(properties, required) {
482
+ return { type: 'object', properties, ...(required?.length ? { required } : {}) };
483
+ }
484
+ function asRecord(input) {
485
+ return input && typeof input === 'object' && !Array.isArray(input) ? input : {};
486
+ }
487
+ function stringInput(input, key) {
488
+ const value = input[key];
489
+ return typeof value === 'string' && value.trim() ? value.trim() : undefined;
490
+ }
491
+ function numberInput(input, key, fallback) {
492
+ const value = input[key];
493
+ return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
494
+ }
495
+ function booleanInput(input, key, fallback) {
496
+ const value = input[key];
497
+ return typeof value === 'boolean' ? value : fallback;
498
+ }
499
+ function objectInput(input, key) {
500
+ const value = input[key];
501
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
502
+ }
503
+ function clone(value) {
504
+ return JSON.parse(JSON.stringify(value));
505
+ }
506
+ function cloneWorkflow(workflow) {
507
+ return clone(workflow);
508
+ }
509
+ //# sourceMappingURL=workflow-editor-tools.js.map