@jesonliu/lark-claudecode-bridge 0.18.0 → 0.19.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 (47) hide show
  1. package/README.md +5 -3
  2. package/assets/web/css/main.css +2 -0
  3. package/assets/web/js/main.js +3 -1
  4. package/assets/web/js/pages/mcp.js +229 -0
  5. package/assets/web/js/pages/skills.js +169 -0
  6. package/assets/web/js/pages/workspaces.js +23 -2
  7. package/dist/bin/lcb.js +5 -0
  8. package/dist/bin/lcb.js.map +1 -1
  9. package/dist/config.js +33 -7
  10. package/dist/config.js.map +1 -1
  11. package/dist/executor/notify-server.d.ts +13 -0
  12. package/dist/executor/notify-server.js +60 -4
  13. package/dist/executor/notify-server.js.map +1 -1
  14. package/dist/executor/plugin-discovery.d.ts +6 -0
  15. package/dist/executor/plugin-discovery.js +14 -0
  16. package/dist/executor/plugin-discovery.js.map +1 -1
  17. package/dist/gateway/card-builder.d.ts +24 -26
  18. package/dist/gateway/card-builder.js +79 -109
  19. package/dist/gateway/card-builder.js.map +1 -1
  20. package/dist/gateway/feishu-gateway.d.ts +10 -0
  21. package/dist/gateway/feishu-gateway.js +48 -0
  22. package/dist/gateway/feishu-gateway.js.map +1 -1
  23. package/dist/gateway/progress-card.d.ts +26 -0
  24. package/dist/gateway/progress-card.js +68 -4
  25. package/dist/gateway/progress-card.js.map +1 -1
  26. package/dist/index.d.ts +7 -1
  27. package/dist/index.js +174 -71
  28. package/dist/index.js.map +1 -1
  29. package/dist/notify-sop.d.ts +1 -0
  30. package/dist/notify-sop.js +30 -0
  31. package/dist/notify-sop.js.map +1 -0
  32. package/dist/types.d.ts +14 -2
  33. package/dist/util/log-tee.d.ts +4 -0
  34. package/dist/util/log-tee.js +99 -0
  35. package/dist/util/log-tee.js.map +1 -0
  36. package/dist/util/runtime-dirs.d.ts +4 -0
  37. package/dist/util/runtime-dirs.js +27 -0
  38. package/dist/util/runtime-dirs.js.map +1 -0
  39. package/dist/web/lifecycle.d.ts +4 -1
  40. package/dist/web/lifecycle.js +11 -25
  41. package/dist/web/lifecycle.js.map +1 -1
  42. package/dist/web/server.js +311 -11
  43. package/dist/web/server.js.map +1 -1
  44. package/dist/web/skills-mcp-api.d.ts +113 -0
  45. package/dist/web/skills-mcp-api.js +312 -0
  46. package/dist/web/skills-mcp-api.js.map +1 -0
  47. package/package.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"progress-card.js","sourceRoot":"","sources":["../../src/gateway/progress-card.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"progress-card.js","sourceRoot":"","sources":["../../src/gateway/progress-card.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -27,7 +27,13 @@ deps: BridgeDeps, opts?: {
27
27
  confirmTimeoutMs?: number;
28
28
  cardSendTimeoutMs?: number;
29
29
  reloadConfig?: () => void;
30
- }): GatewayHandlers;
30
+ }): GatewayHandlers & {
31
+ /** 测试/诊断入口:返回通道当前挂起的内嵌 plan / question requestId */
32
+ _pendingAsk(channelKey: string): {
33
+ planId?: string;
34
+ questionId?: string;
35
+ };
36
+ };
31
37
  /**
32
38
  * 配置热重载器:lcb ws add/remove 写盘后,运行实例下一条消息即见新工作区
33
39
  * (与 access.reload 同款模式——长驻进程不重读会持旧配置直至重启)。
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // 装配主流程:消息 → 访问控制 → 命令 → 通道队列 → 执行 → 确认卡片 → 回传
2
2
  import { randomUUID } from 'node:crypto';
3
- import { existsSync } from 'node:fs';
3
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import { basename, join, resolve } from 'node:path';
5
5
  import { CONFIG_DIR, CONFIG_PATH, DEFAULT_CONTEXT_REMIND_TOKENS, loadConfig, sameApps } from './config.js';
6
6
  import { DEFAULT_CLAUDE_DIR, resolveClaudeDir } from './claude-config.js';
@@ -11,12 +11,14 @@ import { clearPidFile, writePidFile, stopExistingBridgeAndWait } from './web/lif
11
11
  import { AccessControl } from './access/access-control.js';
12
12
  import { FeishuGateway } from './gateway/feishu-gateway.js';
13
13
  import { ProgressCard } from './gateway/progress-card.js';
14
- import { DECISION_TEXT, buildPlanCards, buildPlanResultCard, buildExpiredPlanCard, PROGRESS_TAIL_CHARS, buildQuestionCard, buildQuestionResultCard, buildExpiredQuestionCard, } from './gateway/card-builder.js';
14
+ import { DECISION_TEXT, PROGRESS_TAIL_CHARS, } from './gateway/card-builder.js';
15
15
  import { buildDiffSummaryCards } from './gateway/diff-card.js';
16
16
  import { runTask } from './executor/claude-executor.js';
17
17
  import { PermissionGate, DEFAULT_ALLOW_TOOLS_LIST, DEFAULT_DANGEROUS_COMMANDS, } from './executor/permission-gate.js';
18
18
  import { discoverPlugins, resolvePluginPaths } from './executor/plugin-discovery.js';
19
19
  import { createGatewaySender, createNotifyServer, NOTIFY_SERVER_NAME } from './executor/notify-server.js';
20
+ import { BRIDGE_MCP_JSON, readMcpServersFromJsonFile } from './web/skills-mcp-api.js';
21
+ import { FEISHU_NOTIFY_SOP_PROMPT } from './notify-sop.js';
20
22
  import { collectWorkspaceDiff } from './util/workspace-diff.js';
21
23
  import { isImageFile } from './util/file-types.js';
22
24
  import { FileTracker } from './util/file-tracker.js';
@@ -53,6 +55,8 @@ const PLAN_DECISION_TEXT = {
53
55
  'plan-approve': '✅ 计划已批准,开始执行(后续写操作免确认)',
54
56
  'plan-revise': '📝 修改意见已提交',
55
57
  'plan-reject': '❌ 计划已放弃',
58
+ // plan-view-file 不消费挂起项,仅 toast「正在发送方案文件…」——文案不在此出现,留口供编译期完整性
59
+ 'plan-view-file': '',
56
60
  };
57
61
  export function createBridge(config, app, // 本 bridge 绑定的飞书应用:并发/默认工作区/Claude Code 环境/人格均 per-app
58
62
  deps, opts = {}) {
@@ -85,9 +89,11 @@ deps, opts = {}) {
85
89
  // 用于实现「默认不发非图片文件,用户主动点名才发」的回传规则
86
90
  const fileTracker = new FileTracker();
87
91
  const confirmPending = new Map();
88
- // plan 确认挂起项:requestId → 等待中的计划决策(plan-approve/revise/reject)
92
+ // plan 确认挂起项:requestId → 等待中的计划决策(plan-approve/revise/reject)。
93
+ // 嵌入主卡片后回调侧无 cardId(主卡 messageId 由 ProgressCard 内部管理),改带 channelKey 定位活跃进度卡
89
94
  const planPending = new Map();
90
- // 提问挂起项:requestId → 等待中的问题答案(qa-pick 逐项收集,qa-submit 一次性 resolve)
95
+ // 提问挂起项:requestId → 等待中的问题答案(qa-pick 逐项收集,qa-submit 一次性 resolve)。
96
+ // 嵌入主卡片后无 cardId,改带 channelKey 定位活跃进度卡
91
97
  const qaPending = new Map();
92
98
  // 通道当前任务的进度卡:gate 的 ask/planAsk/askQuestion 闭包随 gate 通道级复用,
93
99
  // 不能捕获任务级 progress 实例(会是首个任务的旧卡)——经此间接引用每次任务的新卡
@@ -98,18 +104,28 @@ deps, opts = {}) {
98
104
  function workspacePath(name) {
99
105
  return config.workspaces.find((w) => w.name === name)?.path ?? config.workspaces[0].path;
100
106
  }
107
+ /** 配置页管理的 MCP(#12):bridge 自管 mcp/servers.json 每任务现读注入 SDK(改配置即热生效,
108
+ * 无须重启)。notify 名固定后展开防被用户配置同名覆盖;文件缺失/损坏返回空(配置页写坏
109
+ * 不应拖垮任务执行) */
110
+ function loadBridgeMcpServers() {
111
+ return readMcpServersFromJsonFile(BRIDGE_MCP_JSON).servers;
112
+ }
101
113
  /** 通道内串行入队(每 key 一条 Promise 链),全局并发由 Semaphore 限制。
102
114
  * 有任务在跑时新消息静默 FIFO 排队——不提示的话用户不知道消息已被接收,会反复重发或误以为丢失。
103
- * queuedCount 计「等待中」消息数:enqueue 一律自增(含首位),executeTask 启动时递减(该条离开队列) */
115
+ * queuedCount 计「等待中」消息数:enqueue 一律自增(含首位),executeTask 启动时递减(该条离开队列)。
116
+ * /new 通过 epoch 代际丢弃旧排队消息:enqueue 捕获当前 epoch 传入 executeTask,不匹配则 no-op */
104
117
  function enqueue(key, msg, prompt, wsName) {
105
118
  const rt = runtimes.get(key) ?? { queue: Promise.resolve() };
119
+ if (rt.epoch === undefined)
120
+ rt.epoch = 0; // 旧状态无 epoch 字段的运行期兜底
106
121
  runtimes.set(key, rt);
107
122
  rt.queuedCount = (rt.queuedCount ?? 0) + 1;
108
123
  if (rt.busy) {
109
124
  void deps.gateway.sendTextTo(msg.chatId, `⏳ 当前有任务在运行,本条消息已排队(第 ${rt.queuedCount} 位),完成后自动开始`)
110
125
  .catch(() => { }); // 提示失败静默:不影响任务本身入队
111
126
  }
112
- rt.queue = rt.queue.then(() => executeTask(key, msg, prompt, wsName)).catch((e) => {
127
+ const myEpoch = rt.epoch; // 闭包捕获本条消息入队时的代际;executeTask 据此判断是否被 /new 作废
128
+ rt.queue = rt.queue.then(() => executeTask(key, msg, prompt, wsName, myEpoch)).catch((e) => {
113
129
  console.error(tag, '[任务异常]', e);
114
130
  });
115
131
  }
@@ -215,17 +231,55 @@ deps, opts = {}) {
215
231
  if (cmd.handled) {
216
232
  if (cmd.reply)
217
233
  await deps.gateway.sendTextTo(msg.chatId, cmd.reply);
218
- // /new 开启新会话:「本次会话不再询问」的授权随之失效(handleCommand 无权访问 wiring
219
- // 持有的 gate,故在命令处理分支后于 wiring 侧检测并 reset)
220
- if (/^\/new(?:\s|$)/.test(msg.text.trim()))
234
+ // /new 开启新会话:
235
+ // 1) 清空旧代际排队消息(epoch 自增,旧任务入 executeTask 即 no-op)
236
+ // 2) abort 正在执行的任务
237
+ // 3) 结算本用户挂起的 confirm/plan/qa 条目(按取消/拒绝处理:对话继续推进或被放弃)
238
+ // 4) 通道级权限闸 reset(spec:「本次会话不再询问」的记忆跨任务生效,直至 /new)
239
+ // 5) 汇总已取消的排队消息数(用户能看到新建会话的真实影响面)
240
+ // ——handleCommand 无权访问 wiring 的 runtime/pending map,统一在 wiring 侧做
241
+ if (/^\/new(?:\s|$)/.test(msg.text.trim())) {
242
+ const rt = runtimes.get(key);
243
+ const cancelled = rt?.queuedCount ?? 0;
244
+ if (rt) {
245
+ rt.epoch = (rt.epoch ?? 0) + 1;
246
+ rt.queuedCount = 0; // 旧代际消息在 executeTask 入口 no-op,不会再 decrement;此处一次性清零即可
247
+ if (rt.abort)
248
+ rt.abort.abort();
249
+ }
250
+ for (const [id, p] of confirmPending) {
251
+ if (p.channelKey !== key)
252
+ continue;
253
+ confirmPending.delete(id);
254
+ p.resolve('deny');
255
+ }
256
+ for (const [id, p] of planPending) {
257
+ if (p.channelKey !== key)
258
+ continue;
259
+ planPending.delete(id);
260
+ p.resolve({ action: 'reject' });
261
+ }
262
+ for (const [id, p] of qaPending) {
263
+ if (p.channelKey !== key)
264
+ continue;
265
+ qaPending.delete(id);
266
+ p.resolve({});
267
+ }
221
268
  gates.get(key)?.reset();
269
+ if (cancelled > 0) {
270
+ await deps.gateway.sendTextTo(msg.chatId, `🗑️ 已因新建会话取消 ${cancelled} 条排队消息`);
271
+ }
272
+ }
222
273
  return;
223
274
  }
224
275
  // 3. 普通文本:通道内串行入队(每 key 一条 Promise 链),全局并发由 Semaphore 限制
225
276
  enqueue(key, msg, cmd.taskText ?? msg.text, currentWorkspace);
226
277
  }
227
- async function executeTask(key, msg, prompt, wsName) {
278
+ async function executeTask(key, msg, prompt, wsName, myEpoch) {
228
279
  const rt = runtimes.get(key);
280
+ // /new 丢弃旧代际:静默 no-op(不入队列、不 decrement、不建进度卡;汇总条数已在 /new 时回)
281
+ if (myEpoch !== (rt.epoch ?? 0))
282
+ return;
229
283
  rt.busy = true; // enqueue 据此给后续消息发「已排队」提示(sem 等待期同样算占用)
230
284
  rt.queuedCount = Math.max(0, (rt.queuedCount ?? 1) - 1); // 本条离开等待队列开始执行
231
285
  const release = await sem.acquire();
@@ -237,7 +291,9 @@ deps, opts = {}) {
237
291
  deleteCard: deps.gateway.deleteCard
238
292
  ? (id) => deps.gateway.deleteCard(id)
239
293
  : undefined,
240
- }, `任务 · ${wsName}`);
294
+ }, `任务 · ${wsName}`,
295
+ // card.width 热生效:每任务现读 config(与同款「同对象 mutate 即生效」语义一致)
296
+ { cardWidthMode: config.card?.width ?? 'default' });
241
297
  const confirmTimeoutMs = opts.confirmTimeoutMs ?? CONFIRM_TIMEOUT_MS;
