@vibe-lark/larkpal 0.1.24 → 0.1.26

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.
package/dist/main.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { t as LarkCliCredentialProvider } from "./lark-cli-provider-CdgwmqSz.mjs";
2
1
  import { n as getLarkRuntime, r as setLarkRuntime, t as larkLogger } from "./lark-logger-D7_pEVQc.mjs";
2
+ import { t as LarkCliCredentialProvider } from "./lark-cli-provider-CR0eLl0q.mjs";
3
+ import { t as createToolInvokeProxyRouter } from "./tool-registry-consumer-DrklfqGF.mjs";
3
4
  import * as fs from "node:fs";
4
5
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
6
  import * as path$1 from "node:path";
@@ -36,7 +37,7 @@ import { Readable } from "node:stream";
36
37
  *
37
38
  * 本模块在启动时检测它们是否已安装,缺失时给出明确的安装指引。
38
39
  */
39
- const log$30 = larkLogger("preflight");
40
+ const log$31 = larkLogger("preflight");
40
41
  function detectCli(name) {
41
42
  try {
42
43
  const cliPath = execSync(`which ${name}`, {
@@ -74,12 +75,12 @@ function detectCli(name) {
74
75
  * 缺失任一依赖时打印安装指引并抛出错误,阻止启动。
75
76
  */
76
77
  function preflight() {
77
- log$30.info("开始前置环境检查...");
78
+ log$31.info("开始前置环境检查...");
78
79
  const claude = detectCli("claude");
79
80
  const larkCli = detectCli("lark-cli");
80
81
  const status = (dep) => dep.installed ? `✅ ${dep.name} (${dep.version ?? "unknown"}) → ${dep.path}` : `❌ ${dep.name} 未安装`;
81
- log$30.info(` ${status(claude)}`);
82
- log$30.info(` ${status(larkCli)}`);
82
+ log$31.info(` ${status(claude)}`);
83
+ log$31.info(` ${status(larkCli)}`);
83
84
  const missing = [];
84
85
  if (!claude.installed) missing.push([
85
86
  "❌ 未找到 Claude Code (claude)",
@@ -109,10 +110,10 @@ function preflight() {
109
110
  "",
110
111
  "请安装缺失的依赖后重新启动 LarkPal。"
111
112
  ].join("\n");
112
- log$30.error(msg);
113
+ log$31.error(msg);
113
114
  throw new Error("前置环境检查未通过,缺少必要依赖");
114
115
  }
115
- log$30.info("前置环境检查通过 ✓");
116
+ log$31.info("前置环境检查通过 ✓");
116
117
  return {
117
118
  claude,
118
119
  larkCli,
@@ -136,7 +137,7 @@ function preflight() {
136
137
  * 同时生成新版 (~/.lark-cli/config.json) 和旧版 (~/.config/lark/config.json)
137
138
  * 格式的配置文件,保证 lark-cli 无论通过哪种路径读取都能正常工作。
138
139
  */
139
- const log$29 = larkLogger("config/ensure-lark-cli");
140
+ const log$30 = larkLogger("config/ensure-lark-cli");
140
141
  /** 新版 lark-cli 配置路径 */
141
142
  const NEW_CONFIG_DIR = join(homedir(), ".lark-cli");
142
143
  const NEW_CONFIG_PATH = join(NEW_CONFIG_DIR, "config.json");
@@ -153,19 +154,19 @@ function ensureLarkCliConfig() {
153
154
  const appId = process.env.LARK_APP_ID;
154
155
  const appSecret = process.env.LARK_APP_SECRET;
155
156
  if (!appId || !appSecret) {
156
- log$29.info("环境变量凭证未设置,跳过 lark-cli 配置同步");
157
+ log$30.info("环境变量凭证未设置,跳过 lark-cli 配置同步");
157
158
  return;
158
159
  }
159
160
  const hasNewConfig = existsSync(NEW_CONFIG_PATH);
160
161
  const hasLegacyConfig = existsSync(LEGACY_CONFIG_PATH);
161
162
  if (hasNewConfig && hasLegacyConfig) {
162
- log$29.info("lark-cli 配置文件已存在,跳过同步", {
163
+ log$30.info("lark-cli 配置文件已存在,跳过同步", {
163
164
  newConfig: NEW_CONFIG_PATH,
164
165
  legacyConfig: LEGACY_CONFIG_PATH
165
166
  });
166
167
  return;
167
168
  }
168
- log$29.info("检测到环境变量凭证但 lark-cli 配置文件缺失,开始同步", {
169
+ log$30.info("检测到环境变量凭证但 lark-cli 配置文件缺失,开始同步", {
169
170
  appId,
170
171
  hasNewConfig,
171
172
  hasLegacyConfig
@@ -184,9 +185,9 @@ function ensureLarkCliConfig() {
184
185
  encoding: "utf-8",
185
186
  mode: 384
186
187
  });
187
- log$29.info("已生成新版 lark-cli 配置文件", { path: NEW_CONFIG_PATH });
188
+ log$30.info("已生成新版 lark-cli 配置文件", { path: NEW_CONFIG_PATH });
188
189
  } catch (err) {
189
- log$29.warn("生成新版 lark-cli 配置文件失败", {
190
+ log$30.warn("生成新版 lark-cli 配置文件失败", {
190
191
  path: NEW_CONFIG_PATH,
191
192
  error: err instanceof Error ? err.message : String(err)
192
193
  });
@@ -205,9 +206,9 @@ function ensureLarkCliConfig() {
205
206
  encoding: "utf-8",
206
207
  mode: 384
207
208
  });
208
- log$29.info("已生成旧版 lark-cli 配置文件", { path: LEGACY_CONFIG_PATH });
209
+ log$30.info("已生成旧版 lark-cli 配置文件", { path: LEGACY_CONFIG_PATH });
209
210
  } catch (err) {
210
- log$29.warn("生成旧版 lark-cli 配置文件失败", {
211
+ log$30.warn("生成旧版 lark-cli 配置文件失败", {
211
212
  path: LEGACY_CONFIG_PATH,
212
213
  error: err instanceof Error ? err.message : String(err)
213
214
  });
@@ -221,7 +222,7 @@ function ensureLarkCliConfig() {
221
222
  * 首次启动时检测并生成 ~/.claude/settings.json 和 ~/.claude/CLAUDE.md,
222
223
  * 以及确保会话工作目录结构完整。
223
224
  */
224
- const logger$7 = larkLogger("config/defaults");
225
+ const logger$8 = larkLogger("config/defaults");
225
226
  const DEFAULT_SETTINGS = {
226
227
  permissions: {
227
228
  allow: [
@@ -344,13 +345,13 @@ async function ensureDefaults() {
344
345
  const settingsPath = join(claudeDir, "settings.json");
345
346
  const claudeMdPath = join(claudeDir, "CLAUDE.md");
346
347
  await mkdir(claudeDir, { recursive: true });
347
- logger$7.info("确保 ~/.claude 目录存在", { path: claudeDir });
348
+ logger$8.info("确保 ~/.claude 目录存在", { path: claudeDir });
348
349
  await writeFile(settingsPath, JSON.stringify(DEFAULT_SETTINGS, null, 2), "utf-8");
349
- logger$7.info("settings.json 已同步(强制覆盖)", { path: settingsPath });
350
- if (await fileExists(claudeMdPath)) logger$7.info("CLAUDE.md 已存在,跳过生成", { path: claudeMdPath });
350
+ logger$8.info("settings.json 已同步(强制覆盖)", { path: settingsPath });
351
+ if (await fileExists(claudeMdPath)) logger$8.info("CLAUDE.md 已存在,跳过生成", { path: claudeMdPath });
351
352
  else {
352
353
  await writeFile(claudeMdPath, DEFAULT_CLAUDE_MD, "utf-8");
353
- logger$7.info("已生成默认 CLAUDE.md", { path: claudeMdPath });
354
+ logger$8.info("已生成默认 CLAUDE.md", { path: claudeMdPath });
354
355
  }
355
356
  }
356
357
  //#endregion
@@ -397,7 +398,7 @@ function getToolDisplayName(toolName) {
397
398
  * - result(data) — 最终结果
398
399
  * - parseError(error, rawLine) — 解析错误(不中断流)
399
400
  */
400
- const log$28 = larkLogger("cc-runtime/stream-parser");
401
+ const log$29 = larkLogger("cc-runtime/stream-parser");
401
402
  var CCStreamParser = class extends EventEmitter {
402
403
  /**
403
404
  * 解析一行 NDJSON 文本
@@ -411,7 +412,7 @@ var CCStreamParser = class extends EventEmitter {
411
412
  try {
412
413
  msg = JSON.parse(trimmed);
413
414
  } catch (err) {
414
- log$28.warn("NDJSON 解析失败,跳过该行", {
415
+ log$29.warn("NDJSON 解析失败,跳过该行", {
415
416
  error: String(err),
416
417
  rawLine: trimmed.slice(0, 200)
417
418
  });
@@ -435,14 +436,14 @@ var CCStreamParser = class extends EventEmitter {
435
436
  this.handleResult(msg);
436
437
  break;
437
438
  case "system":
438
- log$28.info("收到 system 消息", {
439
+ log$29.info("收到 system 消息", {
439
440
  subtype: msg.subtype,
440
441
  detail: JSON.stringify(msg).slice(0, 500)
441
442
  });
442
443
  this.emit("system", msg);
443
444
  break;
444
445
  default:
445
- log$28.debug("收到未知消息类型,已忽略", { type: msg.type });
446
+ log$29.debug("收到未知消息类型,已忽略", { type: msg.type });
446
447
  break;
447
448
  }
448
449
  }
@@ -461,13 +462,13 @@ var CCStreamParser = class extends EventEmitter {
461
462
  switch (event.type) {
462
463
  case "content_block_start": {
463
464
  const block = event.content_block;
464
- log$28.debug("content_block_start 事件", {
465
+ log$29.debug("content_block_start 事件", {
465
466
  blockType: block?.type,
466
467
  toolName: block?.name,
467
468
  toolUseId: block?.id
468
469
  });
469
470
  if (block?.type === "tool_use" && block.name) {
470
- log$28.info("工具调用开始", {
471
+ log$29.info("工具调用开始", {
471
472
  toolName: block.name,
472
473
  displayName: getToolDisplayName(block.name),
473
474
  toolUseId: block.id
@@ -486,7 +487,7 @@ var CCStreamParser = class extends EventEmitter {
486
487
  case "message_delta": {
487
488
  const stopReason = event.delta?.stop_reason;
488
489
  if (stopReason) {
489
- log$28.info("轮次结束", { stopReason });
490
+ log$29.info("轮次结束", { stopReason });
490
491
  this.emit("turnEnd", stopReason);
491
492
  }
492
493
  break;
@@ -506,7 +507,7 @@ var CCStreamParser = class extends EventEmitter {
506
507
  const content = msg.message?.content;
507
508
  const stopReason = msg.message?.stop_reason;
508
509
  const blockTypes = Array.isArray(content) ? content.map((b) => `${b.type}${b.name ? ":" + b.name : ""}`).join(", ") : "none";
509
- log$28.info("收到完整 assistant 消息", {
510
+ log$29.info("收到完整 assistant 消息", {
510
511
  model: msg.message?.model,
511
512
  stopReason,
512
513
  contentBlocks: content?.length,
@@ -514,7 +515,7 @@ var CCStreamParser = class extends EventEmitter {
514
515
  });
515
516
  if (Array.isArray(content)) {
516
517
  for (const block of content) if (block.type === "tool_use" && block.name) {
517
- log$28.info("从 assistant 消息提取工具调用", {
518
+ log$29.info("从 assistant 消息提取工具调用", {
518
519
  toolName: block.name,
519
520
  displayName: getToolDisplayName(block.name),
520
521
  toolUseId: block.id
@@ -533,7 +534,7 @@ var CCStreamParser = class extends EventEmitter {
533
534
  const content = msg.message?.content;
534
535
  if (!Array.isArray(content)) return;
535
536
  for (const item of content) if (item.type === "tool_result" && item.tool_use_id) {
536
- log$28.debug("工具结果返回", { toolUseId: item.tool_use_id });
537
+ log$29.debug("工具结果返回", { toolUseId: item.tool_use_id });
537
538
  this.emit("toolResult", item.tool_use_id);
538
539
  }
539
540
  }
@@ -541,7 +542,7 @@ var CCStreamParser = class extends EventEmitter {
541
542
  * 处理 tool_progress 消息 — 工具执行进度
542
543
  */
543
544
  handleToolProgress(msg) {
544
- log$28.debug("工具执行进度", {
545
+ log$29.debug("工具执行进度", {
545
546
  toolName: msg.tool_name,
546
547
  elapsed: msg.elapsed_time_seconds
547
548
  });
@@ -551,7 +552,7 @@ var CCStreamParser = class extends EventEmitter {
551
552
  * 处理 result 消息 — CC 执行完成的最终结果
552
553
  */
553
554
  handleResult(msg) {
554
- log$28.info("CC 执行完成", {
555
+ log$29.info("CC 执行完成", {
555
556
  subtype: msg.subtype,
556
557
  isError: msg.is_error,
557
558
  durationMs: msg.duration_ms,
@@ -587,7 +588,7 @@ var CCStreamParser = class extends EventEmitter {
587
588
  * - github: { type: "api_key", token }
588
589
  * - custom-api: { type: "http_bearer", base_url, token }
589
590
  */
590
- const log$27 = larkLogger("user/credential-vault");
591
+ const log$28 = larkLogger("user/credential-vault");
591
592
  var CredentialVault = class {
592
593
  credentialPath;
593
594
  encryptionKey;
@@ -599,12 +600,12 @@ var CredentialVault = class {
599
600
  if (keyEnv) {
600
601
  this.encryptionKey = keyEnv.length === 64 ? Buffer.from(keyEnv, "hex") : Buffer.from(keyEnv, "base64");
601
602
  if (this.encryptionKey.length !== 32) {
602
- log$27.error("LARKPAL_ENCRYPTION_KEY 长度不正确,需要 32 字节", { actualLength: this.encryptionKey.length });
603
+ log$28.error("LARKPAL_ENCRYPTION_KEY 长度不正确,需要 32 字节", { actualLength: this.encryptionKey.length });
603
604
  this.encryptionKey = null;
604
- } else log$27.info("凭证加密已启用");
605
+ } else log$28.info("凭证加密已启用");
605
606
  } else {
606
607
  this.encryptionKey = null;
607
- log$27.warn("LARKPAL_ENCRYPTION_KEY 未设置,凭证将以明文存储(仅限开发模式)");
608
+ log$28.warn("LARKPAL_ENCRYPTION_KEY 未设置,凭证将以明文存储(仅限开发模式)");
608
609
  }
609
610
  }
610
611
  /**
@@ -612,7 +613,7 @@ var CredentialVault = class {
612
613
  */
613
614
  async load() {
614
615
  if (!existsSync$1(this.credentialPath)) {
615
- log$27.info("凭证文件不存在,使用空凭证", { path: this.credentialPath });
616
+ log$28.info("凭证文件不存在,使用空凭证", { path: this.credentialPath });
616
617
  this.store = {};
617
618
  this.loaded = true;
618
619
  return;
@@ -621,12 +622,12 @@ var CredentialVault = class {
621
622
  const raw = await readFile$1(this.credentialPath, "utf-8");
622
623
  this.store = JSON.parse(raw);
623
624
  this.loaded = true;
624
- log$27.info("凭证文件加载完成", {
625
+ log$28.info("凭证文件加载完成", {
625
626
  path: this.credentialPath,
626
627
  credentialCount: Object.keys(this.store).length
627
628
  });
628
629
  } catch (err) {
629
- log$27.error("凭证文件加载失败", {
630
+ log$28.error("凭证文件加载失败", {
630
631
  path: this.credentialPath,
631
632
  error: err instanceof Error ? err.message : String(err)
632
633
  });
@@ -640,12 +641,12 @@ var CredentialVault = class {
640
641
  async save() {
641
642
  try {
642
643
  await writeFile$1(this.credentialPath, JSON.stringify(this.store, null, 2), "utf-8");
643
- log$27.info("凭证文件保存完成", {
644
+ log$28.info("凭证文件保存完成", {
644
645
  path: this.credentialPath,
645
646
  credentialCount: Object.keys(this.store).length
646
647
  });
647
648
  } catch (err) {
648
- log$27.error("凭证文件保存失败", {
649
+ log$28.error("凭证文件保存失败", {
649
650
  path: this.credentialPath,
650
651
  error: err instanceof Error ? err.message : String(err)
651
652
  });
@@ -667,7 +668,7 @@ var CredentialVault = class {
667
668
  */
668
669
  async setCredential(name, credential) {
669
670
  if (!this.loaded) await this.load();
670
- log$27.info("设置凭证", {
671
+ log$28.info("设置凭证", {
671
672
  name,
672
673
  fields: Object.keys(credential)
673
674
  });
@@ -683,7 +684,7 @@ var CredentialVault = class {
683
684
  if (!(name in this.store)) return false;
684
685
  delete this.store[name];
685
686
  await this.save();
686
- log$27.info("凭证已删除", { name });
687
+ log$28.info("凭证已删除", { name });
687
688
  return true;
688
689
  }
689
690
  /**
@@ -712,7 +713,7 @@ var CredentialVault = class {
712
713
  envVars[envKey] = String(fieldValue);
713
714
  }
714
715
  } catch (err) {
715
- log$27.warn("凭证解密失败,跳过", {
716
+ log$28.warn("凭证解密失败,跳过", {
716
717
  name,
717
718
  error: err instanceof Error ? err.message : String(err)
718
719
  });
@@ -767,7 +768,7 @@ var CredentialVault = class {
767
768
  * - B 用户的 CC 进程连接 B 的数据库 MCP Server
768
769
  * - 全局工具(lark-cli、文件操作等)所有用户共享
769
770
  */
770
- const log$26 = larkLogger("user/mcp-merge");
771
+ const log$27 = larkLogger("user/mcp-merge");
771
772
  /** 全局 MCP 配置路径 */
772
773
  const GLOBAL_MCP_CONFIG_PATH = path.join(os.homedir(), ".claude", "mcp-servers.json");
773
774
  /**
@@ -781,29 +782,29 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId) {
781
782
  if (existsSync$1(userMcpPath)) try {
782
783
  const raw = await readFile$1(userMcpPath, "utf-8");
783
784
  userConfig = JSON.parse(raw);
784
- log$26.info("用户 MCP 配置加载完成", {
785
+ log$27.info("用户 MCP 配置加载完成", {
785
786
  userId: userCtx.userId,
786
787
  serverCount: Object.keys(userConfig).length,
787
788
  servers: Object.keys(userConfig)
788
789
  });
789
790
  } catch (err) {
790
- log$26.warn("用户 MCP 配置文件解析失败", {
791
+ log$27.warn("用户 MCP 配置文件解析失败", {
791
792
  userId: userCtx.userId,
792
793
  path: userMcpPath,
793
794
  error: err instanceof Error ? err.message : String(err)
794
795
  });
795
796
  }
796
797
  if (Object.keys(userConfig).length === 0) {
797
- log$26.info("用户无自定义 MCP 配置,使用全局默认", { userId: userCtx.userId });
798
+ log$27.info("用户无自定义 MCP 配置,使用全局默认", { userId: userCtx.userId });
798
799
  return null;
799
800
  }
800
801
  let globalConfig = {};
801
802
  if (existsSync$1(GLOBAL_MCP_CONFIG_PATH)) try {
802
803
  const raw = await readFile$1(GLOBAL_MCP_CONFIG_PATH, "utf-8");
803
804
  globalConfig = JSON.parse(raw);
804
- log$26.info("全局 MCP 配置加载完成", { serverCount: Object.keys(globalConfig).length });
805
+ log$27.info("全局 MCP 配置加载完成", { serverCount: Object.keys(globalConfig).length });
805
806
  } catch (err) {
806
- log$26.warn("全局 MCP 配置文件解析失败", {
807
+ log$27.warn("全局 MCP 配置文件解析失败", {
807
808
  path: GLOBAL_MCP_CONFIG_PATH,
808
809
  error: err instanceof Error ? err.message : String(err)
809
810
  });
@@ -812,7 +813,7 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId) {
812
813
  ...globalConfig,
813
814
  ...userConfig
814
815
  };
815
- log$26.info("MCP 配置合并完成", {
816
+ log$27.info("MCP 配置合并完成", {
816
817
  userId: userCtx.userId,
817
818
  globalServers: Object.keys(globalConfig),
818
819
  userServers: Object.keys(userConfig),
@@ -820,7 +821,7 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId) {
820
821
  });
821
822
  const tmpPath = path.join(os.tmpdir(), `mcp-${sessionId}.json`);
822
823
  await writeFile$1(tmpPath, JSON.stringify(merged, null, 2), "utf-8");
823
- log$26.info("MCP 合并配置已写入临时文件", {
824
+ log$27.info("MCP 合并配置已写入临时文件", {
824
825
  path: tmpPath,
825
826
  serverCount: Object.keys(merged).length
826
827
  });
@@ -834,7 +835,7 @@ async function cleanupMcpConfig(sessionId) {
834
835
  try {
835
836
  if (existsSync$1(tmpPath)) {
836
837
  await unlink$1(tmpPath);
837
- log$26.debug("MCP 临时配置文件已清理", { path: tmpPath });
838
+ log$27.debug("MCP 临时配置文件已清理", { path: tmpPath });
838
839
  }
839
840
  } catch {}
840
841
  }
@@ -854,7 +855,7 @@ async function cleanupMcpConfig(sessionId) {
854
855
  * 多轮对话:进程内自动保持完整对话上下文(mutableMessages 数组累积),
855
856
  * 无需外部维护历史。
856
857
  */
857
- const log$25 = larkLogger("cc-runtime/process-manager");
858
+ const log$26 = larkLogger("cc-runtime/process-manager");
858
859
  /**
859
860
  * LarkPal 会话 UUID v5 命名空间
860
861
  *
@@ -963,9 +964,9 @@ var SessionProcessManager = class {
963
964
  const t0 = Date.now();
964
965
  const prevLock = this.sessionLocks.get(sessionId);
965
966
  if (prevLock) {
966
- log$25.info("等待上一条消息处理完成", { sessionId });
967
+ log$26.info("等待上一条消息处理完成", { sessionId });
967
968
  await prevLock;
968
- log$25.info("[perf] 串行锁等待完成", {
969
+ log$26.info("[perf] 串行锁等待完成", {
969
970
  sessionId,
970
971
  waitMs: Date.now() - t0
971
972
  });
@@ -975,7 +976,7 @@ var SessionProcessManager = class {
975
976
  this.activeCallbacks.set(sessionId, callbacks);
976
977
  const existing = this.processes.get(sessionId);
977
978
  if (existing && existing.status === "running" && existing.useStreamInput) {
978
- log$25.info("向常驻进程发送消息", {
979
+ log$26.info("向常驻进程发送消息", {
979
980
  sessionId,
980
981
  promptLength: config.prompt.length,
981
982
  pid: existing.childProcess.pid,
@@ -984,14 +985,14 @@ var SessionProcessManager = class {
984
985
  this.sendMessage(sessionId, config.prompt);
985
986
  this.resetIdleTimer(sessionId);
986
987
  await completionPromise;
987
- log$25.info("[perf] executePrompt 完成(热进程)", {
988
+ log$26.info("[perf] executePrompt 完成(热进程)", {
988
989
  sessionId,
989
990
  totalMs: Date.now() - t0
990
991
  });
991
992
  return;
992
993
  }
993
994
  if (existing && (existing.status === "stopped" || existing.status === "crashed")) {
994
- log$25.info("清理已终止的旧进程,准备重启", {
995
+ log$26.info("清理已终止的旧进程,准备重启", {
995
996
  sessionId,
996
997
  oldStatus: existing.status
997
998
  });
@@ -999,18 +1000,18 @@ var SessionProcessManager = class {
999
1000
  this.sessionLocks.set(sessionId, completionPromise);
1000
1001
  }
1001
1002
  if (existing && existing.status === "running" && !existing.useStreamInput) {
1002
- log$25.warn("停止旧的单次进程后重新启动为常驻模式", { sessionId });
1003
+ log$26.warn("停止旧的单次进程后重新启动为常驻模式", { sessionId });
1003
1004
  await this.stopProcess(sessionId);
1004
1005
  this.sessionLocks.set(sessionId, completionPromise);
1005
1006
  }
1006
1007
  const tStart = Date.now();
1007
1008
  await this.startPersistentProcess(config);
1008
- log$25.info("[perf] 冷启动进程完成", {
1009
+ log$26.info("[perf] 冷启动进程完成", {
1009
1010
  sessionId,
1010
1011
  coldStartMs: Date.now() - tStart
1011
1012
  });
1012
1013
  await completionPromise;
1013
- log$25.info("[perf] executePrompt 完成(冷启动)", {
1014
+ log$26.info("[perf] executePrompt 完成(冷启动)", {
1014
1015
  sessionId,
1015
1016
  totalMs: Date.now() - t0
1016
1017
  });
@@ -1044,14 +1045,14 @@ var SessionProcessManager = class {
1044
1045
  const mcpConfigPath = await mergeAndWriteMcpConfig(config.userContext, sessionId);
1045
1046
  if (mcpConfigPath) {
1046
1047
  args.push("--mcp-config", mcpConfigPath);
1047
- log$25.info("MCP 配置已合并并注入到 CC 进程", {
1048
+ log$26.info("MCP 配置已合并并注入到 CC 进程", {
1048
1049
  sessionId,
1049
1050
  userId: config.userContext.userId,
1050
1051
  mcpConfigPath
1051
1052
  });
1052
1053
  }
1053
1054
  } catch (err) {
1054
- log$25.warn("MCP 配置合并失败,CC 进程将使用全局默认配置", {
1055
+ log$26.warn("MCP 配置合并失败,CC 进程将使用全局默认配置", {
1055
1056
  sessionId,
1056
1057
  error: err instanceof Error ? err.message : String(err)
1057
1058
  });
@@ -1076,12 +1077,12 @@ var SessionProcessManager = class {
1076
1077
  if (larkCred?.user_access_token && typeof larkCred.user_access_token === "string") processEnv.LARK_USER_ACCESS_TOKEN = larkCred.user_access_token;
1077
1078
  } else {
1078
1079
  delete processEnv.LARK_USER_ACCESS_TOKEN;
1079
- log$25.info("LARKPAL_DISABLE_USER_AUTH 已启用,跳过飞书 user_access_token 注入", {
1080
+ log$26.info("LARKPAL_DISABLE_USER_AUTH 已启用,跳过飞书 user_access_token 注入", {
1080
1081
  sessionId,
1081
1082
  userId: userContext.userId
1082
1083
  });
1083
1084
  }
1084
- log$25.info("用户凭证已注入到 CC 进程环境", {
1085
+ log$26.info("用户凭证已注入到 CC 进程环境", {
1085
1086
  sessionId,
1086
1087
  userId: userContext.userId,
1087
1088
  credentialCount: Object.keys(credentialEnvVars).length,
@@ -1089,14 +1090,14 @@ var SessionProcessManager = class {
1089
1090
  disableUserAuth
1090
1091
  });
1091
1092
  } catch (err) {
1092
- log$25.warn("用户凭证加载失败,CC 进程将使用默认环境", {
1093
+ log$26.warn("用户凭证加载失败,CC 进程将使用默认环境", {
1093
1094
  sessionId,
1094
1095
  userId: userContext.userId,
1095
1096
  error: err instanceof Error ? err.message : String(err)
1096
1097
  });
1097
1098
  }
1098
1099
  }
1099
- log$25.info("启动常驻 CC 进程", {
1100
+ log$26.info("启动常驻 CC 进程", {
1100
1101
  sessionId,
1101
1102
  claudeSessionId,
1102
1103
  isResuming,
@@ -1136,7 +1137,7 @@ var SessionProcessManager = class {
1136
1137
  if (pending) {
1137
1138
  clearTimeout(pending.timer);
1138
1139
  this.pendingControlRequests.delete(msg.response.request_id);
1139
- log$25.info("收到 control_response", {
1140
+ log$26.info("收到 control_response", {
1140
1141
  sessionId,
1141
1142
  requestId: msg.response.request_id,
1142
1143
  subtype: msg.response.subtype,
@@ -1153,7 +1154,7 @@ var SessionProcessManager = class {
1153
1154
  if (child.stderr) createInterface({ input: child.stderr }).on("line", (line) => {
1154
1155
  if (line.includes("no stdin data received")) return;
1155
1156
  if (line.toLowerCase().includes("already in use")) stderrSessionInUse = true;
1156
- log$25.warn("CC 进程 stderr", {
1157
+ log$26.warn("CC 进程 stderr", {
1157
1158
  sessionId,
1158
1159
  line: line.slice(0, 500)
1159
1160
  });
@@ -1162,7 +1163,7 @@ var SessionProcessManager = class {
1162
1163
  const exitCode = code ?? -1;
1163
1164
  const exitSignal = signal ?? "none";
1164
1165
  if (exitCode === 1 && stderrSessionInUse && !isResuming) {
1165
- log$25.info("Session ID 已被占用,准备使用 --resume 模式重试", {
1166
+ log$26.info("Session ID 已被占用,准备使用 --resume 模式重试", {
1166
1167
  sessionId,
1167
1168
  claudeSessionId
1168
1169
  });
@@ -1175,21 +1176,21 @@ var SessionProcessManager = class {
1175
1176
  });
1176
1177
  return;
1177
1178
  } catch (retryErr) {
1178
- log$25.error("Session ID 重试失败", {
1179
+ log$26.error("Session ID 重试失败", {
1179
1180
  sessionId,
1180
1181
  error: retryErr instanceof Error ? retryErr.message : String(retryErr)
1181
1182
  });
1182
1183
  }
1183
1184
  }
1184
1185
  if (exitCode === 0) {
1185
- log$25.info("CC 常驻进程正常退出", {
1186
+ log$26.info("CC 常驻进程正常退出", {
1186
1187
  sessionId,
1187
1188
  exitCode,
1188
1189
  signal: exitSignal
1189
1190
  });
1190
1191
  ccProcess.status = "stopped";
1191
1192
  } else {
1192
- log$25.error("CC 常驻进程异常退出", {
1193
+ log$26.error("CC 常驻进程异常退出", {
1193
1194
  sessionId,
1194
1195
  exitCode,
1195
1196
  signal: exitSignal
@@ -1201,7 +1202,7 @@ var SessionProcessManager = class {
1201
1202
  this.cleanup(sessionId);
1202
1203
  });
1203
1204
  child.on("error", (err) => {
1204
- log$25.error("CC 常驻进程启动失败", {
1205
+ log$26.error("CC 常驻进程启动失败", {
1205
1206
  sessionId,
1206
1207
  error: err.message
1207
1208
  });
@@ -1211,7 +1212,7 @@ var SessionProcessManager = class {
1211
1212
  this.cleanup(sessionId);
1212
1213
  });
1213
1214
  ccProcess.status = "running";
1214
- log$25.info("CC 常驻进程已启动", {
1215
+ log$26.info("CC 常驻进程已启动", {
1215
1216
  sessionId,
1216
1217
  pid: child.pid,
1217
1218
  isResuming
@@ -1239,7 +1240,7 @@ var SessionProcessManager = class {
1239
1240
  parser.on("textDelta", (text) => {
1240
1241
  if (!firstTokenLogged) {
1241
1242
  firstTokenLogged = true;
1242
- log$25.info("[perf] 首 token (text)", {
1243
+ log$26.info("[perf] 首 token (text)", {
1243
1244
  sessionId,
1244
1245
  ttftMs: Date.now() - messageSentAt
1245
1246
  });
@@ -1249,7 +1250,7 @@ var SessionProcessManager = class {
1249
1250
  parser.on("thinkingDelta", (text) => {
1250
1251
  if (!firstTokenLogged) {
1251
1252
  firstTokenLogged = true;
1252
- log$25.info("[perf] 首 token (thinking)", {
1253
+ log$26.info("[perf] 首 token (thinking)", {
1253
1254
  sessionId,
1254
1255
  ttftMs: Date.now() - messageSentAt
1255
1256
  });
@@ -1266,7 +1267,7 @@ var SessionProcessManager = class {
1266
1267
  getCallbacks()?.onToolProgress?.(toolName, elapsedSeconds);
1267
1268
  });
1268
1269
  parser.on("turnEnd", (stopReason) => {
1269
- log$25.info("[perf] turnEnd", {
1270
+ log$26.info("[perf] turnEnd", {
1270
1271
  sessionId,
1271
1272
  stopReason,
1272
1273
  sinceMessageMs: Date.now() - messageSentAt
@@ -1274,7 +1275,7 @@ var SessionProcessManager = class {
1274
1275
  getCallbacks()?.onTurnEnd?.(stopReason);
1275
1276
  });
1276
1277
  parser.on("result", (result) => {
1277
- log$25.info("[perf] result 事件", {
1278
+ log$26.info("[perf] result 事件", {
1278
1279
  sessionId,
1279
1280
  sinceMessageMs: Date.now() - messageSentAt
1280
1281
  });
@@ -1282,7 +1283,7 @@ var SessionProcessManager = class {
1282
1283
  this.resolveMessageCompletion(sessionId);
1283
1284
  });
1284
1285
  parser.on("parseError", (error, rawLine) => {
1285
- log$25.warn("流解析错误", {
1286
+ log$26.warn("流解析错误", {
1286
1287
  sessionId,
1287
1288
  error: error.message,
1288
1289
  rawLine: rawLine.slice(0, 200)
@@ -1298,7 +1299,7 @@ var SessionProcessManager = class {
1298
1299
  sendMessage(sessionId, message) {
1299
1300
  const proc = this.processes.get(sessionId);
1300
1301
  if (!proc || !proc.childProcess.stdin) {
1301
- log$25.error("无法发送消息:进程不存在或 stdin 不可用", { sessionId });
1302
+ log$26.error("无法发送消息:进程不存在或 stdin 不可用", { sessionId });
1302
1303
  return;
1303
1304
  }
1304
1305
  proc._resetPerfTimer?.();
@@ -1314,7 +1315,7 @@ var SessionProcessManager = class {
1314
1315
  uuid: userMessageId
1315
1316
  });
1316
1317
  const messageLength = typeof message === "string" ? message.length : message.length;
1317
- log$25.info("通过 stdin 发送用户消息", {
1318
+ log$26.info("通过 stdin 发送用户消息", {
1318
1319
  sessionId,
1319
1320
  messageLength,
1320
1321
  isMultimodal: Array.isArray(message),
@@ -1323,7 +1324,7 @@ var SessionProcessManager = class {
1323
1324
  });
1324
1325
  proc.childProcess.stdin.write(payload + "\n", (err) => {
1325
1326
  if (err) {
1326
- log$25.error("stdin 写入失败", {
1327
+ log$26.error("stdin 写入失败", {
1327
1328
  sessionId,
1328
1329
  error: err.message
1329
1330
  });
@@ -1353,7 +1354,7 @@ var SessionProcessManager = class {
1353
1354
  request_id: requestId,
1354
1355
  request
1355
1356
  });
1356
- log$25.info("发送控制请求", {
1357
+ log$26.info("发送控制请求", {
1357
1358
  sessionId,
1358
1359
  subtype,
1359
1360
  requestId,
@@ -1405,16 +1406,16 @@ var SessionProcessManager = class {
1405
1406
  async ensureProcessForRewind(sessionId) {
1406
1407
  const existing = this.processes.get(sessionId);
1407
1408
  if (existing && existing.status === "running") {
1408
- log$25.info("ensureProcessForRewind: 进程已在运行中,直接使用", { sessionId });
1409
+ log$26.info("ensureProcessForRewind: 进程已在运行中,直接使用", { sessionId });
1409
1410
  return existing;
1410
1411
  }
1411
1412
  const cwd = this.sessionCwdCache.get(sessionId);
1412
1413
  if (!cwd) {
1413
- log$25.warn("ensureProcessForRewind: 无法获取会话 cwd", { sessionId });
1414
+ log$26.warn("ensureProcessForRewind: 无法获取会话 cwd", { sessionId });
1414
1415
  return;
1415
1416
  }
1416
1417
  if (existing) this.cleanup(sessionId);
1417
- log$25.info("ensureProcessForRewind: 启动临时进程用于 rewind", {
1418
+ log$26.info("ensureProcessForRewind: 启动临时进程用于 rewind", {
1418
1419
  sessionId,
1419
1420
  cwd
1420
1421
  });
@@ -1425,7 +1426,7 @@ var SessionProcessManager = class {
1425
1426
  }, { skipInitialMessage: true });
1426
1427
  const proc = this.processes.get(sessionId);
1427
1428
  if (!proc || proc.status !== "running") {
1428
- log$25.warn("ensureProcessForRewind: 进程启动失败", {
1429
+ log$26.warn("ensureProcessForRewind: 进程启动失败", {
1429
1430
  sessionId,
1430
1431
  status: proc?.status
1431
1432
  });
@@ -1447,11 +1448,11 @@ var SessionProcessManager = class {
1447
1448
  async stopProcess(sessionId) {
1448
1449
  const proc = this.processes.get(sessionId);
1449
1450
  if (!proc) {
1450
- log$25.warn("尝试停止不存在的进程", { sessionId });
1451
+ log$26.warn("尝试停止不存在的进程", { sessionId });
1451
1452
  return;
1452
1453
  }
1453
1454
  if (proc.status === "stopped" || proc.status === "crashed") {
1454
- log$25.info("进程已经停止,直接清理", {
1455
+ log$26.info("进程已经停止,直接清理", {
1455
1456
  sessionId,
1456
1457
  status: proc.status
1457
1458
  });
@@ -1459,7 +1460,7 @@ var SessionProcessManager = class {
1459
1460
  return;
1460
1461
  }
1461
1462
  proc.status = "stopping";
1462
- log$25.info("正在停止 CC 常驻进程", {
1463
+ log$26.info("正在停止 CC 常驻进程", {
1463
1464
  sessionId,
1464
1465
  pid: proc.childProcess.pid
1465
1466
  });
@@ -1475,7 +1476,7 @@ var SessionProcessManager = class {
1475
1476
  const child = proc.childProcess;
1476
1477
  const forceKillTimer = setTimeout(() => {
1477
1478
  if (!child.killed) {
1478
- log$25.warn("CC 常驻进程未在规定时间内退出,强制终止", { sessionId });
1479
+ log$26.warn("CC 常驻进程未在规定时间内退出,强制终止", { sessionId });
1479
1480
  child.kill("SIGKILL");
1480
1481
  }
1481
1482
  }, GRACEFUL_SHUTDOWN_MS);
@@ -1495,9 +1496,9 @@ var SessionProcessManager = class {
1495
1496
  async stopAll() {
1496
1497
  const sessionIds = Array.from(this.processes.keys());
1497
1498
  if (sessionIds.length === 0) return;
1498
- log$25.info("正在停止所有 CC 常驻进程", { count: sessionIds.length });
1499
+ log$26.info("正在停止所有 CC 常驻进程", { count: sessionIds.length });
1499
1500
  await Promise.all(sessionIds.map((id) => this.stopProcess(id)));
1500
- log$25.info("所有 CC 常驻进程已停止");
1501
+ log$26.info("所有 CC 常驻进程已停止");
1501
1502
  }
1502
1503
  /**
1503
1504
  * 重置空闲计时器
@@ -1507,7 +1508,7 @@ var SessionProcessManager = class {
1507
1508
  if (existing) clearTimeout(existing);
1508
1509
  if (IDLE_TIMEOUT_MS <= 0) return;
1509
1510
  const timer = setTimeout(() => {
1510
- log$25.info("CC 常驻进程空闲超时,自动停止", {
1511
+ log$26.info("CC 常驻进程空闲超时,自动停止", {
1511
1512
  sessionId,
1512
1513
  timeoutMs: IDLE_TIMEOUT_MS
1513
1514
  });
@@ -1535,7 +1536,7 @@ var SessionProcessManager = class {
1535
1536
  this.idleTimers.delete(sessionId);
1536
1537
  }
1537
1538
  cleanupMcpConfig(sessionId);
1538
- log$25.debug("已清理常驻进程状态", { sessionId });
1539
+ log$26.debug("已清理常驻进程状态", { sessionId });
1539
1540
  }
1540
1541
  /**
1541
1542
  * 将 cwd 编码为 CC 的 projects 子目录名
@@ -1564,7 +1565,7 @@ var SessionProcessManager = class {
1564
1565
  const cwdEncoded = this.encodeCwdForCC(cwd);
1565
1566
  const sessionFile = join(homedir(), ".claude", "projects", cwdEncoded, `${claudeSessionId}.jsonl`);
1566
1567
  const exists = existsSync(sessionFile);
1567
- log$25.debug("检查 CC session 文件", {
1568
+ log$26.debug("检查 CC session 文件", {
1568
1569
  cwd,
1569
1570
  cwdEncoded,
1570
1571
  claudeSessionId,
@@ -1573,14 +1574,14 @@ var SessionProcessManager = class {
1573
1574
  });
1574
1575
  return exists;
1575
1576
  } catch (err) {
1576
- log$25.warn("检查 CC session 文件失败,默认为首次创建", { error: err instanceof Error ? err.message : String(err) });
1577
+ log$26.warn("检查 CC session 文件失败,默认为首次创建", { error: err instanceof Error ? err.message : String(err) });
1577
1578
  return false;
1578
1579
  }
1579
1580
  }
1580
1581
  };
1581
1582
  //#endregion
1582
1583
  //#region src/runtime/claude-code-adapter.ts
1583
- const log$24 = larkLogger("runtime/claude-code-adapter");
1584
+ const log$25 = larkLogger("runtime/claude-code-adapter");
1584
1585
  var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1585
1586
  name = "claude-code";
1586
1587
  processManager;
@@ -1595,7 +1596,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1595
1596
  constructor() {
1596
1597
  this.processManager = new SessionProcessManager();
1597
1598
  ClaudeCodeAdapter._instance = this;
1598
- log$24.info("ClaudeCodeAdapter 初始化完成");
1599
+ log$25.info("ClaudeCodeAdapter 初始化完成");
1599
1600
  }
1600
1601
  /**
1601
1602
  * 获取底层 SessionProcessManager 实例
@@ -1607,7 +1608,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1607
1608
  return this.processManager;
1608
1609
  }
1609
1610
  async executePrompt(config, callbacks) {
1610
- log$24.info("executePrompt via ClaudeCodeAdapter", {
1611
+ log$25.info("executePrompt via ClaudeCodeAdapter", {
1611
1612
  sessionId: config.sessionId,
1612
1613
  cwd: config.cwd,
1613
1614
  promptLength: config.prompt.length,
@@ -1616,11 +1617,11 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1616
1617
  await this.processManager.executePrompt(config, callbacks);
1617
1618
  }
1618
1619
  async stopProcess(sessionId) {
1619
- log$24.info("stopProcess via ClaudeCodeAdapter", { sessionId });
1620
+ log$25.info("stopProcess via ClaudeCodeAdapter", { sessionId });
1620
1621
  await this.processManager.stopProcess(sessionId);
1621
1622
  }
1622
1623
  async stopAll() {
1623
- log$24.info("stopAll via ClaudeCodeAdapter");
1624
+ log$25.info("stopAll via ClaudeCodeAdapter");
1624
1625
  await this.processManager.stopAll();
1625
1626
  }
1626
1627
  getProcessInfo(sessionId) {
@@ -1657,7 +1658,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1657
1658
  * 群聊 → /workspace/chats/group_{chat_id}/
1658
1659
  * 话题 → /workspace/chats/group_{chat_id}/topics/{thread_id}/
1659
1660
  */
1660
- const logger$6 = larkLogger("routing/session-router");
1661
+ const logger$7 = larkLogger("routing/session-router");
1661
1662
  const SESSION_OUTPUT_RULES = [
1662
1663
  `## 内容输出优先级`,
1663
1664
  "",
@@ -1696,7 +1697,7 @@ var SessionRouter = class {
1696
1697
  cwd: path.join(this.workspaceRoot, "chats", `p2p_${safeId}`),
1697
1698
  type: "p2p"
1698
1699
  };
1699
- logger$6.debug("解析私聊路由", {
1700
+ logger$7.debug("解析私聊路由", {
1700
1701
  chatId,
1701
1702
  userId: safeId,
1702
1703
  sessionId: route.sessionId,
@@ -1712,7 +1713,7 @@ var SessionRouter = class {
1712
1713
  cwd: path.join(groupCwd, "topics", threadId),
1713
1714
  type: "topic"
1714
1715
  };
1715
- logger$6.debug("解析话题路由", {
1716
+ logger$7.debug("解析话题路由", {
1716
1717
  chatId,
1717
1718
  threadId,
1718
1719
  sessionId: route.sessionId,
@@ -1725,7 +1726,7 @@ var SessionRouter = class {
1725
1726
  cwd: groupCwd,
1726
1727
  type: "group"
1727
1728
  };
1728
- logger$6.debug("解析群聊路由", {
1729
+ logger$7.debug("解析群聊路由", {
1729
1730
  chatId,
1730
1731
  sessionId: route.sessionId,
1731
1732
  cwd: route.cwd
@@ -1739,7 +1740,7 @@ var SessionRouter = class {
1739
1740
  * 此方法是幂等的,多次调用不会报错也不会覆盖已有文件。
1740
1741
  */
1741
1742
  async ensureSessionDirectory(route) {
1742
- logger$6.info("确保会话目录存在", {
1743
+ logger$7.info("确保会话目录存在", {
1743
1744
  sessionId: route.sessionId,
1744
1745
  cwd: route.cwd
1745
1746
  });
@@ -1749,7 +1750,7 @@ var SessionRouter = class {
1749
1750
  const claudeMdPath = path.join(route.cwd, "CLAUDE.md");
1750
1751
  if (!existsSync$1(claudeMdPath)) {
1751
1752
  await writeFile$1(claudeMdPath, this.generateClaudeMd(route), "utf-8");
1752
- logger$6.info("创建会话级 CLAUDE.md", {
1753
+ logger$7.info("创建会话级 CLAUDE.md", {
1753
1754
  path: claudeMdPath,
1754
1755
  type: route.type
1755
1756
  });
@@ -1813,7 +1814,7 @@ var SessionRouter = class {
1813
1814
  *
1814
1815
  * 内部通过 taskStore 维护所有任务的生命周期状态。
1815
1816
  */
1816
- const logger$5 = larkLogger("gateway/execute");
1817
+ const logger$6 = larkLogger("gateway/execute");
1817
1818
  /** 全局任务存储:taskId → TaskInfo */
1818
1819
  const taskStore = /* @__PURE__ */ new Map();
1819
1820
  /**
@@ -1847,7 +1848,7 @@ function createExecuteRouter(processManager) {
1847
1848
  */
1848
1849
  async function handleExecute(req, res, processManager) {
1849
1850
  const body = req.body;
1850
- logger$5.info("收到执行请求", {
1851
+ logger$6.info("收到执行请求", {
1851
1852
  session_id: body.session_id,
1852
1853
  cwd: body.cwd,
1853
1854
  prompt: body.prompt?.slice(0, 200),
@@ -1856,7 +1857,7 @@ async function handleExecute(req, res, processManager) {
1856
1857
  max_budget_usd: body.max_budget_usd
1857
1858
  });
1858
1859
  if (!body.session_id || !body.cwd || !body.prompt) {
1859
- logger$5.warn("执行请求参数缺失", {
1860
+ logger$6.warn("执行请求参数缺失", {
1860
1861
  hasSessionId: !!body.session_id,
1861
1862
  hasCwd: !!body.cwd,
1862
1863
  hasPrompt: !!body.prompt
@@ -1876,7 +1877,7 @@ async function handleExecute(req, res, processManager) {
1876
1877
  createdAt: /* @__PURE__ */ new Date()
1877
1878
  };
1878
1879
  taskStore.set(taskId, taskInfo);
1879
- logger$5.info("任务已创建", {
1880
+ logger$6.info("任务已创建", {
1880
1881
  taskId,
1881
1882
  sessionId: body.session_id,
1882
1883
  mode
@@ -1892,7 +1893,7 @@ async function handleExecute(req, res, processManager) {
1892
1893
  const callbacks = createTaskCallbacks(taskId);
1893
1894
  processManager.executePrompt(config, callbacks).catch((err) => {
1894
1895
  const errorMsg = err instanceof Error ? err.message : String(err);
1895
- logger$5.error("异步任务执行异常", {
1896
+ logger$6.error("异步任务执行异常", {
1896
1897
  taskId,
1897
1898
  error: errorMsg
1898
1899
  });
@@ -1900,7 +1901,7 @@ async function handleExecute(req, res, processManager) {
1900
1901
  taskInfo.error = errorMsg;
1901
1902
  taskInfo.completedAt = /* @__PURE__ */ new Date();
1902
1903
  });
1903
- logger$5.info("异步任务已启动,立即返回", { taskId });
1904
+ logger$6.info("异步任务已启动,立即返回", { taskId });
1904
1905
  res.status(202).json({
1905
1906
  task_id: taskId,
1906
1907
  status: "running"
@@ -1908,8 +1909,8 @@ async function handleExecute(req, res, processManager) {
1908
1909
  return;
1909
1910
  }
1910
1911
  try {
1911
- const result = await executeAndWaitResult(taskId, config, processManager);
1912
- logger$5.info("同步任务执行完成", {
1912
+ const result = await executeAndWaitResult$1(taskId, config, processManager);
1913
+ logger$6.info("同步任务执行完成", {
1913
1914
  taskId,
1914
1915
  resultSubtype: result?.subtype
1915
1916
  });
@@ -1920,7 +1921,7 @@ async function handleExecute(req, res, processManager) {
1920
1921
  });
1921
1922
  } catch (err) {
1922
1923
  const errorMsg = err instanceof Error ? err.message : String(err);
1923
- logger$5.error("同步任务执行失败", {
1924
+ logger$6.error("同步任务执行失败", {
1924
1925
  taskId,
1925
1926
  error: errorMsg
1926
1927
  });
@@ -1938,12 +1939,12 @@ async function handleExecute(req, res, processManager) {
1938
1939
  */
1939
1940
  async function handleBatchExecute(req, res, processManager) {
1940
1941
  const body = req.body;
1941
- logger$5.info("收到批量执行请求", {
1942
+ logger$6.info("收到批量执行请求", {
1942
1943
  taskCount: body.tasks?.length,
1943
1944
  concurrency: body.concurrency
1944
1945
  });
1945
1946
  if (!body.tasks || !Array.isArray(body.tasks) || body.tasks.length === 0) {
1946
- logger$5.warn("批量执行请求参数缺失: tasks 为空或格式错误");
1947
+ logger$6.warn("批量执行请求参数缺失: tasks 为空或格式错误");
1947
1948
  res.status(400).json({
1948
1949
  error: "Bad Request",
1949
1950
  message: "Missing or empty required field: tasks"
@@ -1953,7 +1954,7 @@ async function handleBatchExecute(req, res, processManager) {
1953
1954
  for (let i = 0; i < body.tasks.length; i++) {
1954
1955
  const task = body.tasks[i];
1955
1956
  if (!task?.session_id || !task?.cwd || !task?.prompt) {
1956
- logger$5.warn("批量执行子任务参数缺失", { index: i });
1957
+ logger$6.warn("批量执行子任务参数缺失", { index: i });
1957
1958
  res.status(400).json({
1958
1959
  error: "Bad Request",
1959
1960
  message: `Task at index ${i} is missing required fields: session_id, cwd, prompt`
@@ -1984,7 +1985,7 @@ async function handleBatchExecute(req, res, processManager) {
1984
1985
  }
1985
1986
  };
1986
1987
  });
1987
- logger$5.info("批量任务已创建", {
1988
+ logger$6.info("批量任务已创建", {
1988
1989
  batchId,
1989
1990
  taskIds,
1990
1991
  concurrency
@@ -2001,17 +2002,17 @@ async function handleBatchExecute(req, res, processManager) {
2001
2002
  */
2002
2003
  function handleGetTask(req, res) {
2003
2004
  const taskId = req.params.taskId;
2004
- logger$5.info("查询任务状态", { taskId });
2005
+ logger$6.info("查询任务状态", { taskId });
2005
2006
  const taskInfo = taskStore.get(taskId);
2006
2007
  if (!taskInfo) {
2007
- logger$5.warn("任务不存在", { taskId });
2008
+ logger$6.warn("任务不存在", { taskId });
2008
2009
  res.status(404).json({
2009
2010
  error: "Not Found",
2010
2011
  message: `Task ${taskId} not found`
2011
2012
  });
2012
2013
  return;
2013
2014
  }
2014
- logger$5.info("返回任务状态", {
2015
+ logger$6.info("返回任务状态", {
2015
2016
  taskId,
2016
2017
  status: taskInfo.status
2017
2018
  });
@@ -2028,10 +2029,10 @@ function handleGetTask(req, res) {
2028
2029
  */
2029
2030
  async function handleCancelTask(req, res, processManager) {
2030
2031
  const taskId = req.params.taskId;
2031
- logger$5.info("收到取消任务请求", { taskId });
2032
+ logger$6.info("收到取消任务请求", { taskId });
2032
2033
  const taskInfo = taskStore.get(taskId);
2033
2034
  if (!taskInfo) {
2034
- logger$5.warn("取消失败:任务不存在", { taskId });
2035
+ logger$6.warn("取消失败:任务不存在", { taskId });
2035
2036
  res.status(404).json({
2036
2037
  error: "Not Found",
2037
2038
  message: `Task ${taskId} not found`
@@ -2039,7 +2040,7 @@ async function handleCancelTask(req, res, processManager) {
2039
2040
  return;
2040
2041
  }
2041
2042
  if (taskInfo.status !== "running") {
2042
- logger$5.warn("取消失败:任务不在运行状态", {
2043
+ logger$6.warn("取消失败:任务不在运行状态", {
2043
2044
  taskId,
2044
2045
  status: taskInfo.status
2045
2046
  });
@@ -2053,7 +2054,7 @@ async function handleCancelTask(req, res, processManager) {
2053
2054
  await processManager.stopProcess(taskInfo.sessionId);
2054
2055
  taskInfo.status = "cancelled";
2055
2056
  taskInfo.completedAt = /* @__PURE__ */ new Date();
2056
- logger$5.info("任务已取消", {
2057
+ logger$6.info("任务已取消", {
2057
2058
  taskId,
2058
2059
  sessionId: taskInfo.sessionId
2059
2060
  });
@@ -2063,7 +2064,7 @@ async function handleCancelTask(req, res, processManager) {
2063
2064
  });
2064
2065
  } catch (err) {
2065
2066
  const errorMsg = err instanceof Error ? err.message : String(err);
2066
- logger$5.error("取消任务时发生错误", {
2067
+ logger$6.error("取消任务时发生错误", {
2067
2068
  taskId,
2068
2069
  error: errorMsg
2069
2070
  });
@@ -2086,7 +2087,7 @@ function createTaskCallbacks(taskId) {
2086
2087
  taskInfo.result = result;
2087
2088
  taskInfo.status = result.isError ? "failed" : "completed";
2088
2089
  taskInfo.completedAt = /* @__PURE__ */ new Date();
2089
- logger$5.info("任务收到结果", {
2090
+ logger$6.info("任务收到结果", {
2090
2091
  taskId,
2091
2092
  status: taskInfo.status,
2092
2093
  subtype: result.subtype,
@@ -2100,7 +2101,7 @@ function createTaskCallbacks(taskId) {
2100
2101
  taskInfo.status = "failed";
2101
2102
  taskInfo.error = error.message;
2102
2103
  taskInfo.completedAt = /* @__PURE__ */ new Date();
2103
- logger$5.error("任务执行出错", {
2104
+ logger$6.error("任务执行出错", {
2104
2105
  taskId,
2105
2106
  error: error.message
2106
2107
  });
@@ -2112,7 +2113,7 @@ function createTaskCallbacks(taskId) {
2112
2113
  *
2113
2114
  * 通过 Promise 包装回调机制,在收到 onResult 或 onError 时 resolve/reject。
2114
2115
  */
2115
- function executeAndWaitResult(taskId, config, processManager) {
2116
+ function executeAndWaitResult$1(taskId, config, processManager) {
2116
2117
  return new Promise((resolve, reject) => {
2117
2118
  processManager.executePrompt(config, {
2118
2119
  onResult: (result) => {
@@ -2122,7 +2123,7 @@ function executeAndWaitResult(taskId, config, processManager) {
2122
2123
  taskInfo.status = result.isError ? "failed" : "completed";
2123
2124
  taskInfo.completedAt = /* @__PURE__ */ new Date();
2124
2125
  }
2125
- logger$5.info("同步任务收到结果", {
2126
+ logger$6.info("同步任务收到结果", {
2126
2127
  taskId,
2127
2128
  subtype: result.subtype,
2128
2129
  durationMs: result.durationMs,
@@ -2137,7 +2138,7 @@ function executeAndWaitResult(taskId, config, processManager) {
2137
2138
  taskInfo.error = error.message;
2138
2139
  taskInfo.completedAt = /* @__PURE__ */ new Date();
2139
2140
  }
2140
- logger$5.error("同步任务执行出错", {
2141
+ logger$6.error("同步任务执行出错", {
2141
2142
  taskId,
2142
2143
  error: error.message
2143
2144
  });
@@ -2145,7 +2146,7 @@ function executeAndWaitResult(taskId, config, processManager) {
2145
2146
  }
2146
2147
  }).catch((err) => {
2147
2148
  const errorMsg = err instanceof Error ? err.message : String(err);
2148
- logger$5.error("同步任务 executePrompt 调用失败", {
2149
+ logger$6.error("同步任务 executePrompt 调用失败", {
2149
2150
  taskId,
2150
2151
  error: errorMsg
2151
2152
  });
@@ -2165,7 +2166,7 @@ function executeAndWaitResult(taskId, config, processManager) {
2165
2166
  * 使用简单的信号量模式控制并发数,逐个启动任务直到所有任务完成。
2166
2167
  */
2167
2168
  async function runBatchTasks(taskConfigs, concurrency, processManager) {
2168
- logger$5.info("开始批量任务执行", {
2169
+ logger$6.info("开始批量任务执行", {
2169
2170
  totalTasks: taskConfigs.length,
2170
2171
  concurrency
2171
2172
  });
@@ -2177,7 +2178,7 @@ async function runBatchTasks(taskConfigs, concurrency, processManager) {
2177
2178
  await processManager.executePrompt(config, callbacks);
2178
2179
  } catch (err) {
2179
2180
  const errorMsg = err instanceof Error ? err.message : String(err);
2180
- logger$5.error("批量子任务执行异常", {
2181
+ logger$6.error("批量子任务执行异常", {
2181
2182
  taskId,
2182
2183
  error: errorMsg
2183
2184
  });
@@ -2194,7 +2195,223 @@ async function runBatchTasks(taskConfigs, concurrency, processManager) {
2194
2195
  if (executing.size >= concurrency) await Promise.race(executing);
2195
2196
  }
2196
2197
  await Promise.all(executing);
2197
- logger$5.info("批量任务全部完成", { totalTasks: taskConfigs.length });
2198
+ logger$6.info("批量任务全部完成", { totalTasks: taskConfigs.length });
2199
+ }
2200
+ //#endregion
2201
+ //#region src/gateway/agent-completion-handler.ts
2202
+ /**
2203
+ * 通用 AI 补全路由 — Agent Gateway 统一能力接口
2204
+ *
2205
+ * 提供 POST /api/agent/completion 接口,接收任意外部服务的 AI 任务请求。
2206
+ * 内部通过 CC(Claude Code)RuntimeAdapter 执行。
2207
+ *
2208
+ * 本模块是通用的 AI 能力网关接口,不包含任何特定业务逻辑。
2209
+ * 各接入方(如 rd-assistant-api 等)自行构造 prompt 和解析结果。
2210
+ *
2211
+ * 支持两种模式:
2212
+ * - 同步模式(默认):等待 CC 完成后直接返回结果文本
2213
+ * - 异步模式:立即返回 accepted,完成后通过 X-Callback-Url 回调通知
2214
+ *
2215
+ * 鉴权:通过 X-Internal-Secret 头验证服务间调用身份
2216
+ */
2217
+ const logger$5 = larkLogger("gateway/agent-completion");
2218
+ /** 内部通信密钥,从环境变量读取 */
2219
+ const INTERNAL_SECRET = process.env.LARKPAL_API_SECRET || "dev-internal-secret";
2220
+ /** CC 执行时的工作目录(使用临时目录) */
2221
+ const COMPLETION_CWD = process.env.LARKPAL_COMPLETION_CWD || "/tmp/larkpal-completion";
2222
+ /**
2223
+ * 创建通用 AI 补全路由
2224
+ *
2225
+ * @param runtimeAdapter - CC 运行时适配器实例
2226
+ */
2227
+ function createAgentCompletionRouter(runtimeAdapter) {
2228
+ const router = Router();
2229
+ router.post("/api/agent/completion", (req, res) => {
2230
+ handleCompletion(req, res, runtimeAdapter);
2231
+ });
2232
+ return router;
2233
+ }
2234
+ async function handleCompletion(req, res, runtimeAdapter) {
2235
+ const secret = req.headers["x-internal-secret"];
2236
+ if (secret !== INTERNAL_SECRET) {
2237
+ logger$5.warn("Agent completion 鉴权失败", { providedSecret: secret?.slice(0, 8) + "..." });
2238
+ res.status(401).json({
2239
+ code: 1,
2240
+ message: "鉴权失败"
2241
+ });
2242
+ return;
2243
+ }
2244
+ const callbackUrl = req.headers["x-callback-url"];
2245
+ const body = req.body;
2246
+ if (!body.taskId || !body.prompt) {
2247
+ logger$5.warn("Agent completion 参数不完整", {
2248
+ hasTaskId: !!body.taskId,
2249
+ hasPrompt: !!body.prompt
2250
+ });
2251
+ res.status(400).json({
2252
+ code: 1,
2253
+ message: "缺少必要参数: taskId, prompt"
2254
+ });
2255
+ return;
2256
+ }
2257
+ logger$5.info("收到 Agent completion 请求", {
2258
+ taskId: body.taskId,
2259
+ task: body.task || "unknown",
2260
+ promptLength: body.prompt.length,
2261
+ fileCount: body.files?.length || 0,
2262
+ hasCallback: !!callbackUrl,
2263
+ maxTurns: body.options?.maxTurns,
2264
+ resultFileName: body.options?.resultFileName
2265
+ });
2266
+ try {
2267
+ const { mkdir, writeFile } = await import("node:fs/promises");
2268
+ const { join } = await import("node:path");
2269
+ const taskDir = join(COMPLETION_CWD, body.taskId);
2270
+ await mkdir(taskDir, { recursive: true });
2271
+ if (body.files && body.files.length > 0) for (const file of body.files) {
2272
+ const filePath = join(taskDir, file.name);
2273
+ const fileBuffer = Buffer.from(file.contentBase64, "base64");
2274
+ await writeFile(filePath, fileBuffer);
2275
+ logger$5.info("附件文件已写入", {
2276
+ filePath,
2277
+ sizeBytes: fileBuffer.length
2278
+ });
2279
+ }
2280
+ const sessionId = `completion-${body.taskId}`;
2281
+ const maxTurns = body.options?.maxTurns || 10;
2282
+ const resultFileName = body.options?.resultFileName;
2283
+ let finalResult = await executeAndWaitResult(runtimeAdapter, {
2284
+ sessionId,
2285
+ cwd: taskDir,
2286
+ prompt: body.prompt,
2287
+ maxTurns
2288
+ });
2289
+ if (resultFileName) try {
2290
+ const { readFile } = await import("node:fs/promises");
2291
+ const resultFilePath = join(taskDir, resultFileName);
2292
+ const fileContent = await readFile(resultFilePath, "utf-8");
2293
+ logger$5.info("从结果文件读取输出", {
2294
+ resultFilePath,
2295
+ contentLength: fileContent.length
2296
+ });
2297
+ finalResult = fileContent;
2298
+ } catch {
2299
+ logger$5.info("结果文件不存在,使用 CC 文本输出");
2300
+ }
2301
+ try {
2302
+ const { rm } = await import("node:fs/promises");
2303
+ await rm(taskDir, {
2304
+ recursive: true,
2305
+ force: true
2306
+ });
2307
+ } catch {}
2308
+ logger$5.info("Agent completion 完成", {
2309
+ taskId: body.taskId,
2310
+ resultLength: finalResult.length
2311
+ });
2312
+ if (callbackUrl) {
2313
+ res.status(200).json({
2314
+ code: 0,
2315
+ message: "accepted",
2316
+ mode: "async"
2317
+ });
2318
+ await sendCallback(callbackUrl, body.taskId, "success", finalResult);
2319
+ } else res.status(200).json({
2320
+ code: 0,
2321
+ message: "ok",
2322
+ mode: "sync",
2323
+ result: finalResult
2324
+ });
2325
+ } catch (err) {
2326
+ const errorMsg = err instanceof Error ? err.message : String(err);
2327
+ logger$5.error("Agent completion 执行失败", {
2328
+ taskId: body.taskId,
2329
+ error: errorMsg
2330
+ });
2331
+ if (callbackUrl) {
2332
+ res.status(200).json({
2333
+ code: 0,
2334
+ message: "accepted",
2335
+ mode: "async"
2336
+ });
2337
+ await sendCallback(callbackUrl, body.taskId, "failed", void 0, errorMsg);
2338
+ } else res.status(500).json({
2339
+ code: 1,
2340
+ message: `执行失败: ${errorMsg}`
2341
+ });
2342
+ }
2343
+ }
2344
+ /**
2345
+ * 调用 CC 执行 prompt 并等待完成
2346
+ */
2347
+ async function executeAndWaitResult(runtimeAdapter, config) {
2348
+ return new Promise((resolve, reject) => {
2349
+ let resultText = "";
2350
+ runtimeAdapter.executePrompt(config, {
2351
+ onTextDelta(text) {
2352
+ resultText += text;
2353
+ },
2354
+ onResult(result) {
2355
+ logger$5.info("CC 执行返回结果", {
2356
+ sessionId: config.sessionId,
2357
+ subtype: result.subtype,
2358
+ resultLength: resultText.length
2359
+ });
2360
+ resolve(resultText || result.result || "");
2361
+ },
2362
+ onError(error) {
2363
+ logger$5.error("CC 执行出错", {
2364
+ sessionId: config.sessionId,
2365
+ error: error.message
2366
+ });
2367
+ reject(error);
2368
+ }
2369
+ }).catch(reject);
2370
+ });
2371
+ }
2372
+ /**
2373
+ * 向调用方发送回调通知
2374
+ */
2375
+ async function sendCallback(callbackUrl, taskId, status, result, error) {
2376
+ try {
2377
+ logger$5.info("发送回调通知", {
2378
+ callbackUrl,
2379
+ taskId,
2380
+ status,
2381
+ resultLength: result?.length
2382
+ });
2383
+ const response = await fetch(callbackUrl, {
2384
+ method: "POST",
2385
+ headers: {
2386
+ "Content-Type": "application/json",
2387
+ "X-Internal-Secret": INTERNAL_SECRET
2388
+ },
2389
+ body: JSON.stringify({
2390
+ taskId,
2391
+ status,
2392
+ result,
2393
+ error
2394
+ })
2395
+ });
2396
+ if (!response.ok) {
2397
+ const errText = await response.text();
2398
+ logger$5.warn("回调通知失败", {
2399
+ callbackUrl,
2400
+ status: response.status,
2401
+ body: errText
2402
+ });
2403
+ } else logger$5.info("回调通知成功", {
2404
+ callbackUrl,
2405
+ taskId
2406
+ });
2407
+ } catch (err) {
2408
+ const errorMsg = err instanceof Error ? err.message : String(err);
2409
+ logger$5.error("回调通知异常", {
2410
+ callbackUrl,
2411
+ taskId,
2412
+ error: errorMsg
2413
+ });
2414
+ }
2198
2415
  }
2199
2416
  //#endregion
2200
2417
  //#region src/gateway/skills-handler.ts
@@ -2648,7 +2865,7 @@ function createHooksRouter() {
2648
2865
  * 提供 /api/scheduled-tasks 的 RESTful CRUD 接口。
2649
2866
  * 每个请求和响应都记录详细日志,便于后续错误排查。
2650
2867
  */
2651
- const log$23 = larkLogger("gateway/scheduler-handler");
2868
+ const log$24 = larkLogger("gateway/scheduler-handler");
2652
2869
  /**
2653
2870
  * 创建定时任务路由
2654
2871
  *
@@ -2659,16 +2876,16 @@ function createSchedulerRouter(taskManager) {
2659
2876
  const router = Router();
2660
2877
  router.get("/api/scheduled-tasks", (req, res) => {
2661
2878
  const requestId = req.headers["x-request-id"];
2662
- log$23.info("请求列出所有定时任务", { requestId });
2879
+ log$24.info("请求列出所有定时任务", { requestId });
2663
2880
  try {
2664
2881
  const tasks = taskManager.listTasks();
2665
- log$23.info("返回定时任务列表", {
2882
+ log$24.info("返回定时任务列表", {
2666
2883
  requestId,
2667
2884
  count: tasks.length
2668
2885
  });
2669
2886
  res.json({ tasks });
2670
2887
  } catch (err) {
2671
- log$23.error("列出定时任务失败", {
2888
+ log$24.error("列出定时任务失败", {
2672
2889
  requestId,
2673
2890
  error: String(err)
2674
2891
  });
@@ -2681,13 +2898,13 @@ function createSchedulerRouter(taskManager) {
2681
2898
  router.post("/api/scheduled-tasks", (req, res) => {
2682
2899
  const requestId = req.headers["x-request-id"];
2683
2900
  const body = req.body;
2684
- log$23.info("请求创建定时任务", {
2901
+ log$24.info("请求创建定时任务", {
2685
2902
  requestId,
2686
2903
  body: JSON.stringify(body)
2687
2904
  });
2688
2905
  const { name, cron: cronExpr, session_id, cwd, prompt, example_output, constraints } = body;
2689
2906
  if (!name || typeof name !== "string") {
2690
- log$23.warn("创建任务参数缺失: name", { requestId });
2907
+ log$24.warn("创建任务参数缺失: name", { requestId });
2691
2908
  res.status(400).json({
2692
2909
  error: "参数错误",
2693
2910
  detail: "name 为必填字符串"
@@ -2695,7 +2912,7 @@ function createSchedulerRouter(taskManager) {
2695
2912
  return;
2696
2913
  }
2697
2914
  if (!cronExpr || typeof cronExpr !== "string") {
2698
- log$23.warn("创建任务参数缺失: cron", { requestId });
2915
+ log$24.warn("创建任务参数缺失: cron", { requestId });
2699
2916
  res.status(400).json({
2700
2917
  error: "参数错误",
2701
2918
  detail: "cron 为必填字符串"
@@ -2703,7 +2920,7 @@ function createSchedulerRouter(taskManager) {
2703
2920
  return;
2704
2921
  }
2705
2922
  if (!session_id || typeof session_id !== "string") {
2706
- log$23.warn("创建任务参数缺失: session_id", { requestId });
2923
+ log$24.warn("创建任务参数缺失: session_id", { requestId });
2707
2924
  res.status(400).json({
2708
2925
  error: "参数错误",
2709
2926
  detail: "session_id 为必填字符串"
@@ -2711,7 +2928,7 @@ function createSchedulerRouter(taskManager) {
2711
2928
  return;
2712
2929
  }
2713
2930
  if (!cwd || typeof cwd !== "string") {
2714
- log$23.warn("创建任务参数缺失: cwd", { requestId });
2931
+ log$24.warn("创建任务参数缺失: cwd", { requestId });
2715
2932
  res.status(400).json({
2716
2933
  error: "参数错误",
2717
2934
  detail: "cwd 为必填字符串"
@@ -2719,7 +2936,7 @@ function createSchedulerRouter(taskManager) {
2719
2936
  return;
2720
2937
  }
2721
2938
  if (!prompt || typeof prompt !== "string") {
2722
- log$23.warn("创建任务参数缺失: prompt", { requestId });
2939
+ log$24.warn("创建任务参数缺失: prompt", { requestId });
2723
2940
  res.status(400).json({
2724
2941
  error: "参数错误",
2725
2942
  detail: "prompt 为必填字符串"
@@ -2727,7 +2944,7 @@ function createSchedulerRouter(taskManager) {
2727
2944
  return;
2728
2945
  }
2729
2946
  if (!cron.validate(cronExpr)) {
2730
- log$23.warn("cron 表达式不合法", {
2947
+ log$24.warn("cron 表达式不合法", {
2731
2948
  requestId,
2732
2949
  cron: cronExpr
2733
2950
  });
@@ -2748,7 +2965,7 @@ function createSchedulerRouter(taskManager) {
2748
2965
  constraints
2749
2966
  };
2750
2967
  const task = taskManager.createTask(params);
2751
- log$23.info("定时任务创建成功", {
2968
+ log$24.info("定时任务创建成功", {
2752
2969
  requestId,
2753
2970
  taskId: task.id,
2754
2971
  name: task.name,
@@ -2756,7 +2973,7 @@ function createSchedulerRouter(taskManager) {
2756
2973
  });
2757
2974
  res.status(201).json({ task });
2758
2975
  } catch (err) {
2759
- log$23.error("创建定时任务失败", {
2976
+ log$24.error("创建定时任务失败", {
2760
2977
  requestId,
2761
2978
  error: String(err)
2762
2979
  });
@@ -2770,13 +2987,13 @@ function createSchedulerRouter(taskManager) {
2770
2987
  const requestId = req.headers["x-request-id"];
2771
2988
  const { id } = req.params;
2772
2989
  const body = req.body;
2773
- log$23.info("请求更新定时任务", {
2990
+ log$24.info("请求更新定时任务", {
2774
2991
  requestId,
2775
2992
  taskId: id,
2776
2993
  body: JSON.stringify(body)
2777
2994
  });
2778
2995
  if (!taskManager.getTask(id)) {
2779
- log$23.warn("更新的任务不存在", {
2996
+ log$24.warn("更新的任务不存在", {
2780
2997
  requestId,
2781
2998
  taskId: id
2782
2999
  });
@@ -2788,7 +3005,7 @@ function createSchedulerRouter(taskManager) {
2788
3005
  }
2789
3006
  if (body.cron !== void 0) {
2790
3007
  if (typeof body.cron !== "string" || !cron.validate(body.cron)) {
2791
- log$23.warn("更新的 cron 表达式不合法", {
3008
+ log$24.warn("更新的 cron 表达式不合法", {
2792
3009
  requestId,
2793
3010
  taskId: id,
2794
3011
  cron: body.cron
@@ -2809,7 +3026,7 @@ function createSchedulerRouter(taskManager) {
2809
3026
  if (body.constraints !== void 0) updates.constraints = body.constraints;
2810
3027
  if (body.enabled !== void 0) updates.enabled = body.enabled;
2811
3028
  const task = taskManager.updateTask(id, updates);
2812
- log$23.info("定时任务更新成功", {
3029
+ log$24.info("定时任务更新成功", {
2813
3030
  requestId,
2814
3031
  taskId: task.id,
2815
3032
  name: task.name,
@@ -2817,7 +3034,7 @@ function createSchedulerRouter(taskManager) {
2817
3034
  });
2818
3035
  res.json({ task });
2819
3036
  } catch (err) {
2820
- log$23.error("更新定时任务失败", {
3037
+ log$24.error("更新定时任务失败", {
2821
3038
  requestId,
2822
3039
  taskId: id,
2823
3040
  error: String(err)
@@ -2831,12 +3048,12 @@ function createSchedulerRouter(taskManager) {
2831
3048
  router.delete("/api/scheduled-tasks/:id", (req, res) => {
2832
3049
  const requestId = req.headers["x-request-id"];
2833
3050
  const id = String(req.params.id);
2834
- log$23.info("请求删除定时任务", {
3051
+ log$24.info("请求删除定时任务", {
2835
3052
  requestId,
2836
3053
  taskId: id
2837
3054
  });
2838
3055
  if (!taskManager.getTask(id)) {
2839
- log$23.warn("删除的任务不存在", {
3056
+ log$24.warn("删除的任务不存在", {
2840
3057
  requestId,
2841
3058
  taskId: id
2842
3059
  });
@@ -2848,7 +3065,7 @@ function createSchedulerRouter(taskManager) {
2848
3065
  }
2849
3066
  try {
2850
3067
  const deleted = taskManager.deleteTask(id);
2851
- log$23.info("定时任务删除结果", {
3068
+ log$24.info("定时任务删除结果", {
2852
3069
  requestId,
2853
3070
  taskId: id,
2854
3071
  deleted
@@ -2858,7 +3075,7 @@ function createSchedulerRouter(taskManager) {
2858
3075
  deleted: true
2859
3076
  });
2860
3077
  } catch (err) {
2861
- log$23.error("删除定时任务失败", {
3078
+ log$24.error("删除定时任务失败", {
2862
3079
  requestId,
2863
3080
  taskId: id,
2864
3081
  error: String(err)
@@ -2888,7 +3105,7 @@ function createSchedulerRouter(taskManager) {
2888
3105
  * - GET /open-apis/application/v6/applications/me?lang=zh_cn(需要 application:application:self_manage 权限,返回完整信息)
2889
3106
  * 如果后者无权限则 fallback 到前者
2890
3107
  */
2891
- const log$22 = larkLogger("core/app-info-sync");
3108
+ const log$23 = larkLogger("core/app-info-sync");
2892
3109
  /**
2893
3110
  * 从飞书获取应用信息
2894
3111
  *
@@ -2899,7 +3116,7 @@ async function fetchAppInfo(credentials) {
2899
3116
  const { appId, appSecret } = credentials;
2900
3117
  const token = await getTenantAccessToken(appId, appSecret);
2901
3118
  if (!token) {
2902
- log$22.error("获取 tenant_access_token 失败,无法同步应用信息");
3119
+ log$23.error("获取 tenant_access_token 失败,无法同步应用信息");
2903
3120
  return null;
2904
3121
  }
2905
3122
  const fullInfo = await fetchFromApplicationApi(token);
@@ -2910,13 +3127,13 @@ async function fetchAppInfo(credentials) {
2910
3127
  async function fetchFromApplicationApi(token) {
2911
3128
  try {
2912
3129
  const data = await (await fetch("https://open.feishu.cn/open-apis/application/v6/applications/me?lang=zh_cn", { headers: { Authorization: `Bearer ${token}` } })).json();
2913
- log$22.info("application/v6 API 响应", {
3130
+ log$23.info("application/v6 API 响应", {
2914
3131
  code: data.code,
2915
3132
  msg: data.msg,
2916
3133
  hasApp: !!data.data?.app
2917
3134
  });
2918
3135
  if (data.code !== 0 || !data.data?.app) {
2919
- log$22.warn("application/v6 API 返回非零或无数据,将 fallback", {
3136
+ log$23.warn("application/v6 API 返回非零或无数据,将 fallback", {
2920
3137
  code: data.code,
2921
3138
  msg: data.msg
2922
3139
  });
@@ -2931,7 +3148,7 @@ async function fetchFromApplicationApi(token) {
2931
3148
  helpDocUrl: zhInfo?.help_use
2932
3149
  };
2933
3150
  } catch (err) {
2934
- log$22.warn("application/v6 API 请求异常", { error: err instanceof Error ? err.message : String(err) });
3151
+ log$23.warn("application/v6 API 请求异常", { error: err instanceof Error ? err.message : String(err) });
2935
3152
  return null;
2936
3153
  }
2937
3154
  }
@@ -2939,13 +3156,13 @@ async function fetchFromApplicationApi(token) {
2939
3156
  async function fetchFromBotApi(token) {
2940
3157
  try {
2941
3158
  const data = await (await fetch("https://open.feishu.cn/open-apis/bot/v3/info", { headers: { Authorization: `Bearer ${token}` } })).json();
2942
- log$22.info("bot/v3/info API 响应", {
3159
+ log$23.info("bot/v3/info API 响应", {
2943
3160
  code: data.code,
2944
3161
  msg: data.msg,
2945
3162
  hasBot: !!data.bot
2946
3163
  });
2947
3164
  if (data.code !== 0 || !data.bot) {
2948
- log$22.error("bot/v3/info API 失败", {
3165
+ log$23.error("bot/v3/info API 失败", {
2949
3166
  code: data.code,
2950
3167
  msg: data.msg
2951
3168
  });
@@ -2956,7 +3173,7 @@ async function fetchFromBotApi(token) {
2956
3173
  avatarUrl: data.bot.avatar_url
2957
3174
  };
2958
3175
  } catch (err) {
2959
- log$22.error("bot/v3/info API 请求异常", { error: err instanceof Error ? err.message : String(err) });
3176
+ log$23.error("bot/v3/info API 请求异常", { error: err instanceof Error ? err.message : String(err) });
2960
3177
  return null;
2961
3178
  }
2962
3179
  }
@@ -2972,7 +3189,7 @@ async function getTenantAccessToken(appId, appSecret) {
2972
3189
  })
2973
3190
  })).json();
2974
3191
  if (data.code !== 0 || !data.tenant_access_token) {
2975
- log$22.error("获取 tenant_access_token 失败", {
3192
+ log$23.error("获取 tenant_access_token 失败", {
2976
3193
  code: data.code,
2977
3194
  msg: data.msg
2978
3195
  });
@@ -2980,7 +3197,7 @@ async function getTenantAccessToken(appId, appSecret) {
2980
3197
  }
2981
3198
  return data.tenant_access_token;
2982
3199
  } catch (err) {
2983
- log$22.error("获取 tenant_access_token 异常", { error: err instanceof Error ? err.message : String(err) });
3200
+ log$23.error("获取 tenant_access_token 异常", { error: err instanceof Error ? err.message : String(err) });
2984
3201
  return null;
2985
3202
  }
2986
3203
  }
@@ -3018,10 +3235,10 @@ function parseDocTokenFromUrl(url) {
3018
3235
  async function fetchDocContent(docUrl, accessToken) {
3019
3236
  const parsed = parseDocTokenFromUrl(docUrl);
3020
3237
  if (!parsed) {
3021
- log$22.warn("无法从 URL 中解析文档 token", { url: docUrl });
3238
+ log$23.warn("无法从 URL 中解析文档 token", { url: docUrl });
3022
3239
  return null;
3023
3240
  }
3024
- log$22.info("开始读取人设文档", {
3241
+ log$23.info("开始读取人设文档", {
3025
3242
  url: docUrl,
3026
3243
  type: parsed.type,
3027
3244
  token: parsed.token
@@ -3029,18 +3246,18 @@ async function fetchDocContent(docUrl, accessToken) {
3029
3246
  let docToken = parsed.token;
3030
3247
  if (parsed.type === "wiki") {
3031
3248
  const realToken = await resolveWikiNodeToDocToken(parsed.token, accessToken);
3032
- if (!realToken) log$22.warn("wiki 节点解析失败,尝试直接使用 token 读取");
3249
+ if (!realToken) log$23.warn("wiki 节点解析失败,尝试直接使用 token 读取");
3033
3250
  else docToken = realToken;
3034
3251
  }
3035
3252
  try {
3036
3253
  const data = await (await fetch(`https://open.feishu.cn/open-apis/docx/v1/documents/${docToken}/raw_content`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
3037
- log$22.info("文档 raw_content API 响应", {
3254
+ log$23.info("文档 raw_content API 响应", {
3038
3255
  code: data.code,
3039
3256
  msg: data.msg,
3040
3257
  contentLength: data.data?.content?.length
3041
3258
  });
3042
3259
  if (data.code !== 0 || !data.data?.content) {
3043
- log$22.warn("读取文档内容失败", {
3260
+ log$23.warn("读取文档内容失败", {
3044
3261
  code: data.code,
3045
3262
  msg: data.msg,
3046
3263
  docToken
@@ -3049,7 +3266,7 @@ async function fetchDocContent(docUrl, accessToken) {
3049
3266
  }
3050
3267
  return data.data.content.trim();
3051
3268
  } catch (err) {
3052
- log$22.error("读取文档内容异常", {
3269
+ log$23.error("读取文档内容异常", {
3053
3270
  error: err instanceof Error ? err.message : String(err),
3054
3271
  docToken
3055
3272
  });
@@ -3060,7 +3277,7 @@ async function fetchDocContent(docUrl, accessToken) {
3060
3277
  async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
3061
3278
  try {
3062
3279
  const data = await (await fetch(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token=${wikiToken}`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
3063
- log$22.info("wiki get_node API 响应", {
3280
+ log$23.info("wiki get_node API 响应", {
3064
3281
  code: data.code,
3065
3282
  msg: data.msg,
3066
3283
  objType: data.data?.node?.obj_type
@@ -3068,7 +3285,7 @@ async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
3068
3285
  if (data.code !== 0 || !data.data?.node?.obj_token) return null;
3069
3286
  return data.data.node.obj_token;
3070
3287
  } catch (err) {
3071
- log$22.warn("wiki get_node 请求异常", { error: err instanceof Error ? err.message : String(err) });
3288
+ log$23.warn("wiki get_node 请求异常", { error: err instanceof Error ? err.message : String(err) });
3072
3289
  return null;
3073
3290
  }
3074
3291
  }
@@ -3090,7 +3307,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3090
3307
  const infoBlock = buildAppInfoBlock(appInfo);
3091
3308
  if (!existsSync$1(claudeMdPath)) {
3092
3309
  await writeFile$1(claudeMdPath, infoBlock + "\n\n" + getDefaultClaudeMdBody(), "utf-8");
3093
- log$22.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
3310
+ log$23.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
3094
3311
  return;
3095
3312
  }
3096
3313
  let content = await readFile$1(claudeMdPath, "utf-8");
@@ -3102,7 +3319,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3102
3319
  content = before + infoBlock + after;
3103
3320
  } else content = infoBlock + "\n\n" + content;
3104
3321
  await writeFile$1(claudeMdPath, content, "utf-8");
3105
- log$22.info("CLAUDE.md 应用信息已同步", {
3322
+ log$23.info("CLAUDE.md 应用信息已同步", {
3106
3323
  appName: appInfo.appName,
3107
3324
  hasDescription: !!appInfo.description,
3108
3325
  hasAvatar: !!appInfo.avatarUrl
@@ -3117,7 +3334,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3117
3334
  async function syncPersonaDocToClaudeMd(personaContent) {
3118
3335
  const claudeMdPath = join$1(homedir$1(), ".claude", "CLAUDE.md");
3119
3336
  if (!existsSync$1(claudeMdPath)) {
3120
- log$22.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
3337
+ log$23.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
3121
3338
  return;
3122
3339
  }
3123
3340
  let content = await readFile$1(claudeMdPath, "utf-8");
@@ -3138,7 +3355,7 @@ async function syncPersonaDocToClaudeMd(personaContent) {
3138
3355
  content = before + personaBlock + after;
3139
3356
  } else content = content.trimEnd() + "\n\n" + personaBlock + "\n";
3140
3357
  await writeFile$1(claudeMdPath, content, "utf-8");
3141
- log$22.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
3358
+ log$23.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
3142
3359
  }
3143
3360
  /** 构建应用信息标记区块 */
3144
3361
  function buildAppInfoBlock(appInfo) {
@@ -3176,24 +3393,24 @@ function getDefaultClaudeMdBody() {
3176
3393
  * @returns 同步后的应用信息,如果失败返回 null
3177
3394
  */
3178
3395
  async function syncAppInfo(credentials) {
3179
- log$22.info("开始同步应用信息", { appId: credentials.appId });
3396
+ log$23.info("开始同步应用信息", { appId: credentials.appId });
3180
3397
  const appInfo = await fetchAppInfo(credentials);
3181
3398
  if (!appInfo) {
3182
- log$22.warn("获取应用信息失败,跳过同步");
3399
+ log$23.warn("获取应用信息失败,跳过同步");
3183
3400
  return null;
3184
3401
  }
3185
3402
  await syncAppInfoToClaudeMd(appInfo);
3186
3403
  if (appInfo.helpDocUrl) {
3187
- log$22.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
3404
+ log$23.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
3188
3405
  const token = await getTenantAccessToken(credentials.appId, credentials.appSecret);
3189
3406
  if (token) {
3190
3407
  const personaContent = await fetchDocContent(appInfo.helpDocUrl, token);
3191
3408
  if (personaContent) await syncPersonaDocToClaudeMd(personaContent);
3192
- else log$22.warn("人设文档内容为空或读取失败,跳过同步");
3409
+ else log$23.warn("人设文档内容为空或读取失败,跳过同步");
3193
3410
  }
3194
3411
  }
3195
3412
  await installSyncSkill();
3196
- log$22.info("应用信息同步完成", {
3413
+ log$23.info("应用信息同步完成", {
3197
3414
  appName: appInfo.appName,
3198
3415
  description: appInfo.description?.substring(0, 50),
3199
3416
  hasPersonaDoc: !!appInfo.helpDocUrl
@@ -3211,7 +3428,7 @@ async function installSyncSkill() {
3211
3428
  if ((await readFile$1(SYNC_SKILL_PATH, "utf-8")).includes(`skill-version: ${SYNC_SKILL_VERSION}`)) return;
3212
3429
  }
3213
3430
  await writeFile$1(SYNC_SKILL_PATH, SYNC_SKILL_CONTENT, "utf-8");
3214
- log$22.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
3431
+ log$23.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
3215
3432
  }
3216
3433
  const SYNC_SKILL_CONTENT = `---
3217
3434
  skill-version: ${SYNC_SKILL_VERSION}
@@ -3299,8 +3516,10 @@ function notImplemented(_req, res) {
3299
3516
  * 其余路由当前仍为占位 handler。
3300
3517
  */
3301
3518
  function registerRoutes(app, processManager, scheduledTaskManager, appCredentials) {
3302
- if (processManager) app.use(createExecuteRouter(processManager));
3303
- else {
3519
+ if (processManager) {
3520
+ app.use(createExecuteRouter(processManager));
3521
+ app.use(createAgentCompletionRouter(processManager));
3522
+ } else {
3304
3523
  logger$1.warn("processManager 未注入,执行路由使用占位 handler");
3305
3524
  app.post("/api/execute", notImplemented);
3306
3525
  app.post("/api/execute/batch", notImplemented);
@@ -3317,6 +3536,7 @@ function registerRoutes(app, processManager, scheduledTaskManager, appCredential
3317
3536
  app.delete("/api/scheduled-tasks/:id", notImplemented);
3318
3537
  }
3319
3538
  app.use("/api", createStatusRouter());
3539
+ app.use(createToolInvokeProxyRouter());
3320
3540
  app.use("/hooks", createHooksRouter());
3321
3541
  if (appCredentials) app.post("/api/sync-app-info", async (_req, res) => {
3322
3542
  try {
@@ -3403,7 +3623,7 @@ function createGatewayServer(config) {
3403
3623
  *
3404
3624
  * 持久化文件: /workspace/config/scheduled-tasks.json
3405
3625
  */
3406
- const log$21 = larkLogger("gateway/scheduler");
3626
+ const log$22 = larkLogger("gateway/scheduler");
3407
3627
  /** 持久化文件路径 */
3408
3628
  const PERSIST_PATH = "/workspace/config/scheduled-tasks.json";
3409
3629
  /**
@@ -3421,7 +3641,7 @@ var ScheduledTaskManager = class {
3421
3641
  processManager;
3422
3642
  constructor(processManager) {
3423
3643
  this.processManager = processManager;
3424
- log$21.info("ScheduledTaskManager 已创建");
3644
+ log$22.info("ScheduledTaskManager 已创建");
3425
3645
  }
3426
3646
  /**
3427
3647
  * 创建定时任务
@@ -3441,7 +3661,7 @@ var ScheduledTaskManager = class {
3441
3661
  enabled: true,
3442
3662
  created_at: (/* @__PURE__ */ new Date()).toISOString()
3443
3663
  };
3444
- log$21.info("创建定时任务", {
3664
+ log$22.info("创建定时任务", {
3445
3665
  taskId: task.id,
3446
3666
  name: task.name,
3447
3667
  cron: task.cron,
@@ -3477,7 +3697,7 @@ var ScheduledTaskManager = class {
3477
3697
  if (updates.example_output !== void 0) task.example_output = updates.example_output;
3478
3698
  if (updates.constraints !== void 0) task.constraints = updates.constraints;
3479
3699
  if (updates.enabled !== void 0) task.enabled = updates.enabled;
3480
- log$21.info("更新定时任务", {
3700
+ log$22.info("更新定时任务", {
3481
3701
  taskId: id,
3482
3702
  name: task.name,
3483
3703
  cronChanged: oldCron !== task.cron,
@@ -3499,10 +3719,10 @@ var ScheduledTaskManager = class {
3499
3719
  deleteTask(id) {
3500
3720
  const task = this.tasks.get(id);
3501
3721
  if (!task) {
3502
- log$21.warn("尝试删除不存在的任务", { taskId: id });
3722
+ log$22.warn("尝试删除不存在的任务", { taskId: id });
3503
3723
  return false;
3504
3724
  }
3505
- log$21.info("删除定时任务", {
3725
+ log$22.info("删除定时任务", {
3506
3726
  taskId: id,
3507
3727
  name: task.name
3508
3728
  });
@@ -3517,28 +3737,28 @@ var ScheduledTaskManager = class {
3517
3737
  * 启动时调用,读取 JSON 文件并为每个 enabled 的任务注册 cron。
3518
3738
  */
3519
3739
  async loadFromDisk() {
3520
- log$21.info("从磁盘加载定时任务", { path: PERSIST_PATH });
3740
+ log$22.info("从磁盘加载定时任务", { path: PERSIST_PATH });
3521
3741
  try {
3522
3742
  const content = await readFile(PERSIST_PATH, "utf-8");
3523
3743
  const data = JSON.parse(content);
3524
3744
  if (!Array.isArray(data)) {
3525
- log$21.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
3745
+ log$22.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
3526
3746
  return;
3527
3747
  }
3528
3748
  for (const task of data) {
3529
3749
  this.tasks.set(task.id, task);
3530
3750
  if (task.enabled) this.registerCronJob(task);
3531
3751
  }
3532
- log$21.info("定时任务加载完成", {
3752
+ log$22.info("定时任务加载完成", {
3533
3753
  total: data.length,
3534
3754
  enabled: data.filter((t) => t.enabled).length
3535
3755
  });
3536
3756
  } catch (err) {
3537
3757
  if (err.code === "ENOENT") {
3538
- log$21.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
3758
+ log$22.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
3539
3759
  return;
3540
3760
  }
3541
- log$21.error("加载定时任务失败", {
3761
+ log$22.error("加载定时任务失败", {
3542
3762
  path: PERSIST_PATH,
3543
3763
  error: String(err)
3544
3764
  });
@@ -3554,12 +3774,12 @@ var ScheduledTaskManager = class {
3554
3774
  try {
3555
3775
  await mkdir(dirname(PERSIST_PATH), { recursive: true });
3556
3776
  await writeFile(PERSIST_PATH, JSON.stringify(tasks, null, 2), "utf-8");
3557
- log$21.info("定时任务已持久化到磁盘", {
3777
+ log$22.info("定时任务已持久化到磁盘", {
3558
3778
  path: PERSIST_PATH,
3559
3779
  count: tasks.length
3560
3780
  });
3561
3781
  } catch (err) {
3562
- log$21.error("持久化定时任务失败", {
3782
+ log$22.error("持久化定时任务失败", {
3563
3783
  path: PERSIST_PATH,
3564
3784
  error: String(err)
3565
3785
  });
@@ -3572,13 +3792,13 @@ var ScheduledTaskManager = class {
3572
3792
  */
3573
3793
  registerCronJob(task) {
3574
3794
  this.unregisterCronJob(task.id);
3575
- log$21.info("注册 cron 调度", {
3795
+ log$22.info("注册 cron 调度", {
3576
3796
  taskId: task.id,
3577
3797
  name: task.name,
3578
3798
  cron: task.cron
3579
3799
  });
3580
3800
  const job = cron.schedule(task.cron, () => {
3581
- log$21.info("cron 触发任务执行", {
3801
+ log$22.info("cron 触发任务执行", {
3582
3802
  taskId: task.id,
3583
3803
  name: task.name
3584
3804
  });
@@ -3594,7 +3814,7 @@ var ScheduledTaskManager = class {
3594
3814
  if (job) {
3595
3815
  job.stop();
3596
3816
  this.cronJobs.delete(taskId);
3597
- log$21.info("已注销 cron 调度", { taskId });
3817
+ log$22.info("已注销 cron 调度", { taskId });
3598
3818
  }
3599
3819
  }
3600
3820
  /**
@@ -3604,10 +3824,10 @@ var ScheduledTaskManager = class {
3604
3824
  */
3605
3825
  stopAll() {
3606
3826
  const count = this.cronJobs.size;
3607
- log$21.info("停止所有 cron 调度", { count });
3827
+ log$22.info("停止所有 cron 调度", { count });
3608
3828
  for (const [taskId, job] of this.cronJobs) {
3609
3829
  job.stop();
3610
- log$21.debug("已停止 cron 调度", { taskId });
3830
+ log$22.debug("已停止 cron 调度", { taskId });
3611
3831
  }
3612
3832
  this.cronJobs.clear();
3613
3833
  }
@@ -3621,17 +3841,17 @@ var ScheduledTaskManager = class {
3621
3841
  async executeTask(taskId) {
3622
3842
  const task = this.tasks.get(taskId);
3623
3843
  if (!task) {
3624
- log$21.warn("任务执行时未找到任务", { taskId });
3844
+ log$22.warn("任务执行时未找到任务", { taskId });
3625
3845
  return;
3626
3846
  }
3627
3847
  if (!task.enabled) {
3628
- log$21.info("任务已禁用,跳过执行", {
3848
+ log$22.info("任务已禁用,跳过执行", {
3629
3849
  taskId,
3630
3850
  name: task.name
3631
3851
  });
3632
3852
  return;
3633
3853
  }
3634
- log$21.info("开始执行定时任务", {
3854
+ log$22.info("开始执行定时任务", {
3635
3855
  taskId: task.id,
3636
3856
  name: task.name,
3637
3857
  sessionId: task.session_id,
@@ -3652,7 +3872,7 @@ var ScheduledTaskManager = class {
3652
3872
  onResult: (result) => {
3653
3873
  const summary = result.result ?? resultText;
3654
3874
  task.last_result = summary.slice(0, 2e3);
3655
- log$21.info("定时任务执行完成", {
3875
+ log$22.info("定时任务执行完成", {
3656
3876
  taskId: task.id,
3657
3877
  name: task.name,
3658
3878
  subtype: result.subtype,
@@ -3665,7 +3885,7 @@ var ScheduledTaskManager = class {
3665
3885
  },
3666
3886
  onError: (error) => {
3667
3887
  task.last_result = `执行失败: ${error.message}`;
3668
- log$21.error("定时任务执行失败", {
3888
+ log$22.error("定时任务执行失败", {
3669
3889
  taskId: task.id,
3670
3890
  name: task.name,
3671
3891
  error: error.message
@@ -3677,7 +3897,7 @@ var ScheduledTaskManager = class {
3677
3897
  await this.processManager.executePrompt(config, callbacks);
3678
3898
  } catch (err) {
3679
3899
  task.last_result = `执行异常: ${String(err)}`;
3680
- log$21.error("定时任务执行异常", {
3900
+ log$22.error("定时任务执行异常", {
3681
3901
  taskId: task.id,
3682
3902
  name: task.name,
3683
3903
  error: String(err)
@@ -3970,7 +4190,7 @@ function getUserAgent() {
3970
4190
  * - `LarkClient.fromCredentials(credentials)` — ephemeral instance (not cached)
3971
4191
  * - `LarkClient.fromProvider(provider, opts)` — from ICredentialProvider (not cached)
3972
4192
  */
3973
- const log$19 = larkLogger("core/lark-client");
4193
+ const log$20 = larkLogger("core/lark-client");
3974
4194
  const GLOBAL_LARK_USER_AGENT_KEY = "LARK_USER_AGENT";
3975
4195
  function installGlobalUserAgent() {
3976
4196
  globalThis[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();
@@ -4062,7 +4282,7 @@ var LarkClient = class LarkClient {
4062
4282
  const existing = cache.get(account.accountId);
4063
4283
  if (existing && existing.account.appId === account.appId && credentialsEqual(existing.account.appSecret, account.appSecret)) return existing;
4064
4284
  if (existing) {
4065
- log$19.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
4285
+ log$20.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
4066
4286
  existing.dispose();
4067
4287
  }
4068
4288
  const instance = new LarkClient(account);
@@ -4105,7 +4325,7 @@ var LarkClient = class LarkClient {
4105
4325
  static fromProvider(provider, opts) {
4106
4326
  const appId = provider.getAppId();
4107
4327
  const appSecret = provider.getAppSecret();
4108
- log$19.info("通过 ICredentialProvider 创建 LarkClient", {
4328
+ log$20.info("通过 ICredentialProvider 创建 LarkClient", {
4109
4329
  appId,
4110
4330
  accountId: opts?.accountId ?? "default",
4111
4331
  brand: opts?.brand ?? "feishu"
@@ -4141,7 +4361,7 @@ var LarkClient = class LarkClient {
4141
4361
  get sdk() {
4142
4362
  if (!this._sdk) {
4143
4363
  const { appId, appSecret } = this.requireCredentials();
4144
- log$19.info("创建 Lark SDK 客户端实例", {
4364
+ log$20.info("创建 Lark SDK 客户端实例", {
4145
4365
  accountId: this.accountId,
4146
4366
  appId,
4147
4367
  brand: this.account.brand
@@ -4156,7 +4376,7 @@ var LarkClient = class LarkClient {
4156
4376
  if (sdkAny.httpInstance?.interceptors) {
4157
4377
  const accountId = this.accountId;
4158
4378
  sdkAny.httpInstance.interceptors.request.use((req) => {
4159
- log$19.debug("飞书 API 请求", {
4379
+ log$20.debug("飞书 API 请求", {
4160
4380
  accountId,
4161
4381
  method: req.method,
4162
4382
  url: req.url,
@@ -4166,7 +4386,7 @@ var LarkClient = class LarkClient {
4166
4386
  return req;
4167
4387
  }, void 0, { synchronous: true });
4168
4388
  sdkAny.httpInstance.interceptors.response.use((res) => {
4169
- log$19.debug("飞书 API 响应", {
4389
+ log$20.debug("飞书 API 响应", {
4170
4390
  accountId,
4171
4391
  code: res?.code,
4172
4392
  msg: res?.msg,
@@ -4174,7 +4394,7 @@ var LarkClient = class LarkClient {
4174
4394
  });
4175
4395
  return res;
4176
4396
  }, (err) => {
4177
- log$19.error("飞书 API 请求失败", {
4397
+ log$20.error("飞书 API 请求失败", {
4178
4398
  accountId,
4179
4399
  error: err instanceof Error ? err.message : String(err),
4180
4400
  url: err?.config?.url
@@ -4261,7 +4481,7 @@ var LarkClient = class LarkClient {
4261
4481
  dispatcher.register(handlers);
4262
4482
  const { appId, appSecret } = this.requireCredentials();
4263
4483
  if (this._wsClient) {
4264
- log$19.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
4484
+ log$20.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
4265
4485
  try {
4266
4486
  this._wsClient.close({ force: true });
4267
4487
  } catch {}
@@ -4278,7 +4498,7 @@ var LarkClient = class LarkClient {
4278
4498
  wsClientAny.handleEventData = (data) => {
4279
4499
  const msgType = data.headers?.find?.((h) => h.key === "type")?.value;
4280
4500
  const eventType = data.headers?.find?.((h) => h.key === "event_type")?.value;
4281
- log$19.info("WS 收到原始事件", {
4501
+ log$20.info("WS 收到原始事件", {
4282
4502
  accountId: this.accountId,
4283
4503
  msgType,
4284
4504
  eventType,
@@ -4304,14 +4524,14 @@ var LarkClient = class LarkClient {
4304
4524
  /** Disconnect WebSocket but keep instance in cache. */
4305
4525
  disconnect() {
4306
4526
  if (this._wsClient) {
4307
- log$19.info(`disconnecting WebSocket`, { accountId: this.accountId });
4527
+ log$20.info(`disconnecting WebSocket`, { accountId: this.accountId });
4308
4528
  try {
4309
4529
  this._wsClient.close({ force: true });
4310
4530
  } catch {}
4311
4531
  }
4312
4532
  this._wsClient = null;
4313
4533
  if (this.messageDedup) {
4314
- log$19.info(`disposing message dedup`, {
4534
+ log$20.info(`disposing message dedup`, {
4315
4535
  accountId: this.accountId,
4316
4536
  size: this.messageDedup.size
4317
4537
  });
@@ -4617,7 +4837,7 @@ function sortTraceValue(value) {
4617
4837
  }
4618
4838
  //#endregion
4619
4839
  //#region src/card/cc-stream-bridge.ts
4620
- const log$18 = larkLogger("card/cc-stream-bridge");
4840
+ const log$19 = larkLogger("card/cc-stream-bridge");
4621
4841
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
4622
4842
  /**
4623
4843
  * CCStreamBridge — 将 CC 流事件桥接到 StreamingCardController
@@ -4642,7 +4862,7 @@ var CCStreamBridge = class {
4642
4862
  sessionKey: options?.sessionKey
4643
4863
  };
4644
4864
  if (this.options.sessionKey) startToolUseTraceRun(this.options.sessionKey);
4645
- log$18.info("CCStreamBridge 初始化", {
4865
+ log$19.info("CCStreamBridge 初始化", {
4646
4866
  autoCompleteOnTurnEnd: this.options.autoCompleteOnTurnEnd,
4647
4867
  sessionKey: this.options.sessionKey ?? "(none)"
4648
4868
  });
@@ -4650,7 +4870,7 @@ var CCStreamBridge = class {
4650
4870
  /** 文本增量 → 累积后调用 controller.onPartialReply */
4651
4871
  onTextDelta(text) {
4652
4872
  this.accumulatedText += text;
4653
- log$18.debug("textDelta 事件", {
4873
+ log$19.debug("textDelta 事件", {
4654
4874
  deltaLen: text.length,
4655
4875
  totalLen: this.accumulatedText.length
4656
4876
  });
@@ -4659,7 +4879,7 @@ var CCStreamBridge = class {
4659
4879
  /** 思考增量 → 累积后调用 controller.onReasoningStream */
4660
4880
  onThinkingDelta(text) {
4661
4881
  this.accumulatedThinkingText += text;
4662
- log$18.debug("thinkingDelta 事件", {
4882
+ log$19.debug("thinkingDelta 事件", {
4663
4883
  deltaLen: text.length,
4664
4884
  totalLen: this.accumulatedThinkingText.length
4665
4885
  });
@@ -4671,7 +4891,7 @@ var CCStreamBridge = class {
4671
4891
  const displayName = getToolDisplayName(toolName);
4672
4892
  const toolParams = typeof _toolInput === "object" && _toolInput !== null ? _toolInput : void 0;
4673
4893
  const hasParams = toolParams && Object.keys(toolParams).length > 0;
4674
- log$18.info("toolUseStart 事件", {
4894
+ log$19.info("toolUseStart 事件", {
4675
4895
  toolName,
4676
4896
  displayName,
4677
4897
  activeToolsCount: this.activeTools.size,
@@ -4684,7 +4904,7 @@ var CCStreamBridge = class {
4684
4904
  toolName,
4685
4905
  toolParams
4686
4906
  })) {
4687
- log$18.info("toolUseStart: 更新已有步骤的 params", { toolName });
4907
+ log$19.info("toolUseStart: 更新已有步骤的 params", { toolName });
4688
4908
  this.controller.onToolStart({
4689
4909
  name: toolName,
4690
4910
  phase: "start"
@@ -4708,7 +4928,7 @@ var CCStreamBridge = class {
4708
4928
  const toolName = this.activeTools.get(toolUseId) ?? this.lastToolName ?? "unknown";
4709
4929
  const displayName = getToolDisplayName(toolName);
4710
4930
  this.activeTools.delete(toolUseId);
4711
- log$18.info("toolResult 事件", {
4931
+ log$19.info("toolResult 事件", {
4712
4932
  toolUseId,
4713
4933
  toolName,
4714
4934
  displayName,
@@ -4723,7 +4943,7 @@ var CCStreamBridge = class {
4723
4943
  }
4724
4944
  /** 工具执行进度 → 记录日志 */
4725
4945
  onToolProgress(toolName, elapsedSeconds) {
4726
- log$18.debug("toolProgress 事件", {
4946
+ log$19.debug("toolProgress 事件", {
4727
4947
  toolName,
4728
4948
  displayName: getToolDisplayName(toolName),
4729
4949
  elapsedSeconds
@@ -4731,7 +4951,7 @@ var CCStreamBridge = class {
4731
4951
  }
4732
4952
  /** 轮次结束 → 仅在 end_turn 时标记完成 + 触发 onIdle */
4733
4953
  onTurnEnd(stopReason) {
4734
- log$18.info("turnEnd 事件", {
4954
+ log$19.info("turnEnd 事件", {
4735
4955
  stopReason,
4736
4956
  accumulatedTextLen: this.accumulatedText.length,
4737
4957
  accumulatedThinkingTextLen: this.accumulatedThinkingText.length
@@ -4739,7 +4959,7 @@ var CCStreamBridge = class {
4739
4959
  if (this.options.autoCompleteOnTurnEnd && stopReason === "end_turn") {
4740
4960
  const trimmedText = this.accumulatedText.trim();
4741
4961
  if (trimmedText === CC_INTERNAL_PLACEHOLDER || trimmedText === "") {
4742
- log$18.info("检测到 CC 内部占位消息,静默丢弃", {
4962
+ log$19.info("检测到 CC 内部占位消息,静默丢弃", {
4743
4963
  text: trimmedText.slice(0, 50),
4744
4964
  sessionKey: this.options.sessionKey
4745
4965
  });
@@ -4752,7 +4972,7 @@ var CCStreamBridge = class {
4752
4972
  }
4753
4973
  /** 最终结果 → 兜底最终化 + 错误处理 */
4754
4974
  onResult(data) {
4755
- log$18.info("result 事件", {
4975
+ log$19.info("result 事件", {
4756
4976
  subtype: data.subtype,
4757
4977
  isError: data.isError,
4758
4978
  durationMs: data.durationMs,
@@ -4764,7 +4984,7 @@ var CCStreamBridge = class {
4764
4984
  const pm = ClaudeCodeAdapter.getInstance()?.getProcessManager();
4765
4985
  const sessionId = this.options.sessionKey;
4766
4986
  if (sessionId && pm ? pm.consumeAborted(sessionId) : false) {
4767
- log$18.info("用户主动中断,按正常完成处理", {
4987
+ log$19.info("用户主动中断,按正常完成处理", {
4768
4988
  subtype: data.subtype,
4769
4989
  sessionId
4770
4990
  });
@@ -4773,7 +4993,7 @@ var CCStreamBridge = class {
4773
4993
  this.controller.onIdle();
4774
4994
  } else {
4775
4995
  const errorMessage = data.result ?? `CC 执行失败: ${data.subtype}`;
4776
- log$18.error("CC 执行返回错误", {
4996
+ log$19.error("CC 执行返回错误", {
4777
4997
  subtype: data.subtype,
4778
4998
  errorMessage: errorMessage.slice(0, 200)
4779
4999
  });
@@ -4790,7 +5010,7 @@ var CCStreamBridge = class {
4790
5010
  * 内部复用上面的直接调用方法。
4791
5011
  */
4792
5012
  bindParser(parser) {
4793
- log$18.info("绑定 CCStreamParser 事件到卡片控制器");
5013
+ log$19.info("绑定 CCStreamParser 事件到卡片控制器");
4794
5014
  parser.on("textDelta", (text) => this.onTextDelta(text));
4795
5015
  parser.on("thinkingDelta", (text) => this.onThinkingDelta(text));
4796
5016
  parser.on("toolUseStart", (toolName, toolInput) => this.onToolUseStart(toolName, toolInput));
@@ -4798,7 +5018,7 @@ var CCStreamBridge = class {
4798
5018
  parser.on("toolProgress", (toolName, elapsedSeconds) => this.onToolProgress(toolName, elapsedSeconds));
4799
5019
  parser.on("turnEnd", (stopReason) => this.onTurnEnd(stopReason));
4800
5020
  parser.on("result", (data) => this.onResult(data));
4801
- log$18.info("CCStreamParser 事件绑定完成");
5021
+ log$19.info("CCStreamParser 事件绑定完成");
4802
5022
  }
4803
5023
  };
4804
5024
  //#endregion
@@ -6814,7 +7034,7 @@ function resolveLarkSdk(cfg, accountId) {
6814
7034
  if (cached) return cached.sdk;
6815
7035
  return LarkClient.fromCfg(cfg, accountId).sdk;
6816
7036
  }
6817
- const log$17 = larkLogger("card/cardkit");
7037
+ const log$18 = larkLogger("card/cardkit");
6818
7038
  /**
6819
7039
  * 记录 CardKit API 响应日志,检测错误码并抛出异常。
6820
7040
  *
@@ -6824,13 +7044,13 @@ const log$17 = larkLogger("card/cardkit");
6824
7044
  function logCardKitResponse(params) {
6825
7045
  const { resp, api, context } = params;
6826
7046
  const { code, msg } = resp;
6827
- log$17.info(`cardkit ${api} response`, {
7047
+ log$18.info(`cardkit ${api} response`, {
6828
7048
  code,
6829
7049
  msg,
6830
7050
  context
6831
7051
  });
6832
7052
  if (code && code !== 0) {
6833
- log$17.warn(`cardkit ${api} FAILED`, {
7053
+ log$18.warn(`cardkit ${api} FAILED`, {
6834
7054
  code,
6835
7055
  msg,
6836
7056
  context,
@@ -7241,7 +7461,7 @@ function validateLocalMediaRoots(filePath, localRoots) {
7241
7461
  * Feishu messages, uploading media to the Feishu IM storage, and
7242
7462
  * sending image / file messages to chats.
7243
7463
  */
7244
- const log$16 = larkLogger("outbound/media");
7464
+ const log$17 = larkLogger("outbound/media");
7245
7465
  /**
7246
7466
  * Upload an image to Feishu IM storage.
7247
7467
  *
@@ -7309,7 +7529,7 @@ async function validateRemoteUrl(raw) {
7309
7529
  for (const addr of addresses) if (isPrivateIP(addr)) throw new Error(`[feishu-media] Domain "${hostname}" resolves to private/reserved IP "${addr}" (SSRF protection). URL: "${raw}"`);
7310
7530
  } catch (err) {
7311
7531
  if (err instanceof Error && err.message.includes("SSRF protection")) throw err;
7312
- log$16.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
7532
+ log$17.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
7313
7533
  }
7314
7534
  }
7315
7535
  /**
@@ -7327,21 +7547,21 @@ async function fetchMediaBuffer(urlOrPath, localRoots) {
7327
7547
  if (localRoots !== void 0) validateLocalMediaRoots(filePath, localRoots);
7328
7548
  else throw new Error(`[feishu-media] Local file access denied for "${filePath}": mediaLocalRoots is not configured. Configure mediaLocalRoots to explicitly allow local file access.`);
7329
7549
  const buf = fs.readFileSync(filePath);
7330
- log$16.debug(`local file read: "${filePath}", ${buf.length} bytes`);
7550
+ log$17.debug(`local file read: "${filePath}", ${buf.length} bytes`);
7331
7551
  return buf;
7332
7552
  }
7333
7553
  await validateRemoteUrl(raw);
7334
7554
  const FETCH_TIMEOUT_MS = 3e4;
7335
- log$16.info(`fetching remote media: ${raw}`);
7555
+ log$17.info(`fetching remote media: ${raw}`);
7336
7556
  const response = await fetch(raw, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
7337
7557
  if (!response.ok) throw new Error(`[feishu-media] Failed to fetch media from "${raw}": HTTP ${response.status} ${response.statusText}. Verify the URL is accessible and returns a valid media resource.`);
7338
7558
  const arrayBuffer = await response.arrayBuffer();
7339
- log$16.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
7559
+ log$17.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
7340
7560
  return Buffer.from(arrayBuffer);
7341
7561
  }
7342
7562
  //#endregion
7343
7563
  //#region src/card/image-resolver.ts
7344
- const log$15 = larkLogger("card/image-resolver");
7564
+ const log$16 = larkLogger("card/image-resolver");
7345
7565
  /** Matches complete markdown image syntax: `![alt](value)` */
7346
7566
  const IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
7347
7567
  var ImageResolver = class {
@@ -7387,14 +7607,14 @@ var ImageResolver = class {
7387
7607
  async resolveImagesAwait(text, timeoutMs) {
7388
7608
  this.resolveImages(text);
7389
7609
  if (this.pending.size > 0) {
7390
- log$15.info("resolveImagesAwait: waiting for uploads", {
7610
+ log$16.info("resolveImagesAwait: waiting for uploads", {
7391
7611
  count: this.pending.size,
7392
7612
  timeoutMs
7393
7613
  });
7394
7614
  const allUploads = Promise.all(this.pending.values());
7395
7615
  const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
7396
7616
  await Promise.race([allUploads, timeout]);
7397
- if (this.pending.size > 0) log$15.warn("resolveImagesAwait: timed out with pending uploads", { remaining: this.pending.size });
7617
+ if (this.pending.size > 0) log$16.warn("resolveImagesAwait: timed out with pending uploads", { remaining: this.pending.size });
7398
7618
  }
7399
7619
  return this.resolveImages(text);
7400
7620
  }
@@ -7404,7 +7624,7 @@ var ImageResolver = class {
7404
7624
  }
7405
7625
  async doUpload(url) {
7406
7626
  try {
7407
- log$15.info("uploading image", { url });
7627
+ log$16.info("uploading image", { url });
7408
7628
  const buffer = await fetchRemoteImageBuffer(url);
7409
7629
  const { imageKey } = await uploadImageLark({
7410
7630
  cfg: this.cfg,
@@ -7412,7 +7632,7 @@ var ImageResolver = class {
7412
7632
  imageType: "message",
7413
7633
  accountId: this.accountId
7414
7634
  });
7415
- log$15.info("image uploaded", {
7635
+ log$16.info("image uploaded", {
7416
7636
  url,
7417
7637
  imageKey
7418
7638
  });
@@ -7421,7 +7641,7 @@ var ImageResolver = class {
7421
7641
  this.onImageResolved();
7422
7642
  return imageKey;
7423
7643
  } catch (err) {
7424
- log$15.warn("image upload failed", {
7644
+ log$16.warn("image upload failed", {
7425
7645
  url,
7426
7646
  error: String(err)
7427
7647
  });
@@ -7442,7 +7662,7 @@ var ImageResolver = class {
7442
7662
  * Encapsulates the terminateDueToUnavailable / shouldSkipForUnavailable
7443
7663
  * logic previously scattered as closures in reply-dispatcher.ts.
7444
7664
  */
7445
- const log$14 = larkLogger("card/unavailable-guard");
7665
+ const log$15 = larkLogger("card/unavailable-guard");
7446
7666
  var UnavailableGuard = class {
7447
7667
  terminated = false;
7448
7668
  replyToMessageId;
@@ -7495,7 +7715,7 @@ var UnavailableGuard = class {
7495
7715
  this.terminated = true;
7496
7716
  this.onTerminate();
7497
7717
  const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? "unknown";
7498
- log$14.warn("reply pipeline terminated by unavailable message", {
7718
+ log$15.warn("reply pipeline terminated by unavailable message", {
7499
7719
  source,
7500
7720
  apiCode,
7501
7721
  messageId: affectedMessageId
@@ -7532,7 +7752,7 @@ function getActiveCard(sessionId) {
7532
7752
  * Delegates throttling to FlushController and message-unavailable
7533
7753
  * detection to UnavailableGuard.
7534
7754
  */
7535
- const log$13 = larkLogger("card/streaming");
7755
+ const log$14 = larkLogger("card/streaming");
7536
7756
  var StreamingCardController = class StreamingCardController {
7537
7757
  phase = "idle";
7538
7758
  cardKit = {
@@ -7620,7 +7840,7 @@ var StreamingCardController = class StreamingCardController {
7620
7840
  }
7621
7841
  }
7622
7842
  if (!entry) {
7623
- log$13.debug("footer metrics lookup: session entry missing", {
7843
+ log$14.debug("footer metrics lookup: session entry missing", {
7624
7844
  sessionKey: this.deps.sessionKey,
7625
7845
  candidateKeys,
7626
7846
  storePath,
@@ -7638,7 +7858,7 @@ var StreamingCardController = class StreamingCardController {
7638
7858
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
7639
7859
  model: typeof entry.model === "string" ? entry.model : void 0
7640
7860
  };
7641
- log$13.debug("footer metrics lookup: session entry found", {
7861
+ log$14.debug("footer metrics lookup: session entry found", {
7642
7862
  sessionKey: this.deps.sessionKey,
7643
7863
  matchedKey,
7644
7864
  storePath,
@@ -7663,7 +7883,7 @@ var StreamingCardController = class StreamingCardController {
7663
7883
  }
7664
7884
  }
7665
7885
  if (!entry) {
7666
- log$13.debug("footer metrics lookup: session entry missing", {
7886
+ log$14.debug("footer metrics lookup: session entry missing", {
7667
7887
  sessionKey: this.deps.sessionKey,
7668
7888
  candidateKeys,
7669
7889
  storePath,
@@ -7681,7 +7901,7 @@ var StreamingCardController = class StreamingCardController {
7681
7901
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
7682
7902
  model: typeof entry.model === "string" ? entry.model : void 0
7683
7903
  };
7684
- log$13.debug("footer metrics lookup: session entry found", {
7904
+ log$14.debug("footer metrics lookup: session entry found", {
7685
7905
  sessionKey: this.deps.sessionKey,
7686
7906
  matchedKey,
7687
7907
  storePath,
@@ -7689,7 +7909,7 @@ var StreamingCardController = class StreamingCardController {
7689
7909
  });
7690
7910
  return metrics;
7691
7911
  } catch (err) {
7692
- log$13.warn("footer metrics lookup failed", {
7912
+ log$14.warn("footer metrics lookup failed", {
7693
7913
  error: String(err),
7694
7914
  sessionKey: this.deps.sessionKey
7695
7915
  });
@@ -7796,7 +8016,7 @@ var StreamingCardController = class StreamingCardController {
7796
8016
  const from = this.phase;
7797
8017
  if (from === to) return false;
7798
8018
  if (!PHASE_TRANSITIONS[from].has(to)) {
7799
- log$13.warn("phase transition rejected", {
8019
+ log$14.warn("phase transition rejected", {
7800
8020
  from,
7801
8021
  to,
7802
8022
  source
@@ -7804,7 +8024,7 @@ var StreamingCardController = class StreamingCardController {
7804
8024
  return false;
7805
8025
  }
7806
8026
  this.phase = to;
7807
- log$13.info("phase transition", {
8027
+ log$14.info("phase transition", {
7808
8028
  from,
7809
8029
  to,
7810
8030
  source,
@@ -7924,7 +8144,7 @@ var StreamingCardController = class StreamingCardController {
7924
8144
  this.reasoning.dirty = true;
7925
8145
  }
7926
8146
  const text = split.answerText ?? stripReasoningTags(rawText);
7927
- log$13.debug("onPartialReply", { len: text.length });
8147
+ log$14.debug("onPartialReply", { len: text.length });
7928
8148
  if (!text) return;
7929
8149
  this.captureToolUseElapsed();
7930
8150
  if (!this.reasoning.reasoningStartTime) this.reasoning.reasoningStartTime = Date.now();
@@ -7936,7 +8156,7 @@ var StreamingCardController = class StreamingCardController {
7936
8156
  this.text.lastPartialText = text;
7937
8157
  this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + "\n\n" + text : text;
7938
8158
  if (!this.text.streamingPrefix && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {
7939
- log$13.debug("onPartialReply: buffering NO_REPLY prefix");
8159
+ log$14.debug("onPartialReply: buffering NO_REPLY prefix");
7940
8160
  return;
7941
8161
  }
7942
8162
  await this.ensureCardCreated();
@@ -7946,7 +8166,7 @@ var StreamingCardController = class StreamingCardController {
7946
8166
  }
7947
8167
  async onError(err, info) {
7948
8168
  if (this.guard.terminate("onError", err)) return;
7949
- log$13.error(`${info.kind} reply failed`, { error: String(err) });
8169
+ log$14.error(`${info.kind} reply failed`, { error: String(err) });
7950
8170
  this.captureToolUseElapsed();
7951
8171
  this.finalizeCard("onError", "error");
7952
8172
  await this.flush.waitForFlush();
@@ -8002,7 +8222,7 @@ var StreamingCardController = class StreamingCardController {
8002
8222
  if (this.cardKit.cardMessageId) {
8003
8223
  const isNoReplyLeak = !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
8004
8224
  const displayText = this.text.completedText || (isNoReplyLeak ? "" : this.text.accumulatedText) || "Done.";
8005
- if (!this.text.completedText && !this.text.accumulatedText) log$13.warn("reply completed without visible text, using empty-reply fallback");
8225
+ if (!this.text.completedText && !this.text.accumulatedText) log$14.warn("reply completed without visible text, using empty-reply fallback");
8006
8226
  const resolvedDisplayText = await this.imageResolver.resolveImagesAwait(displayText, 15e3);
8007
8227
  const idleToolUseDisplay = this.computeToolUseDisplay();
8008
8228
  const terminalContent = prepareTerminalCardContent({
@@ -8030,7 +8250,7 @@ var StreamingCardController = class StreamingCardController {
8030
8250
  const rewindSessionId = this.deps.sessionKey;
8031
8251
  const rewindMessageId = ClaudeCodeAdapter.getInstance()?.getProcessManager()?.getLastUserMessageId(this.deps.sessionKey);
8032
8252
  const rewindHasFileChanges = this.hasFileChangingTools(idleToolUseDisplay?.steps);
8033
- log$13.info("onIdle: rewind button conditions", {
8253
+ log$14.info("onIdle: rewind button conditions", {
8034
8254
  sessionId: rewindSessionId,
8035
8255
  messageId: rewindMessageId,
8036
8256
  hasFileChanges: rewindHasFileChanges,
@@ -8043,7 +8263,7 @@ var StreamingCardController = class StreamingCardController {
8043
8263
  if (idleEffectiveCardId) {
8044
8264
  const seqBeforeClose = this.cardKit.cardKitSequence;
8045
8265
  this.cardKit.cardKitSequence += 1;
8046
- log$13.info("onIdle: closing streaming mode", {
8266
+ log$14.info("onIdle: closing streaming mode", {
8047
8267
  attempt,
8048
8268
  seqBefore: seqBeforeClose,
8049
8269
  seqAfter: this.cardKit.cardKitSequence
@@ -8057,7 +8277,7 @@ var StreamingCardController = class StreamingCardController {
8057
8277
  });
8058
8278
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
8059
8279
  this.cardKit.cardKitSequence += 1;
8060
- log$13.info("onIdle: updating final card", {
8280
+ log$14.info("onIdle: updating final card", {
8061
8281
  attempt,
8062
8282
  seqBefore: seqBeforeUpdate,
8063
8283
  seqAfter: this.cardKit.cardKitSequence
@@ -8075,33 +8295,33 @@ var StreamingCardController = class StreamingCardController {
8075
8295
  card: completeCard,
8076
8296
  accountId: this.deps.accountId
8077
8297
  });
8078
- log$13.info("reply completed, card finalized", {
8298
+ log$14.info("reply completed, card finalized", {
8079
8299
  elapsedMs: this.elapsed(),
8080
8300
  isCardKit: !!idleEffectiveCardId,
8081
8301
  attempt
8082
8302
  });
8083
8303
  break;
8084
8304
  } catch (retryErr) {
8085
- log$13.warn("final card update attempt failed", {
8305
+ log$14.warn("final card update attempt failed", {
8086
8306
  attempt,
8087
8307
  maxRetries: MAX_FINAL_UPDATE_RETRIES,
8088
8308
  error: String(retryErr)
8089
8309
  });
8090
8310
  if (attempt < MAX_FINAL_UPDATE_RETRIES) await new Promise((resolve) => setTimeout(resolve, FINAL_UPDATE_RETRY_DELAY_MS * attempt));
8091
- else log$13.error("final card update exhausted all retries, card may remain in streaming state", {
8311
+ else log$14.error("final card update exhausted all retries, card may remain in streaming state", {
8092
8312
  cardId: idleEffectiveCardId,
8093
8313
  messageId: this.cardKit.cardMessageId
8094
8314
  });
8095
8315
  }
8096
8316
  }
8097
8317
  } catch (err) {
8098
- log$13.error("final card update failed (outer)", { error: String(err) });
8318
+ log$14.error("final card update failed (outer)", { error: String(err) });
8099
8319
  } finally {
8100
8320
  clearToolUseTraceRun(this.deps.sessionKey);
8101
8321
  }
8102
8322
  }
8103
8323
  markFullyComplete() {
8104
- log$13.debug("markFullyComplete", {
8324
+ log$14.debug("markFullyComplete", {
8105
8325
  completedTextLen: this.text.completedText.length,
8106
8326
  accumulatedTextLen: this.text.accumulatedText.length
8107
8327
  });
@@ -8140,7 +8360,7 @@ var StreamingCardController = class StreamingCardController {
8140
8360
  footerMetrics
8141
8361
  });
8142
8362
  await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, "abortCard");
8143
- log$13.info("abortCard completed", { effectiveCardId });
8363
+ log$14.info("abortCard completed", { effectiveCardId });
8144
8364
  } else if (this.cardKit.cardMessageId) {
8145
8365
  const abortCard = buildCardContent("complete", {
8146
8366
  text: terminalContent.text,
@@ -8161,10 +8381,10 @@ var StreamingCardController = class StreamingCardController {
8161
8381
  card: abortCard,
8162
8382
  accountId: this.deps.accountId
8163
8383
  });
8164
- log$13.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
8384
+ log$14.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
8165
8385
  }
8166
8386
  } catch (err) {
8167
- log$13.warn("abortCard failed", { error: String(err) });
8387
+ log$14.warn("abortCard failed", { error: String(err) });
8168
8388
  } finally {
8169
8389
  clearToolUseTraceRun(this.deps.sessionKey);
8170
8390
  }
@@ -8179,10 +8399,10 @@ var StreamingCardController = class StreamingCardController {
8179
8399
  async forceCloseStreaming() {
8180
8400
  const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
8181
8401
  if (!effectiveCardId && !this.cardKit.cardMessageId) {
8182
- log$13.warn("forceCloseStreaming: no card to close");
8402
+ log$14.warn("forceCloseStreaming: no card to close");
8183
8403
  return;
8184
8404
  }
8185
- log$13.info("forceCloseStreaming: 强制终态化卡片", {
8405
+ log$14.info("forceCloseStreaming: 强制终态化卡片", {
8186
8406
  cardId: effectiveCardId,
8187
8407
  messageId: this.cardKit.cardMessageId,
8188
8408
  phase: this.phase
@@ -8232,10 +8452,10 @@ var StreamingCardController = class StreamingCardController {
8232
8452
  card: completeCard,
8233
8453
  accountId: this.deps.accountId
8234
8454
  });
8235
- log$13.info("forceCloseStreaming: 卡片已强制终态化");
8455
+ log$14.info("forceCloseStreaming: 卡片已强制终态化");
8236
8456
  if (!this.isTerminalPhase) this.finalizeCard("forceCloseStreaming", "abort");
8237
8457
  } catch (err) {
8238
- log$13.error("forceCloseStreaming failed", { error: String(err) });
8458
+ log$14.error("forceCloseStreaming failed", { error: String(err) });
8239
8459
  } finally {
8240
8460
  unregisterActiveCard(this.deps.sessionKey);
8241
8461
  clearToolUseTraceRun(this.deps.sessionKey);
@@ -8259,7 +8479,7 @@ var StreamingCardController = class StreamingCardController {
8259
8479
  this.disposeShutdownHook = null;
8260
8480
  return;
8261
8481
  }
8262
- log$13.info("reusing placeholder card", {
8482
+ log$14.info("reusing placeholder card", {
8263
8483
  cardId: this.deps.placeholderCardId,
8264
8484
  messageId: this.deps.placeholderMessageId
8265
8485
  });
@@ -8283,7 +8503,7 @@ var StreamingCardController = class StreamingCardController {
8283
8503
  accountId: this.deps.accountId
8284
8504
  });
8285
8505
  if (this.isStaleCreate(epoch)) {
8286
- log$13.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
8506
+ log$14.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
8287
8507
  epoch,
8288
8508
  phase: this.phase
8289
8509
  });
@@ -8294,7 +8514,7 @@ var StreamingCardController = class StreamingCardController {
8294
8514
  this.cardKit.originalCardKitCardId = cId;
8295
8515
  this.cardKit.cardKitSequence = 1;
8296
8516
  this.disposeShutdownHook = registerShutdownHook(`streaming-card:${cId}`, () => this.abortCard());
8297
- log$13.info("created CardKit entity", {
8517
+ log$14.info("created CardKit entity", {
8298
8518
  cardId: cId,
8299
8519
  initialSequence: this.cardKit.cardKitSequence
8300
8520
  });
@@ -8307,7 +8527,7 @@ var StreamingCardController = class StreamingCardController {
8307
8527
  accountId: this.deps.accountId
8308
8528
  });
8309
8529
  if (this.isStaleCreate(epoch)) {
8310
- log$13.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
8530
+ log$14.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
8311
8531
  epoch,
8312
8532
  phase: this.phase
8313
8533
  });
@@ -8322,13 +8542,13 @@ var StreamingCardController = class StreamingCardController {
8322
8542
  this.disposeShutdownHook = null;
8323
8543
  return;
8324
8544
  }
8325
- log$13.info("sent CardKit card", { messageId: result.messageId });
8545
+ log$14.info("sent CardKit card", { messageId: result.messageId });
8326
8546
  } else throw new Error("card.create returned empty card_id");
8327
8547
  } catch (cardKitErr) {
8328
8548
  if (this.isStaleCreate(epoch)) return;
8329
8549
  if (this.guard.terminate("ensureCardCreated.cardkitFlow", cardKitErr)) return;
8330
8550
  const apiDetail = extractApiDetail(cardKitErr);
8331
- log$13.warn("CardKit flow failed, falling back to IM", { apiDetail });
8551
+ log$14.warn("CardKit flow failed, falling back to IM", { apiDetail });
8332
8552
  this.cardKit.cardKitCardId = null;
8333
8553
  this.cardKit.originalCardKitCardId = null;
8334
8554
  const fallbackCard = buildCardContent("streaming", { showToolUse: this.deps.toolUseDisplay.showToolUse });
@@ -8341,7 +8561,7 @@ var StreamingCardController = class StreamingCardController {
8341
8561
  accountId: this.deps.accountId
8342
8562
  });
8343
8563
  if (this.isStaleCreate(epoch)) {
8344
- log$13.info("ensureCardCreated: stale epoch after IM fallback send, bailing out", {
8564
+ log$14.info("ensureCardCreated: stale epoch after IM fallback send, bailing out", {
8345
8565
  epoch,
8346
8566
  phase: this.phase
8347
8567
  });
@@ -8350,12 +8570,12 @@ var StreamingCardController = class StreamingCardController {
8350
8570
  this.cardKit.cardMessageId = result.messageId;
8351
8571
  this.flush.setCardMessageReady(true);
8352
8572
  if (!this.transition("streaming", "ensureCardCreated.imFallback")) return;
8353
- log$13.info("sent fallback IM card", { messageId: result.messageId });
8573
+ log$14.info("sent fallback IM card", { messageId: result.messageId });
8354
8574
  }
8355
8575
  } catch (err) {
8356
8576
  if (this.isStaleCreate(epoch)) return;
8357
8577
  if (this.guard.terminate("ensureCardCreated.outer", err)) return;
8358
- log$13.warn("thinking card failed, falling back to static", { error: String(err) });
8578
+ log$14.warn("thinking card failed, falling back to static", { error: String(err) });
8359
8579
  this.transition("creation_failed", "ensureCardCreated.outer", "creation_failed");
8360
8580
  }
8361
8581
  })();
@@ -8364,10 +8584,10 @@ var StreamingCardController = class StreamingCardController {
8364
8584
  async performFlush() {
8365
8585
  if (!this.cardKit.cardMessageId || this.isTerminalPhase) return;
8366
8586
  if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {
8367
- log$13.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
8587
+ log$14.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
8368
8588
  return;
8369
8589
  }
8370
- log$13.debug("flushCardUpdate: enter", {
8590
+ log$14.debug("flushCardUpdate: enter", {
8371
8591
  seq: this.cardKit.cardKitSequence,
8372
8592
  isCardKit: !!this.cardKit.cardKitCardId
8373
8593
  });
@@ -8389,7 +8609,7 @@ var StreamingCardController = class StreamingCardController {
8389
8609
  reasoningText: this.reasoning.accumulatedReasoningText || void 0
8390
8610
  });
8391
8611
  this.cardKit.cardKitSequence += 1;
8392
- log$13.debug("flushCardUpdate: full card update (dirty)", {
8612
+ log$14.debug("flushCardUpdate: full card update (dirty)", {
8393
8613
  seq: this.cardKit.cardKitSequence,
8394
8614
  stepCount: display?.stepCount,
8395
8615
  hasReasoning: !!this.reasoning.accumulatedReasoningText
@@ -8405,7 +8625,7 @@ var StreamingCardController = class StreamingCardController {
8405
8625
  } else if (resolvedText !== this.text.lastFlushedText) {
8406
8626
  const prevSeq = this.cardKit.cardKitSequence;
8407
8627
  this.cardKit.cardKitSequence += 1;
8408
- log$13.debug("flushCardUpdate: answer seq bump", {
8628
+ log$14.debug("flushCardUpdate: answer seq bump", {
8409
8629
  seqBefore: prevSeq,
8410
8630
  seqAfter: this.cardKit.cardKitSequence
8411
8631
  });
@@ -8420,7 +8640,7 @@ var StreamingCardController = class StreamingCardController {
8420
8640
  this.text.lastFlushedText = resolvedText;
8421
8641
  }
8422
8642
  } else {
8423
- log$13.debug("flushCardUpdate: IM patch fallback");
8643
+ log$14.debug("flushCardUpdate: IM patch fallback");
8424
8644
  const flushDisplay = this.computeToolUseDisplay();
8425
8645
  const card = buildCardContent("streaming", {
8426
8646
  text: this.reasoning.isReasoningPhase ? "" : resolvedText,
@@ -8440,31 +8660,31 @@ var StreamingCardController = class StreamingCardController {
8440
8660
  if (this.guard.terminate("flushCardUpdate", err)) return;
8441
8661
  const apiCode = extractLarkApiCode(err);
8442
8662
  if (isCardRateLimitError(err)) {
8443
- log$13.info("flushCardUpdate: rate limited (230020), skipping", { seq: this.cardKit.cardKitSequence });
8663
+ log$14.info("flushCardUpdate: rate limited (230020), skipping", { seq: this.cardKit.cardKitSequence });
8444
8664
  return;
8445
8665
  }
8446
8666
  if (isCardTableLimitError(err)) {
8447
- log$13.warn("flushCardUpdate: card table limit exceeded (230099/11310), disabling CardKit streaming", { seq: this.cardKit.cardKitSequence });
8667
+ log$14.warn("flushCardUpdate: card table limit exceeded (230099/11310), disabling CardKit streaming", { seq: this.cardKit.cardKitSequence });
8448
8668
  this.cardKit.cardKitCardId = null;
8449
8669
  return;
8450
8670
  }
8451
8671
  if (apiCode === 300317 && this.cardKit.cardKitCardId) {
8452
8672
  const oldSeq = this.cardKit.cardKitSequence;
8453
8673
  this.cardKit.cardKitSequence += 100;
8454
- log$13.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
8674
+ log$14.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
8455
8675
  oldSeq,
8456
8676
  newSeq: this.cardKit.cardKitSequence
8457
8677
  });
8458
8678
  return;
8459
8679
  }
8460
8680
  const apiDetail = extractApiDetail(err);
8461
- log$13.error("card stream update failed", {
8681
+ log$14.error("card stream update failed", {
8462
8682
  apiCode,
8463
8683
  seq: this.cardKit.cardKitSequence,
8464
8684
  apiDetail
8465
8685
  });
8466
8686
  if (this.cardKit.cardKitCardId) {
8467
- log$13.warn("disabling CardKit streaming, falling back to im.message.patch");
8687
+ log$14.warn("disabling CardKit streaming, falling back to im.message.patch");
8468
8688
  this.cardKit.cardKitCardId = null;
8469
8689
  }
8470
8690
  }
@@ -8489,7 +8709,7 @@ var StreamingCardController = class StreamingCardController {
8489
8709
  if (!this.cardKit.cardKitCardId || this.isTerminalPhase) return;
8490
8710
  try {
8491
8711
  const display = this.computeToolUseDisplay();
8492
- log$13.info("updateToolUseStatus", {
8712
+ log$14.info("updateToolUseStatus", {
8493
8713
  hasDisplay: !!display,
8494
8714
  stepCount: display?.stepCount,
8495
8715
  steps: display?.steps?.map((s) => `${s.title}:${s.status}`).join(", ")
@@ -8511,7 +8731,7 @@ var StreamingCardController = class StreamingCardController {
8511
8731
  accountId: this.deps.accountId
8512
8732
  });
8513
8733
  } catch (err) {
8514
- log$13.debug("updateToolUseStatus failed", { error: String(err) });
8734
+ log$14.debug("updateToolUseStatus failed", { error: String(err) });
8515
8735
  }
8516
8736
  }
8517
8737
  finalizeCard(source, reason) {
@@ -8523,7 +8743,7 @@ var StreamingCardController = class StreamingCardController {
8523
8743
  async closeStreamingAndUpdate(cardId, card, label) {
8524
8744
  const seqBeforeClose = this.cardKit.cardKitSequence;
8525
8745
  this.cardKit.cardKitSequence += 1;
8526
- log$13.info(`${label}: closing streaming mode`, {
8746
+ log$14.info(`${label}: closing streaming mode`, {
8527
8747
  seqBefore: seqBeforeClose,
8528
8748
  seqAfter: this.cardKit.cardKitSequence
8529
8749
  });
@@ -8536,7 +8756,7 @@ var StreamingCardController = class StreamingCardController {
8536
8756
  });
8537
8757
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
8538
8758
  this.cardKit.cardKitSequence += 1;
8539
- log$13.info(`${label}: updating card`, {
8759
+ log$14.info(`${label}: updating card`, {
8540
8760
  seqBefore: seqBeforeUpdate,
8541
8761
  seqAfter: this.cardKit.cardKitSequence
8542
8762
  });
@@ -8570,7 +8790,7 @@ function extractApiDetail(err) {
8570
8790
  }
8571
8791
  //#endregion
8572
8792
  //#region src/messaging/format-for-cc.ts
8573
- const log$12 = larkLogger("messaging/format-for-cc");
8793
+ const log$13 = larkLogger("messaging/format-for-cc");
8574
8794
  function safeParseJSON(raw) {
8575
8795
  try {
8576
8796
  return JSON.parse(raw);
@@ -8626,7 +8846,7 @@ function extractPostText(rawContent) {
8626
8846
  */
8627
8847
  function formatContentByType(ctx) {
8628
8848
  const { contentType, content, resources } = ctx;
8629
- log$12.debug("formatContentByType 开始格式化", {
8849
+ log$13.debug("formatContentByType 开始格式化", {
8630
8850
  contentType,
8631
8851
  contentLength: content?.length ?? 0,
8632
8852
  resourceCount: resources?.length ?? 0
@@ -8668,7 +8888,7 @@ function formatContentByType(ctx) {
8668
8888
  case "todo":
8669
8889
  case "vote": return content;
8670
8890
  default:
8671
- log$12.warn("遇到不支持的消息类型", { contentType });
8891
+ log$13.warn("遇到不支持的消息类型", { contentType });
8672
8892
  return `[不支持的消息类型: ${contentType}]`;
8673
8893
  }
8674
8894
  }
@@ -8711,7 +8931,7 @@ function formatMessageTime(createTime) {
8711
8931
  * @returns 格式化后的纯文本字符串
8712
8932
  */
8713
8933
  function formatForCC(ctx, isGroup) {
8714
- log$12.info("formatForCC 开始处理", {
8934
+ log$13.info("formatForCC 开始处理", {
8715
8935
  messageId: ctx.messageId,
8716
8936
  contentType: ctx.contentType,
8717
8937
  chatType: ctx.chatType,
@@ -8727,7 +8947,7 @@ function formatForCC(ctx, isGroup) {
8727
8947
  if (isGroup && ctx.senderName) parts.push(`[${ctx.senderName}]`);
8728
8948
  if (ctx.threadId) parts.push("[话题回复]");
8729
8949
  const result = (parts.length > 0 ? parts.join(" ") + " " : "") + formattedContent;
8730
- log$12.info("formatForCC 完成", {
8950
+ log$13.info("formatForCC 完成", {
8731
8951
  messageId: ctx.messageId,
8732
8952
  resultLength: result.length,
8733
8953
  hasTime: !!timeStr,
@@ -8737,7 +8957,7 @@ function formatForCC(ctx, isGroup) {
8737
8957
  }
8738
8958
  //#endregion
8739
8959
  //#region src/messaging/inbound/dispatch-cc.ts
8740
- const log$11 = larkLogger("inbound/dispatch-cc");
8960
+ const log$12 = larkLogger("inbound/dispatch-cc");
8741
8961
  async function dispatchToCC(params) {
8742
8962
  const { ctx, account, sessionRouter, processManager } = params;
8743
8963
  const chatId = ctx.chatId;
@@ -8747,7 +8967,7 @@ async function dispatchToCC(params) {
8747
8967
  const ccModel = params.model;
8748
8968
  const maxTurns = params.maxTurns;
8749
8969
  const maxBudgetUsd = params.maxBudgetUsd;
8750
- log$11.info("dispatchToCC 开始", {
8970
+ log$12.info("dispatchToCC 开始", {
8751
8971
  msgId,
8752
8972
  chatId,
8753
8973
  chatType,
@@ -8760,12 +8980,12 @@ async function dispatchToCC(params) {
8760
8980
  threadId,
8761
8981
  userId: ctx.senderId
8762
8982
  });
8763
- log$11.info("会话路由解析完成", {
8983
+ log$12.info("会话路由解析完成", {
8764
8984
  sessionId: route.sessionId,
8765
8985
  cwd: route.cwd,
8766
8986
  type: route.type
8767
8987
  });
8768
- if (params.userContext) log$11.info("用户上下文已注入", {
8988
+ if (params.userContext) log$12.info("用户上下文已注入", {
8769
8989
  userId: params.userContext.userId,
8770
8990
  isTenantIdentity: params.userContext.isTenantIdentity,
8771
8991
  credentialDir: params.userContext.credentialDir
@@ -8802,7 +9022,7 @@ async function dispatchToCC(params) {
8802
9022
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
8803
9023
  buffer = Buffer.concat(chunks);
8804
9024
  } else {
8805
- log$11.warn("图片资源下载返回未知格式,跳过", {
9025
+ log$12.warn("图片资源下载返回未知格式,跳过", {
8806
9026
  fileKey: imgRes.fileKey,
8807
9027
  responseType: typeof response
8808
9028
  });
@@ -8810,7 +9030,7 @@ async function dispatchToCC(params) {
8810
9030
  }
8811
9031
  if (resp.headers?.["content-type"]) mediaType = resp.headers["content-type"];
8812
9032
  } else {
8813
- log$11.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
9033
+ log$12.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
8814
9034
  continue;
8815
9035
  }
8816
9036
  const imgFileName = `${imgRes.fileKey}.png`;
@@ -8820,13 +9040,13 @@ async function dispatchToCC(params) {
8820
9040
  const { writeFile, mkdir } = await import("fs/promises");
8821
9041
  await mkdir(filesDir, { recursive: true });
8822
9042
  await writeFile(imgFilePath, buffer);
8823
- log$11.info("图片已保存到工作目录", {
9043
+ log$12.info("图片已保存到工作目录", {
8824
9044
  fileKey: imgRes.fileKey,
8825
9045
  path: imgFilePath,
8826
9046
  sizeBytes: buffer.length
8827
9047
  });
8828
9048
  } catch (saveErr) {
8829
- log$11.warn("图片保存到工作目录失败", {
9049
+ log$12.warn("图片保存到工作目录失败", {
8830
9050
  fileKey: imgRes.fileKey,
8831
9051
  path: imgFilePath,
8832
9052
  error: saveErr instanceof Error ? saveErr.message : String(saveErr)
@@ -8841,13 +9061,13 @@ async function dispatchToCC(params) {
8841
9061
  data: base64Data
8842
9062
  }
8843
9063
  });
8844
- log$11.info("图片资源下载成功", {
9064
+ log$12.info("图片资源下载成功", {
8845
9065
  fileKey: imgRes.fileKey,
8846
9066
  mediaType,
8847
9067
  sizeBytes: buffer.length
8848
9068
  });
8849
9069
  } catch (err) {
8850
- log$11.warn("图片资源下载失败,跳过", {
9070
+ log$12.warn("图片资源下载失败,跳过", {
8851
9071
  fileKey: imgRes.fileKey,
8852
9072
  error: err instanceof Error ? err.message : String(err)
8853
9073
  });
@@ -8857,7 +9077,7 @@ async function dispatchToCC(params) {
8857
9077
  type: "text",
8858
9078
  text: textPrompt
8859
9079
  }, ...imageBlocks];
8860
- log$11.info("已构建多模态 prompt", {
9080
+ log$12.info("已构建多模态 prompt", {
8861
9081
  textLength: textPrompt.length,
8862
9082
  imageCount: imageBlocks.length
8863
9083
  });
@@ -8870,7 +9090,7 @@ async function dispatchToCC(params) {
8870
9090
  await fsMkdir(filesDir, { recursive: true });
8871
9091
  let updatedTextPrompt = typeof prompt === "string" ? prompt : prompt[0].text;
8872
9092
  for (const res of attachmentResources) try {
8873
- log$11.info("开始下载非图片附件", {
9093
+ log$12.info("开始下载非图片附件", {
8874
9094
  type: res.type,
8875
9095
  fileKey: res.fileKey,
8876
9096
  fileName: res.fileName,
@@ -8884,7 +9104,7 @@ async function dispatchToCC(params) {
8884
9104
  },
8885
9105
  params: { type: "file" }
8886
9106
  });
8887
- log$11.info("非图片附件 API 响应已收到", {
9107
+ log$12.info("非图片附件 API 响应已收到", {
8888
9108
  type: res.type,
8889
9109
  fileKey: res.fileKey,
8890
9110
  responseType: typeof response,
@@ -8903,7 +9123,7 @@ async function dispatchToCC(params) {
8903
9123
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
8904
9124
  buffer = Buffer.concat(chunks);
8905
9125
  } else {
8906
- log$11.warn("非图片附件下载返回未知格式,跳过", {
9126
+ log$12.warn("非图片附件下载返回未知格式,跳过", {
8907
9127
  type: res.type,
8908
9128
  fileKey: res.fileKey,
8909
9129
  responseType: typeof response
@@ -8911,7 +9131,7 @@ async function dispatchToCC(params) {
8911
9131
  continue;
8912
9132
  }
8913
9133
  } else {
8914
- log$11.warn("非图片附件下载返回空,跳过", {
9134
+ log$12.warn("非图片附件下载返回空,跳过", {
8915
9135
  type: res.type,
8916
9136
  fileKey: res.fileKey
8917
9137
  });
@@ -8934,7 +9154,7 @@ async function dispatchToCC(params) {
8934
9154
  }
8935
9155
  const filePath = path.join(filesDir, savedFileName);
8936
9156
  await fsWriteFile(filePath, buffer);
8937
- log$11.info("非图片附件已保存到工作目录", {
9157
+ log$12.info("非图片附件已保存到工作目录", {
8938
9158
  type: res.type,
8939
9159
  fileKey: res.fileKey,
8940
9160
  savedFileName,
@@ -8968,7 +9188,7 @@ async function dispatchToCC(params) {
8968
9188
  }
8969
9189
  }
8970
9190
  } catch (err) {
8971
- log$11.warn("非图片附件下载失败,保留原始占位符", {
9191
+ log$12.warn("非图片附件下载失败,保留原始占位符", {
8972
9192
  type: res.type,
8973
9193
  fileKey: res.fileKey,
8974
9194
  error: err instanceof Error ? err.message : String(err)
@@ -8979,12 +9199,12 @@ async function dispatchToCC(params) {
8979
9199
  const textBlock = prompt.find((b) => b.type === "text");
8980
9200
  if (textBlock) textBlock.text = updatedTextPrompt;
8981
9201
  }
8982
- log$11.info("非图片附件处理完成", {
9202
+ log$12.info("非图片附件处理完成", {
8983
9203
  totalAttachments: attachmentResources.length,
8984
9204
  promptLength: typeof prompt === "string" ? prompt.length : prompt.find((b) => b.type === "text")?.text?.length
8985
9205
  });
8986
9206
  }
8987
- log$11.info("消息格式化完成", {
9207
+ log$12.info("消息格式化完成", {
8988
9208
  promptLength: typeof prompt === "string" ? prompt.length : prompt.length,
8989
9209
  isMultimodal: Array.isArray(prompt),
8990
9210
  isGroup
@@ -9016,50 +9236,50 @@ async function dispatchToCC(params) {
9016
9236
  };
9017
9237
  const cardController = new StreamingCardController(cardDeps);
9018
9238
  registerActiveCard(route.sessionId, cardController);
9019
- log$11.info("StreamingCardController 已创建", {
9239
+ log$12.info("StreamingCardController 已创建", {
9020
9240
  sessionId: route.sessionId,
9021
9241
  chatId,
9022
9242
  replyToMessageId: cardDeps.replyToMessageId
9023
9243
  });
9024
9244
  cardController.ensureCardCreated().catch((err) => {
9025
- log$11.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
9245
+ log$12.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
9026
9246
  });
9027
9247
  const bridge = new CCStreamBridge(cardController, {
9028
9248
  autoCompleteOnTurnEnd: true,
9029
9249
  sessionKey: route.sessionId
9030
9250
  });
9031
- log$11.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
9251
+ log$12.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
9032
9252
  const callbacks = {
9033
9253
  onTextDelta: (text) => {
9034
- log$11.debug("CC onTextDelta", {
9254
+ log$12.debug("CC onTextDelta", {
9035
9255
  sessionId: route.sessionId,
9036
9256
  deltaLen: text.length
9037
9257
  });
9038
9258
  bridge.onTextDelta(text);
9039
9259
  },
9040
9260
  onThinkingDelta: (text) => {
9041
- log$11.debug("CC onThinkingDelta", {
9261
+ log$12.debug("CC onThinkingDelta", {
9042
9262
  sessionId: route.sessionId,
9043
9263
  deltaLen: text.length
9044
9264
  });
9045
9265
  bridge.onThinkingDelta(text);
9046
9266
  },
9047
9267
  onToolUseStart: (toolName, toolInput) => {
9048
- log$11.info("CC onToolUseStart", {
9268
+ log$12.info("CC onToolUseStart", {
9049
9269
  sessionId: route.sessionId,
9050
9270
  toolName
9051
9271
  });
9052
9272
  bridge.onToolUseStart(toolName, toolInput);
9053
9273
  },
9054
9274
  onToolResult: (toolUseId) => {
9055
- log$11.info("CC onToolResult", {
9275
+ log$12.info("CC onToolResult", {
9056
9276
  sessionId: route.sessionId,
9057
9277
  toolUseId
9058
9278
  });
9059
9279
  bridge.onToolResult(toolUseId);
9060
9280
  },
9061
9281
  onToolProgress: (toolName, elapsedSeconds) => {
9062
- log$11.debug("CC onToolProgress", {
9282
+ log$12.debug("CC onToolProgress", {
9063
9283
  sessionId: route.sessionId,
9064
9284
  toolName,
9065
9285
  elapsedSeconds
@@ -9067,14 +9287,14 @@ async function dispatchToCC(params) {
9067
9287
  bridge.onToolProgress(toolName, elapsedSeconds);
9068
9288
  },
9069
9289
  onTurnEnd: (stopReason) => {
9070
- log$11.info("CC onTurnEnd", {
9290
+ log$12.info("CC onTurnEnd", {
9071
9291
  sessionId: route.sessionId,
9072
9292
  stopReason
9073
9293
  });
9074
9294
  bridge.onTurnEnd(stopReason);
9075
9295
  },
9076
9296
  onResult: (result) => {
9077
- log$11.info("CC onResult", {
9297
+ log$12.info("CC onResult", {
9078
9298
  sessionId: route.sessionId,
9079
9299
  subtype: result.subtype,
9080
9300
  isError: result.isError,
@@ -9085,14 +9305,14 @@ async function dispatchToCC(params) {
9085
9305
  bridge.onResult(result);
9086
9306
  },
9087
9307
  onError: (error) => {
9088
- log$11.error("CC 进程错误", {
9308
+ log$12.error("CC 进程错误", {
9089
9309
  sessionId: route.sessionId,
9090
9310
  error: error.message
9091
9311
  });
9092
9312
  cardController.onError(error, { kind: "cc-process" });
9093
9313
  }
9094
9314
  };
9095
- log$11.info("开始执行 CC 进程", {
9315
+ log$12.info("开始执行 CC 进程", {
9096
9316
  sessionId: route.sessionId,
9097
9317
  cwd: route.cwd,
9098
9318
  promptLength: prompt.length,
@@ -9109,10 +9329,10 @@ async function dispatchToCC(params) {
9109
9329
  maxBudgetUsd,
9110
9330
  userContext: params.userContext
9111
9331
  }, callbacks);
9112
- log$11.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
9332
+ log$12.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
9113
9333
  } catch (err) {
9114
9334
  const errorMessage = err instanceof Error ? err.message : String(err);
9115
- log$11.error("CC 进程 executePrompt 调用异常", {
9335
+ log$12.error("CC 进程 executePrompt 调用异常", {
9116
9336
  sessionId: route.sessionId,
9117
9337
  error: errorMessage
9118
9338
  });
@@ -9134,7 +9354,7 @@ async function dispatchToCC(params) {
9134
9354
  */
9135
9355
  async function dispatchTeammateEval(params) {
9136
9356
  const { chatId, prompt, account, sessionRouter, processManager } = params;
9137
- log$11.info("dispatchTeammateEval 开始", {
9357
+ log$12.info("dispatchTeammateEval 开始", {
9138
9358
  chatId,
9139
9359
  promptLength: prompt.length
9140
9360
  });
@@ -9151,12 +9371,11 @@ async function dispatchTeammateEval(params) {
9151
9371
  startToolUseTraceRun(teammateSessionKey);
9152
9372
  const NO_REPLY_TOKEN = SILENT_REPLY_TOKEN;
9153
9373
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
9154
- const PREFIX_LEN = 22;
9155
9374
  /** 缓冲的 thinking 内容 */
9156
9375
  let thinkingBuffer = "";
9157
- /** text 前缀缓冲(用于判断是否为 NO_REPLY) */
9158
- let textPrefixBuffer = "";
9159
- /** 是否已确认 CC 决定回复(前缀检测通过) */
9376
+ /** 全部 text 输出缓冲(end_turn 时统一判断是否含 NO_REPLY) */
9377
+ let fullTextBuffer = "";
9378
+ /** 是否已确认 CC 决定回复(仅在 end_turn 时才确认) */
9160
9379
  let confirmed = false;
9161
9380
  /** 是否已确认 CC 决定静默 */
9162
9381
  let silenced = false;
@@ -9169,15 +9388,15 @@ async function dispatchTeammateEval(params) {
9169
9388
  const pendingToolEvents = [];
9170
9389
  let finalStopReason = "";
9171
9390
  /**
9172
- * 确认 CC 要回复 — 创建卡片 + bridge,灌入缓冲的 thinking + text 前缀
9391
+ * 确认 CC 要回复 — 创建卡片 + bridge,灌入缓冲的 thinking + text
9173
9392
  */
9174
9393
  const confirmReply = () => {
9175
9394
  if (confirmed) return;
9176
9395
  confirmed = true;
9177
- log$11.info("teammate 确认回复,创建流式卡片", {
9396
+ log$12.info("teammate 确认回复,创建流式卡片", {
9178
9397
  chatId,
9179
9398
  thinkingLen: thinkingBuffer.length,
9180
- textPrefixLen: textPrefixBuffer.length
9399
+ textLen: fullTextBuffer.length
9181
9400
  });
9182
9401
  cardController = new StreamingCardController({
9183
9402
  cfg: {},
@@ -9208,13 +9427,18 @@ async function dispatchTeammateEval(params) {
9208
9427
  sessionKey: teammateSessionKey
9209
9428
  });
9210
9429
  if (thinkingBuffer) bridge.onThinkingDelta(thinkingBuffer);
9211
- if (textPrefixBuffer) bridge.onTextDelta(textPrefixBuffer);
9430
+ const cleanedText = fullTextBuffer.replace(/\s*NO_REPLY\s*$/, "").trim();
9431
+ if (cleanedText) bridge.onTextDelta(cleanedText);
9212
9432
  for (const evt of pendingToolEvents) if (evt.type === "start") bridge.onToolUseStart(evt.name, evt.input);
9213
9433
  else bridge.onToolResult(evt.id);
9214
9434
  pendingToolEvents.length = 0;
9215
9435
  };
9216
9436
  /**
9217
- * 处理 text delta — 前缀检测 + 流式转发
9437
+ * 处理 text delta — 全量缓冲,直到 end_turn 时统一判断
9438
+ *
9439
+ * 策略:CC 在 teammate 模式下可能先执行工具再输出文本,且文本中可能正文在前、
9440
+ * NO_REPLY 在末尾。因此不做前缀检测,而是缓冲所有 text,在 onTurnEnd(end_turn)
9441
+ * 时统一判断完整输出是否包含 NO_REPLY。
9218
9442
  */
9219
9443
  const handleTextDelta = (text) => {
9220
9444
  if (silenced) return;
@@ -9222,24 +9446,7 @@ async function dispatchTeammateEval(params) {
9222
9446
  bridge.onTextDelta(text);
9223
9447
  return;
9224
9448
  }
9225
- textPrefixBuffer += text;
9226
- if (textPrefixBuffer.length >= PREFIX_LEN) {
9227
- const trimmed = textPrefixBuffer.trim();
9228
- if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER) {
9229
- silenced = true;
9230
- log$11.info("teammate 前缀检测: 静默", {
9231
- chatId,
9232
- trimmed: trimmed.slice(0, 30)
9233
- });
9234
- return;
9235
- }
9236
- confirmReply();
9237
- return;
9238
- }
9239
- const currentTrimmed = textPrefixBuffer.trim();
9240
- const couldBeNoReply = NO_REPLY_TOKEN.startsWith(currentTrimmed);
9241
- const couldBePlaceholder = CC_INTERNAL_PLACEHOLDER.startsWith(currentTrimmed);
9242
- if (!couldBeNoReply && !couldBePlaceholder) confirmReply();
9449
+ fullTextBuffer += text;
9243
9450
  };
9244
9451
  try {
9245
9452
  await new Promise((resolve) => {
@@ -9280,17 +9487,17 @@ async function dispatchTeammateEval(params) {
9280
9487
  onTurnEnd: (stopReason) => {
9281
9488
  finalStopReason = stopReason;
9282
9489
  if (stopReason === "tool_use") {
9283
- log$11.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
9490
+ log$12.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
9284
9491
  if (confirmed && bridge) bridge.onTurnEnd(stopReason);
9285
9492
  return;
9286
9493
  }
9287
9494
  if (!confirmed && !silenced) {
9288
- const trimmed = textPrefixBuffer.trim();
9289
- if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed === "") {
9495
+ const trimmed = fullTextBuffer.trim();
9496
+ if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed.endsWith(NO_REPLY_TOKEN) || trimmed === "") {
9290
9497
  silenced = true;
9291
- log$11.info("teammate turnEnd 最终判断: 静默", {
9498
+ log$12.info("teammate turnEnd 最终判断: 静默", {
9292
9499
  chatId,
9293
- trimmed: trimmed.slice(0, 30)
9500
+ trimmed: trimmed.slice(0, 60)
9294
9501
  });
9295
9502
  } else {
9296
9503
  confirmReply();
@@ -9303,7 +9510,7 @@ async function dispatchTeammateEval(params) {
9303
9510
  resolve();
9304
9511
  },
9305
9512
  onError: (error) => {
9306
- log$11.error("teammate 评估 CC 进程错误", {
9513
+ log$12.error("teammate 评估 CC 进程错误", {
9307
9514
  chatId,
9308
9515
  error: error.message
9309
9516
  });
@@ -9316,25 +9523,25 @@ async function dispatchTeammateEval(params) {
9316
9523
  prompt,
9317
9524
  model: params.model
9318
9525
  }, callbacks).catch((err) => {
9319
- log$11.error("teammate executePrompt 异常", {
9526
+ log$12.error("teammate executePrompt 异常", {
9320
9527
  chatId,
9321
9528
  error: err instanceof Error ? err.message : String(err)
9322
9529
  });
9323
9530
  resolve();
9324
9531
  });
9325
9532
  });
9326
- log$11.info("dispatchTeammateEval 完成", {
9533
+ log$12.info("dispatchTeammateEval 完成", {
9327
9534
  chatId,
9328
9535
  confirmed,
9329
9536
  silenced,
9330
- textPrefixBufferLen: textPrefixBuffer.length,
9537
+ fullTextBufferLen: fullTextBuffer.length,
9331
9538
  thinkingBufferLen: thinkingBuffer.length,
9332
9539
  stopReason: finalStopReason
9333
9540
  });
9334
9541
  return { replied: confirmed };
9335
9542
  } catch (err) {
9336
9543
  const errorMessage = err instanceof Error ? err.message : String(err);
9337
- log$11.error("dispatchTeammateEval 异常", {
9544
+ log$12.error("dispatchTeammateEval 异常", {
9338
9545
  chatId,
9339
9546
  error: errorMessage
9340
9547
  });
@@ -10846,7 +11053,7 @@ const convertLocation = (raw) => {
10846
11053
  * injected via callbacks in `ConvertContext`. Callers are responsible
10847
11054
  * for creating the appropriate callbacks (UAT / TAT / event push).
10848
11055
  */
10849
- const log$10 = larkLogger("converters/merge-forward");
11056
+ const log$11 = larkLogger("converters/merge-forward");
10850
11057
  /**
10851
11058
  * Recursively expand a merge_forward message.
10852
11059
  *
@@ -10874,7 +11081,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
10874
11081
  try {
10875
11082
  items = await fetchSubMessages(messageId);
10876
11083
  } catch (error) {
10877
- log$10.error("fetch sub-messages failed", {
11084
+ log$11.error("fetch sub-messages failed", {
10878
11085
  messageId,
10879
11086
  error: error instanceof Error ? error.message : String(error)
10880
11087
  });
@@ -10886,7 +11093,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
10886
11093
  if (senderIds.length > 0 && batchResolveNames) try {
10887
11094
  await batchResolveNames(senderIds);
10888
11095
  } catch (err) {
10889
- log$10.debug("batchResolveNames failed (best-effort)", { error: err instanceof Error ? err.message : String(err) });
11096
+ log$11.debug("batchResolveNames failed (best-effort)", { error: err instanceof Error ? err.message : String(err) });
10890
11097
  }
10891
11098
  return formatSubTree(messageId, childrenMap, accountId, resolveUserName, convertContent);
10892
11099
  }
@@ -10966,7 +11173,7 @@ async function formatSubTree(parentId, childrenMap, accountId, resolveUserName,
10966
11173
  const indented = indentLines(content, " ");
10967
11174
  parts.push(`[${timestamp}] ${displayName}:\n${indented}`);
10968
11175
  } catch (err) {
10969
- log$10.warn("failed to convert sub-message", {
11176
+ log$11.warn("failed to convert sub-message", {
10970
11177
  messageId: item.message_id,
10971
11178
  msgType: item.msg_type ?? "unknown",
10972
11179
  error: err instanceof Error ? err.message : String(err)
@@ -11180,7 +11387,7 @@ async function convertMessageContent(raw, messageType, ctx) {
11180
11387
  }
11181
11388
  //#endregion
11182
11389
  //#region src/messaging/inbound/parse-io.ts
11183
- const log$9 = larkLogger("inbound/parse-io");
11390
+ const log$10 = larkLogger("inbound/parse-io");
11184
11391
  /**
11185
11392
  * 对 interactive 消息,通过 TAT 调用 API 获取完整 v2 卡片内容。
11186
11393
  * 事件推送的 content 可能不包含 json_card,API 调用可返回完整的 raw_card_content。
@@ -11200,7 +11407,7 @@ async function fetchCardContent(messageId, larkClient) {
11200
11407
  }
11201
11408
  }))?.data?.items?.[0]?.body?.content ?? void 0;
11202
11409
  } catch (err) {
11203
- log$9.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);
11410
+ log$10.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);
11204
11411
  return;
11205
11412
  }
11206
11413
  }
@@ -11234,11 +11441,11 @@ function createFetchSubMessages(larkClient) {
11234
11441
  * the account and log function.
11235
11442
  */
11236
11443
  function createParseResolveNames(account) {
11237
- return createBatchResolveNames(account, (...args) => log$9.info(args.map(String).join(" ")));
11444
+ return createBatchResolveNames(account, (...args) => log$10.info(args.map(String).join(" ")));
11238
11445
  }
11239
11446
  //#endregion
11240
11447
  //#region src/messaging/inbound/parse.ts
11241
- const log$8 = larkLogger("inbound/parse");
11448
+ const log$9 = larkLogger("inbound/parse");
11242
11449
  /**
11243
11450
  * Parse a raw Feishu message event into a normalised MessageContext.
11244
11451
  *
@@ -11288,7 +11495,7 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
11288
11495
  const fullContent = await fetchCardContent(event.message.message_id, larkClient);
11289
11496
  if (fullContent) {
11290
11497
  effectiveContent = fullContent;
11291
- log$8.info("replaced interactive content with full v2 card data");
11498
+ log$9.info("replaced interactive content with full v2 card data");
11292
11499
  }
11293
11500
  }
11294
11501
  const convertCtx = {
@@ -11408,6 +11615,123 @@ var MessageDedup = class {
11408
11615
  }
11409
11616
  };
11410
11617
  //#endregion
11618
+ //#region src/messaging/inbound/media-download.ts
11619
+ const log$8 = larkLogger("inbound/media-download");
11620
+ /**
11621
+ * 从飞书 API 下载单个图片资源,返回 Buffer + mediaType
11622
+ */
11623
+ async function downloadLarkImage(params) {
11624
+ const { account, messageId, fileKey } = params;
11625
+ try {
11626
+ const response = await LarkClient.fromAccount(account).sdk.im.messageResource.get({
11627
+ path: {
11628
+ message_id: messageId,
11629
+ file_key: fileKey
11630
+ },
11631
+ params: { type: "image" }
11632
+ });
11633
+ let buffer;
11634
+ let mediaType = "image/png";
11635
+ if (response instanceof ArrayBuffer) buffer = Buffer.from(response);
11636
+ else if (Buffer.isBuffer(response)) buffer = response;
11637
+ else if (response && typeof response === "object") {
11638
+ const resp = response;
11639
+ if (resp.data instanceof ArrayBuffer) buffer = Buffer.from(resp.data);
11640
+ else if (Buffer.isBuffer(resp.data)) buffer = resp.data;
11641
+ else if (resp.writeFile) {
11642
+ const chunks = [];
11643
+ const readable = resp.getReadableStream();
11644
+ for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
11645
+ buffer = Buffer.concat(chunks);
11646
+ } else {
11647
+ log$8.warn("图片资源下载返回未知格式", {
11648
+ fileKey,
11649
+ responseType: typeof response
11650
+ });
11651
+ return null;
11652
+ }
11653
+ if (resp.headers?.["content-type"]) mediaType = resp.headers["content-type"];
11654
+ } else {
11655
+ log$8.warn("图片资源下载返回空", { fileKey });
11656
+ return null;
11657
+ }
11658
+ log$8.info("图片资源下载成功", {
11659
+ fileKey,
11660
+ mediaType,
11661
+ sizeBytes: buffer.length
11662
+ });
11663
+ return {
11664
+ buffer,
11665
+ mediaType
11666
+ };
11667
+ } catch (err) {
11668
+ log$8.warn("图片资源下载失败", {
11669
+ fileKey,
11670
+ error: err instanceof Error ? err.message : String(err)
11671
+ });
11672
+ return null;
11673
+ }
11674
+ }
11675
+ /**
11676
+ * 批量下载图片资源并构建 CCContentBlock[] 多模态 prompt
11677
+ *
11678
+ * @param textPrompt - 文本部分
11679
+ * @param imageResources - 图片资源描述符列表
11680
+ * @param account - 飞书账号(用于 API 调用)
11681
+ * @param messageId - 消息 ID(用于下载)
11682
+ * @param saveDir - 可选,保存图片到该目录(供 CC 文件系统访问)
11683
+ * @returns 多模态 prompt(如果有图片)或纯文本 prompt
11684
+ */
11685
+ async function buildMultimodalPrompt(params) {
11686
+ const { textPrompt, imageResources, account, messageId, saveDir } = params;
11687
+ if (imageResources.length === 0) return textPrompt;
11688
+ const imageBlocks = [];
11689
+ for (const imgRes of imageResources) {
11690
+ const result = await downloadLarkImage({
11691
+ account,
11692
+ messageId,
11693
+ fileKey: imgRes.fileKey
11694
+ });
11695
+ if (!result) continue;
11696
+ const { buffer, mediaType } = result;
11697
+ if (saveDir) try {
11698
+ const { writeFile, mkdir } = await import("fs/promises");
11699
+ const path = await import("path");
11700
+ await mkdir(saveDir, { recursive: true });
11701
+ const imgFilePath = path.join(saveDir, `${imgRes.fileKey}.png`);
11702
+ await writeFile(imgFilePath, buffer);
11703
+ log$8.info("图片已保存到工作目录", {
11704
+ fileKey: imgRes.fileKey,
11705
+ path: imgFilePath
11706
+ });
11707
+ } catch (saveErr) {
11708
+ log$8.warn("图片保存失败", {
11709
+ fileKey: imgRes.fileKey,
11710
+ error: saveErr instanceof Error ? saveErr.message : String(saveErr)
11711
+ });
11712
+ }
11713
+ imageBlocks.push({
11714
+ type: "image",
11715
+ source: {
11716
+ type: "base64",
11717
+ media_type: mediaType,
11718
+ data: buffer.toString("base64")
11719
+ }
11720
+ });
11721
+ }
11722
+ if (imageBlocks.length > 0) {
11723
+ log$8.info("已构建多模态 prompt", {
11724
+ textLength: textPrompt.length,
11725
+ imageCount: imageBlocks.length
11726
+ });
11727
+ return [{
11728
+ type: "text",
11729
+ text: textPrompt
11730
+ }, ...imageBlocks];
11731
+ }
11732
+ return textPrompt;
11733
+ }
11734
+ //#endregion
11411
11735
  //#region src/user/types.ts
11412
11736
  /** Tenant 级别的 UserContext(群聊默认身份) */
11413
11737
  const TENANT_USER_ID = "tenant";
@@ -11533,9 +11857,7 @@ async function ensureUserDirectory(userCtx) {
11533
11857
  larkLogger("user/oauth-proxy");
11534
11858
  //#endregion
11535
11859
  //#region src/gateway/plugins/loader.ts
11536
- const PLUGIN_REGISTRY = { "preview-proxy": async () => {
11537
- return (await import("./preview-proxy-KMPQK_j4.mjs")).createPreviewProxyPlugin;
11538
- } };
11860
+ const PLUGIN_REGISTRY = {};
11539
11861
  const log$4 = larkLogger("gateway/plugin-loader");
11540
11862
  /** 已加载的插件实例列表 */
11541
11863
  let loadedPlugins = [];
@@ -11983,7 +12305,8 @@ var TeammateBuffer = class {
11983
12305
  this.evalCallback({
11984
12306
  chatId,
11985
12307
  prompt,
11986
- messageCount: messages.length
12308
+ messageCount: messages.length,
12309
+ messages: [...messages]
11987
12310
  }).catch((err) => {
11988
12311
  log$1.error("teammate 评估回调异常", {
11989
12312
  chatId,
@@ -12003,8 +12326,11 @@ var TeammateBuffer = class {
12003
12326
  const header = [
12004
12327
  "[旁听评估任务] 以下是群聊中最近的对话,你作为团队成员正在旁听。",
12005
12328
  "请判断是否需要主动参与讨论。如果不需要,只输出 NO_REPLY。",
12006
- "如果需要参与,直接输出你的回复内容。",
12007
- "注意:这是一次旁听评估,仅在本次评估中适用 NO_REPLY 规则。后续如果用户直接 @你 则必须正常回复。",
12329
+ "如果需要参与,直接输出你的回复内容(不要加 NO_REPLY)。",
12330
+ "",
12331
+ "重要规则:",
12332
+ "- 这是一次旁听评估,仅在本次评估中适用 NO_REPLY 规则。后续如果用户直接 @你 则必须正常回复。",
12333
+ "- 不要使用工具来查找或分析消息内容,直接基于消息内容判断是否参与。",
12008
12334
  "",
12009
12335
  "---"
12010
12336
  ].join("\n");
@@ -12516,6 +12842,8 @@ async function main() {
12516
12842
  workspaceRoot,
12517
12843
  externalBaseUrl: process.env.LARKPAL_EXTERNAL_URL
12518
12844
  });
12845
+ const { discoverTools } = await import("./tool-registry-consumer-DrklfqGF.mjs").then((n) => n.n);
12846
+ await discoverTools(gatewayPort);
12519
12847
  await gateway.start();
12520
12848
  logger.info("网关 HTTP 服务启动完成", {
12521
12849
  host: gatewayHost,
@@ -12557,13 +12885,29 @@ async function main() {
12557
12885
  messageCount: payload.messageCount,
12558
12886
  promptLength: payload.prompt.length
12559
12887
  });
12888
+ const allImageResources = payload.messages.flatMap((m) => (m.resources ?? []).filter((r) => r.type === "image"));
12889
+ const lastImageMsg = [...payload.messages].reverse().find((m) => m.resources?.some((r) => r.type === "image"));
12890
+ let evalPrompt = payload.prompt;
12891
+ if (allImageResources.length > 0 && lastImageMsg) {
12892
+ evalPrompt = await buildMultimodalPrompt({
12893
+ textPrompt: payload.prompt,
12894
+ imageResources: allImageResources,
12895
+ account,
12896
+ messageId: lastImageMsg.messageId
12897
+ });
12898
+ logger.info("teammate 评估已构建多模态 prompt", {
12899
+ chatId: payload.chatId,
12900
+ imageCount: allImageResources.length,
12901
+ isMultimodal: Array.isArray(evalPrompt)
12902
+ });
12903
+ }
12560
12904
  const { status: queueStatus } = enqueueFeishuChatTask({
12561
12905
  accountId: "default",
12562
12906
  chatId: payload.chatId,
12563
12907
  task: async () => {
12564
12908
  const result = await dispatchTeammateEval({
12565
12909
  chatId: payload.chatId,
12566
- prompt: payload.prompt,
12910
+ prompt: evalPrompt,
12567
12911
  account,
12568
12912
  sessionRouter,
12569
12913
  processManager: runtimeAdapter,
@@ -12652,7 +12996,8 @@ async function main() {
12652
12996
  teammateBuffer.push(parsed.chatId, {
12653
12997
  formattedText,
12654
12998
  bufferedAt: Date.now(),
12655
- messageId: msgId
12999
+ messageId: msgId,
13000
+ resources: parsed.resources.length > 0 ? parsed.resources : void 0
12656
13001
  });
12657
13002
  logger.info("回复策略: teammate 消息已写入缓冲", {
12658
13003
  msgId,