zen-gitsync 2.18.20 → 2.18.22

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 (88) hide show
  1. package/package.json +4 -1
  2. package/src/cli/ai/agent.js +9 -2
  3. package/src/cli/ai/extensions.js +1 -1
  4. package/src/cli/ai/runtime.test.js +57 -1
  5. package/src/cli/ai/termui.js +11 -2
  6. package/src/cli/ai/tools.js +266 -5
  7. package/src/cli/ai/tools.test.js +81 -0
  8. package/src/cli/ai/transport.js +1 -1
  9. package/src/cli/ai/turn.js +59 -3
  10. package/src/config.js +60 -4
  11. package/src/mcp/install.js +548 -529
  12. package/src/ui/public/assets/AgentView-DdiyBMDD.js +26 -0
  13. package/src/ui/public/assets/{AgentView-BE7GD8rc.css → AgentView-_DUYxFmp.css} +1 -1
  14. package/src/ui/public/assets/{AppVersionBadge-86tbKU20.js → AppVersionBadge-CSEM45L4.js} +2 -2
  15. package/src/ui/public/assets/{BranchSelector-CsrkmTCA.js → BranchSelector-cSx2ZxYM.js} +1 -1
  16. package/src/ui/public/assets/ChatSilenceBadge-Dtv23ZmV.js +16 -0
  17. package/src/ui/public/assets/{CommitForm-DBK0GXEh.js → CommitForm-CrAsbDkq.js} +2 -2
  18. package/src/ui/public/assets/{CommonDialog-BkuDIWro.js → CommonDialog-BtO3uKdw.js} +1 -1
  19. package/src/ui/public/assets/{EditorView-CnWsMdHg.css → EditorView-B-t-nuf0.css} +1 -1
  20. package/src/ui/public/assets/EditorView-BesiWTV2.js +10 -0
  21. package/src/ui/public/assets/{FlowExecutionViewer-ClUkAmA2.js → FlowExecutionViewer-CfU9DLFG.js} +1 -1
  22. package/src/ui/public/assets/{FlowOrchestrationWorkspace-B3sOK3fz.js → FlowOrchestrationWorkspace-D3CSRMvG.js} +1 -1
  23. package/src/ui/public/assets/{LogList-6aO0-2xh.js → LogList-CT-SRIS3.js} +4 -4
  24. package/src/ui/public/assets/MindmapView-BCGq2zdh.js +35 -0
  25. package/src/ui/public/assets/MindmapView-CoUbyadF.css +1 -0
  26. package/src/ui/public/assets/{MonitorView-fcNrgVaU.js → MonitorView-BuBJJVhR.js} +1 -1
  27. package/src/ui/public/assets/{ProjectManagerView-CZfrML6A.js → ProjectManagerView-DlSfRn9N.js} +2 -2
  28. package/src/ui/public/assets/{ProjectStartupButton-D5MREz6F.js → ProjectStartupButton-DKZttDb3.js} +1 -1
  29. package/src/ui/public/assets/RemoteManagerDialog-CVrhhUPg.js +4 -0
  30. package/src/ui/public/assets/{RemoteRepoCard-CTnmfv2y.js → RemoteRepoCard-CmdYDF24.js} +1 -1
  31. package/src/ui/public/assets/{SourceMapView-B4q5qiE8.js → SourceMapView-DClgTmda.js} +1 -1
  32. package/src/ui/public/assets/{SvgIcon-WpEiC1rI.js → SvgIcon-D5fhBiJr.js} +1 -1
  33. package/src/ui/public/assets/TaskExecutorPicker-CzvWdS_K.js +1 -0
  34. package/src/ui/public/assets/TaskExecutorPicker-Dn8SgrTZ.css +1 -0
  35. package/src/ui/public/assets/{UserInputNode-Deoc3UvQ.js → UserInputNode-CNuW_Dtu.js} +1 -1
  36. package/src/ui/public/assets/WorkbenchView-D61DxNnn.css +1 -0
  37. package/src/ui/public/assets/WorkbenchView-WKNPn-oz.js +341 -0
  38. package/src/ui/public/assets/_plugin-vue_export-helper-E2AaPXn7.js +19 -0
  39. package/src/ui/public/assets/configStore-JIX2MOKo.js +1 -0
  40. package/src/ui/public/assets/{dagre-CpKxpMNH.js → dagre-yiQDclZv.js} +3 -3
  41. package/src/ui/public/assets/{element-plus-Bz6lYur-.js → element-plus-BeZBQw4X.js} +1 -1
  42. package/src/ui/public/assets/{flow-mindmap-cTg78Krq.js → flow-mindmap-CGjPqCv0.js} +1 -1
  43. package/src/ui/public/assets/{index-cDiVh_ZL.js → index-Cjv2Z6nF.js} +134 -125
  44. package/src/ui/public/assets/index-IjYv2goK.css +1 -0
  45. package/src/ui/public/assets/{monaco-CvMddat7.js → monaco-CMQow_j-.js} +1 -1
  46. package/src/ui/public/assets/{office-docx-VOB-i4K2.js → office-docx-nuGPI15f.js} +1 -1
  47. package/src/ui/public/assets/{office-excel-CJ2JvfA8.js → office-excel-CK-HOYi8.js} +1 -1
  48. package/src/ui/public/assets/{office-pptx-Z88k9qnx.js → office-pptx-9onMeLux.js} +1 -1
  49. package/src/ui/public/assets/{relativeTime-CoXAs57j.js → relativeTime-DHhE9Ihn.js} +1 -1
  50. package/src/ui/public/assets/taskExecutionExport-DlBMELzX.js +15 -0
  51. package/src/ui/public/assets/toolsStore-h5iqPyST.js +1 -0
  52. package/src/ui/public/assets/{useWorkbenchProjects-BS59r5Rj.js → useWorkbenchProjects-DW3vBHpK.js} +1 -1
  53. package/src/ui/public/assets/{vendor-CSFGMcMB.css → vendor-CAhoE06w.css} +1 -1
  54. package/src/ui/public/assets/{vendor-C2k0GBzj.js → vendor-KxSgeiUX.js} +303 -303
  55. package/src/ui/public/assets/{vue-flow-Cnxq2qrs.js → vue-flow-BMMiguH1.js} +1 -1
  56. package/src/ui/public/index.html +14 -14
  57. package/src/ui/server/routes/config.js +13 -0
  58. package/src/ui/server/routes/fileOpen.js +1851 -1736
  59. package/src/ui/server/routes/fileOpen.test.js +39 -1
  60. package/src/ui/server/routes/workbench/agentChat.js +48 -8
  61. package/src/ui/server/routes/workbench/agentChatShared.test.js +584 -496
  62. package/src/ui/server/routes/workbench/agentEngines.js +7 -4
  63. package/src/ui/server/routes/workbench/executorModels.js +25 -4
  64. package/src/ui/server/routes/workbench/executorModels.test.js +557 -530
  65. package/src/ui/server/routes/workbench/jobActivity.js +2 -2
  66. package/src/ui/server/routes/workbench/taskRunner.codearts.test.js +252 -0
  67. package/src/ui/server/routes/workbench/taskRunner.codebuddy.test.js +317 -317
  68. package/src/ui/server/routes/workbench/taskRunner.js +216 -7
  69. package/src/ui/server/routes/workbench/taskRunner.opencode.test.js +130 -130
  70. package/src/ui/server/routes/workbench/taskRunner.pi.test.js +298 -298
  71. package/src/ui/public/assets/AgentView-Du9akUeZ.js +0 -12
  72. package/src/ui/public/assets/ChatSilenceBadge-BBoFeSqe.js +0 -21
  73. package/src/ui/public/assets/EditorAgentPanel-BMUphuG0.css +0 -1
  74. package/src/ui/public/assets/EditorAgentPanel-KiHbQPJP.js +0 -34
  75. package/src/ui/public/assets/EditorView-B18B4yNJ.js +0 -13
  76. package/src/ui/public/assets/MindmapView-BDI3D_1h.css +0 -1
  77. package/src/ui/public/assets/MindmapView-aJ9TTijE.js +0 -4
  78. package/src/ui/public/assets/RecentDirectoriesChat-DBGoUlkn.css +0 -1
  79. package/src/ui/public/assets/RecentDirectoriesChat-wfkSF6NU.js +0 -3
  80. package/src/ui/public/assets/RemoteManagerDialog-BBwNlK9J.js +0 -4
  81. package/src/ui/public/assets/WorkbenchView-CVqXb__l.css +0 -1
  82. package/src/ui/public/assets/WorkbenchView-CijzpDWN.js +0 -360
  83. package/src/ui/public/assets/_plugin-vue_export-helper-BhdsvhKo.js +0 -19
  84. package/src/ui/public/assets/agentConversations-DaAtF1v4.js +0 -10
  85. package/src/ui/public/assets/configStore-Drlw8KtR.js +0 -1
  86. package/src/ui/public/assets/index-DSQ6mp_N.css +0 -1
  87. package/src/ui/public/assets/toolsStore-CnX34C96.js +0 -1
  88. package/src/ui/public/assets/useOrchestrator-BXmxJsRl.js +0 -1