242
298
  const cardSendTimeoutMs = opts.cardSendTimeoutMs ?? CARD_SEND_TIMEOUT_MS;
243
299
  // 通道级复用:已存在则沿用(allow-session 记忆跨任务),不存在才建。
@@ -279,30 +335,43 @@ deps, opts = {}) {
279
335
  },
280
336
  req,
281
337
  ownerId: msg.userId,
338
+ channelKey: key,
282
339
  onSettled: settle,
283
340
  });
284
341
  });
285
342
  },
286
- // plan 确认:发计划卡片组(首卡带按钮+输入框)挂起等待;超时自动放弃。
287
- // chatId/userId 绑定构造时任务——通道 key = chatId+userId,同通道内固定,闭包安全
343
+ // plan 确认(#3+#6):不再单独发计划卡片组。计划正文先落盘到 ~/.lark-claudecode-bridge/claude/plans/
344
+ // (slug + 时间戳,方便后续 review 与 SOP 要求的 plan 本地归档),然后嵌入当前任务的进度卡:
345
+ // 卡片正文仅露 600 字预览 + 「查看完整方案」按钮(callback send_file 发送原 md 文件,不塞卡片正文
346
+ // ——飞书单卡 30KB 上限与可读性都不允许长方案铺在卡片里);批准/放弃/按意见修改三按钮与意见输入框
347
+ // 仍在主卡片的内嵌 form 内。超时由本闭包自管:到点 clearPlan + resolve reject
288
348
  planAsk: async (req) => {
289
349
  const planReq = { requestId: randomUUID(), plan: req.plan, workspaceName: req.workspaceName };
290
- const ids = [];
291
- for (const c of buildPlanCards(planReq)) {
292
- const id = await raceFallback(deps.gateway.sendCardTo(msg.chatId, c), cardSendTimeoutMs, '');
293
- if (!id) {
294
- void deps.gateway.sendTextTo(msg.chatId, '⚠️ 计划卡发送失败,本次计划已自动放弃;模型重新提交计划时会再次弹出')
295
- .catch(() => { });
296
- return { action: 'reject' };
297
- }
298
- ids.push(id);
350
+ // 落盘计划原文:路径走 ensureRuntimeDirs 已建的 claude/plans 目录,文件名加时间戳避免并发冲突
351
+ const plansDir = join(CONFIG_DIR, 'claude', 'plans');
352
+ try {
353
+ mkdirSync(plansDir, { recursive: true });
299
354
  }
300
- const cardId = ids[0];
301
- void activeProgress.get(key)?.sinkToBottom();
355
+ catch { /* 已存在 */ }
356
+ const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
357
+ const slug = (req.workspaceName || 'plan').replace(/[^\w-]+/g, '_').slice(0, 32);
358
+ const planFilePath = join(plansDir, `${ts}-${slug}.md`);
359
+ try {
360
+ writeFileSync(planFilePath, `# 执行计划 · ${req.workspaceName} · ${new Date().toLocaleString('zh-CN', { hour12: false })}\n\n${req.plan}\n`, 'utf8');
361
+ }
362
+ catch (e) {
363
+ console.error(tag, '[计划落盘失败](不影响流程,仅「查看完整方案」可能不可用):', e);
364
+ }
365
+ const progress = activeProgress.get(key);
366
+ if (!progress)
367
+ return { action: 'reject' }; // 进度卡不在 = 任务已结束/被 abort,按放弃处理
368
+ progress.setPlan({ requestId: planReq.requestId, plan: req.plan, workspaceName: req.workspaceName, planFilePath });
369
+ // 不再 sinkToBottom:plan 区嵌入主进度卡,flush 自然 PATCH,删旧卡重发只会让用户看到计划区跳一下
302
370
  return new Promise((resolve) => {
303
371
  const gc = setTimeout(() => {
304
372
  planPending.delete(planReq.requestId);
305
- void deps.gateway.updateCard(cardId, buildExpiredPlanCard(planReq, confirmTimeoutMs)).catch(() => { });
373
+ progress.setPlan(undefined);
374
+ progress.setStatus(`⏰ 计划确认超时(${Math.round(confirmTimeoutMs / 60000)} 分钟未确认),已自动放弃;模型重新提交计划时会再次弹出`);
306
375
  resolve({ action: 'reject' });
307
376
  }, confirmTimeoutMs);
308
377
  gc.unref();
@@ -313,12 +382,14 @@ deps, opts = {}) {
313
382
  },
314
383
  req: planReq,
315
384
  ownerId: msg.userId,
316
- cardId,
385
+ channelKey: key,
386
+ chatId: msg.chatId,
387
+ planFilePath,
317
388
  });
318
389
  });
