@vibe-lark/larkpal 0.1.25 → 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
  });
@@ -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) {
@@ -2197,40 +2198,43 @@ async function runBatchTasks(taskConfigs, concurrency, processManager) {
2197
2198
  logger$6.info("批量任务全部完成", { totalTasks: taskConfigs.length });
2198
2199
  }
2199
2200
  //#endregion
2200
- //#region src/gateway/ai-extract-handler.ts
2201
+ //#region src/gateway/agent-completion-handler.ts
2201
2202
  /**
2202
- * AI 数据提取路由统一 AI 能力网关
2203
+ * 通用 AI 补全路由Agent Gateway 统一能力接口
2203
2204
  *
2204
- * 提供 POST /api/ai/extract 接口,接收外部服务(如 rd-assistant-api)的结构化数据提取请求。
2205
- * 内部通过 CC(Claude Code)RuntimeAdapter 执行 AI 提取任务。
2205
+ * 提供 POST /api/agent/completion 接口,接收任意外部服务的 AI 任务请求。
2206
+ * 内部通过 CC(Claude Code)RuntimeAdapter 执行。
2207
+ *
2208
+ * 本模块是通用的 AI 能力网关接口,不包含任何特定业务逻辑。
2209
+ * 各接入方(如 rd-assistant-api 等)自行构造 prompt 和解析结果。
2206
2210
  *
2207
2211
  * 支持两种模式:
2208
- * - 同步模式(默认):等待 CC 完成后直接返回提取结果
2209
- * - 异步模式:立即返回,完成后通过 X-Callback-Url 回调通知
2212
+ * - 同步模式(默认):等待 CC 完成后直接返回结果文本
2213
+ * - 异步模式:立即返回 accepted,完成后通过 X-Callback-Url 回调通知
2210
2214
  *
2211
2215
  * 鉴权:通过 X-Internal-Secret 头验证服务间调用身份
2212
2216
  */
2213
- const logger$5 = larkLogger("gateway/ai-extract");
2217
+ const logger$5 = larkLogger("gateway/agent-completion");
2214
2218
  /** 内部通信密钥,从环境变量读取 */
2215
2219
  const INTERNAL_SECRET = process.env.LARKPAL_API_SECRET || "dev-internal-secret";
2216
- /** CC 执行时的工作目录(使用临时目录,不需要真正的项目上下文) */
2217
- const AI_EXTRACT_CWD = process.env.LARKPAL_AI_EXTRACT_CWD || "/tmp/larkpal-ai-extract";
2220
+ /** CC 执行时的工作目录(使用临时目录) */
2221
+ const COMPLETION_CWD = process.env.LARKPAL_COMPLETION_CWD || "/tmp/larkpal-completion";
2218
2222
  /**
2219
- * 创建 AI 提取路由
2223
+ * 创建通用 AI 补全路由
2220
2224
  *
2221
- * @param runtimeAdapter - CC 运行时适配器实例,用于执行 AI 提取 prompt
2225
+ * @param runtimeAdapter - CC 运行时适配器实例
2222
2226
  */
2223
- function createAiExtractRouter(runtimeAdapter) {
2227
+ function createAgentCompletionRouter(runtimeAdapter) {
2224
2228
  const router = Router();
2225
- router.post("/api/ai/extract", (req, res) => {
2226
- handleExtract(req, res, runtimeAdapter);
2229
+ router.post("/api/agent/completion", (req, res) => {
2230
+ handleCompletion(req, res, runtimeAdapter);
2227
2231
  });
2228
2232
  return router;
2229
2233
  }
2230
- async function handleExtract(req, res, runtimeAdapter) {
2234
+ async function handleCompletion(req, res, runtimeAdapter) {
2231
2235
  const secret = req.headers["x-internal-secret"];
2232
2236
  if (secret !== INTERNAL_SECRET) {
2233
- logger$5.warn("AI 提取请求鉴权失败", { providedSecret: secret?.slice(0, 8) + "..." });
2237
+ logger$5.warn("Agent completion 鉴权失败", { providedSecret: secret?.slice(0, 8) + "..." });
2234
2238
  res.status(401).json({
2235
2239
  code: 1,
2236
2240
  message: "鉴权失败"
@@ -2239,70 +2243,71 @@ async function handleExtract(req, res, runtimeAdapter) {
2239
2243
  }
2240
2244
  const callbackUrl = req.headers["x-callback-url"];
2241
2245
  const body = req.body;
2242
- if (!body.taskId || !body.fileName || !body.fileBase64 || !body.moduleName) {
2243
- logger$5.warn("AI 提取请求参数不完整", {
2246
+ if (!body.taskId || !body.prompt) {
2247
+ logger$5.warn("Agent completion 参数不完整", {
2244
2248
  hasTaskId: !!body.taskId,
2245
- hasFileName: !!body.fileName,
2246
- hasFileBase64: !!body.fileBase64,
2247
- hasModuleName: !!body.moduleName
2249
+ hasPrompt: !!body.prompt
2248
2250
  });
2249
2251
  res.status(400).json({
2250
2252
  code: 1,
2251
- message: "缺少必要参数: taskId, fileName, fileBase64, moduleName"
2253
+ message: "缺少必要参数: taskId, prompt"
2252
2254
  });
2253
2255
  return;
2254
2256
  }
2255
- logger$5.info("收到 AI 提取请求", {
2257
+ logger$5.info("收到 Agent completion 请求", {
2256
2258
  taskId: body.taskId,
2257
- fileName: body.fileName,
2258
- moduleName: body.moduleName,
2259
- extractionType: body.extractionType,
2260
- indicatorCount: body.indicators?.length || 0,
2261
- fileSize: body.fileBase64.length,
2262
- hasCallback: !!callbackUrl
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
2263
2265
  });
2264
- const prompt = buildExtractionPrompt(body);
2265
2266
  try {
2266
2267
  const { mkdir, writeFile } = await import("node:fs/promises");
2267
2268
  const { join } = await import("node:path");
2268
- await mkdir(AI_EXTRACT_CWD, { recursive: true });
2269
- const fileExt = body.fileName.split(".").pop() || "bin";
2270
- const tempFilePath = join(AI_EXTRACT_CWD, `${body.taskId}.${fileExt}`);
2271
- const fileBuffer = Buffer.from(body.fileBase64, "base64");
2272
- await writeFile(tempFilePath, fileBuffer);
2273
- logger$5.info("临时文件已写入", {
2274
- tempFilePath,
2275
- sizeBytes: fileBuffer.length
2276
- });
2277
- const sessionId = `ai-extract-${body.taskId}`;
2278
- const outputFilePath = join(AI_EXTRACT_CWD, `${body.taskId}-result.json`);
2279
- const result = await executeAndWaitResult(runtimeAdapter, {
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, {
2280
2284
  sessionId,
2281
- cwd: AI_EXTRACT_CWD,
2282
- prompt,
2283
- maxTurns: 10
2285
+ cwd: taskDir,
2286
+ prompt: body.prompt,
2287
+ maxTurns
2284
2288
  });
2285
- let extractedData = [];
2286
- try {
2289
+ if (resultFileName) try {
2287
2290
  const { readFile } = await import("node:fs/promises");
2288
- const fileContent = await readFile(outputFilePath, "utf-8");
2289
- logger$5.info("从结果文件读取提取数据", {
2290
- outputFilePath,
2291
+ const resultFilePath = join(taskDir, resultFileName);
2292
+ const fileContent = await readFile(resultFilePath, "utf-8");
2293
+ logger$5.info("从结果文件读取输出", {
2294
+ resultFilePath,
2291
2295
  contentLength: fileContent.length
2292
2296
  });
2293
- extractedData = parseExtractionResult(fileContent);
2297
+ finalResult = fileContent;
2294
2298
  } catch {
2295
- logger$5.info("结果文件不存在,从 CC 文本输出解析");
2296
- extractedData = parseExtractionResult(result);
2299
+ logger$5.info("结果文件不存在,使用 CC 文本输出");
2297
2300
  }
2298
2301
  try {
2299
- const { unlink } = await import("node:fs/promises");
2300
- await unlink(tempFilePath);
2301
- await unlink(outputFilePath).catch(() => {});
2302
+ const { rm } = await import("node:fs/promises");
2303
+ await rm(taskDir, {
2304
+ recursive: true,
2305
+ force: true
2306
+ });
2302
2307
  } catch {}
2303
- logger$5.info("AI 提取完成", {
2308
+ logger$5.info("Agent completion 完成", {
2304
2309
  taskId: body.taskId,
2305
- recordCount: extractedData.length
2310
+ resultLength: finalResult.length
2306
2311
  });
2307
2312
  if (callbackUrl) {
2308
2313
  res.status(200).json({
@@ -2310,16 +2315,16 @@ async function handleExtract(req, res, runtimeAdapter) {
2310
2315
  message: "accepted",
2311
2316
  mode: "async"
2312
2317
  });
2313
- await sendCallback(callbackUrl, body.taskId, "success", extractedData);
2318
+ await sendCallback(callbackUrl, body.taskId, "success", finalResult);
2314
2319
  } else res.status(200).json({
2315
2320
  code: 0,
2316
2321
  message: "ok",
2317
2322
  mode: "sync",
2318
- data: extractedData
2323
+ result: finalResult
2319
2324
  });
2320
2325
  } catch (err) {
2321
2326
  const errorMsg = err instanceof Error ? err.message : String(err);
2322
- logger$5.error("AI 提取执行失败", {
2327
+ logger$5.error("Agent completion 执行失败", {
2323
2328
  taskId: body.taskId,
2324
2329
  error: errorMsg
2325
2330
  });
@@ -2332,63 +2337,11 @@ async function handleExtract(req, res, runtimeAdapter) {
2332
2337
  await sendCallback(callbackUrl, body.taskId, "failed", void 0, errorMsg);
2333
2338
  } else res.status(500).json({
2334
2339
  code: 1,
2335
- message: `提取失败: ${errorMsg}`
2340
+ message: `执行失败: ${errorMsg}`
2336
2341
  });
2337
2342
  }
2338
2343
  }
2339
2344
  /**
2340
- * 构造用于数据提取的结构化 Prompt
2341
- */
2342
- function buildExtractionPrompt(body) {
2343
- const indicatorsList = (body.indicators || []).map((ind, i) => ` ${i + 1}. ${ind.name}${ind.unit ? ` (单位: ${ind.unit})` : ""}${ind.format ? ` [格式: ${ind.format}]` : ""}`).join("\n");
2344
- const fileExt = body.fileName.split(".").pop() || "";
2345
- return `你是一个精确的数据提取助手。请从提供的文件中提取结构化性能测试数据。
2346
-
2347
- ## 任务说明
2348
- - 文件名: ${body.fileName}
2349
- - 性能模块: ${body.moduleName}
2350
- - 提取方式: ${body.extractionType}
2351
-
2352
- ## 需要提取的指标
2353
- ${indicatorsList}
2354
-
2355
- ## 文件路径
2356
- 文件已保存在当前目录: ./${body.taskId}.${fileExt}
2357
-
2358
- ## 输出要求
2359
- 请读取文件内容,提取出所有样品/配方的性能数据。
2360
-
2361
- **必须以严格 JSON 格式输出**,不要包含任何其他文字。输出格式如下:
2362
- \`\`\`json
2363
- [
2364
- {
2365
- "formulaNo": "样品/配方编号(如果文件中有)",
2366
- "indicators": {
2367
- "指标名称1": 数值或字符串,
2368
- "指标名称2": 数值或字符串
2369
- },
2370
- "testConditions": {
2371
- "测试温度": "23°C",
2372
- "测试标准": "相关标准号"
2373
- },
2374
- "confidence": 0.95,
2375
- "notes": "备注信息(如有异常值或不确定项)"
2376
- }
2377
- ]
2378
- \`\`\`
2379
-
2380
- ## 注意事项
2381
- 1. confidence 为 0-1 之间的浮点数,表示对该条数据提取准确性的置信度
2382
- 2. 如果文件中某个指标的值模糊或无法确定,将 confidence 降低并在 notes 中说明
2383
- 3. 如果文件是图片(OCR),先描述图片内容再提取数据
2384
- 4. 每个独立的样品/配方/实验组应作为数组中的一个元素
2385
- 5. 数值类指标请直接输出数字类型(不要带单位),单位已在指标定义中给出
2386
-
2387
- 请直接读取文件并输出 JSON 结果。
2388
-
2389
- **重要:你必须将最终的 JSON 结果写入文件 \`./${body.taskId}-result.json\`(只写纯 JSON 数组,不要包含 markdown 代码块标记)。**`;
2390
- }
2391
- /**
2392
2345
  * 调用 CC 执行 prompt 并等待完成
2393
2346
  */
2394
2347
  async function executeAndWaitResult(runtimeAdapter, config) {
@@ -2417,55 +2370,15 @@ async function executeAndWaitResult(runtimeAdapter, config) {
2417
2370
  });
2418
2371
  }
2419
2372
  /**
2420
- * 从 CC 输出中解析结构化数据
2421
- */
2422
- function parseExtractionResult(rawOutput) {
2423
- logger$5.info("解析 CC 输出", {
2424
- outputLength: rawOutput.length,
2425
- preview: rawOutput.slice(0, 200)
2426
- });
2427
- const codeBlockMatch = rawOutput.match(/```json\s*\n?([\s\S]*?)```/);
2428
- if (codeBlockMatch) try {
2429
- return parseAndValidateJson(codeBlockMatch[1].trim());
2430
- } catch {}
2431
- const arrayMatch = rawOutput.match(/\[[\s\S]*\]/);
2432
- if (arrayMatch) try {
2433
- return parseAndValidateJson(arrayMatch[0]);
2434
- } catch {}
2435
- const objMatch = rawOutput.match(/\{[\s\S]*\}/);
2436
- if (objMatch) try {
2437
- return parseAndValidateJson(`[${objMatch[0]}]`);
2438
- } catch {}
2439
- try {
2440
- return parseAndValidateJson(rawOutput.trim());
2441
- } catch {
2442
- logger$5.warn("无法从 CC 输出中解析 JSON,返回空结果", { output: rawOutput.slice(0, 500) });
2443
- return [];
2444
- }
2445
- }
2446
- /**
2447
- * 解析并校验 JSON 格式的提取结果
2448
- */
2449
- function parseAndValidateJson(jsonStr) {
2450
- const parsed = JSON.parse(jsonStr);
2451
- return (Array.isArray(parsed) ? parsed : [parsed]).map((item) => ({
2452
- formulaNo: item.formulaNo || item.formula_no || item.sampleId || item.sample_id,
2453
- indicators: item.indicators || item.values || {},
2454
- testConditions: item.testConditions || item.test_conditions || item.conditions,
2455
- confidence: typeof item.confidence === "number" ? Math.max(0, Math.min(1, item.confidence)) : .8,
2456
- notes: item.notes || item.remarks
2457
- }));
2458
- }
2459
- /**
2460
2373
  * 向调用方发送回调通知
2461
2374
  */
2462
- async function sendCallback(callbackUrl, taskId, status, data, error) {
2375
+ async function sendCallback(callbackUrl, taskId, status, result, error) {
2463
2376
  try {
2464
2377
  logger$5.info("发送回调通知", {
2465
2378
  callbackUrl,
2466
2379
  taskId,
2467
2380
  status,
2468
- recordCount: data?.length
2381
+ resultLength: result?.length
2469
2382
  });
2470
2383
  const response = await fetch(callbackUrl, {
2471
2384
  method: "POST",
@@ -2476,7 +2389,7 @@ async function sendCallback(callbackUrl, taskId, status, data, error) {
2476
2389
  body: JSON.stringify({
2477
2390
  taskId,
2478
2391
  status,
2479
- data,
2392
+ result,
2480
2393
  error
2481
2394
  })
2482
2395
  });
@@ -2952,7 +2865,7 @@ function createHooksRouter() {
2952
2865
  * 提供 /api/scheduled-tasks 的 RESTful CRUD 接口。
2953
2866
  * 每个请求和响应都记录详细日志,便于后续错误排查。
2954
2867
  */
2955
- const log$23 = larkLogger("gateway/scheduler-handler");
2868
+ const log$24 = larkLogger("gateway/scheduler-handler");
2956
2869
  /**
2957
2870
  * 创建定时任务路由
2958
2871
  *
@@ -2963,16 +2876,16 @@ function createSchedulerRouter(taskManager) {
2963
2876
  const router = Router();
2964
2877
  router.get("/api/scheduled-tasks", (req, res) => {
2965
2878
  const requestId = req.headers["x-request-id"];
2966
- log$23.info("请求列出所有定时任务", { requestId });
2879
+ log$24.info("请求列出所有定时任务", { requestId });
2967
2880
  try {
2968
2881
  const tasks = taskManager.listTasks();
2969
- log$23.info("返回定时任务列表", {
2882
+ log$24.info("返回定时任务列表", {
2970
2883
  requestId,
2971
2884
  count: tasks.length
2972
2885
  });
2973
2886
  res.json({ tasks });
2974
2887
  } catch (err) {
2975
- log$23.error("列出定时任务失败", {
2888
+ log$24.error("列出定时任务失败", {
2976
2889
  requestId,
2977
2890
  error: String(err)
2978
2891
  });
@@ -2985,13 +2898,13 @@ function createSchedulerRouter(taskManager) {
2985
2898
  router.post("/api/scheduled-tasks", (req, res) => {
2986
2899
  const requestId = req.headers["x-request-id"];
2987
2900
  const body = req.body;
2988
- log$23.info("请求创建定时任务", {
2901
+ log$24.info("请求创建定时任务", {
2989
2902
  requestId,
2990
2903
  body: JSON.stringify(body)
2991
2904
  });
2992
2905
  const { name, cron: cronExpr, session_id, cwd, prompt, example_output, constraints } = body;
2993
2906
  if (!name || typeof name !== "string") {
2994
- log$23.warn("创建任务参数缺失: name", { requestId });
2907
+ log$24.warn("创建任务参数缺失: name", { requestId });
2995
2908
  res.status(400).json({
2996
2909
  error: "参数错误",
2997
2910
  detail: "name 为必填字符串"
@@ -2999,7 +2912,7 @@ function createSchedulerRouter(taskManager) {
2999
2912
  return;
3000
2913
  }
3001
2914
  if (!cronExpr || typeof cronExpr !== "string") {
3002
- log$23.warn("创建任务参数缺失: cron", { requestId });
2915
+ log$24.warn("创建任务参数缺失: cron", { requestId });
3003
2916
  res.status(400).json({
3004
2917
  error: "参数错误",
3005
2918
  detail: "cron 为必填字符串"
@@ -3007,7 +2920,7 @@ function createSchedulerRouter(taskManager) {
3007
2920
  return;
3008
2921
  }
3009
2922
  if (!session_id || typeof session_id !== "string") {
3010
- log$23.warn("创建任务参数缺失: session_id", { requestId });
2923
+ log$24.warn("创建任务参数缺失: session_id", { requestId });
3011
2924
  res.status(400).json({
3012
2925
  error: "参数错误",
3013
2926
  detail: "session_id 为必填字符串"
@@ -3015,7 +2928,7 @@ function createSchedulerRouter(taskManager) {
3015
2928
  return;
3016
2929
  }
3017
2930
  if (!cwd || typeof cwd !== "string") {
3018
- log$23.warn("创建任务参数缺失: cwd", { requestId });
2931
+ log$24.warn("创建任务参数缺失: cwd", { requestId });
3019
2932
  res.status(400).json({
3020
2933
  error: "参数错误",
3021
2934
  detail: "cwd 为必填字符串"
@@ -3023,7 +2936,7 @@ function createSchedulerRouter(taskManager) {
3023
2936
  return;
3024
2937
  }
3025
2938
  if (!prompt || typeof prompt !== "string") {
3026
- log$23.warn("创建任务参数缺失: prompt", { requestId });
2939
+ log$24.warn("创建任务参数缺失: prompt", { requestId });
3027
2940
  res.status(400).json({
3028
2941
  error: "参数错误",
3029
2942
  detail: "prompt 为必填字符串"
@@ -3031,7 +2944,7 @@ function createSchedulerRouter(taskManager) {
3031
2944
  return;
3032
2945
  }
3033
2946
  if (!cron.validate(cronExpr)) {
3034
- log$23.warn("cron 表达式不合法", {
2947
+ log$24.warn("cron 表达式不合法", {
3035
2948
  requestId,
3036
2949
  cron: cronExpr
3037
2950
  });
@@ -3052,7 +2965,7 @@ function createSchedulerRouter(taskManager) {
3052
2965
  constraints
3053
2966
  };
3054
2967
  const task = taskManager.createTask(params);
3055
- log$23.info("定时任务创建成功", {
2968
+ log$24.info("定时任务创建成功", {
3056
2969
  requestId,
3057
2970
  taskId: task.id,
3058
2971
  name: task.name,
@@ -3060,7 +2973,7 @@ function createSchedulerRouter(taskManager) {
3060
2973
  });
3061
2974
  res.status(201).json({ task });
3062
2975
  } catch (err) {
3063
- log$23.error("创建定时任务失败", {
2976
+ log$24.error("创建定时任务失败", {
3064
2977
  requestId,
3065
2978
  error: String(err)
3066
2979
  });
@@ -3074,13 +2987,13 @@ function createSchedulerRouter(taskManager) {
3074
2987
  const requestId = req.headers["x-request-id"];
3075
2988
  const { id } = req.params;
3076
2989
  const body = req.body;
3077
- log$23.info("请求更新定时任务", {
2990
+ log$24.info("请求更新定时任务", {
3078
2991
  requestId,
3079
2992
  taskId: id,
3080
2993
  body: JSON.stringify(body)
3081
2994
  });
3082
2995
  if (!taskManager.getTask(id)) {
3083
- log$23.warn("更新的任务不存在", {
2996
+ log$24.warn("更新的任务不存在", {
3084
2997
  requestId,
3085
2998
  taskId: id
3086
2999
  });
@@ -3092,7 +3005,7 @@ function createSchedulerRouter(taskManager) {
3092
3005
  }
3093
3006
  if (body.cron !== void 0) {
3094
3007
  if (typeof body.cron !== "string" || !cron.validate(body.cron)) {
3095
- log$23.warn("更新的 cron 表达式不合法", {
3008
+ log$24.warn("更新的 cron 表达式不合法", {
3096
3009
  requestId,
3097
3010
  taskId: id,
3098
3011
  cron: body.cron
@@ -3113,7 +3026,7 @@ function createSchedulerRouter(taskManager) {
3113
3026
  if (body.constraints !== void 0) updates.constraints = body.constraints;
3114
3027
  if (body.enabled !== void 0) updates.enabled = body.enabled;
3115
3028
  const task = taskManager.updateTask(id, updates);
3116
- log$23.info("定时任务更新成功", {
3029
+ log$24.info("定时任务更新成功", {
3117
3030
  requestId,
3118
3031
  taskId: task.id,
3119
3032
  name: task.name,
@@ -3121,7 +3034,7 @@ function createSchedulerRouter(taskManager) {
3121
3034
  });
3122
3035
  res.json({ task });
3123
3036
  } catch (err) {
3124
- log$23.error("更新定时任务失败", {
3037
+ log$24.error("更新定时任务失败", {
3125
3038
  requestId,
3126
3039
  taskId: id,
3127
3040
  error: String(err)
@@ -3135,12 +3048,12 @@ function createSchedulerRouter(taskManager) {
3135
3048
  router.delete("/api/scheduled-tasks/:id", (req, res) => {
3136
3049
  const requestId = req.headers["x-request-id"];
3137
3050
  const id = String(req.params.id);
3138
- log$23.info("请求删除定时任务", {
3051
+ log$24.info("请求删除定时任务", {
3139
3052
  requestId,
3140
3053
  taskId: id
3141
3054
  });
3142
3055
  if (!taskManager.getTask(id)) {
3143
- log$23.warn("删除的任务不存在", {
3056
+ log$24.warn("删除的任务不存在", {
3144
3057
  requestId,
3145
3058
  taskId: id
3146
3059
  });
@@ -3152,7 +3065,7 @@ function createSchedulerRouter(taskManager) {
3152
3065
  }
3153
3066
  try {
3154
3067
  const deleted = taskManager.deleteTask(id);
3155
- log$23.info("定时任务删除结果", {
3068
+ log$24.info("定时任务删除结果", {
3156
3069
  requestId,
3157
3070
  taskId: id,
3158
3071
  deleted
@@ -3162,7 +3075,7 @@ function createSchedulerRouter(taskManager) {
3162
3075
  deleted: true
3163
3076
  });
3164
3077
  } catch (err) {
3165
- log$23.error("删除定时任务失败", {
3078
+ log$24.error("删除定时任务失败", {
3166
3079
  requestId,
3167
3080
  taskId: id,
3168
3081
  error: String(err)
@@ -3192,7 +3105,7 @@ function createSchedulerRouter(taskManager) {
3192
3105
  * - GET /open-apis/application/v6/applications/me?lang=zh_cn(需要 application:application:self_manage 权限,返回完整信息)
3193
3106
  * 如果后者无权限则 fallback 到前者
3194
3107
  */
3195
- const log$22 = larkLogger("core/app-info-sync");
3108
+ const log$23 = larkLogger("core/app-info-sync");
3196
3109
  /**
3197
3110
  * 从飞书获取应用信息
3198
3111
  *
@@ -3203,7 +3116,7 @@ async function fetchAppInfo(credentials) {
3203
3116
  const { appId, appSecret } = credentials;
3204
3117
  const token = await getTenantAccessToken(appId, appSecret);
3205
3118
  if (!token) {
3206
- log$22.error("获取 tenant_access_token 失败,无法同步应用信息");
3119
+ log$23.error("获取 tenant_access_token 失败,无法同步应用信息");
3207
3120
  return null;
3208
3121
  }
3209
3122
  const fullInfo = await fetchFromApplicationApi(token);
@@ -3214,13 +3127,13 @@ async function fetchAppInfo(credentials) {
3214
3127
  async function fetchFromApplicationApi(token) {
3215
3128
  try {
3216
3129
  const data = await (await fetch("https://open.feishu.cn/open-apis/application/v6/applications/me?lang=zh_cn", { headers: { Authorization: `Bearer ${token}` } })).json();
3217
- log$22.info("application/v6 API 响应", {
3130
+ log$23.info("application/v6 API 响应", {
3218
3131
  code: data.code,
3219
3132
  msg: data.msg,
3220
3133
  hasApp: !!data.data?.app
3221
3134
  });
3222
3135
  if (data.code !== 0 || !data.data?.app) {
3223
- log$22.warn("application/v6 API 返回非零或无数据,将 fallback", {
3136
+ log$23.warn("application/v6 API 返回非零或无数据,将 fallback", {
3224
3137
  code: data.code,
3225
3138
  msg: data.msg
3226
3139
  });
@@ -3235,7 +3148,7 @@ async function fetchFromApplicationApi(token) {
3235
3148
  helpDocUrl: zhInfo?.help_use
3236
3149
  };
3237
3150
  } catch (err) {
3238
- 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) });
3239
3152
  return null;
3240
3153
  }
3241
3154
  }
@@ -3243,13 +3156,13 @@ async function fetchFromApplicationApi(token) {
3243
3156
  async function fetchFromBotApi(token) {
3244
3157
  try {
3245
3158
  const data = await (await fetch("https://open.feishu.cn/open-apis/bot/v3/info", { headers: { Authorization: `Bearer ${token}` } })).json();
3246
- log$22.info("bot/v3/info API 响应", {
3159
+ log$23.info("bot/v3/info API 响应", {
3247
3160
  code: data.code,
3248
3161
  msg: data.msg,
3249
3162
  hasBot: !!data.bot
3250
3163
  });
3251
3164
  if (data.code !== 0 || !data.bot) {
3252
- log$22.error("bot/v3/info API 失败", {
3165
+ log$23.error("bot/v3/info API 失败", {
3253
3166
  code: data.code,
3254
3167
  msg: data.msg
3255
3168
  });
@@ -3260,7 +3173,7 @@ async function fetchFromBotApi(token) {
3260
3173
  avatarUrl: data.bot.avatar_url
3261
3174
  };
3262
3175
  } catch (err) {
3263
- 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) });
3264
3177
  return null;
3265
3178
  }
3266
3179
  }
@@ -3276,7 +3189,7 @@ async function getTenantAccessToken(appId, appSecret) {
3276
3189
  })
3277
3190
  })).json();
3278
3191
  if (data.code !== 0 || !data.tenant_access_token) {
3279
- log$22.error("获取 tenant_access_token 失败", {
3192
+ log$23.error("获取 tenant_access_token 失败", {
3280
3193
  code: data.code,
3281
3194
  msg: data.msg
3282
3195
  });
@@ -3284,7 +3197,7 @@ async function getTenantAccessToken(appId, appSecret) {
3284
3197
  }
3285
3198
  return data.tenant_access_token;
3286
3199
  } catch (err) {
3287
- 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) });
3288
3201
  return null;
3289
3202
  }
3290
3203
  }
@@ -3322,10 +3235,10 @@ function parseDocTokenFromUrl(url) {
3322
3235
  async function fetchDocContent(docUrl, accessToken) {
3323
3236
  const parsed = parseDocTokenFromUrl(docUrl);
3324
3237
  if (!parsed) {
3325
- log$22.warn("无法从 URL 中解析文档 token", { url: docUrl });
3238
+ log$23.warn("无法从 URL 中解析文档 token", { url: docUrl });
3326
3239
  return null;
3327
3240
  }
3328
- log$22.info("开始读取人设文档", {
3241
+ log$23.info("开始读取人设文档", {
3329
3242
  url: docUrl,
3330
3243
  type: parsed.type,
3331
3244
  token: parsed.token
@@ -3333,18 +3246,18 @@ async function fetchDocContent(docUrl, accessToken) {
3333
3246
  let docToken = parsed.token;
3334
3247
  if (parsed.type === "wiki") {
3335
3248
  const realToken = await resolveWikiNodeToDocToken(parsed.token, accessToken);
3336
- if (!realToken) log$22.warn("wiki 节点解析失败,尝试直接使用 token 读取");
3249
+ if (!realToken) log$23.warn("wiki 节点解析失败,尝试直接使用 token 读取");
3337
3250
  else docToken = realToken;
3338
3251
  }
3339
3252
  try {
3340
3253
  const data = await (await fetch(`https://open.feishu.cn/open-apis/docx/v1/documents/${docToken}/raw_content`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
3341
- log$22.info("文档 raw_content API 响应", {
3254
+ log$23.info("文档 raw_content API 响应", {
3342
3255
  code: data.code,
3343
3256
  msg: data.msg,
3344
3257
  contentLength: data.data?.content?.length
3345
3258
  });
3346
3259
  if (data.code !== 0 || !data.data?.content) {
3347
- log$22.warn("读取文档内容失败", {
3260
+ log$23.warn("读取文档内容失败", {
3348
3261
  code: data.code,
3349
3262
  msg: data.msg,
3350
3263
  docToken
@@ -3353,7 +3266,7 @@ async function fetchDocContent(docUrl, accessToken) {
3353
3266
  }
3354
3267
  return data.data.content.trim();
3355
3268
  } catch (err) {
3356
- log$22.error("读取文档内容异常", {
3269
+ log$23.error("读取文档内容异常", {
3357
3270
  error: err instanceof Error ? err.message : String(err),
3358
3271
  docToken
3359
3272
  });
@@ -3364,7 +3277,7 @@ async function fetchDocContent(docUrl, accessToken) {
3364
3277
  async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
3365
3278
  try {
3366
3279
  const data = await (await fetch(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token=${wikiToken}`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
3367
- log$22.info("wiki get_node API 响应", {
3280
+ log$23.info("wiki get_node API 响应", {
3368
3281
  code: data.code,
3369
3282
  msg: data.msg,
3370
3283
  objType: data.data?.node?.obj_type
@@ -3372,7 +3285,7 @@ async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
3372
3285
  if (data.code !== 0 || !data.data?.node?.obj_token) return null;
3373
3286
  return data.data.node.obj_token;
3374
3287
  } catch (err) {
3375
- 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) });
3376
3289
  return null;
3377
3290
  }
3378
3291
  }
@@ -3394,7 +3307,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3394
3307
  const infoBlock = buildAppInfoBlock(appInfo);
3395
3308
  if (!existsSync$1(claudeMdPath)) {
3396
3309
  await writeFile$1(claudeMdPath, infoBlock + "\n\n" + getDefaultClaudeMdBody(), "utf-8");
3397
- log$22.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
3310
+ log$23.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
3398
3311
  return;
3399
3312
  }
3400
3313
  let content = await readFile$1(claudeMdPath, "utf-8");
@@ -3406,7 +3319,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3406
3319
  content = before + infoBlock + after;
3407
3320
  } else content = infoBlock + "\n\n" + content;
3408
3321
  await writeFile$1(claudeMdPath, content, "utf-8");
3409
- log$22.info("CLAUDE.md 应用信息已同步", {
3322
+ log$23.info("CLAUDE.md 应用信息已同步", {
3410
3323
  appName: appInfo.appName,
3411
3324
  hasDescription: !!appInfo.description,
3412
3325
  hasAvatar: !!appInfo.avatarUrl
@@ -3421,7 +3334,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
3421
3334
  async function syncPersonaDocToClaudeMd(personaContent) {
3422
3335
  const claudeMdPath = join$1(homedir$1(), ".claude", "CLAUDE.md");
3423
3336
  if (!existsSync$1(claudeMdPath)) {
3424
- log$22.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
3337
+ log$23.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
3425
3338
  return;
3426
3339
  }
3427
3340
  let content = await readFile$1(claudeMdPath, "utf-8");
@@ -3442,7 +3355,7 @@ async function syncPersonaDocToClaudeMd(personaContent) {
3442
3355
  content = before + personaBlock + after;
3443
3356
  } else content = content.trimEnd() + "\n\n" + personaBlock + "\n";
3444
3357
  await writeFile$1(claudeMdPath, content, "utf-8");
3445
- log$22.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
3358
+ log$23.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
3446
3359
  }
3447
3360
  /** 构建应用信息标记区块 */
3448
3361
  function buildAppInfoBlock(appInfo) {
@@ -3480,24 +3393,24 @@ function getDefaultClaudeMdBody() {
3480
3393
  * @returns 同步后的应用信息,如果失败返回 null
3481
3394
  */
3482
3395
  async function syncAppInfo(credentials) {
3483
- log$22.info("开始同步应用信息", { appId: credentials.appId });
3396
+ log$23.info("开始同步应用信息", { appId: credentials.appId });
3484
3397
  const appInfo = await fetchAppInfo(credentials);
3485
3398
  if (!appInfo) {
3486
- log$22.warn("获取应用信息失败,跳过同步");
3399
+ log$23.warn("获取应用信息失败,跳过同步");
3487
3400
  return null;
3488
3401
  }
3489
3402
  await syncAppInfoToClaudeMd(appInfo);
3490
3403
  if (appInfo.helpDocUrl) {
3491
- log$22.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
3404
+ log$23.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
3492
3405
  const token = await getTenantAccessToken(credentials.appId, credentials.appSecret);
3493
3406
  if (token) {
3494
3407
  const personaContent = await fetchDocContent(appInfo.helpDocUrl, token);
3495
3408
  if (personaContent) await syncPersonaDocToClaudeMd(personaContent);
3496
- else log$22.warn("人设文档内容为空或读取失败,跳过同步");
3409
+ else log$23.warn("人设文档内容为空或读取失败,跳过同步");
3497
3410
  }
3498
3411
  }
3499
3412
  await installSyncSkill();
3500
- log$22.info("应用信息同步完成", {
3413
+ log$23.info("应用信息同步完成", {
3501
3414
  appName: appInfo.appName,
3502
3415
  description: appInfo.description?.substring(0, 50),
3503
3416
  hasPersonaDoc: !!appInfo.helpDocUrl
@@ -3515,7 +3428,7 @@ async function installSyncSkill() {
3515
3428
  if ((await readFile$1(SYNC_SKILL_PATH, "utf-8")).includes(`skill-version: ${SYNC_SKILL_VERSION}`)) return;
3516
3429
  }
3517
3430
  await writeFile$1(SYNC_SKILL_PATH, SYNC_SKILL_CONTENT, "utf-8");
3518
- log$22.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
3431
+ log$23.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
3519
3432
  }
3520
3433
  const SYNC_SKILL_CONTENT = `---
3521
3434
  skill-version: ${SYNC_SKILL_VERSION}
@@ -3605,7 +3518,7 @@ function notImplemented(_req, res) {
3605
3518
  function registerRoutes(app, processManager, scheduledTaskManager, appCredentials) {
3606
3519
  if (processManager) {
3607
3520
  app.use(createExecuteRouter(processManager));
3608
- app.use(createAiExtractRouter(processManager));
3521
+ app.use(createAgentCompletionRouter(processManager));
3609
3522
  } else {
3610
3523
  logger$1.warn("processManager 未注入,执行路由使用占位 handler");
3611
3524
  app.post("/api/execute", notImplemented);
@@ -3623,6 +3536,7 @@ function registerRoutes(app, processManager, scheduledTaskManager, appCredential
3623
3536
  app.delete("/api/scheduled-tasks/:id", notImplemented);
3624
3537
  }
3625
3538
  app.use("/api", createStatusRouter());
3539
+ app.use(createToolInvokeProxyRouter());
3626
3540
  app.use("/hooks", createHooksRouter());
3627
3541
  if (appCredentials) app.post("/api/sync-app-info", async (_req, res) => {
3628
3542
  try {
@@ -3709,7 +3623,7 @@ function createGatewayServer(config) {
3709
3623
  *
3710
3624
  * 持久化文件: /workspace/config/scheduled-tasks.json
3711
3625
  */
3712
- const log$21 = larkLogger("gateway/scheduler");
3626
+ const log$22 = larkLogger("gateway/scheduler");
3713
3627
  /** 持久化文件路径 */
3714
3628
  const PERSIST_PATH = "/workspace/config/scheduled-tasks.json";
3715
3629
  /**
@@ -3727,7 +3641,7 @@ var ScheduledTaskManager = class {
3727
3641
  processManager;
3728
3642
  constructor(processManager) {
3729
3643
  this.processManager = processManager;
3730
- log$21.info("ScheduledTaskManager 已创建");
3644
+ log$22.info("ScheduledTaskManager 已创建");
3731
3645
  }
3732
3646
  /**
3733
3647
  * 创建定时任务
@@ -3747,7 +3661,7 @@ var ScheduledTaskManager = class {
3747
3661
  enabled: true,
3748
3662
  created_at: (/* @__PURE__ */ new Date()).toISOString()
3749
3663
  };
3750
- log$21.info("创建定时任务", {
3664
+ log$22.info("创建定时任务", {
3751
3665
  taskId: task.id,
3752
3666
  name: task.name,
3753
3667
  cron: task.cron,
@@ -3783,7 +3697,7 @@ var ScheduledTaskManager = class {
3783
3697
  if (updates.example_output !== void 0) task.example_output = updates.example_output;
3784
3698
  if (updates.constraints !== void 0) task.constraints = updates.constraints;
3785
3699
  if (updates.enabled !== void 0) task.enabled = updates.enabled;
3786
- log$21.info("更新定时任务", {
3700
+ log$22.info("更新定时任务", {
3787
3701
  taskId: id,
3788
3702
  name: task.name,
3789
3703
  cronChanged: oldCron !== task.cron,
@@ -3805,10 +3719,10 @@ var ScheduledTaskManager = class {
3805
3719
  deleteTask(id) {
3806
3720
  const task = this.tasks.get(id);
3807
3721
  if (!task) {
3808
- log$21.warn("尝试删除不存在的任务", { taskId: id });
3722
+ log$22.warn("尝试删除不存在的任务", { taskId: id });
3809
3723
  return false;
3810
3724
  }
3811
- log$21.info("删除定时任务", {
3725
+ log$22.info("删除定时任务", {
3812
3726
  taskId: id,
3813
3727
  name: task.name
3814
3728
  });
@@ -3823,28 +3737,28 @@ var ScheduledTaskManager = class {
3823
3737
  * 启动时调用,读取 JSON 文件并为每个 enabled 的任务注册 cron。
3824
3738
  */
3825
3739
  async loadFromDisk() {
3826
- log$21.info("从磁盘加载定时任务", { path: PERSIST_PATH });
3740
+ log$22.info("从磁盘加载定时任务", { path: PERSIST_PATH });
3827
3741
  try {
3828
3742
  const content = await readFile(PERSIST_PATH, "utf-8");
3829
3743
  const data = JSON.parse(content);
3830
3744
  if (!Array.isArray(data)) {
3831
- log$21.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
3745
+ log$22.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
3832
3746
  return;
3833
3747
  }
3834
3748
  for (const task of data) {
3835
3749
  this.tasks.set(task.id, task);
3836
3750
  if (task.enabled) this.registerCronJob(task);
3837
3751
  }
3838
- log$21.info("定时任务加载完成", {
3752
+ log$22.info("定时任务加载完成", {
3839
3753
  total: data.length,
3840
3754
  enabled: data.filter((t) => t.enabled).length
3841
3755
  });
3842
3756
  } catch (err) {
3843
3757
  if (err.code === "ENOENT") {
3844
- log$21.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
3758
+ log$22.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
3845
3759
  return;
3846
3760
  }
3847
- log$21.error("加载定时任务失败", {
3761
+ log$22.error("加载定时任务失败", {
3848
3762
  path: PERSIST_PATH,
3849
3763
  error: String(err)
3850
3764
  });
@@ -3860,12 +3774,12 @@ var ScheduledTaskManager = class {
3860
3774
  try {
3861
3775
  await mkdir(dirname(PERSIST_PATH), { recursive: true });
3862
3776
  await writeFile(PERSIST_PATH, JSON.stringify(tasks, null, 2), "utf-8");
3863
- log$21.info("定时任务已持久化到磁盘", {
3777
+ log$22.info("定时任务已持久化到磁盘", {
3864
3778
  path: PERSIST_PATH,
3865
3779
  count: tasks.length
3866
3780
  });
3867
3781
  } catch (err) {
3868
- log$21.error("持久化定时任务失败", {
3782
+ log$22.error("持久化定时任务失败", {
3869
3783
  path: PERSIST_PATH,
3870
3784
  error: String(err)
3871
3785
  });
@@ -3878,13 +3792,13 @@ var ScheduledTaskManager = class {
3878
3792
  */
3879
3793
  registerCronJob(task) {
3880
3794
  this.unregisterCronJob(task.id);
3881
- log$21.info("注册 cron 调度", {
3795
+ log$22.info("注册 cron 调度", {
3882
3796
  taskId: task.id,
3883
3797
  name: task.name,
3884
3798
  cron: task.cron
3885
3799
  });
3886
3800
  const job = cron.schedule(task.cron, () => {
3887
- log$21.info("cron 触发任务执行", {
3801
+ log$22.info("cron 触发任务执行", {
3888
3802
  taskId: task.id,
3889
3803
  name: task.name
3890
3804
  });
@@ -3900,7 +3814,7 @@ var ScheduledTaskManager = class {
3900
3814
  if (job) {
3901
3815
  job.stop();
3902
3816
  this.cronJobs.delete(taskId);
3903
- log$21.info("已注销 cron 调度", { taskId });
3817
+ log$22.info("已注销 cron 调度", { taskId });
3904
3818
  }
3905
3819
  }
3906
3820
  /**
@@ -3910,10 +3824,10 @@ var ScheduledTaskManager = class {
3910
3824
  */
3911
3825
  stopAll() {
3912
3826
  const count = this.cronJobs.size;
3913
- log$21.info("停止所有 cron 调度", { count });
3827
+ log$22.info("停止所有 cron 调度", { count });
3914
3828
  for (const [taskId, job] of this.cronJobs) {
3915
3829
  job.stop();
3916
- log$21.debug("已停止 cron 调度", { taskId });
3830
+ log$22.debug("已停止 cron 调度", { taskId });
3917
3831
  }
3918
3832
  this.cronJobs.clear();
3919
3833
  }
@@ -3927,17 +3841,17 @@ var ScheduledTaskManager = class {
3927
3841
  async executeTask(taskId) {
3928
3842
  const task = this.tasks.get(taskId);
3929
3843
  if (!task) {
3930
- log$21.warn("任务执行时未找到任务", { taskId });
3844
+ log$22.warn("任务执行时未找到任务", { taskId });
3931
3845
  return;
3932
3846
  }
3933
3847
  if (!task.enabled) {
3934
- log$21.info("任务已禁用,跳过执行", {
3848
+ log$22.info("任务已禁用,跳过执行", {
3935
3849
  taskId,
3936
3850
  name: task.name
3937
3851
  });
3938
3852
  return;
3939
3853
  }
3940
- log$21.info("开始执行定时任务", {
3854
+ log$22.info("开始执行定时任务", {
3941
3855
  taskId: task.id,
3942
3856
  name: task.name,
3943
3857
  sessionId: task.session_id,
@@ -3958,7 +3872,7 @@ var ScheduledTaskManager = class {
3958
3872
  onResult: (result) => {
3959
3873
  const summary = result.result ?? resultText;
3960
3874
  task.last_result = summary.slice(0, 2e3);
3961
- log$21.info("定时任务执行完成", {
3875
+ log$22.info("定时任务执行完成", {
3962
3876
  taskId: task.id,
3963
3877
  name: task.name,
3964
3878
  subtype: result.subtype,
@@ -3971,7 +3885,7 @@ var ScheduledTaskManager = class {
3971
3885
  },
3972
3886
  onError: (error) => {
3973
3887
  task.last_result = `执行失败: ${error.message}`;
3974
- log$21.error("定时任务执行失败", {
3888
+ log$22.error("定时任务执行失败", {
3975
3889
  taskId: task.id,
3976
3890
  name: task.name,
3977
3891
  error: error.message
@@ -3983,7 +3897,7 @@ var ScheduledTaskManager = class {
3983
3897
  await this.processManager.executePrompt(config, callbacks);
3984
3898
  } catch (err) {
3985
3899
  task.last_result = `执行异常: ${String(err)}`;
3986
- log$21.error("定时任务执行异常", {
3900
+ log$22.error("定时任务执行异常", {
3987
3901
  taskId: task.id,
3988
3902
  name: task.name,
3989
3903
  error: String(err)
@@ -4276,7 +4190,7 @@ function getUserAgent() {
4276
4190
  * - `LarkClient.fromCredentials(credentials)` — ephemeral instance (not cached)
4277
4191
  * - `LarkClient.fromProvider(provider, opts)` — from ICredentialProvider (not cached)
4278
4192
  */
4279
- const log$19 = larkLogger("core/lark-client");
4193
+ const log$20 = larkLogger("core/lark-client");
4280
4194
  const GLOBAL_LARK_USER_AGENT_KEY = "LARK_USER_AGENT";
4281
4195
  function installGlobalUserAgent() {
4282
4196
  globalThis[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();
@@ -4368,7 +4282,7 @@ var LarkClient = class LarkClient {
4368
4282
  const existing = cache.get(account.accountId);
4369
4283
  if (existing && existing.account.appId === account.appId && credentialsEqual(existing.account.appSecret, account.appSecret)) return existing;
4370
4284
  if (existing) {
4371
- log$19.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
4285
+ log$20.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
4372
4286
  existing.dispose();
4373
4287
  }
4374
4288
  const instance = new LarkClient(account);
@@ -4411,7 +4325,7 @@ var LarkClient = class LarkClient {
4411
4325
  static fromProvider(provider, opts) {
4412
4326
  const appId = provider.getAppId();
4413
4327
  const appSecret = provider.getAppSecret();
4414
- log$19.info("通过 ICredentialProvider 创建 LarkClient", {
4328
+ log$20.info("通过 ICredentialProvider 创建 LarkClient", {
4415
4329
  appId,
4416
4330
  accountId: opts?.accountId ?? "default",
4417
4331
  brand: opts?.brand ?? "feishu"
@@ -4447,7 +4361,7 @@ var LarkClient = class LarkClient {
4447
4361
  get sdk() {
4448
4362
  if (!this._sdk) {
4449
4363
  const { appId, appSecret } = this.requireCredentials();
4450
- log$19.info("创建 Lark SDK 客户端实例", {
4364
+ log$20.info("创建 Lark SDK 客户端实例", {
4451
4365
  accountId: this.accountId,
4452
4366
  appId,
4453
4367
  brand: this.account.brand
@@ -4462,7 +4376,7 @@ var LarkClient = class LarkClient {
4462
4376
  if (sdkAny.httpInstance?.interceptors) {
4463
4377
  const accountId = this.accountId;
4464
4378
  sdkAny.httpInstance.interceptors.request.use((req) => {
4465
- log$19.debug("飞书 API 请求", {
4379
+ log$20.debug("飞书 API 请求", {
4466
4380
  accountId,
4467
4381
  method: req.method,
4468
4382
  url: req.url,
@@ -4472,7 +4386,7 @@ var LarkClient = class LarkClient {
4472
4386
  return req;
4473
4387
  }, void 0, { synchronous: true });
4474
4388
  sdkAny.httpInstance.interceptors.response.use((res) => {
4475
- log$19.debug("飞书 API 响应", {
4389
+ log$20.debug("飞书 API 响应", {
4476
4390
  accountId,
4477
4391
  code: res?.code,
4478
4392
  msg: res?.msg,
@@ -4480,7 +4394,7 @@ var LarkClient = class LarkClient {
4480
4394
  });
4481
4395
  return res;
4482
4396
  }, (err) => {
4483
- log$19.error("飞书 API 请求失败", {
4397
+ log$20.error("飞书 API 请求失败", {
4484
4398
  accountId,
4485
4399
  error: err instanceof Error ? err.message : String(err),
4486
4400
  url: err?.config?.url
@@ -4567,7 +4481,7 @@ var LarkClient = class LarkClient {
4567
4481
  dispatcher.register(handlers);
4568
4482
  const { appId, appSecret } = this.requireCredentials();
4569
4483
  if (this._wsClient) {
4570
- log$19.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
4484
+ log$20.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
4571
4485
  try {
4572
4486
  this._wsClient.close({ force: true });
4573
4487
  } catch {}
@@ -4584,7 +4498,7 @@ var LarkClient = class LarkClient {
4584
4498
  wsClientAny.handleEventData = (data) => {
4585
4499
  const msgType = data.headers?.find?.((h) => h.key === "type")?.value;
4586
4500
  const eventType = data.headers?.find?.((h) => h.key === "event_type")?.value;
4587
- log$19.info("WS 收到原始事件", {
4501
+ log$20.info("WS 收到原始事件", {
4588
4502
  accountId: this.accountId,
4589
4503
  msgType,
4590
4504
  eventType,
@@ -4610,14 +4524,14 @@ var LarkClient = class LarkClient {
4610
4524
  /** Disconnect WebSocket but keep instance in cache. */
4611
4525
  disconnect() {
4612
4526
  if (this._wsClient) {
4613
- log$19.info(`disconnecting WebSocket`, { accountId: this.accountId });
4527
+ log$20.info(`disconnecting WebSocket`, { accountId: this.accountId });
4614
4528
  try {
4615
4529
  this._wsClient.close({ force: true });
4616
4530
  } catch {}
4617
4531
  }
4618
4532
  this._wsClient = null;
4619
4533
  if (this.messageDedup) {
4620
- log$19.info(`disposing message dedup`, {
4534
+ log$20.info(`disposing message dedup`, {
4621
4535
  accountId: this.accountId,
4622
4536
  size: this.messageDedup.size
4623
4537
  });
@@ -4923,7 +4837,7 @@ function sortTraceValue(value) {
4923
4837
  }
4924
4838
  //#endregion
4925
4839
  //#region src/card/cc-stream-bridge.ts
4926
- const log$18 = larkLogger("card/cc-stream-bridge");
4840
+ const log$19 = larkLogger("card/cc-stream-bridge");
4927
4841
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
4928
4842
  /**
4929
4843
  * CCStreamBridge — 将 CC 流事件桥接到 StreamingCardController
@@ -4948,7 +4862,7 @@ var CCStreamBridge = class {
4948
4862
  sessionKey: options?.sessionKey
4949
4863
  };
4950
4864
  if (this.options.sessionKey) startToolUseTraceRun(this.options.sessionKey);
4951
- log$18.info("CCStreamBridge 初始化", {
4865
+ log$19.info("CCStreamBridge 初始化", {
4952
4866
  autoCompleteOnTurnEnd: this.options.autoCompleteOnTurnEnd,
4953
4867
  sessionKey: this.options.sessionKey ?? "(none)"
4954
4868
  });
@@ -4956,7 +4870,7 @@ var CCStreamBridge = class {
4956
4870
  /** 文本增量 → 累积后调用 controller.onPartialReply */
4957
4871
  onTextDelta(text) {
4958
4872
  this.accumulatedText += text;
4959
- log$18.debug("textDelta 事件", {
4873
+ log$19.debug("textDelta 事件", {
4960
4874
  deltaLen: text.length,
4961
4875
  totalLen: this.accumulatedText.length
4962
4876
  });
@@ -4965,7 +4879,7 @@ var CCStreamBridge = class {
4965
4879
  /** 思考增量 → 累积后调用 controller.onReasoningStream */
4966
4880
  onThinkingDelta(text) {
4967
4881
  this.accumulatedThinkingText += text;
4968
- log$18.debug("thinkingDelta 事件", {
4882
+ log$19.debug("thinkingDelta 事件", {
4969
4883
  deltaLen: text.length,
4970
4884
  totalLen: this.accumulatedThinkingText.length
4971
4885
  });
@@ -4977,7 +4891,7 @@ var CCStreamBridge = class {
4977
4891
  const displayName = getToolDisplayName(toolName);
4978
4892
  const toolParams = typeof _toolInput === "object" && _toolInput !== null ? _toolInput : void 0;
4979
4893
  const hasParams = toolParams && Object.keys(toolParams).length > 0;
4980
- log$18.info("toolUseStart 事件", {
4894
+ log$19.info("toolUseStart 事件", {
4981
4895
  toolName,
4982
4896
  displayName,
4983
4897
  activeToolsCount: this.activeTools.size,
@@ -4990,7 +4904,7 @@ var CCStreamBridge = class {
4990
4904
  toolName,
4991
4905
  toolParams
4992
4906
  })) {
4993
- log$18.info("toolUseStart: 更新已有步骤的 params", { toolName });
4907
+ log$19.info("toolUseStart: 更新已有步骤的 params", { toolName });
4994
4908
  this.controller.onToolStart({
4995
4909
  name: toolName,
4996
4910
  phase: "start"
@@ -5014,7 +4928,7 @@ var CCStreamBridge = class {
5014
4928
  const toolName = this.activeTools.get(toolUseId) ?? this.lastToolName ?? "unknown";
5015
4929
  const displayName = getToolDisplayName(toolName);
5016
4930
  this.activeTools.delete(toolUseId);
5017
- log$18.info("toolResult 事件", {
4931
+ log$19.info("toolResult 事件", {
5018
4932
  toolUseId,
5019
4933
  toolName,
5020
4934
  displayName,
@@ -5029,7 +4943,7 @@ var CCStreamBridge = class {
5029
4943
  }
5030
4944
  /** 工具执行进度 → 记录日志 */
5031
4945
  onToolProgress(toolName, elapsedSeconds) {
5032
- log$18.debug("toolProgress 事件", {
4946
+ log$19.debug("toolProgress 事件", {
5033
4947
  toolName,
5034
4948
  displayName: getToolDisplayName(toolName),
5035
4949
  elapsedSeconds
@@ -5037,7 +4951,7 @@ var CCStreamBridge = class {
5037
4951
  }
5038
4952
  /** 轮次结束 → 仅在 end_turn 时标记完成 + 触发 onIdle */
5039
4953
  onTurnEnd(stopReason) {
5040
- log$18.info("turnEnd 事件", {
4954
+ log$19.info("turnEnd 事件", {
5041
4955
  stopReason,
5042
4956
  accumulatedTextLen: this.accumulatedText.length,
5043
4957
  accumulatedThinkingTextLen: this.accumulatedThinkingText.length
@@ -5045,7 +4959,7 @@ var CCStreamBridge = class {
5045
4959
  if (this.options.autoCompleteOnTurnEnd && stopReason === "end_turn") {
5046
4960
  const trimmedText = this.accumulatedText.trim();
5047
4961
  if (trimmedText === CC_INTERNAL_PLACEHOLDER || trimmedText === "") {
5048
- log$18.info("检测到 CC 内部占位消息,静默丢弃", {
4962
+ log$19.info("检测到 CC 内部占位消息,静默丢弃", {
5049
4963
  text: trimmedText.slice(0, 50),
5050
4964
  sessionKey: this.options.sessionKey
5051
4965
  });
@@ -5058,7 +4972,7 @@ var CCStreamBridge = class {
5058
4972
  }
5059
4973
  /** 最终结果 → 兜底最终化 + 错误处理 */
5060
4974
  onResult(data) {
5061
- log$18.info("result 事件", {
4975
+ log$19.info("result 事件", {
5062
4976
  subtype: data.subtype,
5063
4977
  isError: data.isError,
5064
4978
  durationMs: data.durationMs,
@@ -5070,7 +4984,7 @@ var CCStreamBridge = class {
5070
4984
  const pm = ClaudeCodeAdapter.getInstance()?.getProcessManager();
5071
4985
  const sessionId = this.options.sessionKey;
5072
4986
  if (sessionId && pm ? pm.consumeAborted(sessionId) : false) {
5073
- log$18.info("用户主动中断,按正常完成处理", {
4987
+ log$19.info("用户主动中断,按正常完成处理", {
5074
4988
  subtype: data.subtype,
5075
4989
  sessionId
5076
4990
  });
@@ -5079,7 +4993,7 @@ var CCStreamBridge = class {
5079
4993
  this.controller.onIdle();
5080
4994
  } else {
5081
4995
  const errorMessage = data.result ?? `CC 执行失败: ${data.subtype}`;
5082
- log$18.error("CC 执行返回错误", {
4996
+ log$19.error("CC 执行返回错误", {
5083
4997
  subtype: data.subtype,
5084
4998
  errorMessage: errorMessage.slice(0, 200)
5085
4999
  });
@@ -5096,7 +5010,7 @@ var CCStreamBridge = class {
5096
5010
  * 内部复用上面的直接调用方法。
5097
5011
  */
5098
5012
  bindParser(parser) {
5099
- log$18.info("绑定 CCStreamParser 事件到卡片控制器");
5013
+ log$19.info("绑定 CCStreamParser 事件到卡片控制器");
5100
5014
  parser.on("textDelta", (text) => this.onTextDelta(text));
5101
5015
  parser.on("thinkingDelta", (text) => this.onThinkingDelta(text));
5102
5016
  parser.on("toolUseStart", (toolName, toolInput) => this.onToolUseStart(toolName, toolInput));
@@ -5104,7 +5018,7 @@ var CCStreamBridge = class {
5104
5018
  parser.on("toolProgress", (toolName, elapsedSeconds) => this.onToolProgress(toolName, elapsedSeconds));
5105
5019
  parser.on("turnEnd", (stopReason) => this.onTurnEnd(stopReason));
5106
5020
  parser.on("result", (data) => this.onResult(data));
5107
- log$18.info("CCStreamParser 事件绑定完成");
5021
+ log$19.info("CCStreamParser 事件绑定完成");
5108
5022
  }
5109
5023
  };
5110
5024
  //#endregion
@@ -7120,7 +7034,7 @@ function resolveLarkSdk(cfg, accountId) {
7120
7034
  if (cached) return cached.sdk;
7121
7035
  return LarkClient.fromCfg(cfg, accountId).sdk;
7122
7036
  }
7123
- const log$17 = larkLogger("card/cardkit");
7037
+ const log$18 = larkLogger("card/cardkit");
7124
7038
  /**
7125
7039
  * 记录 CardKit API 响应日志,检测错误码并抛出异常。
7126
7040
  *
@@ -7130,13 +7044,13 @@ const log$17 = larkLogger("card/cardkit");
7130
7044
  function logCardKitResponse(params) {
7131
7045
  const { resp, api, context } = params;
7132
7046
  const { code, msg } = resp;
7133
- log$17.info(`cardkit ${api} response`, {
7047
+ log$18.info(`cardkit ${api} response`, {
7134
7048
  code,
7135
7049
  msg,
7136
7050
  context
7137
7051
  });
7138
7052
  if (code && code !== 0) {
7139
- log$17.warn(`cardkit ${api} FAILED`, {
7053
+ log$18.warn(`cardkit ${api} FAILED`, {
7140
7054
  code,
7141
7055
  msg,
7142
7056
  context,
@@ -7547,7 +7461,7 @@ function validateLocalMediaRoots(filePath, localRoots) {
7547
7461
  * Feishu messages, uploading media to the Feishu IM storage, and
7548
7462
  * sending image / file messages to chats.
7549
7463
  */
7550
- const log$16 = larkLogger("outbound/media");
7464
+ const log$17 = larkLogger("outbound/media");
7551
7465
  /**
7552
7466
  * Upload an image to Feishu IM storage.
7553
7467
  *
@@ -7615,7 +7529,7 @@ async function validateRemoteUrl(raw) {
7615
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}"`);
7616
7530
  } catch (err) {
7617
7531
  if (err instanceof Error && err.message.includes("SSRF protection")) throw err;
7618
- log$16.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
7532
+ log$17.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
7619
7533
  }
7620
7534
  }
7621
7535
  /**
@@ -7633,21 +7547,21 @@ async function fetchMediaBuffer(urlOrPath, localRoots) {
7633
7547
  if (localRoots !== void 0) validateLocalMediaRoots(filePath, localRoots);
7634
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.`);
7635
7549
  const buf = fs.readFileSync(filePath);
7636
- log$16.debug(`local file read: "${filePath}", ${buf.length} bytes`);
7550
+ log$17.debug(`local file read: "${filePath}", ${buf.length} bytes`);
7637
7551
  return buf;
7638
7552
  }
7639
7553
  await validateRemoteUrl(raw);
7640
7554
  const FETCH_TIMEOUT_MS = 3e4;
7641
- log$16.info(`fetching remote media: ${raw}`);
7555
+ log$17.info(`fetching remote media: ${raw}`);
7642
7556
  const response = await fetch(raw, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
7643
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.`);
7644
7558
  const arrayBuffer = await response.arrayBuffer();
7645
- log$16.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
7559
+ log$17.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
7646
7560
  return Buffer.from(arrayBuffer);
7647
7561
  }
7648
7562
  //#endregion
7649
7563
  //#region src/card/image-resolver.ts
7650
- const log$15 = larkLogger("card/image-resolver");
7564
+ const log$16 = larkLogger("card/image-resolver");
7651
7565
  /** Matches complete markdown image syntax: `![alt](value)` */
7652
7566
  const IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
7653
7567
  var ImageResolver = class {
@@ -7693,14 +7607,14 @@ var ImageResolver = class {
7693
7607
  async resolveImagesAwait(text, timeoutMs) {
7694
7608
  this.resolveImages(text);
7695
7609
  if (this.pending.size > 0) {
7696
- log$15.info("resolveImagesAwait: waiting for uploads", {
7610
+ log$16.info("resolveImagesAwait: waiting for uploads", {
7697
7611
  count: this.pending.size,
7698
7612
  timeoutMs
7699
7613
  });
7700
7614
  const allUploads = Promise.all(this.pending.values());
7701
7615
  const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
7702
7616
  await Promise.race([allUploads, timeout]);
7703
- 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 });
7704
7618
  }
7705
7619
  return this.resolveImages(text);
7706
7620
  }
@@ -7710,7 +7624,7 @@ var ImageResolver = class {
7710
7624
  }
7711
7625
  async doUpload(url) {
7712
7626
  try {
7713
- log$15.info("uploading image", { url });
7627
+ log$16.info("uploading image", { url });
7714
7628
  const buffer = await fetchRemoteImageBuffer(url);
7715
7629
  const { imageKey } = await uploadImageLark({
7716
7630
  cfg: this.cfg,
@@ -7718,7 +7632,7 @@ var ImageResolver = class {
7718
7632
  imageType: "message",
7719
7633
  accountId: this.accountId
7720
7634
  });
7721
- log$15.info("image uploaded", {
7635
+ log$16.info("image uploaded", {
7722
7636
  url,
7723
7637
  imageKey
7724
7638
  });
@@ -7727,7 +7641,7 @@ var ImageResolver = class {
7727
7641
  this.onImageResolved();
7728
7642
  return imageKey;
7729
7643
  } catch (err) {
7730
- log$15.warn("image upload failed", {
7644
+ log$16.warn("image upload failed", {
7731
7645
  url,
7732
7646
  error: String(err)
7733
7647
  });
@@ -7748,7 +7662,7 @@ var ImageResolver = class {
7748
7662
  * Encapsulates the terminateDueToUnavailable / shouldSkipForUnavailable
7749
7663
  * logic previously scattered as closures in reply-dispatcher.ts.
7750
7664
  */
7751
- const log$14 = larkLogger("card/unavailable-guard");
7665
+ const log$15 = larkLogger("card/unavailable-guard");
7752
7666
  var UnavailableGuard = class {
7753
7667
  terminated = false;
7754
7668
  replyToMessageId;
@@ -7801,7 +7715,7 @@ var UnavailableGuard = class {
7801
7715
  this.terminated = true;
7802
7716
  this.onTerminate();
7803
7717
  const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? "unknown";
7804
- log$14.warn("reply pipeline terminated by unavailable message", {
7718
+ log$15.warn("reply pipeline terminated by unavailable message", {
7805
7719
  source,
7806
7720
  apiCode,
7807
7721
  messageId: affectedMessageId
@@ -7838,7 +7752,7 @@ function getActiveCard(sessionId) {
7838
7752
  * Delegates throttling to FlushController and message-unavailable
7839
7753
  * detection to UnavailableGuard.
7840
7754
  */
7841
- const log$13 = larkLogger("card/streaming");
7755
+ const log$14 = larkLogger("card/streaming");
7842
7756
  var StreamingCardController = class StreamingCardController {
7843
7757
  phase = "idle";
7844
7758
  cardKit = {
@@ -7926,7 +7840,7 @@ var StreamingCardController = class StreamingCardController {
7926
7840
  }
7927
7841
  }
7928
7842
  if (!entry) {
7929
- log$13.debug("footer metrics lookup: session entry missing", {
7843
+ log$14.debug("footer metrics lookup: session entry missing", {
7930
7844
  sessionKey: this.deps.sessionKey,
7931
7845
  candidateKeys,
7932
7846
  storePath,
@@ -7944,7 +7858,7 @@ var StreamingCardController = class StreamingCardController {
7944
7858
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
7945
7859
  model: typeof entry.model === "string" ? entry.model : void 0
7946
7860
  };
7947
- log$13.debug("footer metrics lookup: session entry found", {
7861
+ log$14.debug("footer metrics lookup: session entry found", {
7948
7862
  sessionKey: this.deps.sessionKey,
7949
7863
  matchedKey,
7950
7864
  storePath,
@@ -7969,7 +7883,7 @@ var StreamingCardController = class StreamingCardController {
7969
7883
  }
7970
7884
  }
7971
7885
  if (!entry) {
7972
- log$13.debug("footer metrics lookup: session entry missing", {
7886
+ log$14.debug("footer metrics lookup: session entry missing", {
7973
7887
  sessionKey: this.deps.sessionKey,
7974
7888
  candidateKeys,
7975
7889
  storePath,
@@ -7987,7 +7901,7 @@ var StreamingCardController = class StreamingCardController {
7987
7901
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
7988
7902
  model: typeof entry.model === "string" ? entry.model : void 0
7989
7903
  };
7990
- log$13.debug("footer metrics lookup: session entry found", {
7904
+ log$14.debug("footer metrics lookup: session entry found", {
7991
7905
  sessionKey: this.deps.sessionKey,
7992
7906
  matchedKey,
7993
7907
  storePath,
@@ -7995,7 +7909,7 @@ var StreamingCardController = class StreamingCardController {
7995
7909
  });
7996
7910
  return metrics;
7997
7911
  } catch (err) {
7998
- log$13.warn("footer metrics lookup failed", {
7912
+ log$14.warn("footer metrics lookup failed", {
7999
7913
  error: String(err),
8000
7914
  sessionKey: this.deps.sessionKey
8001
7915
  });
@@ -8102,7 +8016,7 @@ var StreamingCardController = class StreamingCardController {
8102
8016
  const from = this.phase;
8103
8017
  if (from === to) return false;
8104
8018
  if (!PHASE_TRANSITIONS[from].has(to)) {
8105
- log$13.warn("phase transition rejected", {
8019
+ log$14.warn("phase transition rejected", {
8106
8020
  from,
8107
8021
  to,
8108
8022
  source
@@ -8110,7 +8024,7 @@ var StreamingCardController = class StreamingCardController {
8110
8024
  return false;
8111
8025
  }
8112
8026
  this.phase = to;
8113
- log$13.info("phase transition", {
8027
+ log$14.info("phase transition", {
8114
8028
  from,
8115
8029
  to,
8116
8030
  source,
@@ -8230,7 +8144,7 @@ var StreamingCardController = class StreamingCardController {
8230
8144
  this.reasoning.dirty = true;
8231
8145
  }
8232
8146
  const text = split.answerText ?? stripReasoningTags(rawText);
8233
- log$13.debug("onPartialReply", { len: text.length });
8147
+ log$14.debug("onPartialReply", { len: text.length });
8234
8148
  if (!text) return;
8235
8149
  this.captureToolUseElapsed();
8236
8150
  if (!this.reasoning.reasoningStartTime) this.reasoning.reasoningStartTime = Date.now();
@@ -8242,7 +8156,7 @@ var StreamingCardController = class StreamingCardController {
8242
8156
  this.text.lastPartialText = text;
8243
8157
  this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + "\n\n" + text : text;
8244
8158
  if (!this.text.streamingPrefix && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {
8245
- log$13.debug("onPartialReply: buffering NO_REPLY prefix");
8159
+ log$14.debug("onPartialReply: buffering NO_REPLY prefix");
8246
8160
  return;
8247
8161
  }
8248
8162
  await this.ensureCardCreated();
@@ -8252,7 +8166,7 @@ var StreamingCardController = class StreamingCardController {
8252
8166
  }
8253
8167
  async onError(err, info) {
8254
8168
  if (this.guard.terminate("onError", err)) return;
8255
- log$13.error(`${info.kind} reply failed`, { error: String(err) });
8169
+ log$14.error(`${info.kind} reply failed`, { error: String(err) });
8256
8170
  this.captureToolUseElapsed();
8257
8171
  this.finalizeCard("onError", "error");
8258
8172
  await this.flush.waitForFlush();
@@ -8308,7 +8222,7 @@ var StreamingCardController = class StreamingCardController {
8308
8222
  if (this.cardKit.cardMessageId) {
8309
8223
  const isNoReplyLeak = !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
8310
8224
  const displayText = this.text.completedText || (isNoReplyLeak ? "" : this.text.accumulatedText) || "Done.";
8311
- 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");
8312
8226
  const resolvedDisplayText = await this.imageResolver.resolveImagesAwait(displayText, 15e3);
8313
8227
  const idleToolUseDisplay = this.computeToolUseDisplay();
8314
8228
  const terminalContent = prepareTerminalCardContent({
@@ -8336,7 +8250,7 @@ var StreamingCardController = class StreamingCardController {
8336
8250
  const rewindSessionId = this.deps.sessionKey;
8337
8251
  const rewindMessageId = ClaudeCodeAdapter.getInstance()?.getProcessManager()?.getLastUserMessageId(this.deps.sessionKey);
8338
8252
  const rewindHasFileChanges = this.hasFileChangingTools(idleToolUseDisplay?.steps);
8339
- log$13.info("onIdle: rewind button conditions", {
8253
+ log$14.info("onIdle: rewind button conditions", {
8340
8254
  sessionId: rewindSessionId,
8341
8255
  messageId: rewindMessageId,
8342
8256
  hasFileChanges: rewindHasFileChanges,
@@ -8349,7 +8263,7 @@ var StreamingCardController = class StreamingCardController {
8349
8263
  if (idleEffectiveCardId) {
8350
8264
  const seqBeforeClose = this.cardKit.cardKitSequence;
8351
8265
  this.cardKit.cardKitSequence += 1;
8352
- log$13.info("onIdle: closing streaming mode", {
8266
+ log$14.info("onIdle: closing streaming mode", {
8353
8267
  attempt,
8354
8268
  seqBefore: seqBeforeClose,
8355
8269
  seqAfter: this.cardKit.cardKitSequence
@@ -8363,7 +8277,7 @@ var StreamingCardController = class StreamingCardController {
8363
8277
  });
8364
8278
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
8365
8279
  this.cardKit.cardKitSequence += 1;
8366
- log$13.info("onIdle: updating final card", {
8280
+ log$14.info("onIdle: updating final card", {
8367
8281
  attempt,
8368
8282
  seqBefore: seqBeforeUpdate,
8369
8283
  seqAfter: this.cardKit.cardKitSequence
@@ -8381,33 +8295,33 @@ var StreamingCardController = class StreamingCardController {
8381
8295
  card: completeCard,
8382
8296
  accountId: this.deps.accountId
8383
8297
  });
8384
- log$13.info("reply completed, card finalized", {
8298
+ log$14.info("reply completed, card finalized", {
8385
8299
  elapsedMs: this.elapsed(),
8386
8300
  isCardKit: !!idleEffectiveCardId,
8387
8301
  attempt
8388
8302
  });
8389
8303
  break;
8390
8304
  } catch (retryErr) {
8391
- log$13.warn("final card update attempt failed", {
8305
+ log$14.warn("final card update attempt failed", {
8392
8306
  attempt,
8393
8307
  maxRetries: MAX_FINAL_UPDATE_RETRIES,
8394
8308
  error: String(retryErr)
8395
8309
  });
8396
8310
  if (attempt < MAX_FINAL_UPDATE_RETRIES) await new Promise((resolve) => setTimeout(resolve, FINAL_UPDATE_RETRY_DELAY_MS * attempt));
8397
- 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", {
8398
8312
  cardId: idleEffectiveCardId,
8399
8313
  messageId: this.cardKit.cardMessageId
8400
8314
  });
8401
8315
  }
8402
8316
  }
8403
8317
  } catch (err) {
8404
- log$13.error("final card update failed (outer)", { error: String(err) });
8318
+ log$14.error("final card update failed (outer)", { error: String(err) });
8405
8319
  } finally {
8406
8320
  clearToolUseTraceRun(this.deps.sessionKey);
8407
8321
  }
8408
8322
  }
8409
8323
  markFullyComplete() {
8410
- log$13.debug("markFullyComplete", {
8324
+ log$14.debug("markFullyComplete", {
8411
8325
  completedTextLen: this.text.completedText.length,
8412
8326
  accumulatedTextLen: this.text.accumulatedText.length
8413
8327
  });
@@ -8446,7 +8360,7 @@ var StreamingCardController = class StreamingCardController {
8446
8360
  footerMetrics
8447
8361
  });
8448
8362
  await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, "abortCard");
8449
- log$13.info("abortCard completed", { effectiveCardId });
8363
+ log$14.info("abortCard completed", { effectiveCardId });
8450
8364
  } else if (this.cardKit.cardMessageId) {
8451
8365
  const abortCard = buildCardContent("complete", {
8452
8366
  text: terminalContent.text,
@@ -8467,10 +8381,10 @@ var StreamingCardController = class StreamingCardController {
8467
8381
  card: abortCard,
8468
8382
  accountId: this.deps.accountId
8469
8383
  });
8470
- log$13.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
8384
+ log$14.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
8471
8385
  }
8472
8386
  } catch (err) {
8473
- log$13.warn("abortCard failed", { error: String(err) });
8387
+ log$14.warn("abortCard failed", { error: String(err) });
8474
8388
  } finally {
8475
8389
  clearToolUseTraceRun(this.deps.sessionKey);
8476
8390
  }
@@ -8485,10 +8399,10 @@ var StreamingCardController = class StreamingCardController {
8485
8399
  async forceCloseStreaming() {
8486
8400
  const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
8487
8401
  if (!effectiveCardId && !this.cardKit.cardMessageId) {
8488
- log$13.warn("forceCloseStreaming: no card to close");
8402
+ log$14.warn("forceCloseStreaming: no card to close");
8489
8403
  return;
8490
8404
  }
8491
- log$13.info("forceCloseStreaming: 强制终态化卡片", {
8405
+ log$14.info("forceCloseStreaming: 强制终态化卡片", {
8492
8406
  cardId: effectiveCardId,
8493
8407
  messageId: this.cardKit.cardMessageId,
8494
8408
  phase: this.phase
@@ -8538,10 +8452,10 @@ var StreamingCardController = class StreamingCardController {
8538
8452
  card: completeCard,
8539
8453
  accountId: this.deps.accountId
8540
8454
  });
8541
- log$13.info("forceCloseStreaming: 卡片已强制终态化");
8455
+ log$14.info("forceCloseStreaming: 卡片已强制终态化");
8542
8456
  if (!this.isTerminalPhase) this.finalizeCard("forceCloseStreaming", "abort");
8543
8457
  } catch (err) {
8544
- log$13.error("forceCloseStreaming failed", { error: String(err) });
8458
+ log$14.error("forceCloseStreaming failed", { error: String(err) });
8545
8459
  } finally {
8546
8460
  unregisterActiveCard(this.deps.sessionKey);
8547
8461
  clearToolUseTraceRun(this.deps.sessionKey);
@@ -8565,7 +8479,7 @@ var StreamingCardController = class StreamingCardController {
8565
8479
  this.disposeShutdownHook = null;
8566
8480
  return;
8567
8481
  }
8568
- log$13.info("reusing placeholder card", {
8482
+ log$14.info("reusing placeholder card", {
8569
8483
  cardId: this.deps.placeholderCardId,
8570
8484
  messageId: this.deps.placeholderMessageId
8571
8485
  });
@@ -8589,7 +8503,7 @@ var StreamingCardController = class StreamingCardController {
8589
8503
  accountId: this.deps.accountId
8590
8504
  });
8591
8505
  if (this.isStaleCreate(epoch)) {
8592
- log$13.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
8506
+ log$14.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
8593
8507
  epoch,
8594
8508
  phase: this.phase
8595
8509
  });
@@ -8600,7 +8514,7 @@ var StreamingCardController = class StreamingCardController {
8600
8514
  this.cardKit.originalCardKitCardId = cId;
8601
8515
  this.cardKit.cardKitSequence = 1;
8602
8516
  this.disposeShutdownHook = registerShutdownHook(`streaming-card:${cId}`, () => this.abortCard());
8603
- log$13.info("created CardKit entity", {
8517
+ log$14.info("created CardKit entity", {
8604
8518
  cardId: cId,
8605
8519
  initialSequence: this.cardKit.cardKitSequence
8606
8520
  });
@@ -8613,7 +8527,7 @@ var StreamingCardController = class StreamingCardController {
8613
8527
  accountId: this.deps.accountId
8614
8528
  });
8615
8529
  if (this.isStaleCreate(epoch)) {
8616
- log$13.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
8530
+ log$14.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
8617
8531
  epoch,
8618
8532
  phase: this.phase
8619
8533
  });
@@ -8628,13 +8542,13 @@ var StreamingCardController = class StreamingCardController {
8628
8542
  this.disposeShutdownHook = null;
8629
8543
  return;
8630
8544
  }
8631
- log$13.info("sent CardKit card", { messageId: result.messageId });
8545
+ log$14.info("sent CardKit card", { messageId: result.messageId });
8632
8546
  } else throw new Error("card.create returned empty card_id");
8633
8547
  } catch (cardKitErr) {
8634
8548
  if (this.isStaleCreate(epoch)) return;
8635
8549
  if (this.guard.terminate("ensureCardCreated.cardkitFlow", cardKitErr)) return;
8636
8550
  const apiDetail = extractApiDetail(cardKitErr);
8637
- log$13.warn("CardKit flow failed, falling back to IM", { apiDetail });
8551
+ log$14.warn("CardKit flow failed, falling back to IM", { apiDetail });
8638
8552
  this.cardKit.cardKitCardId = null;
8639
8553
  this.cardKit.originalCardKitCardId = null;
8640
8554
  const fallbackCard = buildCardContent("streaming", { showToolUse: this.deps.toolUseDisplay.showToolUse });
@@ -8647,7 +8561,7 @@ var StreamingCardController = class StreamingCardController {
8647
8561
  accountId: this.deps.accountId
8648
8562
  });
8649
8563
  if (this.isStaleCreate(epoch)) {
8650
- 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", {
8651
8565
  epoch,
8652
8566
  phase: this.phase
8653
8567
  });
@@ -8656,12 +8570,12 @@ var StreamingCardController = class StreamingCardController {
8656
8570
  this.cardKit.cardMessageId = result.messageId;
8657
8571
  this.flush.setCardMessageReady(true);
8658
8572
  if (!this.transition("streaming", "ensureCardCreated.imFallback")) return;
8659
- log$13.info("sent fallback IM card", { messageId: result.messageId });
8573
+ log$14.info("sent fallback IM card", { messageId: result.messageId });
8660
8574
  }
8661
8575
  } catch (err) {
8662
8576
  if (this.isStaleCreate(epoch)) return;
8663
8577
  if (this.guard.terminate("ensureCardCreated.outer", err)) return;
8664
- 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) });
8665
8579
  this.transition("creation_failed", "ensureCardCreated.outer", "creation_failed");
8666
8580
  }
8667
8581
  })();
@@ -8670,10 +8584,10 @@ var StreamingCardController = class StreamingCardController {
8670
8584
  async performFlush() {
8671
8585
  if (!this.cardKit.cardMessageId || this.isTerminalPhase) return;
8672
8586
  if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {
8673
- log$13.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
8587
+ log$14.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
8674
8588
  return;
8675
8589
  }
8676
- log$13.debug("flushCardUpdate: enter", {
8590
+ log$14.debug("flushCardUpdate: enter", {
8677
8591
  seq: this.cardKit.cardKitSequence,
8678
8592
  isCardKit: !!this.cardKit.cardKitCardId
8679
8593
  });
@@ -8695,7 +8609,7 @@ var StreamingCardController = class StreamingCardController {
8695
8609
  reasoningText: this.reasoning.accumulatedReasoningText || void 0
8696
8610
  });
8697
8611
  this.cardKit.cardKitSequence += 1;
8698
- log$13.debug("flushCardUpdate: full card update (dirty)", {
8612
+ log$14.debug("flushCardUpdate: full card update (dirty)", {
8699
8613
  seq: this.cardKit.cardKitSequence,
8700
8614
  stepCount: display?.stepCount,
8701
8615
  hasReasoning: !!this.reasoning.accumulatedReasoningText
@@ -8711,7 +8625,7 @@ var StreamingCardController = class StreamingCardController {
8711
8625
  } else if (resolvedText !== this.text.lastFlushedText) {
8712
8626
  const prevSeq = this.cardKit.cardKitSequence;
8713
8627
  this.cardKit.cardKitSequence += 1;
8714
- log$13.debug("flushCardUpdate: answer seq bump", {
8628
+ log$14.debug("flushCardUpdate: answer seq bump", {
8715
8629
  seqBefore: prevSeq,
8716
8630
  seqAfter: this.cardKit.cardKitSequence
8717
8631
  });
@@ -8726,7 +8640,7 @@ var StreamingCardController = class StreamingCardController {
8726
8640
  this.text.lastFlushedText = resolvedText;
8727
8641
  }
8728
8642
  } else {
8729
- log$13.debug("flushCardUpdate: IM patch fallback");
8643
+ log$14.debug("flushCardUpdate: IM patch fallback");
8730
8644
  const flushDisplay = this.computeToolUseDisplay();
8731
8645
  const card = buildCardContent("streaming", {
8732
8646
  text: this.reasoning.isReasoningPhase ? "" : resolvedText,
@@ -8746,31 +8660,31 @@ var StreamingCardController = class StreamingCardController {
8746
8660
  if (this.guard.terminate("flushCardUpdate", err)) return;
8747
8661
  const apiCode = extractLarkApiCode(err);
8748
8662
  if (isCardRateLimitError(err)) {
8749
- 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 });
8750
8664
  return;
8751
8665
  }
8752
8666
  if (isCardTableLimitError(err)) {
8753
- 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 });
8754
8668
  this.cardKit.cardKitCardId = null;
8755
8669
  return;
8756
8670
  }
8757
8671
  if (apiCode === 300317 && this.cardKit.cardKitCardId) {
8758
8672
  const oldSeq = this.cardKit.cardKitSequence;
8759
8673
  this.cardKit.cardKitSequence += 100;
8760
- log$13.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
8674
+ log$14.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
8761
8675
  oldSeq,
8762
8676
  newSeq: this.cardKit.cardKitSequence
8763
8677
  });
8764
8678
  return;
8765
8679
  }
8766
8680
  const apiDetail = extractApiDetail(err);
8767
- log$13.error("card stream update failed", {
8681
+ log$14.error("card stream update failed", {
8768
8682
  apiCode,
8769
8683
  seq: this.cardKit.cardKitSequence,
8770
8684
  apiDetail
8771
8685
  });
8772
8686
  if (this.cardKit.cardKitCardId) {
8773
- 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");
8774
8688
  this.cardKit.cardKitCardId = null;
8775
8689
  }
8776
8690
  }
@@ -8795,7 +8709,7 @@ var StreamingCardController = class StreamingCardController {
8795
8709
  if (!this.cardKit.cardKitCardId || this.isTerminalPhase) return;
8796
8710
  try {
8797
8711
  const display = this.computeToolUseDisplay();
8798
- log$13.info("updateToolUseStatus", {
8712
+ log$14.info("updateToolUseStatus", {
8799
8713
  hasDisplay: !!display,
8800
8714
  stepCount: display?.stepCount,
8801
8715
  steps: display?.steps?.map((s) => `${s.title}:${s.status}`).join(", ")
@@ -8817,7 +8731,7 @@ var StreamingCardController = class StreamingCardController {
8817
8731
  accountId: this.deps.accountId
8818
8732
  });
8819
8733
  } catch (err) {
8820
- log$13.debug("updateToolUseStatus failed", { error: String(err) });
8734
+ log$14.debug("updateToolUseStatus failed", { error: String(err) });
8821
8735
  }
8822
8736
  }
8823
8737
  finalizeCard(source, reason) {
@@ -8829,7 +8743,7 @@ var StreamingCardController = class StreamingCardController {
8829
8743
  async closeStreamingAndUpdate(cardId, card, label) {
8830
8744
  const seqBeforeClose = this.cardKit.cardKitSequence;
8831
8745
  this.cardKit.cardKitSequence += 1;
8832
- log$13.info(`${label}: closing streaming mode`, {
8746
+ log$14.info(`${label}: closing streaming mode`, {
8833
8747
  seqBefore: seqBeforeClose,
8834
8748
  seqAfter: this.cardKit.cardKitSequence
8835
8749
  });
@@ -8842,7 +8756,7 @@ var StreamingCardController = class StreamingCardController {
8842
8756
  });
8843
8757
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
8844
8758
  this.cardKit.cardKitSequence += 1;
8845
- log$13.info(`${label}: updating card`, {
8759
+ log$14.info(`${label}: updating card`, {
8846
8760
  seqBefore: seqBeforeUpdate,
8847
8761
  seqAfter: this.cardKit.cardKitSequence
8848
8762
  });
@@ -8876,7 +8790,7 @@ function extractApiDetail(err) {
8876
8790
  }
8877
8791
  //#endregion
8878
8792
  //#region src/messaging/format-for-cc.ts
8879
- const log$12 = larkLogger("messaging/format-for-cc");
8793
+ const log$13 = larkLogger("messaging/format-for-cc");
8880
8794
  function safeParseJSON(raw) {
8881
8795
  try {
8882
8796
  return JSON.parse(raw);
@@ -8932,7 +8846,7 @@ function extractPostText(rawContent) {
8932
8846
  */
8933
8847
  function formatContentByType(ctx) {
8934
8848
  const { contentType, content, resources } = ctx;
8935
- log$12.debug("formatContentByType 开始格式化", {
8849
+ log$13.debug("formatContentByType 开始格式化", {
8936
8850
  contentType,
8937
8851
  contentLength: content?.length ?? 0,
8938
8852
  resourceCount: resources?.length ?? 0
@@ -8974,7 +8888,7 @@ function formatContentByType(ctx) {
8974
8888
  case "todo":
8975
8889
  case "vote": return content;
8976
8890
  default:
8977
- log$12.warn("遇到不支持的消息类型", { contentType });
8891
+ log$13.warn("遇到不支持的消息类型", { contentType });
8978
8892
  return `[不支持的消息类型: ${contentType}]`;
8979
8893
  }
8980
8894
  }
@@ -9017,7 +8931,7 @@ function formatMessageTime(createTime) {
9017
8931
  * @returns 格式化后的纯文本字符串
9018
8932
  */
9019
8933
  function formatForCC(ctx, isGroup) {
9020
- log$12.info("formatForCC 开始处理", {
8934
+ log$13.info("formatForCC 开始处理", {
9021
8935
  messageId: ctx.messageId,
9022
8936
  contentType: ctx.contentType,
9023
8937
  chatType: ctx.chatType,
@@ -9033,7 +8947,7 @@ function formatForCC(ctx, isGroup) {
9033
8947
  if (isGroup && ctx.senderName) parts.push(`[${ctx.senderName}]`);
9034
8948
  if (ctx.threadId) parts.push("[话题回复]");
9035
8949
  const result = (parts.length > 0 ? parts.join(" ") + " " : "") + formattedContent;
9036
- log$12.info("formatForCC 完成", {
8950
+ log$13.info("formatForCC 完成", {
9037
8951
  messageId: ctx.messageId,
9038
8952
  resultLength: result.length,
9039
8953
  hasTime: !!timeStr,
@@ -9043,7 +8957,7 @@ function formatForCC(ctx, isGroup) {
9043
8957
  }
9044
8958
  //#endregion
9045
8959
  //#region src/messaging/inbound/dispatch-cc.ts
9046
- const log$11 = larkLogger("inbound/dispatch-cc");
8960
+ const log$12 = larkLogger("inbound/dispatch-cc");
9047
8961
  async function dispatchToCC(params) {
9048
8962
  const { ctx, account, sessionRouter, processManager } = params;
9049
8963
  const chatId = ctx.chatId;
@@ -9053,7 +8967,7 @@ async function dispatchToCC(params) {
9053
8967
  const ccModel = params.model;
9054
8968
  const maxTurns = params.maxTurns;
9055
8969
  const maxBudgetUsd = params.maxBudgetUsd;
9056
- log$11.info("dispatchToCC 开始", {
8970
+ log$12.info("dispatchToCC 开始", {
9057
8971
  msgId,
9058
8972
  chatId,
9059
8973
  chatType,
@@ -9066,12 +8980,12 @@ async function dispatchToCC(params) {
9066
8980
  threadId,
9067
8981
  userId: ctx.senderId
9068
8982
  });
9069
- log$11.info("会话路由解析完成", {
8983
+ log$12.info("会话路由解析完成", {
9070
8984
  sessionId: route.sessionId,
9071
8985
  cwd: route.cwd,
9072
8986
  type: route.type
9073
8987
  });
9074
- if (params.userContext) log$11.info("用户上下文已注入", {
8988
+ if (params.userContext) log$12.info("用户上下文已注入", {
9075
8989
  userId: params.userContext.userId,
9076
8990
  isTenantIdentity: params.userContext.isTenantIdentity,
9077
8991
  credentialDir: params.userContext.credentialDir
@@ -9108,7 +9022,7 @@ async function dispatchToCC(params) {
9108
9022
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
9109
9023
  buffer = Buffer.concat(chunks);
9110
9024
  } else {
9111
- log$11.warn("图片资源下载返回未知格式,跳过", {
9025
+ log$12.warn("图片资源下载返回未知格式,跳过", {
9112
9026
  fileKey: imgRes.fileKey,
9113
9027
  responseType: typeof response
9114
9028
  });
@@ -9116,7 +9030,7 @@ async function dispatchToCC(params) {
9116
9030
  }
9117
9031
  if (resp.headers?.["content-type"]) mediaType = resp.headers["content-type"];
9118
9032
  } else {
9119
- log$11.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
9033
+ log$12.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
9120
9034
  continue;
9121
9035
  }
9122
9036
  const imgFileName = `${imgRes.fileKey}.png`;
@@ -9126,13 +9040,13 @@ async function dispatchToCC(params) {
9126
9040
  const { writeFile, mkdir } = await import("fs/promises");
9127
9041
  await mkdir(filesDir, { recursive: true });
9128
9042
  await writeFile(imgFilePath, buffer);
9129
- log$11.info("图片已保存到工作目录", {
9043
+ log$12.info("图片已保存到工作目录", {
9130
9044
  fileKey: imgRes.fileKey,
9131
9045
  path: imgFilePath,
9132
9046
  sizeBytes: buffer.length
9133
9047
  });
9134
9048
  } catch (saveErr) {
9135
- log$11.warn("图片保存到工作目录失败", {
9049
+ log$12.warn("图片保存到工作目录失败", {
9136
9050
  fileKey: imgRes.fileKey,
9137
9051
  path: imgFilePath,
9138
9052
  error: saveErr instanceof Error ? saveErr.message : String(saveErr)
@@ -9147,13 +9061,13 @@ async function dispatchToCC(params) {
9147
9061
  data: base64Data
9148
9062
  }
9149
9063
  });
9150
- log$11.info("图片资源下载成功", {
9064
+ log$12.info("图片资源下载成功", {
9151
9065
  fileKey: imgRes.fileKey,
9152
9066
  mediaType,
9153
9067
  sizeBytes: buffer.length
9154
9068
  });
9155
9069
  } catch (err) {
9156
- log$11.warn("图片资源下载失败,跳过", {
9070
+ log$12.warn("图片资源下载失败,跳过", {
9157
9071
  fileKey: imgRes.fileKey,
9158
9072
  error: err instanceof Error ? err.message : String(err)
9159
9073
  });
@@ -9163,7 +9077,7 @@ async function dispatchToCC(params) {
9163
9077
  type: "text",
9164
9078
  text: textPrompt
9165
9079
  }, ...imageBlocks];
9166
- log$11.info("已构建多模态 prompt", {
9080
+ log$12.info("已构建多模态 prompt", {
9167
9081
  textLength: textPrompt.length,
9168
9082
  imageCount: imageBlocks.length
9169
9083
  });
@@ -9176,7 +9090,7 @@ async function dispatchToCC(params) {
9176
9090
  await fsMkdir(filesDir, { recursive: true });
9177
9091
  let updatedTextPrompt = typeof prompt === "string" ? prompt : prompt[0].text;
9178
9092
  for (const res of attachmentResources) try {
9179
- log$11.info("开始下载非图片附件", {
9093
+ log$12.info("开始下载非图片附件", {
9180
9094
  type: res.type,
9181
9095
  fileKey: res.fileKey,
9182
9096
  fileName: res.fileName,
@@ -9190,7 +9104,7 @@ async function dispatchToCC(params) {
9190
9104
  },
9191
9105
  params: { type: "file" }
9192
9106
  });
9193
- log$11.info("非图片附件 API 响应已收到", {
9107
+ log$12.info("非图片附件 API 响应已收到", {
9194
9108
  type: res.type,
9195
9109
  fileKey: res.fileKey,
9196
9110
  responseType: typeof response,
@@ -9209,7 +9123,7 @@ async function dispatchToCC(params) {
9209
9123
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
9210
9124
  buffer = Buffer.concat(chunks);
9211
9125
  } else {
9212
- log$11.warn("非图片附件下载返回未知格式,跳过", {
9126
+ log$12.warn("非图片附件下载返回未知格式,跳过", {
9213
9127
  type: res.type,
9214
9128
  fileKey: res.fileKey,
9215
9129
  responseType: typeof response
@@ -9217,7 +9131,7 @@ async function dispatchToCC(params) {
9217
9131
  continue;
9218
9132
  }
9219
9133
  } else {
9220
- log$11.warn("非图片附件下载返回空,跳过", {
9134
+ log$12.warn("非图片附件下载返回空,跳过", {
9221
9135
  type: res.type,
9222
9136
  fileKey: res.fileKey
9223
9137
  });
@@ -9240,7 +9154,7 @@ async function dispatchToCC(params) {
9240
9154
  }
9241
9155
  const filePath = path.join(filesDir, savedFileName);
9242
9156
  await fsWriteFile(filePath, buffer);
9243
- log$11.info("非图片附件已保存到工作目录", {
9157
+ log$12.info("非图片附件已保存到工作目录", {
9244
9158
  type: res.type,
9245
9159
  fileKey: res.fileKey,
9246
9160
  savedFileName,
@@ -9274,7 +9188,7 @@ async function dispatchToCC(params) {
9274
9188
  }
9275
9189
  }
9276
9190
  } catch (err) {
9277
- log$11.warn("非图片附件下载失败,保留原始占位符", {
9191
+ log$12.warn("非图片附件下载失败,保留原始占位符", {
9278
9192
  type: res.type,
9279
9193
  fileKey: res.fileKey,
9280
9194
  error: err instanceof Error ? err.message : String(err)
@@ -9285,12 +9199,12 @@ async function dispatchToCC(params) {
9285
9199
  const textBlock = prompt.find((b) => b.type === "text");
9286
9200
  if (textBlock) textBlock.text = updatedTextPrompt;
9287
9201
  }
9288
- log$11.info("非图片附件处理完成", {
9202
+ log$12.info("非图片附件处理完成", {
9289
9203
  totalAttachments: attachmentResources.length,
9290
9204
  promptLength: typeof prompt === "string" ? prompt.length : prompt.find((b) => b.type === "text")?.text?.length
9291
9205
  });
9292
9206
  }
9293
- log$11.info("消息格式化完成", {
9207
+ log$12.info("消息格式化完成", {
9294
9208
  promptLength: typeof prompt === "string" ? prompt.length : prompt.length,
9295
9209
  isMultimodal: Array.isArray(prompt),
9296
9210
  isGroup
@@ -9322,50 +9236,50 @@ async function dispatchToCC(params) {
9322
9236
  };
9323
9237
  const cardController = new StreamingCardController(cardDeps);
9324
9238
  registerActiveCard(route.sessionId, cardController);
9325
- log$11.info("StreamingCardController 已创建", {
9239
+ log$12.info("StreamingCardController 已创建", {
9326
9240
  sessionId: route.sessionId,
9327
9241
  chatId,
9328
9242
  replyToMessageId: cardDeps.replyToMessageId
9329
9243
  });
9330
9244
  cardController.ensureCardCreated().catch((err) => {
9331
- log$11.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
9245
+ log$12.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
9332
9246
  });
9333
9247
  const bridge = new CCStreamBridge(cardController, {
9334
9248
  autoCompleteOnTurnEnd: true,
9335
9249
  sessionKey: route.sessionId
9336
9250
  });
9337
- log$11.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
9251
+ log$12.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
9338
9252
  const callbacks = {
9339
9253
  onTextDelta: (text) => {
9340
- log$11.debug("CC onTextDelta", {
9254
+ log$12.debug("CC onTextDelta", {
9341
9255
  sessionId: route.sessionId,
9342
9256
  deltaLen: text.length
9343
9257
  });
9344
9258
  bridge.onTextDelta(text);
9345
9259
  },
9346
9260
  onThinkingDelta: (text) => {
9347
- log$11.debug("CC onThinkingDelta", {
9261
+ log$12.debug("CC onThinkingDelta", {
9348
9262
  sessionId: route.sessionId,
9349
9263
  deltaLen: text.length
9350
9264
  });
9351
9265
  bridge.onThinkingDelta(text);
9352
9266
  },
9353
9267
  onToolUseStart: (toolName, toolInput) => {
9354
- log$11.info("CC onToolUseStart", {
9268
+ log$12.info("CC onToolUseStart", {
9355
9269
  sessionId: route.sessionId,
9356
9270
  toolName
9357
9271
  });
9358
9272
  bridge.onToolUseStart(toolName, toolInput);
9359
9273
  },
9360
9274
  onToolResult: (toolUseId) => {
9361
- log$11.info("CC onToolResult", {
9275
+ log$12.info("CC onToolResult", {
9362
9276
  sessionId: route.sessionId,
9363
9277
  toolUseId
9364
9278
  });
9365
9279
  bridge.onToolResult(toolUseId);
9366
9280
  },
9367
9281
  onToolProgress: (toolName, elapsedSeconds) => {
9368
- log$11.debug("CC onToolProgress", {
9282
+ log$12.debug("CC onToolProgress", {
9369
9283
  sessionId: route.sessionId,
9370
9284
  toolName,
9371
9285
  elapsedSeconds
@@ -9373,14 +9287,14 @@ async function dispatchToCC(params) {
9373
9287
  bridge.onToolProgress(toolName, elapsedSeconds);
9374
9288
  },
9375
9289
  onTurnEnd: (stopReason) => {
9376
- log$11.info("CC onTurnEnd", {
9290
+ log$12.info("CC onTurnEnd", {
9377
9291
  sessionId: route.sessionId,
9378
9292
  stopReason
9379
9293
  });
9380
9294
  bridge.onTurnEnd(stopReason);
9381
9295
  },
9382
9296
  onResult: (result) => {
9383
- log$11.info("CC onResult", {
9297
+ log$12.info("CC onResult", {
9384
9298
  sessionId: route.sessionId,
9385
9299
  subtype: result.subtype,
9386
9300
  isError: result.isError,
@@ -9391,14 +9305,14 @@ async function dispatchToCC(params) {
9391
9305
  bridge.onResult(result);
9392
9306
  },
9393
9307
  onError: (error) => {
9394
- log$11.error("CC 进程错误", {
9308
+ log$12.error("CC 进程错误", {
9395
9309
  sessionId: route.sessionId,
9396
9310
  error: error.message
9397
9311
  });
9398
9312
  cardController.onError(error, { kind: "cc-process" });
9399
9313
  }
9400
9314
  };
9401
- log$11.info("开始执行 CC 进程", {
9315
+ log$12.info("开始执行 CC 进程", {
9402
9316
  sessionId: route.sessionId,
9403
9317
  cwd: route.cwd,
9404
9318
  promptLength: prompt.length,
@@ -9415,10 +9329,10 @@ async function dispatchToCC(params) {
9415
9329
  maxBudgetUsd,
9416
9330
  userContext: params.userContext
9417
9331
  }, callbacks);
9418
- log$11.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
9332
+ log$12.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
9419
9333
  } catch (err) {
9420
9334
  const errorMessage = err instanceof Error ? err.message : String(err);
9421
- log$11.error("CC 进程 executePrompt 调用异常", {
9335
+ log$12.error("CC 进程 executePrompt 调用异常", {
9422
9336
  sessionId: route.sessionId,
9423
9337
  error: errorMessage
9424
9338
  });
@@ -9440,7 +9354,7 @@ async function dispatchToCC(params) {
9440
9354
  */
9441
9355
  async function dispatchTeammateEval(params) {
9442
9356
  const { chatId, prompt, account, sessionRouter, processManager } = params;
9443
- log$11.info("dispatchTeammateEval 开始", {
9357
+ log$12.info("dispatchTeammateEval 开始", {
9444
9358
  chatId,
9445
9359
  promptLength: prompt.length
9446
9360
  });
@@ -9479,7 +9393,7 @@ async function dispatchTeammateEval(params) {
9479
9393
  const confirmReply = () => {
9480
9394
  if (confirmed) return;
9481
9395
  confirmed = true;
9482
- log$11.info("teammate 确认回复,创建流式卡片", {
9396
+ log$12.info("teammate 确认回复,创建流式卡片", {
9483
9397
  chatId,
9484
9398
  thinkingLen: thinkingBuffer.length,
9485
9399
  textLen: fullTextBuffer.length
@@ -9573,7 +9487,7 @@ async function dispatchTeammateEval(params) {
9573
9487
  onTurnEnd: (stopReason) => {
9574
9488
  finalStopReason = stopReason;
9575
9489
  if (stopReason === "tool_use") {
9576
- log$11.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
9490
+ log$12.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
9577
9491
  if (confirmed && bridge) bridge.onTurnEnd(stopReason);
9578
9492
  return;
9579
9493
  }
@@ -9581,7 +9495,7 @@ async function dispatchTeammateEval(params) {
9581
9495
  const trimmed = fullTextBuffer.trim();
9582
9496
  if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed.endsWith(NO_REPLY_TOKEN) || trimmed === "") {
9583
9497
  silenced = true;
9584
- log$11.info("teammate turnEnd 最终判断: 静默", {
9498
+ log$12.info("teammate turnEnd 最终判断: 静默", {
9585
9499
  chatId,
9586
9500
  trimmed: trimmed.slice(0, 60)
9587
9501
  });
@@ -9596,7 +9510,7 @@ async function dispatchTeammateEval(params) {
9596
9510
  resolve();
9597
9511
  },
9598
9512
  onError: (error) => {
9599
- log$11.error("teammate 评估 CC 进程错误", {
9513
+ log$12.error("teammate 评估 CC 进程错误", {
9600
9514
  chatId,
9601
9515
  error: error.message
9602
9516
  });
@@ -9609,14 +9523,14 @@ async function dispatchTeammateEval(params) {
9609
9523
  prompt,
9610
9524
  model: params.model
9611
9525
  }, callbacks).catch((err) => {
9612
- log$11.error("teammate executePrompt 异常", {
9526
+ log$12.error("teammate executePrompt 异常", {
9613
9527
  chatId,
9614
9528
  error: err instanceof Error ? err.message : String(err)
9615
9529
  });
9616
9530
  resolve();
9617
9531
  });
9618
9532
  });
9619
- log$11.info("dispatchTeammateEval 完成", {
9533
+ log$12.info("dispatchTeammateEval 完成", {
9620
9534
  chatId,
9621
9535
  confirmed,
9622
9536
  silenced,
@@ -9627,7 +9541,7 @@ async function dispatchTeammateEval(params) {
9627
9541
  return { replied: confirmed };
9628
9542
  } catch (err) {
9629
9543
  const errorMessage = err instanceof Error ? err.message : String(err);
9630
- log$11.error("dispatchTeammateEval 异常", {
9544
+ log$12.error("dispatchTeammateEval 异常", {
9631
9545
  chatId,
9632
9546
  error: errorMessage
9633
9547
  });
@@ -11139,7 +11053,7 @@ const convertLocation = (raw) => {
11139
11053
  * injected via callbacks in `ConvertContext`. Callers are responsible
11140
11054
  * for creating the appropriate callbacks (UAT / TAT / event push).
11141
11055
  */
11142
- const log$10 = larkLogger("converters/merge-forward");
11056
+ const log$11 = larkLogger("converters/merge-forward");
11143
11057
  /**
11144
11058
  * Recursively expand a merge_forward message.
11145
11059
  *
@@ -11167,7 +11081,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
11167
11081
  try {
11168
11082
  items = await fetchSubMessages(messageId);
11169
11083
  } catch (error) {
11170
- log$10.error("fetch sub-messages failed", {
11084
+ log$11.error("fetch sub-messages failed", {
11171
11085
  messageId,
11172
11086
  error: error instanceof Error ? error.message : String(error)
11173
11087
  });
@@ -11179,7 +11093,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
11179
11093
  if (senderIds.length > 0 && batchResolveNames) try {
11180
11094
  await batchResolveNames(senderIds);
11181
11095
  } catch (err) {
11182
- 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) });
11183
11097
  }
11184
11098
  return formatSubTree(messageId, childrenMap, accountId, resolveUserName, convertContent);
11185
11099
  }
@@ -11259,7 +11173,7 @@ async function formatSubTree(parentId, childrenMap, accountId, resolveUserName,
11259
11173
  const indented = indentLines(content, " ");
11260
11174
  parts.push(`[${timestamp}] ${displayName}:\n${indented}`);
11261
11175
  } catch (err) {
11262
- log$10.warn("failed to convert sub-message", {
11176
+ log$11.warn("failed to convert sub-message", {
11263
11177
  messageId: item.message_id,
11264
11178
  msgType: item.msg_type ?? "unknown",
11265
11179
  error: err instanceof Error ? err.message : String(err)
@@ -11473,7 +11387,7 @@ async function convertMessageContent(raw, messageType, ctx) {
11473
11387
  }
11474
11388
  //#endregion
11475
11389
  //#region src/messaging/inbound/parse-io.ts
11476
- const log$9 = larkLogger("inbound/parse-io");
11390
+ const log$10 = larkLogger("inbound/parse-io");
11477
11391
  /**
11478
11392
  * 对 interactive 消息,通过 TAT 调用 API 获取完整 v2 卡片内容。
11479
11393
  * 事件推送的 content 可能不包含 json_card,API 调用可返回完整的 raw_card_content。
@@ -11493,7 +11407,7 @@ async function fetchCardContent(messageId, larkClient) {
11493
11407
  }
11494
11408
  }))?.data?.items?.[0]?.body?.content ?? void 0;
11495
11409
  } catch (err) {
11496
- 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)}`);
11497
11411
  return;
11498
11412
  }
11499
11413
  }
@@ -11527,11 +11441,11 @@ function createFetchSubMessages(larkClient) {
11527
11441
  * the account and log function.
11528
11442
  */
11529
11443
  function createParseResolveNames(account) {
11530
- return createBatchResolveNames(account, (...args) => log$9.info(args.map(String).join(" ")));
11444
+ return createBatchResolveNames(account, (...args) => log$10.info(args.map(String).join(" ")));
11531
11445
  }
11532
11446
  //#endregion
11533
11447
  //#region src/messaging/inbound/parse.ts
11534
- const log$8 = larkLogger("inbound/parse");
11448
+ const log$9 = larkLogger("inbound/parse");
11535
11449
  /**
11536
11450
  * Parse a raw Feishu message event into a normalised MessageContext.
11537
11451
  *
@@ -11581,7 +11495,7 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
11581
11495
  const fullContent = await fetchCardContent(event.message.message_id, larkClient);
11582
11496
  if (fullContent) {
11583
11497
  effectiveContent = fullContent;
11584
- log$8.info("replaced interactive content with full v2 card data");
11498
+ log$9.info("replaced interactive content with full v2 card data");
11585
11499
  }
11586
11500
  }
11587
11501
  const convertCtx = {
@@ -11701,6 +11615,123 @@ var MessageDedup = class {
11701
11615
  }
11702
11616
  };
11703
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
11704
11735
  //#region src/user/types.ts
11705
11736
  /** Tenant 级别的 UserContext(群聊默认身份) */
11706
11737
  const TENANT_USER_ID = "tenant";
@@ -11826,9 +11857,7 @@ async function ensureUserDirectory(userCtx) {
11826
11857
  larkLogger("user/oauth-proxy");
11827
11858
  //#endregion
11828
11859
  //#region src/gateway/plugins/loader.ts
11829
- const PLUGIN_REGISTRY = { "preview-proxy": async () => {
11830
- return (await import("./preview-proxy-KMPQK_j4.mjs")).createPreviewProxyPlugin;
11831
- } };
11860
+ const PLUGIN_REGISTRY = {};
11832
11861
  const log$4 = larkLogger("gateway/plugin-loader");
11833
11862
  /** 已加载的插件实例列表 */
11834
11863
  let loadedPlugins = [];
@@ -12276,7 +12305,8 @@ var TeammateBuffer = class {
12276
12305
  this.evalCallback({
12277
12306
  chatId,
12278
12307
  prompt,
12279
- messageCount: messages.length
12308
+ messageCount: messages.length,
12309
+ messages: [...messages]
12280
12310
  }).catch((err) => {
12281
12311
  log$1.error("teammate 评估回调异常", {
12282
12312
  chatId,
@@ -12300,9 +12330,7 @@ var TeammateBuffer = class {
12300
12330
  "",
12301
12331
  "重要规则:",
12302
12332
  "- 这是一次旁听评估,仅在本次评估中适用 NO_REPLY 规则。后续如果用户直接 @你 则必须正常回复。",
12303
- "- 你无法查看图片/文件的实际内容。如果消息只包含图片或文件且没有文字上下文,直接输出 NO_REPLY。",
12304
- "- 不要使用任何工具(Bash/Glob/Read 等)来尝试查找或分析图片文件。",
12305
- "- 只基于文字内容判断是否参与。",
12333
+ "- 不要使用工具来查找或分析消息内容,直接基于消息内容判断是否参与。",
12306
12334
  "",
12307
12335
  "---"
12308
12336
  ].join("\n");
@@ -12814,6 +12842,8 @@ async function main() {
12814
12842
  workspaceRoot,
12815
12843
  externalBaseUrl: process.env.LARKPAL_EXTERNAL_URL
12816
12844
  });
12845
+ const { discoverTools } = await import("./tool-registry-consumer-DrklfqGF.mjs").then((n) => n.n);
12846
+ await discoverTools(gatewayPort);
12817
12847
  await gateway.start();
12818
12848
  logger.info("网关 HTTP 服务启动完成", {
12819
12849
  host: gatewayHost,
@@ -12855,13 +12885,29 @@ async function main() {
12855
12885
  messageCount: payload.messageCount,
12856
12886
  promptLength: payload.prompt.length
12857
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
+ }
12858
12904
  const { status: queueStatus } = enqueueFeishuChatTask({
12859
12905
  accountId: "default",
12860
12906
  chatId: payload.chatId,
12861
12907
  task: async () => {
12862
12908
  const result = await dispatchTeammateEval({
12863
12909
  chatId: payload.chatId,
12864
- prompt: payload.prompt,
12910
+ prompt: evalPrompt,
12865
12911
  account,
12866
12912
  sessionRouter,
12867
12913
  processManager: runtimeAdapter,
@@ -12950,7 +12996,8 @@ async function main() {
12950
12996
  teammateBuffer.push(parsed.chatId, {
12951
12997
  formattedText,
12952
12998
  bufferedAt: Date.now(),
12953
- messageId: msgId
12999
+ messageId: msgId,
13000
+ resources: parsed.resources.length > 0 ? parsed.resources : void 0
12954
13001
  });
12955
13002
  logger.info("回复策略: teammate 消息已写入缓冲", {
12956
13003
  msgId,