@@ -1,4 +1,4 @@
1
- import { executeTool, splitToolOutput, toolMessageContent } from './tools.js'
1
+ import { executeTool, splitToolOutput, toolMessageContent, splitToolBatches } from './tools.js'
2
2
  import { streamChatOnce } from './transport.js'
3
3
  import { createThinkFilter } from './streamFilter.js'
4
4
  import { imageToDataUrl } from './images.js'
@@ -14,6 +14,9 @@ import * as terminal from './termui.js'
14
14
  // 它自己的交互界面(问题 + 编号选项 + 提示符)就是进度提示,不需要再叠一层。
15
15
  const INTERACTIVE_TOOLS = new Set(['ask_user'])
16
16
 
17
+ // 取消时回给模型的占位文本(串行/并发两条路径共用一份口径)
18
+ const NOT_EXECUTED = 'Cancelled by user; this tool was not executed.'
19
+
17
20
  export async function runAgentTurn(state, userText, t, images = [], dependencies = {}) {
18
21
  const chat = dependencies.chat || streamChatOnce
19
22
  const baseExecute = dependencies.execute || executeTool
@@ -136,14 +139,67 @@ export async function runAgentTurn(state, userText, t, images = [], dependencies
136
139
  toolCalls.forEach((call, i) => { if (!call.id) call.id = `call_${iter}_${i}` })
137
140
  state.messages.push({ ...assistant, tool_calls: toolCalls })
138
141
  await checkpoint()
139
- for (const tc of toolCalls) {
142
+ // 同一轮里模型经常一次发好几个只读工具(并行读 3 个文件、同时搜两处)。
143
+ // 串行跑等于把 IO 延迟叠 N 遍,所以按批执行:连续的只读工具并发,
144
+ // 其余(写文件 / 跑命令 / 问用户)一律串行 —— 它们有顺序依赖或会接管终端。
145
+ for (const batch of splitToolBatches(toolCalls)) {
146
+ if (batch.parallel) {
147
+ // 执行阶段并发;渲染和入栈仍按模型给的顺序,保证 tool 消息与
148
+ // assistant.tool_calls 一一对齐(协议要求顺序一致,乱序会污染后续上下文)。
149
+ const calls = batch.calls
150
+ const names = calls.map(c => c.function?.name || '').filter(Boolean).join(' · ')
151
+ const spinText = typeof t.toolRunningBatch === 'function'
152
+ ? t.toolRunningBatch(calls.length, names)
153
+ : t.toolRunning(names)
154
+ // 只能开一个 spinner:ora 是单实例单行,并发起多个会互抢同一行刷屏
155
+ const batchSpinner = ui.startSpinner(spinText)
156
+ const batchStart = performance.now()
157
+ let results
158
+ try {
159
+ results = await Promise.all(calls.map(async (tc) => {
160
+ const name = tc.function?.name || ''
161
+ if (cancelled()) return { tc, name, output: NOT_EXECUTED, executed: false }
162
+ let args
163
+ try { args = JSON.parse(tc.function?.arguments || '{}') }
164
+ catch { return { tc, name, output: '错误: 工具参数不是合法 JSON,请修正后重试。', executed: false } }
165
+ const toolStart = performance.now()
166
+ let output
167
+ // 并发路径必须自己兜住异常:一个工具抛错会让 Promise.all 整体 reject,
168
+ // 把同一批里其他已经成功的结果一起丢掉
169
+ try { output = await execute(name, args, { ...state.ctx, signal: state.abortController?.signal }) }
170
+ catch (err) { output = `错误: ${name} 执行失败: ${err?.message || err}` }
171
+ return { tc, name, args, output, executed: true, ms: performance.now() - toolStart }
172
+ }))
173
+ } finally {
174
+ batchSpinner.stop()
175
+ }
176
+ // 工具耗时按墙钟记:并发批里各工具耗时之和会大于实际等待时间,
177
+ // 累加会让 summary 的「工具」时长超过整轮时长
178
+ stats.toolsMs += performance.now() - batchStart
179
+ for (const r of results) {
180
+ const { text: outText, images: outImages } = splitToolOutput(r.output)
181
+ if (r.executed) {
182
+ stats.toolCalls++
183
+ ui.printToolHeader(r.name, ui.summarizeToolArgs(r.name, r.args, { chars: t.chars }), undefined, { locale: state.locale })
184
+ ui.printToolResult(outText, undefined, r.ms, { full: state.fullTools, locale: state.locale })
185
+ ui.afterTool?.(r.name, r.args, outText, { locale: state.locale })
186
+ } else if (r.output !== NOT_EXECUTED) {
187
+ // 取消的那几条和串行路径一样:只入历史、不渲染;参数错误要显式回显
188
+ ui.printToolResult(r.output)
189
+ }
190
+ state.messages.push({ role: 'tool', tool_call_id: r.tc.id, name: r.name, content: toolMessageContent(outText, outImages) })
191
+ }
192
+ await checkpoint()
193
+ continue
194
+ }
195
+ const tc = batch.calls[0]
140
196
  const name = tc.function?.name || ''
141
197
  let output
142
198
  let args
143
199
  let toolMs // 只有真执行过的工具才有耗时(见下面的渲染判断)
144
200
  if (cancelled()) {
145
201
  // 与改动前一致:取消的这次调用只入历史,不渲染结果块
146
- output = 'Cancelled by user; this tool was not executed.'
202
+ output = NOT_EXECUTED
147
203
  } else {
148
204
  try { args = JSON.parse(tc.function?.arguments || '{}') }
149
205
  catch { output = '错误: 工具参数不是合法 JSON,请修正后重试。' }
package/src/config.js CHANGED
@@ -78,11 +78,12 @@ function normalizeAiMaxToolIterations(value) {
78
78
  }
79
79
 
80
80
  // 工作台任务执行器白名单。'claude' 是历史默认;'opencode' / 'codex' / 'codebuddy' /
81
- // 'pi' 为可选执行器(opencode run --format json / codex exec --json /
81
+ // 'pi' / 'codearts' 为可选执行器(opencode run --format json / codex exec --json /
82
82
  // codebuddy -p --output-format stream-json -y --port 0 /
83
- // pi --mode json -a,模型都跟随各自 CLI 的配置)。
83
+ // pi --mode json -a / codearts run --format json --auto,
84
+ // 模型都跟随各自 CLI 的配置)。
84
85
  // ⚠️ taskRunner.js 里有一份同名单用于运行时分发(见那里的注释),加执行器两处都要改。
85
- export const TASK_EXECUTORS = ['claude', 'opencode', 'codex', 'codebuddy', 'pi'];
86
+ export const TASK_EXECUTORS = ['claude', 'opencode', 'codex', 'codebuddy', 'pi', 'codearts'];
86
87
 
87
88
  /**
88
89
  * 规范化任务执行器。非法值返回 null(交给调用方取默认),不抛错 ——
@@ -179,6 +180,43 @@ export function normalizeAgentPresetPrompts(value) {
179
180
  return out;
180
181
  }
181
182
 
183
+ /**
184
+ * 「按板块的预设提示词」允许的板块 id。必须与前端 utils/agentPresets.ts 的
185
+ * AGENT_PRESET_VIEW_IDS 一致。
186
+ *
187
+ * 白名单之外的键**丢弃**而不是整组拒绝:磁盘上可能留着旧版本删掉的板块(或手改时
188
+ * 写错一个键),一个不认识的板块不该让整份配置失效 —— 与"单个条目不合法就整组拒绝"
189
+ * 是不同层级的判断(条目坏了会点出空卡片,键坏了只是没人读)。
190
+ */
191
+ const AGENT_PRESET_VIEW_IDS = [
192
+ 'git', 'console', 'editor', 'source-map', 'workbench', 'projects', 'monitor', 'mindmap',
193
+ ];
194
+
195
+ /**
196
+ * 规范化「按板块的预设提示词」(全局配置,跨项目/跨板块共享)。
197
+ *
198
+ * 形状:{ [viewId]: [{id, label, prompt}] }
199
+ *
200
+ * 语义约定(2026-10-25 新增):
201
+ * · undefined / null → null(没配过,调用方回落 defaultConfig = {})
202
+ * · 非普通对象(含数组)/ 任一条目结构坏 → null(整体拒绝,理由同
203
+ * normalizeAgentPresetPrompts:宁可整组回落内置,也不生成半截卡片)
204
+ * · 某个板块的值归一化后为空数组 → **不写这个键**(= 该板块没有自定义,
205
+ * 继承「通用」那套)。两种"没有"在磁盘上只留一种表达。
206
+ */
207
+ export function normalizeAgentPresetPromptsByView(value) {
208
+ if (value === undefined || value === null) return null;
209
+ if (typeof value !== 'object' || Array.isArray(value)) return null;
210
+ const out = {};
211
+ for (const view of AGENT_PRESET_VIEW_IDS) {
212
+ if (!Object.prototype.hasOwnProperty.call(value, view)) continue;
213
+ const normalized = normalizeAgentPresetPrompts(value[view]);
214
+ if (normalized === null) return null;
215
+ if (normalized.length > 0) out[view] = normalized;
216
+ }
217
+ return out;
218
+ }
219
+
182
220
  // 「g ai 智能体的默认提示词」上限。
183
221
  // 与派发预设提示词同值(4000):两者都是"每一轮对话都会被拼进 prompt 的约束",
184
222
  // 到这个量级已经能写下一整套规范,再长只是让每次请求都白烧一遍 token。
@@ -291,7 +329,12 @@ const defaultConfig = {
291
329
  // 智能体视图欢迎页的「预设提示词」快捷卡片(全局配置,CLI 不消费、只有 Web 端用)。
292
330
  // 空数组 = 使用前端内置的 5 条默认(zh/en 文案随界面语言走,见 utils/agentPresets.ts);
293
331
  // 非空 = 用户自定义全量(每条 {id, label, prompt})。归一化见 normalizeAgentPresetPrompts。
332
+ // 这一份是**所有板块的兜底**:某个板块没有单独配置时用它。
294
333
  agentPresetPrompts: [],
334
+ // 按板块的「预设提示词」:{ [viewId]: [{id, label, prompt}] },只收配过的板块。
335
+ // 优先级(前端 resolveAgentPresets):本板块自定义 > 上面那份通用自定义 > 板块内置 > 通用内置。
336
+ // 归一化见 normalizeAgentPresetPromptsByView。
337
+ agentPresetPromptsByView: {},
295
338
  // g ai 智能体的默认提示词(全局配置,跨项目共享)。
296
339
  // 和 g ai 对话时每一轮都会拼进系统提示词(Web 智能体视图 / 文件空间面板 / 定时任务,
297
340
  // 见 workbench/chatPrompts.js)。空串 = 不注入。
@@ -645,6 +688,8 @@ async function loadConfig() {
645
688
  taskExecutor: normalizeTaskExecutor(raw.taskExecutor) ?? defaultConfig.taskExecutor,
646
689
  agentPresetPrompts: normalizeAgentPresetPrompts(raw.agentPresetPrompts)
647
690
  ?? defaultConfig.agentPresetPrompts,
691
+ agentPresetPromptsByView: normalizeAgentPresetPromptsByView(raw.agentPresetPromptsByView)
692
+ ?? defaultConfig.agentPresetPromptsByView,
648
693
  agentDefaultPrompt: normalizeAgentDefaultPrompt(raw.agentDefaultPrompt)
649
694
  ?? defaultConfig.agentDefaultPrompt,
650
695
  ...resolveNotifySwitches(raw)
@@ -671,6 +716,9 @@ async function loadConfig() {
671
716
  agentPresetPrompts: normalizeAgentPresetPrompts(raw?.agentPresetPrompts)
672
717
  ?? defaultConfig.agentPresetPrompts,
673
718
  // 同 agentPresetPrompts:全局配置,始终取顶层,防止被项目配置里的旧值覆盖
719
+ agentPresetPromptsByView: normalizeAgentPresetPromptsByView(raw?.agentPresetPromptsByView)
720
+ ?? defaultConfig.agentPresetPromptsByView,
721
+ // 同 agentPresetPrompts:全局配置,始终取顶层,防止被项目配置里的旧值覆盖
674
722
  agentDefaultPrompt: normalizeAgentDefaultPrompt(raw?.agentDefaultPrompt)
675
723
  ?? defaultConfig.agentDefaultPrompt,
676
724
  ...resolveNotifySwitches(raw)
@@ -726,7 +774,7 @@ async function saveConfig(config) {
726
774
  // 项目级是这一族键最容易踩的坑。解构出来本身不写回顶层(它已不再被任何地方读取)。
727
775
  const {
728
776
  theme, locale, models, ui, aiMaxToolIterations, aiMaxRequestTokens, taskExecutor,
729
- agentPresetPrompts, agentDefaultPrompt,
777
+ agentPresetPrompts, agentPresetPromptsByView, agentDefaultPrompt,
730
778
  notifyOnTaskDone: legacyNotifyOnTaskDone,
731
779
  notifyPageOnTaskDone, notifyBrowserOnTaskDone, notifySoundOnTaskDone,
732
780
  ...projectConfig
@@ -780,6 +828,12 @@ async function saveConfig(config) {
780
828
  if (normalizedPresets !== null) {
781
829
  raw.agentPresetPrompts = normalizedPresets;
782
830
  }
831
+ // 按板块的预设提示词:空对象是**合法值**(= 所有板块都不单独配置),照写;
832
+ // 非法值(结构坏)不落盘,保留磁盘旧值 —— 与上面各键同一套语义。
833
+ const normalizedPresetsByView = normalizeAgentPresetPromptsByView(agentPresetPromptsByView);
834
+ if (normalizedPresetsByView !== null) {
835
+ raw.agentPresetPromptsByView = normalizedPresetsByView;
836
+ }
783
837
  // g ai 默认提示词同属全局设置:**空串是合法值**(= 清除),照写;
784
838
  // 非字符串不落盘,保留磁盘旧值 —— 与上面各键同一套语义。
785
839
  const normalizedAgentPrompt = normalizeAgentDefaultPrompt(agentDefaultPrompt);
@@ -994,6 +1048,8 @@ export default {
994
1048
  normalizeNotifySwitch,
995
1049
  // 智能体视图「预设提示词」的规范化(GUI 保存前也要用,见 /api/config/save-ai-settings)
996
1050
  normalizeAgentPresetPrompts,
1051
+ // 按板块的预设提示词的规范化(同上)
1052
+ normalizeAgentPresetPromptsByView,
997
1053
  // g ai 智能体默认提示词的规范化(同上,见 /api/config/save-ai-settings)
998
1054
  normalizeAgentDefaultPrompt,
999
1055
  };