319
390
  },
320
- // 提问确认:AskUserQuestion 的问题选项卡(每问选项按钮 + 提交);qa-pick 逐项
321
- // 更新选中态(PATCH 重渲染),qa-submit 全部作答后一次性 resolve。超时自动跳过
391
+ // 提问确认(#3):AskUserQuestion 的选项 + 提交按钮全部嵌入主进度卡(#3 决策:彻底替换独立提问卡)。
392
+ // qa-pick 仅 PATCH 选中态(卡片不刷新整卡,仅更新 answer map),qa-submit 一次性 resolve 并 clearQuestion
322
393
  askQuestion: async (req) => {
323
394
  const qaReq = {
324
395
  requestId: randomUUID(),
@@ -326,18 +397,16 @@ deps, opts = {}) {
326
397
  workspaceName: req.workspaceName,
327
398
  };
328
399
  const answers = {};
329
- // 发卡失败/超时返回空答案:gate 对空答案统一按「未作答」deny,模型自行决策或停下等待
330
- const cardId = await raceFallback(deps.gateway.sendCardTo(msg.chatId, buildQuestionCard(qaReq, answers)), cardSendTimeoutMs, '');
331
- if (!cardId) {
332
- void deps.gateway.sendTextTo(msg.chatId, '⚠️ 提问卡发送失败,已按未作答处理;模型会自行决策或停下等待你的回复')
333
- .catch(() => { });
334
- return {};
335
- }
336
- void activeProgress.get(key)?.sinkToBottom();
400
+ const progress = activeProgress.get(key);
401
+ if (!progress)
402
+ return {}; // 进度卡不在 = 任务已结束/被 abort,按未作答处理
403
+ progress.setQuestion({ requestId: qaReq.requestId, questions: req.questions, workspaceName: req.workspaceName, answers });
404
+ // 不再 sinkToBottom:提问区嵌入主进度卡后,flush 自然 PATCH(理由同 planAsk 上方注释)
337
405
  return new Promise((resolve) => {
338
406
  const gc = setTimeout(() => {
339
407
  qaPending.delete(qaReq.requestId);
340
- void deps.gateway.updateCard(cardId, buildExpiredQuestionCard(qaReq, confirmTimeoutMs)).catch(() => { });
408
+ progress.setQuestion(undefined);
409
+ progress.setStatus(`⏰ 提问超时(${Math.round(confirmTimeoutMs / 60000)} 分钟未作答),已按未作答处理;模型自行决策或停下等待你的回复`);
341
410
  resolve({});
342
411
  }, confirmTimeoutMs);
343
412
  gc.unref();
@@ -348,7 +417,8 @@ deps, opts = {}) {
348
417
  },
349
418
  req: qaReq,
350
419
  ownerId: msg.userId,
351
- cardId,
420
+ channelKey: key,
421
+ chatId: msg.chatId,
352
422
  answers,
353
423
  });
354
424
  });
@@ -363,6 +433,8 @@ deps, opts = {}) {
363
433
  // lcb-notify 发送能力:chatId 硬绑定当前任务(权限闸直通的安全前提);
364
434
  // sentPaths 记录中途已推送的文件,任务收尾的产出回传据此去重(用户已收过的不重发)。
365
435
  // 每次中途推送后进度卡沉底一次(删旧卡重发),保持「任务是否还在跑」始终可见于会话底部
436
+ // SOP 开关:本任务级现读 config.session.notifySop(缺省 true),决定后续 sendText 是否被强制降级
437
+ const sopEnabled = config.session?.notifySop !== false;
366
438
  const sentPaths = new Set();
367
439
  const sink = async () => { await activeProgress.get(key)?.sinkToBottom(); };
368
440
  const notifySender = createGatewaySender({
@@ -384,6 +456,10 @@ deps, opts = {}) {
384
456
  await deps.gateway.uploadAndSendFile(c, p);
385
457
  await sink();
386
458
  },
459
+ }, {
460
+ enabled: sopEnabled,
461
+ // #11 SOP 提示文案前缀(用户看到的「为什么自动转附件」提示)
462
+ sopTag: '⚠️ 推送上限',
387
463
  });
388
464
  activeProgress.set(key, progress);
389
465
  try {
@@ -405,15 +481,22 @@ deps, opts = {}) {
405
481
  resumeSessionId: resumeId,
406
482
  signal: abort.signal,
407
483
  env: appEnv,
408
- appendSystemPrompt: app.appendSystemPrompt,
484
+ // #11 SOP 软约束:app.appendSystemPrompt 后追加 SOP 摘要(关闭时仅传 app 原有部分)
485
+ appendSystemPrompt: (() => {
486
+ const base = app.appendSystemPrompt?.trim() ?? '';
487
+ if (!sopEnabled)
488
+ return base || undefined;
489
+ return base ? `${base}\n\n${FEISHU_NOTIFY_SOP_PROMPT}` : FEISHU_NOTIFY_SOP_PROMPT;
490
+ })(),
409
491
  // code-dev 工作区统一 plan mode:模型先出计划(ExitPlanMode → planAsk 飞书卡片),
410
492
  // 用户批准后 SDK 自动切回可编辑模式继续执行
411
493
  ...(config.workspaces.find((w) => w.name === wsName)?.type === 'code-dev' ? { permissionMode: 'plan' } : {}),
412
494
  // 通道级模型覆盖(/model 设置,每任务现读——改完下一条消息即生效);未设 = 跟随 ~/.claude 全局
413
495
  ...(state?.model ? { model: state.model } : {}),
414
496
  // 进程内通知工具(send_text/send_image/send_file):中间产物实时推给当前聊天。
415
- // server 实例按任务构造,chatId 在 sender 闭包内硬绑定——模型无法选择接收者
416
- mcpServers: { [NOTIFY_SERVER_NAME]: createNotifyServer(notifySender) },
497
+ // server 实例按任务构造,chatId 在 sender 闭包内硬绑定——模型无法选择接收者。
498
+ // 配置页 MCP(#12)一并注入:bridge 自管 servers.json 每任务现读(热生效),notify 后展开保名
499
+ mcpServers: { ...loadBridgeMcpServers(), [NOTIFY_SERVER_NAME]: createNotifyServer(notifySender) },
417
500
  // 插件:config.yaml 显式配置(开发期指源码目录)+ Claude 配置目录已启用的 marketplace
418
501
  // 插件自动发现,同名显式优先;SDK 对无效路径静默跳过,实际加载以 init 清单(/plugins 命令)为准。
419
502
  // managed 模式合并发现两处目录(自管 + 本机 ~/.claude):按绝对 installPath 传 SDK,
@@ -610,10 +693,17 @@ deps, opts = {}) {
610
693
  const plan = planPending.get(action.value.requestId);
611
694
  if (plan) {
612
695
  if (action.operatorId !== plan.ownerId) {
613
- console.log(tag, `[卡片回调] 非发起人 ${action.operatorId} 点击计划卡 ${action.value.requestId},已忽略`);
696
+ console.log(tag, `[卡片回调] 非发起人 ${action.operatorId} 点击计划区 ${action.value.requestId},已忽略`);
614
697
  return { toast: { type: 'info', content: '仅任务发起人可操作计划' } };
615
698
  }
616
699
  const decision = action.value.decision;
700
+ // 「查看完整方案」(#6):callback 触发 send_file 直接发原 md 文件,不消耗挂起项
701
+ if (decision === 'plan-view-file') {
702
+ // 文件在 planAsk 时已落盘;这里直接 send_file(feishuGateway 走 file_type:stream,md 等任意后缀可发)
703
+ void deps.gateway.uploadAndSendFile(plan.chatId, plan.planFilePath)
704
+ .catch((e) => console.error('[查看方案发送失败]', action.value.requestId, e));
705
+ return { toast: { type: 'info', content: '正在发送方案文件…' } };
706
+ }
617
707
  if (decision !== 'plan-approve' && decision !== 'plan-revise' && decision !== 'plan-reject') {
618
708
  return { toast: { type: 'info', content: '该计划确认已被处理或已过期' } };
619
709
  }
@@ -626,19 +716,20 @@ deps, opts = {}) {
626
716
  plan.resolve(decision === 'plan-approve' ? { action: 'approve' }
627
717
  : decision === 'plan-revise' ? { action: 'revise', feedback }
628
718
  : { action: 'reject' });
629
- const resultCard = buildPlanResultCard(plan.req, decision, '任务发起人', feedback);
630
- void deps.gateway.updateCard(plan.cardId, resultCard)
631
- .catch((e) => console.error('[计划卡兜底更新失败]', action.value.requestId, e));
632
- return {
633
- toast: { type: 'success', content: PLAN_DECISION_TEXT[decision] },
634
- card: { type: 'raw', data: resultCard },
635
- };
719
+ // 清除主进度卡的内嵌计划区(按钮随 setPlan(undefined) 的 flush 消失)
720
+ const prog = activeProgress.get(plan.channelKey);
721
+ if (prog) {
722
+ prog.setPlan(undefined);
723
+ prog.setStatus(PLAN_DECISION_TEXT[decision]);
724
+ }
725
+ return { toast: { type: 'success', content: PLAN_DECISION_TEXT[decision] } };
636
726
  }
637
- // 提问回调分流:qa-pick 更新选中态(卡片 PATCH 重渲染,不消耗挂起项);qa-submit 全部作答后 resolve
727
+ // 提问回调分流(#3):qa-pick 仅 PATCH 选中态(不消耗挂起项,等所有题答完才 submit);
728
+ // qa-submit 全部作答后一次性 resolve 并 clearQuestion。主进度卡的 PATCH 由 ProgressCard 内部 flush 串行化
638
729
  const qa = qaPending.get(action.value.requestId);
639
730
  if (qa) {
640
731
  if (action.operatorId !== qa.ownerId) {
641
- console.log(tag, `[卡片回调] 非发起人 ${action.operatorId} 点击提问卡 ${action.value.requestId},已忽略`);
732
+ console.log(tag, `[卡片回调] 非发起人 ${action.operatorId} 点击提问区 ${action.value.requestId},已忽略`);
642
733
  return { toast: { type: 'info', content: '仅任务发起人可作答' } };
643
734
  }
644
735
  const { decision } = action.value;
@@ -648,18 +739,16 @@ deps, opts = {}) {
648
739
  if (!q || !label || !q.options.some((o) => o.label === label)) {
649
740
  return { toast: { type: 'info', content: '该选项无效' } };
650
741
  }
651
- const cur = qa.answers[action.value.qIndex];
652
- if (q.multiSelect) {
653
- const arr = Array.isArray(cur) ? cur : cur !== undefined ? [cur] : [];
654
- qa.answers[action.value.qIndex] = arr.includes(label)
655
- ? (arr.length > 1 ? arr.filter((x) => x !== label) : arr) // 至少保留一项:全取消等于未答
656
- : [...arr, label];
657
- }
658
- else {
659
- qa.answers[action.value.qIndex] = label;
660
- }
661
- void deps.gateway.updateCard(qa.cardId, buildQuestionCard(qa.req, qa.answers)).catch(() => { });
662
- return { toast: { type: 'success', content: `已选:${label}` } };
742
+ // 切换选中态由 ProgressCard.updateQuestionAnswer 唯一维护(toggle / 至少保留一项);本处只调 PATCH
743
+ const prog = activeProgress.get(qa.channelKey);
744
+ const before = prog?.getQuestionAnswer(action.value.qIndex);
745
+ const willUnpick = !!q.multiSelect && Array.isArray(before) && before.includes(label) && before.length > 1;
746
+ prog?.updateQuestionAnswer(action.value.qIndex, label, !!q.multiSelect);
747
+ // 把 progress 的最新答案同步回 qa.answers 供 submit 校验,避免双 mutate(toggle 重复计数)
748
+ if (prog)
749
+ qa.answers[action.value.qIndex] = prog.getQuestionAnswer(action.value.qIndex)
750
+ ?? (q.multiSelect ? [] : label);
751
+ return { toast: { type: 'success', content: willUnpick ? `已取消:${label}` : `已选:${label}` } };
663
752
  }
664
753
  if (decision === 'qa-submit') {
665
754
  const missing = qa.req.questions
@@ -671,13 +760,13 @@ deps, opts = {}) {
671
760
  const result = {};
672
761
  qa.req.questions.forEach((q, i) => { result[q.question] = qa.answers[i]; });
673
762
  qa.resolve(result);
674
- const resultCard = buildQuestionResultCard(qa.req, qa.answers, '任务发起人');
675
- void deps.gateway.updateCard(qa.cardId, resultCard)
676
- .catch((e) => console.error('[提问卡兜底更新失败]', action.value.requestId, e));
677
- return {
678
- toast: { type: 'success', content: '✅ 答案已提交' },
679
- card: { type: 'raw', data: resultCard },
680
- };
763
+ // 清除主进度卡的内嵌提问区(按钮随 setQuestion(undefined) 的 flush 消失)
764
+ const prog = activeProgress.get(qa.channelKey);
765
+ if (prog) {
766
+ prog.setQuestion(undefined);
767
+ prog.setStatus('✅ 答案已提交,继续执行…');
768
+ }
769
+ return { toast: { type: 'success', content: '✅ 答案已提交' } };
681
770
  }
682
771
  return { toast: { type: 'info', content: '该提问已被处理或已过期' } };
683
772
  }
@@ -709,7 +798,21 @@ deps, opts = {}) {
709
798
  return { toast: { type: 'error', content: retryable ? '处理失败,请重试' : '处理出现异常,决策可能已生效,请勿盲目重试' } };
710
799
  }
711
800
  }
712
- return { onMessage: handleIncoming, onCardAction: handleCardAction };
801
+ return {
802
+ onMessage: handleIncoming,
803
+ onCardAction: handleCardAction,
804
+ /**
805
+ * 测试/诊断入口:返回通道当前挂起的内嵌 plan / question requestId。
806
+ * 仅用于测试断言与生产期诊断,不影响业务流。channelKey 取 chatId + userId。
807
+ * 内嵌挂起项被 settle/超时清空时对应字段变 undefined
808
+ */
809
+ _pendingAsk(channelKey) {
810
+ const prog = activeProgress.get(channelKey);
811
+ if (!prog)
812
+ return {};
813
+ return prog.pendingAsk();
814
+ },
815
+ };
713
816
  }
714
817
  /**
715
818
  * 配置热重载器:lcb ws add/remove 写盘后,运行实例下一条消息即见新工作区