@vibe-lark/larkpal 0.1.58 → 0.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/main.mjs +833 -677
  2. package/package.json +2 -2
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ import * as path$2 from "node:path";
6
6
  import path, { basename, dirname, extname, join, resolve } from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import "dotenv/config";
9
- import { access, lstat, mkdir, readFile, readdir, readlink, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
9
+ import { access, lstat, mkdir, open, readFile, readdir, readlink, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
10
10
  import { homedir, tmpdir } from "node:os";
11
11
  import { execFileSync, spawn } from "node:child_process";
12
12
  import { randomUUID } from "node:crypto";
@@ -38,7 +38,7 @@ import { Readable } from "node:stream";
38
38
  *
39
39
  * 本模块在启动时检测它们是否已安装,缺失时给出明确的安装指引。
40
40
  */
41
- const log$38 = larkLogger("preflight");
41
+ const log$39 = larkLogger("preflight");
42
42
  function detectCli(name) {
43
43
  try {
44
44
  const cliPath = execFileSync("which", [name], {
@@ -76,14 +76,14 @@ function detectCli(name) {
76
76
  * 缺失任一依赖时打印安装指引并抛出错误,阻止启动。
77
77
  */
78
78
  function preflight() {
79
- log$38.info("开始前置环境检查...");
79
+ log$39.info("开始前置环境检查...");
80
80
  const useCodex = (process.env.LARKPAL_RUNTIME || "claude-code") === "codex-app-server";
81
81
  const claude = detectCli("claude");
82
82
  const codex = useCodex ? detectCli(process.env.LARKPAL_CODEX_BIN || "codex") : void 0;
83
83
  const larkCli = detectCli("lark-cli");
84
84
  const status = (dep) => dep.installed ? `✅ ${dep.name} (${dep.version ?? "unknown"}) → ${dep.path}` : `❌ ${dep.name} 未安装`;
85
- log$38.info(` ${status(useCodex && codex ? codex : claude)}`);
86
- log$38.info(` ${status(larkCli)}`);
85
+ log$39.info(` ${status(useCodex && codex ? codex : claude)}`);
86
+ log$39.info(` ${status(larkCli)}`);
87
87
  const missing = [];
88
88
  if (useCodex) {
89
89
  if (!codex?.installed) missing.push([
@@ -119,10 +119,10 @@ function preflight() {
119
119
  "",
120
120
  "请安装缺失的依赖后重新启动 LarkPal。"
121
121
  ].join("\n");
122
- log$38.error(msg);
122
+ log$39.error(msg);
123
123
  throw new Error("前置环境检查未通过,缺少必要依赖");
124
124
  }
125
- log$38.info("前置环境检查通过 ✓");
125
+ log$39.info("前置环境检查通过 ✓");
126
126
  return {
127
127
  claude,
128
128
  codex,
@@ -147,7 +147,7 @@ function preflight() {
147
147
  * 同时生成新版 (~/.lark-cli/config.json) 和旧版 (~/.config/lark/config.json)
148
148
  * 格式的配置文件,保证 lark-cli 无论通过哪种路径读取都能正常工作。
149
149
  */
150
- const log$37 = larkLogger("config/ensure-lark-cli");
150
+ const log$38 = larkLogger("config/ensure-lark-cli");
151
151
  /** 新版 lark-cli 配置路径 */
152
152
  const NEW_CONFIG_DIR = join(homedir(), ".lark-cli");
153
153
  const NEW_CONFIG_PATH = join(NEW_CONFIG_DIR, "config.json");
@@ -164,19 +164,19 @@ function ensureLarkCliConfig() {
164
164
  const appId = process.env.LARK_APP_ID;
165
165
  const appSecret = process.env.LARK_APP_SECRET;
166
166
  if (!appId || !appSecret) {
167
- log$37.info("环境变量凭证未设置,跳过 lark-cli 配置同步");
167
+ log$38.info("环境变量凭证未设置,跳过 lark-cli 配置同步");
168
168
  return;
169
169
  }
170
170
  const hasNewConfig = existsSync(NEW_CONFIG_PATH);
171
171
  const hasLegacyConfig = existsSync(LEGACY_CONFIG_PATH);
172
172
  if (hasNewConfig && hasLegacyConfig) {
173
- log$37.info("lark-cli 配置文件已存在,跳过同步", {
173
+ log$38.info("lark-cli 配置文件已存在,跳过同步", {
174
174
  newConfig: NEW_CONFIG_PATH,
175
175
  legacyConfig: LEGACY_CONFIG_PATH
176
176
  });
177
177
  return;
178
178
  }
179
- log$37.info("检测到环境变量凭证但 lark-cli 配置文件缺失,开始同步", {
179
+ log$38.info("检测到环境变量凭证但 lark-cli 配置文件缺失,开始同步", {
180
180
  appId,
181
181
  hasNewConfig,
182
182
  hasLegacyConfig
@@ -195,9 +195,9 @@ function ensureLarkCliConfig() {
195
195
  encoding: "utf-8",
196
196
  mode: 384
197
197
  });
198
- log$37.info("已生成新版 lark-cli 配置文件", { path: NEW_CONFIG_PATH });
198
+ log$38.info("已生成新版 lark-cli 配置文件", { path: NEW_CONFIG_PATH });
199
199
  } catch (err) {
200
- log$37.warn("生成新版 lark-cli 配置文件失败", {
200
+ log$38.warn("生成新版 lark-cli 配置文件失败", {
201
201
  path: NEW_CONFIG_PATH,
202
202
  error: err instanceof Error ? err.message : String(err)
203
203
  });
@@ -216,9 +216,9 @@ function ensureLarkCliConfig() {
216
216
  encoding: "utf-8",
217
217
  mode: 384
218
218
  });
219
- log$37.info("已生成旧版 lark-cli 配置文件", { path: LEGACY_CONFIG_PATH });
219
+ log$38.info("已生成旧版 lark-cli 配置文件", { path: LEGACY_CONFIG_PATH });
220
220
  } catch (err) {
221
- log$37.warn("生成旧版 lark-cli 配置文件失败", {
221
+ log$38.warn("生成旧版 lark-cli 配置文件失败", {
222
222
  path: LEGACY_CONFIG_PATH,
223
223
  error: err instanceof Error ? err.message : String(err)
224
224
  });
@@ -439,7 +439,7 @@ async function ensureDefaults() {
439
439
  * - github: { type: "api_key", token }
440
440
  * - custom-api: { type: "http_bearer", base_url, token }
441
441
  */
442
- const log$36 = larkLogger("user/credential-vault");
442
+ const log$37 = larkLogger("user/credential-vault");
443
443
  var CredentialVault = class {
444
444
  credentialPath;
445
445
  encryptionKey;
@@ -451,12 +451,12 @@ var CredentialVault = class {
451
451
  if (keyEnv) {
452
452
  this.encryptionKey = keyEnv.length === 64 ? Buffer.from(keyEnv, "hex") : Buffer.from(keyEnv, "base64");
453
453
  if (this.encryptionKey.length !== 32) {
454
- log$36.error("LARKPAL_ENCRYPTION_KEY 长度不正确,需要 32 字节", { actualLength: this.encryptionKey.length });
454
+ log$37.error("LARKPAL_ENCRYPTION_KEY 长度不正确,需要 32 字节", { actualLength: this.encryptionKey.length });
455
455
  this.encryptionKey = null;
456
- } else log$36.info("凭证加密已启用");
456
+ } else log$37.info("凭证加密已启用");
457
457
  } else {
458
458
  this.encryptionKey = null;
459
- log$36.warn("LARKPAL_ENCRYPTION_KEY 未设置,凭证将以明文存储(仅限开发模式)");
459
+ log$37.warn("LARKPAL_ENCRYPTION_KEY 未设置,凭证将以明文存储(仅限开发模式)");
460
460
  }
461
461
  }
462
462
  /**
@@ -464,7 +464,7 @@ var CredentialVault = class {
464
464
  */
465
465
  async load() {
466
466
  if (!existsSync$1(this.credentialPath)) {
467
- log$36.info("凭证文件不存在,使用空凭证", { path: this.credentialPath });
467
+ log$37.info("凭证文件不存在,使用空凭证", { path: this.credentialPath });
468
468
  this.store = {};
469
469
  this.loaded = true;
470
470
  return;
@@ -473,12 +473,12 @@ var CredentialVault = class {
473
473
  const raw = await readFile$1(this.credentialPath, "utf-8");
474
474
  this.store = JSON.parse(raw);
475
475
  this.loaded = true;
476
- log$36.info("凭证文件加载完成", {
476
+ log$37.info("凭证文件加载完成", {
477
477
  path: this.credentialPath,
478
478
  credentialCount: Object.keys(this.store).length
479
479
  });
480
480
  } catch (err) {
481
- log$36.error("凭证文件加载失败", {
481
+ log$37.error("凭证文件加载失败", {
482
482
  path: this.credentialPath,
483
483
  error: err instanceof Error ? err.message : String(err)
484
484
  });
@@ -492,12 +492,12 @@ var CredentialVault = class {
492
492
  async save() {
493
493
  try {
494
494
  await writeFile$1(this.credentialPath, JSON.stringify(this.store, null, 2), "utf-8");
495
- log$36.info("凭证文件保存完成", {
495
+ log$37.info("凭证文件保存完成", {
496
496
  path: this.credentialPath,
497
497
  credentialCount: Object.keys(this.store).length
498
498
  });
499
499
  } catch (err) {
500
- log$36.error("凭证文件保存失败", {
500
+ log$37.error("凭证文件保存失败", {
501
501
  path: this.credentialPath,
502
502
  error: err instanceof Error ? err.message : String(err)
503
503
  });
@@ -519,7 +519,7 @@ var CredentialVault = class {
519
519
  */
520
520
  async setCredential(name, credential) {
521
521
  if (!this.loaded) await this.load();
522
- log$36.info("设置凭证", {
522
+ log$37.info("设置凭证", {
523
523
  name,
524
524
  fields: Object.keys(credential)
525
525
  });
@@ -535,7 +535,7 @@ var CredentialVault = class {
535
535
  if (!(name in this.store)) return false;
536
536
  delete this.store[name];
537
537
  await this.save();
538
- log$36.info("凭证已删除", { name });
538
+ log$37.info("凭证已删除", { name });
539
539
  return true;
540
540
  }
541
541
  /**
@@ -564,7 +564,7 @@ var CredentialVault = class {
564
564
  envVars[envKey] = String(fieldValue);
565
565
  }
566
566
  } catch (err) {
567
- log$36.warn("凭证解密失败,跳过", {
567
+ log$37.warn("凭证解密失败,跳过", {
568
568
  name,
569
569
  error: err instanceof Error ? err.message : String(err)
570
570
  });
@@ -621,7 +621,7 @@ var CredentialVault = class {
621
621
  * - B 用户的 CC 进程连接 B 的数据库 MCP Server
622
622
  * - 全局工具(lark-cli、文件操作等)所有用户共享
623
623
  */
624
- const log$35 = larkLogger("user/mcp-merge");
624
+ const log$36 = larkLogger("user/mcp-merge");
625
625
  /** 全局 MCP 配置路径 */
626
626
  const GLOBAL_MCP_CONFIG_PATH = path$1.join(os.homedir(), ".claude", "mcp-servers.json");
627
627
  /**
@@ -677,9 +677,9 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId, cwd) {
677
677
  if (existsSync$1(GLOBAL_MCP_CONFIG_PATH)) try {
678
678
  const raw = await readFile$1(GLOBAL_MCP_CONFIG_PATH, "utf-8");
679
679
  globalConfig = JSON.parse(raw);
680
- log$35.info("全局 MCP 配置加载完成", { serverCount: Object.keys(globalConfig).length });
680
+ log$36.info("全局 MCP 配置加载完成", { serverCount: Object.keys(globalConfig).length });
681
681
  } catch (err) {
682
- log$35.warn("全局 MCP 配置文件解析失败", {
682
+ log$36.warn("全局 MCP 配置文件解析失败", {
683
683
  path: GLOBAL_MCP_CONFIG_PATH,
684
684
  error: err instanceof Error ? err.message : String(err)
685
685
  });
@@ -690,13 +690,13 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId, cwd) {
690
690
  if (existsSync$1(userMcpPath)) try {
691
691
  const raw = await readFile$1(userMcpPath, "utf-8");
692
692
  userConfig = JSON.parse(raw);
693
- log$35.info("用户 MCP 配置加载完成", {
693
+ log$36.info("用户 MCP 配置加载完成", {
694
694
  userId: userCtx.userId,
695
695
  serverCount: Object.keys(userConfig).length,
696
696
  servers: Object.keys(userConfig)
697
697
  });
698
698
  } catch (err) {
699
- log$35.warn("用户 MCP 配置文件解析失败", {
699
+ log$36.warn("用户 MCP 配置文件解析失败", {
700
700
  userId: userCtx?.userId,
701
701
  path: userMcpPath,
702
702
  error: err instanceof Error ? err.message : String(err)
@@ -708,7 +708,7 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId, cwd) {
708
708
  ...globalConfig,
709
709
  ...userConfig
710
710
  };
711
- log$35.info("MCP 配置合并完成", {
711
+ log$36.info("MCP 配置合并完成", {
712
712
  userId: userCtx?.userId,
713
713
  builtinServers: Object.keys(builtinConfig),
714
714
  globalServers: Object.keys(globalConfig),
@@ -717,7 +717,7 @@ async function mergeAndWriteMcpConfig(userCtx, sessionId, cwd) {
717
717
  });
718
718
  const tmpPath = path$1.join(os.tmpdir(), `mcp-${sessionId}.json`);
719
719
  await writeFile$1(tmpPath, JSON.stringify({ mcpServers: merged }, null, 2), "utf-8");
720
- log$35.info("MCP 合并配置已写入临时文件", {
720
+ log$36.info("MCP 合并配置已写入临时文件", {
721
721
  path: tmpPath,
722
722
  serverCount: Object.keys(merged).length
723
723
  });
@@ -731,7 +731,7 @@ async function cleanupMcpConfig(sessionId) {
731
731
  try {
732
732
  if (existsSync$1(tmpPath)) {
733
733
  await unlink$1(tmpPath);
734
- log$35.debug("MCP 临时配置文件已清理", { path: tmpPath });
734
+ log$36.debug("MCP 临时配置文件已清理", { path: tmpPath });
735
735
  }
736
736
  } catch {}
737
737
  }
@@ -755,8 +755,7 @@ const TOOL_DISPLAY_NAMES = {
755
755
  WebSearch: "正在搜索网页",
756
756
  WebFetch: "正在获取网页内容",
757
757
  TodoWrite: "正在更新任务列表",
758
- Task: "正在执行子任务",
759
- _AutoCompact: "正在压缩对话历史"
758
+ Task: "正在执行子任务"
760
759
  };
761
760
  /** 获取工具的中文显示名称,未知工具返回通用描述 */
762
761
  function getToolDisplayName(toolName) {
@@ -780,7 +779,7 @@ function getToolDisplayName(toolName) {
780
779
  * - result(data) — 最终结果
781
780
  * - parseError(error, rawLine) — 解析错误(不中断流)
782
781
  */
783
- const log$34 = larkLogger("cc-runtime/stream-parser");
782
+ const log$35 = larkLogger("cc-runtime/stream-parser");
784
783
  var CCStreamParser = class extends EventEmitter {
785
784
  /**
786
785
  * 解析一行 NDJSON 文本
@@ -794,7 +793,7 @@ var CCStreamParser = class extends EventEmitter {
794
793
  try {
795
794
  msg = JSON.parse(trimmed);
796
795
  } catch (err) {
797
- log$34.warn("NDJSON 解析失败,跳过该行", {
796
+ log$35.warn("NDJSON 解析失败,跳过该行", {
798
797
  error: String(err),
799
798
  rawLine: trimmed.slice(0, 200)
800
799
  });
@@ -802,7 +801,7 @@ var CCStreamParser = class extends EventEmitter {
802
801
  return;
803
802
  }
804
803
  if (msg.parent_tool_use_id != null && msg.type !== "result") {
805
- log$34.debug("跳过 subagent 内部事件", {
804
+ log$35.debug("跳过 subagent 内部事件", {
806
805
  type: msg.type,
807
806
  parentToolUseId: msg.parent_tool_use_id
808
807
  });
@@ -825,14 +824,14 @@ var CCStreamParser = class extends EventEmitter {
825
824
  this.handleResult(msg);
826
825
  break;
827
826
  case "system":
828
- log$34.info("收到 system 消息", {
827
+ log$35.info("收到 system 消息", {
829
828
  subtype: msg.subtype,
830
829
  detail: JSON.stringify(msg).slice(0, 500)
831
830
  });
832
831
  this.emit("system", msg);
833
832
  break;
834
833
  default:
835
- log$34.debug("收到未知消息类型,已忽略", { type: msg.type });
834
+ log$35.debug("收到未知消息类型,已忽略", { type: msg.type });
836
835
  break;
837
836
  }
838
837
  }
@@ -851,13 +850,13 @@ var CCStreamParser = class extends EventEmitter {
851
850
  switch (event.type) {
852
851
  case "content_block_start": {
853
852
  const block = event.content_block;
854
- log$34.debug("content_block_start 事件", {
853
+ log$35.debug("content_block_start 事件", {
855
854
  blockType: block?.type,
856
855
  toolName: block?.name,
857
856
  toolUseId: block?.id
858
857
  });
859
858
  if (block?.type === "tool_use" && block.name) {
860
- log$34.info("工具调用开始", {
859
+ log$35.info("工具调用开始", {
861
860
  toolName: block.name,
862
861
  displayName: getToolDisplayName(block.name),
863
862
  toolUseId: block.id
@@ -876,7 +875,7 @@ var CCStreamParser = class extends EventEmitter {
876
875
  case "message_delta": {
877
876
  const stopReason = event.delta?.stop_reason;
878
877
  if (stopReason) {
879
- log$34.info("轮次结束", { stopReason });
878
+ log$35.info("轮次结束", { stopReason });
880
879
  this.emit("turnEnd", stopReason);
881
880
  }
882
881
  break;
@@ -896,7 +895,7 @@ var CCStreamParser = class extends EventEmitter {
896
895
  const content = msg.message?.content;
897
896
  const stopReason = msg.message?.stop_reason;
898
897
  const blockTypes = Array.isArray(content) ? content.map((b) => `${b.type}${b.name ? ":" + b.name : ""}`).join(", ") : "none";
899
- log$34.info("收到完整 assistant 消息", {
898
+ log$35.info("收到完整 assistant 消息", {
900
899
  model: msg.message?.model,
901
900
  stopReason,
902
901
  contentBlocks: content?.length,
@@ -904,7 +903,7 @@ var CCStreamParser = class extends EventEmitter {
904
903
  });
905
904
  if (Array.isArray(content)) {
906
905
  for (const block of content) if (block.type === "tool_use" && block.name) {
907
- log$34.info("从 assistant 消息提取工具调用", {
906
+ log$35.info("从 assistant 消息提取工具调用", {
908
907
  toolName: block.name,
909
908
  displayName: getToolDisplayName(block.name),
910
909
  toolUseId: block.id
@@ -912,11 +911,6 @@ var CCStreamParser = class extends EventEmitter {
912
911
  this.emit("toolUseStart", block.name, block.input ?? {});
913
912
  }
914
913
  }
915
- const usage = msg.message?.usage;
916
- if (usage && typeof usage.input_tokens === "number" && usage.input_tokens > 0) this.emit("usage", {
917
- input_tokens: usage.input_tokens,
918
- output_tokens: usage.output_tokens ?? 0
919
- });
920
914
  this.emit("assistant", msg);
921
915
  }
922
916
  /**
@@ -928,7 +922,7 @@ var CCStreamParser = class extends EventEmitter {
928
922
  const content = msg.message?.content;
929
923
  if (!Array.isArray(content)) return;
930
924
  for (const item of content) if (item.type === "tool_result" && item.tool_use_id) {
931
- log$34.debug("工具结果返回", { toolUseId: item.tool_use_id });
925
+ log$35.debug("工具结果返回", { toolUseId: item.tool_use_id });
932
926
  this.emit("toolResult", item.tool_use_id);
933
927
  }
934
928
  }
@@ -936,7 +930,7 @@ var CCStreamParser = class extends EventEmitter {
936
930
  * 处理 tool_progress 消息 — 工具执行进度
937
931
  */
938
932
  handleToolProgress(msg) {
939
- log$34.debug("工具执行进度", {
933
+ log$35.debug("工具执行进度", {
940
934
  toolName: msg.tool_name,
941
935
  elapsed: msg.elapsed_time_seconds
942
936
  });
@@ -946,18 +940,13 @@ var CCStreamParser = class extends EventEmitter {
946
940
  * 处理 result 消息 — CC 执行完成的最终结果
947
941
  */
948
942
  handleResult(msg) {
949
- log$34.info("CC 执行完成", {
943
+ log$35.info("CC 执行完成", {
950
944
  subtype: msg.subtype,
951
945
  isError: msg.is_error,
952
946
  durationMs: msg.duration_ms,
953
947
  totalCostUsd: msg.total_cost_usd,
954
948
  numTurns: msg.num_turns
955
949
  });
956
- const usage = msg.usage;
957
- if (usage && typeof usage.input_tokens === "number" && usage.input_tokens > 0) this.emit("usage", {
958
- input_tokens: usage.input_tokens,
959
- output_tokens: usage.output_tokens ?? 0
960
- });
961
950
  this.emit("result", {
962
951
  subtype: msg.subtype,
963
952
  result: msg.result,
@@ -984,7 +973,7 @@ var CCStreamParser = class extends EventEmitter {
984
973
  * 多轮对话:进程内自动保持完整对话上下文(mutableMessages 数组累积),
985
974
  * 无需外部维护历史。
986
975
  */
987
- const log$33 = larkLogger("cc-runtime/process-manager");
976
+ const log$34 = larkLogger("cc-runtime/process-manager");
988
977
  /**
989
978
  * LarkPal 会话 UUID v5 命名空间
990
979
  *
@@ -1005,25 +994,6 @@ const IDLE_TIMEOUT_MS = (() => {
1005
994
  return seconds * 1e3;
1006
995
  })();
1007
996
  /**
1008
- * 自动压缩阈值(input_tokens 数)。
1009
- *
1010
- * 当 CC 返回的 input_tokens 超过此阈值时,在下一次用户消息发送前
1011
- * 自动执行 /compact 命令压缩对话历史。
1012
- *
1013
- * 通过 LARKPAL_CC_AUTO_COMPACT_THRESHOLD 环境变量配置,默认 0(禁用)。
1014
- * 设为 0 表示禁用自动压缩。
1015
- *
1016
- * LarkPal 现在优先通过 runtime-owned memory/ephemeral teammate eval 管理上下文;
1017
- * 该开关只作为显式启用的应急制动。
1018
- */
1019
- const AUTO_COMPACT_THRESHOLD = (() => {
1020
- const envVal = process.env.LARKPAL_CC_AUTO_COMPACT_THRESHOLD;
1021
- if (envVal === void 0 || envVal === "") return 0;
1022
- const tokens = parseInt(envVal, 10);
1023
- if (isNaN(tokens) || tokens < 0) return 5e4;
1024
- return tokens;
1025
- })();
1026
- /**
1027
997
  * CC 推理努力级别。
1028
998
  *
1029
999
  * 控制模型的 thinking budget(思维深度),影响延迟和成本:
@@ -1079,17 +1049,6 @@ var SessionProcessManager = class {
1079
1049
  messageCompletionResolvers = /* @__PURE__ */ new Map();
1080
1050
  /** 消息级串行锁:确保同一 session 的消息按顺序处理完成 */
1081
1051
  sessionLocks = /* @__PURE__ */ new Map();
1082
- /** 等待下一次 result 的维护任务 resolver(如 auto compact),不参与用户消息完成语义 */
1083
- resultWaiters = /* @__PURE__ */ new Map();
1084
- /**
1085
- * 最近一次 CC 响应的 input_tokens:sessionId → token 数
1086
- *
1087
- * 由 parser 的 usage 事件更新。用于在下一次 executePrompt 前判断
1088
- * 是否需要自动触发 /compact 压缩对话历史。
1089
- */
1090
- lastInputTokens = /* @__PURE__ */ new Map();
1091
- /** auto compact 失败熔断:sessionId → 下次允许尝试的时间戳 */
1092
- compactRetryAfter = /* @__PURE__ */ new Map();
1093
1052
  /** 标记 session 为用户主动中断 */
1094
1053
  markAborted(sessionId) {
1095
1054
  this.abortedSessions.add(sessionId);
@@ -1135,9 +1094,9 @@ var SessionProcessManager = class {
1135
1094
  const t0 = Date.now();
1136
1095
  const prevLock = this.sessionLocks.get(sessionId);
1137
1096
  if (prevLock) {
1138
- log$33.info("等待上一条消息处理完成", { sessionId });
1097
+ log$34.info("等待上一条消息处理完成", { sessionId });
1139
1098
  await prevLock;
1140
- log$33.info("[perf] 串行锁等待完成", {
1099
+ log$34.info("[perf] 串行锁等待完成", {
1141
1100
  sessionId,
1142
1101
  waitMs: Date.now() - t0
1143
1102
  });
@@ -1151,36 +1110,24 @@ var SessionProcessManager = class {
1151
1110
  try {
1152
1111
  const existing = this.processes.get(sessionId);
1153
1112
  if (existing && existing.status === "running" && existing.useStreamInput) {
1154
- log$33.info("向常驻进程发送消息", {
1113
+ log$34.info("向常驻进程发送消息", {
1155
1114
  sessionId,
1156
1115
  promptLength: config.prompt.length,
1157
1116
  pid: existing.childProcess.pid,
1158
1117
  prepareMs: Date.now() - t0
1159
1118
  });
1160
- await this.autoCompactIfNeeded(sessionId);
1161
1119
  const completionPromise = this.createMessageCompletionPromise(sessionId);
1162
- const current = this.processes.get(sessionId);
1163
- if (!current || current.status !== "running" || !current.useStreamInput) {
1164
- log$33.warn("[auto-compact] 压缩后热进程不可用,重新冷启动发送用户消息", { sessionId });
1165
- await this.startPersistentProcess(config);
1166
- await completionPromise;
1167
- log$33.info("[perf] executePrompt 完成(compact 后冷启动)", {
1168
- sessionId,
1169
- totalMs: Date.now() - t0
1170
- });
1171
- return;
1172
- }
1173
1120
  this.sendMessage(sessionId, config.prompt);
1174
1121
  this.resetIdleTimer(sessionId);
1175
1122
  await completionPromise;
1176
- log$33.info("[perf] executePrompt 完成(热进程)", {
1123
+ log$34.info("[perf] executePrompt 完成(热进程)", {
1177
1124
  sessionId,
1178
1125
  totalMs: Date.now() - t0
1179
1126
  });
1180
1127
  return;
1181
1128
  }
1182
1129
  if (existing && (existing.status === "stopped" || existing.status === "crashed")) {
1183
- log$33.info("清理已终止的旧进程,准备重启", {
1130
+ log$34.info("清理已终止的旧进程,准备重启", {
1184
1131
  sessionId,
1185
1132
  oldStatus: existing.status
1186
1133
  });
@@ -1188,25 +1135,25 @@ var SessionProcessManager = class {
1188
1135
  this.sessionLocks.set(sessionId, sessionLock);
1189
1136
  }
1190
1137
  if (existing && existing.status === "running" && !existing.useStreamInput) {
1191
- log$33.warn("停止旧的单次进程后重新启动为常驻模式", { sessionId });
1138
+ log$34.warn("停止旧的单次进程后重新启动为常驻模式", { sessionId });
1192
1139
  await this.stopProcess(sessionId);
1193
1140
  this.sessionLocks.set(sessionId, sessionLock);
1194
1141
  }
1195
1142
  const completionPromise = this.createMessageCompletionPromise(sessionId);
1196
1143
  const tStart = Date.now();
1197
1144
  await this.startPersistentProcess(config);
1198
- log$33.info("[perf] 冷启动进程完成", {
1145
+ log$34.info("[perf] 冷启动进程完成", {
1199
1146
  sessionId,
1200
1147
  coldStartMs: Date.now() - tStart
1201
1148
  });
1202
1149
  await completionPromise;
1203
- log$33.info("[perf] executePrompt 完成(冷启动)", {
1150
+ log$34.info("[perf] executePrompt 完成(冷启动)", {
1204
1151
  sessionId,
1205
1152
  totalMs: Date.now() - t0
1206
1153
  });
1207
1154
  } finally {
1208
1155
  if (config.transcriptMode === "ephemeral") await this.stopProcess(sessionId).catch((err) => {
1209
- log$33.warn("ephemeral CC 进程清理失败", {
1156
+ log$34.warn("ephemeral CC 进程清理失败", {
1210
1157
  sessionId,
1211
1158
  error: err instanceof Error ? err.message : String(err)
1212
1159
  });
@@ -1245,13 +1192,13 @@ var SessionProcessManager = class {
1245
1192
  try {
1246
1193
  const mcpConfigPath = await mergeAndWriteMcpConfig(config.userContext && !config.userContext.isTenantIdentity ? config.userContext : null, sessionId, cwd);
1247
1194
  args.push("--mcp-config", mcpConfigPath);
1248
- log$33.info("MCP 配置已合并并注入到 CC 进程", {
1195
+ log$34.info("MCP 配置已合并并注入到 CC 进程", {
1249
1196
  sessionId,
1250
1197
  userId: config.userContext?.userId,
1251
1198
  mcpConfigPath
1252
1199
  });
1253
1200
  } catch (err) {
1254
- log$33.warn("MCP 配置合并失败,CC 进程将使用全局默认配置", {
1201
+ log$34.warn("MCP 配置合并失败,CC 进程将使用全局默认配置", {
1255
1202
  sessionId,
1256
1203
  error: err instanceof Error ? err.message : String(err)
1257
1204
  });
@@ -1278,12 +1225,12 @@ var SessionProcessManager = class {
1278
1225
  if (larkCred?.user_access_token && typeof larkCred.user_access_token === "string") processEnv.LARK_USER_ACCESS_TOKEN = larkCred.user_access_token;
1279
1226
  } else {
1280
1227
  delete processEnv.LARK_USER_ACCESS_TOKEN;
1281
- log$33.info("LARKPAL_DISABLE_USER_AUTH 已启用,跳过飞书 user_access_token 注入", {
1228
+ log$34.info("LARKPAL_DISABLE_USER_AUTH 已启用,跳过飞书 user_access_token 注入", {
1282
1229
  sessionId,
1283
1230
  userId: userContext.userId
1284
1231
  });
1285
1232
  }
1286
- log$33.info("用户凭证已注入到 CC 进程环境", {
1233
+ log$34.info("用户凭证已注入到 CC 进程环境", {
1287
1234
  sessionId,
1288
1235
  userId: userContext.userId,
1289
1236
  credentialCount: Object.keys(credentialEnvVars).length,
@@ -1291,14 +1238,14 @@ var SessionProcessManager = class {
1291
1238
  disableUserAuth
1292
1239
  });
1293
1240
  } catch (err) {
1294
- log$33.warn("用户凭证加载失败,CC 进程将使用默认环境", {
1241
+ log$34.warn("用户凭证加载失败,CC 进程将使用默认环境", {
1295
1242
  sessionId,
1296
1243
  userId: userContext.userId,
1297
1244
  error: err instanceof Error ? err.message : String(err)
1298
1245
  });
1299
1246
  }
1300
1247
  }
1301
- log$33.info("启动常驻 CC 进程", {
1248
+ log$34.info("启动常驻 CC 进程", {
1302
1249
  sessionId,
1303
1250
  claudeSessionId,
1304
1251
  isResuming,
@@ -1338,7 +1285,7 @@ var SessionProcessManager = class {
1338
1285
  if (pending) {
1339
1286
  clearTimeout(pending.timer);
1340
1287
  this.pendingControlRequests.delete(msg.response.request_id);
1341
- log$33.info("收到 control_response", {
1288
+ log$34.info("收到 control_response", {
1342
1289
  sessionId,
1343
1290
  requestId: msg.response.request_id,
1344
1291
  subtype: msg.response.subtype,
@@ -1355,7 +1302,7 @@ var SessionProcessManager = class {
1355
1302
  if (child.stderr) createInterface({ input: child.stderr }).on("line", (line) => {
1356
1303
  if (line.includes("no stdin data received")) return;
1357
1304
  if (line.toLowerCase().includes("already in use")) stderrSessionInUse = true;
1358
- log$33.warn("CC 进程 stderr", {
1305
+ log$34.warn("CC 进程 stderr", {
1359
1306
  sessionId,
1360
1307
  line: line.slice(0, 500)
1361
1308
  });
@@ -1364,7 +1311,7 @@ var SessionProcessManager = class {
1364
1311
  const exitCode = code ?? -1;
1365
1312
  const exitSignal = signal ?? "none";
1366
1313
  if (exitCode === 1 && stderrSessionInUse && !isResuming) {
1367
- log$33.info("Session ID 已被占用,准备使用 --resume 模式重试", {
1314
+ log$34.info("Session ID 已被占用,准备使用 --resume 模式重试", {
1368
1315
  sessionId,
1369
1316
  claudeSessionId
1370
1317
  });
@@ -1377,21 +1324,21 @@ var SessionProcessManager = class {
1377
1324
  });
1378
1325
  return;
1379
1326
  } catch (retryErr) {
1380
- log$33.error("Session ID 重试失败", {
1327
+ log$34.error("Session ID 重试失败", {
1381
1328
  sessionId,
1382
1329
  error: retryErr instanceof Error ? retryErr.message : String(retryErr)
1383
1330
  });
1384
1331
  }
1385
1332
  }
1386
1333
  if (exitCode === 0) {
1387
- log$33.info("CC 常驻进程正常退出", {
1334
+ log$34.info("CC 常驻进程正常退出", {
1388
1335
  sessionId,
1389
1336
  exitCode,
1390
1337
  signal: exitSignal
1391
1338
  });
1392
1339
  ccProcess.status = "stopped";
1393
1340
  } else {
1394
- log$33.error("CC 常驻进程异常退出", {
1341
+ log$34.error("CC 常驻进程异常退出", {
1395
1342
  sessionId,
1396
1343
  exitCode,
1397
1344
  signal: exitSignal
@@ -1403,7 +1350,7 @@ var SessionProcessManager = class {
1403
1350
  this.cleanup(sessionId);
1404
1351
  });
1405
1352
  child.on("error", (err) => {
1406
- log$33.error("CC 常驻进程启动失败", {
1353
+ log$34.error("CC 常驻进程启动失败", {
1407
1354
  sessionId,
1408
1355
  error: err.message
1409
1356
  });
@@ -1413,7 +1360,7 @@ var SessionProcessManager = class {
1413
1360
  this.cleanup(sessionId);
1414
1361
  });
1415
1362
  ccProcess.status = "running";
1416
- log$33.info("CC 常驻进程已启动", {
1363
+ log$34.info("CC 常驻进程已启动", {
1417
1364
  sessionId,
1418
1365
  pid: child.pid,
1419
1366
  isResuming
@@ -1441,7 +1388,7 @@ var SessionProcessManager = class {
1441
1388
  parser.on("textDelta", (text) => {
1442
1389
  if (!firstTokenLogged) {
1443
1390
  firstTokenLogged = true;
1444
- log$33.info("[perf] 首 token (text)", {
1391
+ log$34.info("[perf] 首 token (text)", {
1445
1392
  sessionId,
1446
1393
  ttftMs: Date.now() - messageSentAt
1447
1394
  });
@@ -1451,7 +1398,7 @@ var SessionProcessManager = class {
1451
1398
  parser.on("thinkingDelta", (text) => {
1452
1399
  if (!firstTokenLogged) {
1453
1400
  firstTokenLogged = true;
1454
- log$33.info("[perf] 首 token (thinking)", {
1401
+ log$34.info("[perf] 首 token (thinking)", {
1455
1402
  sessionId,
1456
1403
  ttftMs: Date.now() - messageSentAt
1457
1404
  });
@@ -1468,7 +1415,7 @@ var SessionProcessManager = class {
1468
1415
  getCallbacks()?.onToolProgress?.(toolName, elapsedSeconds);
1469
1416
  });
1470
1417
  parser.on("turnEnd", (stopReason) => {
1471
- log$33.info("[perf] turnEnd", {
1418
+ log$34.info("[perf] turnEnd", {
1472
1419
  sessionId,
1473
1420
  stopReason,
1474
1421
  sinceMessageMs: Date.now() - messageSentAt
@@ -1476,27 +1423,15 @@ var SessionProcessManager = class {
1476
1423
  getCallbacks()?.onTurnEnd?.(stopReason);
1477
1424
  });
1478
1425
  parser.on("result", (result) => {
1479
- log$33.info("[perf] result 事件", {
1426
+ log$34.info("[perf] result 事件", {
1480
1427
  sessionId,
1481
1428
  sinceMessageMs: Date.now() - messageSentAt
1482
1429
  });
1483
- this.resolveResultWaiters(sessionId);
1484
1430
  getCallbacks()?.onResult?.(result);
1485
1431
  this.resolveMessageCompletion(sessionId);
1486
1432
  });
1487
- parser.on("usage", (usage) => {
1488
- if (usage.input_tokens <= 0) return;
1489
- this.lastInputTokens.set(sessionId, usage.input_tokens);
1490
- log$33.info("[auto-compact] 更新 input_tokens", {
1491
- sessionId,
1492
- inputTokens: usage.input_tokens,
1493
- outputTokens: usage.output_tokens,
1494
- threshold: AUTO_COMPACT_THRESHOLD,
1495
- needsCompact: AUTO_COMPACT_THRESHOLD > 0 && usage.input_tokens > AUTO_COMPACT_THRESHOLD
1496
- });
1497
- });
1498
1433
  parser.on("parseError", (error, rawLine) => {
1499
- log$33.warn("流解析错误", {
1434
+ log$34.warn("流解析错误", {
1500
1435
  sessionId,
1501
1436
  error: error.message,
1502
1437
  rawLine: rawLine.slice(0, 200)
@@ -1512,7 +1447,7 @@ var SessionProcessManager = class {
1512
1447
  sendMessage(sessionId, message) {
1513
1448
  const proc = this.processes.get(sessionId);
1514
1449
  if (!proc || !proc.childProcess.stdin) {
1515
- log$33.error("无法发送消息:进程不存在或 stdin 不可用", { sessionId });
1450
+ log$34.error("无法发送消息:进程不存在或 stdin 不可用", { sessionId });
1516
1451
  return;
1517
1452
  }
1518
1453
  proc._resetPerfTimer?.();
@@ -1528,7 +1463,7 @@ var SessionProcessManager = class {
1528
1463
  uuid: userMessageId
1529
1464
  });
1530
1465
  const messageLength = typeof message === "string" ? message.length : message.length;
1531
- log$33.info("通过 stdin 发送用户消息", {
1466
+ log$34.info("通过 stdin 发送用户消息", {
1532
1467
  sessionId,
1533
1468
  messageLength,
1534
1469
  isMultimodal: Array.isArray(message),
@@ -1537,7 +1472,7 @@ var SessionProcessManager = class {
1537
1472
  });
1538
1473
  proc.childProcess.stdin.write(payload + "\n", (err) => {
1539
1474
  if (err) {
1540
- log$33.error("stdin 写入失败", {
1475
+ log$34.error("stdin 写入失败", {
1541
1476
  sessionId,
1542
1477
  error: err.message
1543
1478
  });
@@ -1567,7 +1502,7 @@ var SessionProcessManager = class {
1567
1502
  request_id: requestId,
1568
1503
  request
1569
1504
  });
1570
- log$33.info("发送控制请求", {
1505
+ log$34.info("发送控制请求", {
1571
1506
  sessionId,
1572
1507
  subtype,
1573
1508
  requestId,
@@ -1594,144 +1529,6 @@ var SessionProcessManager = class {
1594
1529
  });
1595
1530
  }
1596
1531
  /**
1597
- * 检查是否需要自动压缩,如需要则执行 /compact 命令
1598
- *
1599
- * 判断逻辑:
1600
- * 1. AUTO_COMPACT_THRESHOLD > 0(未禁用)
1601
- * 2. lastInputTokens[sessionId] > threshold
1602
- *
1603
- * 执行方式:通过 stdin 发送 "/compact" 作为用户消息,等待 CC 返回 result。
1604
- * compact 期间:
1605
- * - 通过 callbacks.onToolUseStart 通知 UI 显示"正在压缩对话历史"
1606
- * - compact 产生的文本/result 被静默回调吞掉,不泄露到用户卡片
1607
- * - compact 完成后通过 callbacks.onToolResult 通知 UI 压缩完成
1608
- */
1609
- async autoCompactIfNeeded(sessionId) {
1610
- if (AUTO_COMPACT_THRESHOLD <= 0) return;
1611
- const lastTokens = this.lastInputTokens.get(sessionId);
1612
- if (!lastTokens || lastTokens <= AUTO_COMPACT_THRESHOLD) return;
1613
- const retryAfter = this.compactRetryAfter.get(sessionId);
1614
- if (retryAfter && Date.now() < retryAfter) {
1615
- log$33.info("[auto-compact] 熔断窗口内跳过压缩", {
1616
- sessionId,
1617
- retryAfter
1618
- });
1619
- return;
1620
- }
1621
- log$33.info("[auto-compact] 触发自动压缩", {
1622
- sessionId,
1623
- lastInputTokens: lastTokens,
1624
- threshold: AUTO_COMPACT_THRESHOLD
1625
- });
1626
- const tCompact = Date.now();
1627
- const realCallbacks = this.activeCallbacks.get(sessionId);
1628
- const compactToolUseId = `compact-${Date.now()}`;
1629
- realCallbacks?.onToolUseStart?.("_AutoCompact", {
1630
- inputTokens: lastTokens,
1631
- threshold: AUTO_COMPACT_THRESHOLD
1632
- });
1633
- this.activeCallbacks.set(sessionId, {
1634
- onTextDelta: () => {},
1635
- onThinkingDelta: () => {},
1636
- onToolUseStart: () => {},
1637
- onToolResult: () => {},
1638
- onToolProgress: () => {},
1639
- onTurnEnd: () => {},
1640
- onResult: () => {},
1641
- onError: (err) => {
1642
- log$33.warn("[auto-compact] compact 期间收到错误", {
1643
- sessionId,
1644
- error: err.message
1645
- });
1646
- }
1647
- });
1648
- try {
1649
- await this.sendCompactCommand(sessionId);
1650
- this.lastInputTokens.delete(sessionId);
1651
- this.compactRetryAfter.delete(sessionId);
1652
- log$33.info("[auto-compact] 压缩完成", {
1653
- sessionId,
1654
- durationMs: Date.now() - tCompact
1655
- });
1656
- } catch (err) {
1657
- log$33.warn("[auto-compact] 压缩失败,继续发送原始消息", {
1658
- sessionId,
1659
- error: err instanceof Error ? err.message : String(err),
1660
- durationMs: Date.now() - tCompact
1661
- });
1662
- this.compactRetryAfter.set(sessionId, Date.now() + 1800 * 1e3);
1663
- } finally {
1664
- if (realCallbacks) this.activeCallbacks.set(sessionId, realCallbacks);
1665
- realCallbacks?.onToolResult?.(compactToolUseId);
1666
- }
1667
- }
1668
- /**
1669
- * 向 CC 进程发送 /compact 命令并等待完成
1670
- *
1671
- * /compact 是 CC 的内置 slash command,通过 stdin 作为用户消息发送。
1672
- * CC 会压缩对话历史并返回 result 事件表示完成。
1673
- *
1674
- * 超时 120 秒(compact 可能需要处理大量历史)。
1675
- */
1676
- sendCompactCommand(sessionId) {
1677
- const proc = this.processes.get(sessionId);
1678
- if (!proc || proc.status !== "running") return Promise.reject(/* @__PURE__ */ new Error(`会话 ${sessionId} 无运行中的进程,无法执行 compact`));
1679
- if (!proc.childProcess.stdin || proc.childProcess.stdin.destroyed) return Promise.reject(/* @__PURE__ */ new Error(`会话 ${sessionId} 的 stdin 不可用,无法执行 compact`));
1680
- const COMPACT_TIMEOUT_MS = 12e4;
1681
- return new Promise((resolve, reject) => {
1682
- const onResult = () => {
1683
- clearTimeout(timer);
1684
- resolve();
1685
- };
1686
- const timer = setTimeout(() => {
1687
- this.removeResultWaiter(sessionId, onResult);
1688
- this.stopProcess(sessionId);
1689
- reject(/* @__PURE__ */ new Error(`/compact 超时 (${COMPACT_TIMEOUT_MS}ms)`));
1690
- }, COMPACT_TIMEOUT_MS);
1691
- this.addResultWaiter(sessionId, onResult);
1692
- const compactPayload = JSON.stringify({
1693
- type: "user",
1694
- message: {
1695
- role: "user",
1696
- content: "/compact"
1697
- },
1698
- parent_tool_use_id: null,
1699
- uuid: v4()
1700
- });
1701
- log$33.info("[auto-compact] 发送 /compact 命令", {
1702
- sessionId,
1703
- pid: proc.childProcess.pid
1704
- });
1705
- proc.childProcess.stdin.write(compactPayload + "\n", (err) => {
1706
- if (err) {
1707
- clearTimeout(timer);
1708
- this.removeResultWaiter(sessionId, onResult);
1709
- reject(/* @__PURE__ */ new Error(`/compact stdin 写入失败: ${err.message}`));
1710
- }
1711
- });
1712
- });
1713
- }
1714
- addResultWaiter(sessionId, waiter) {
1715
- let waiters = this.resultWaiters.get(sessionId);
1716
- if (!waiters) {
1717
- waiters = /* @__PURE__ */ new Set();
1718
- this.resultWaiters.set(sessionId, waiters);
1719
- }
1720
- waiters.add(waiter);
1721
- }
1722
- removeResultWaiter(sessionId, waiter) {
1723
- const waiters = this.resultWaiters.get(sessionId);
1724
- if (!waiters) return;
1725
- waiters.delete(waiter);
1726
- if (waiters.size === 0) this.resultWaiters.delete(sessionId);
1727
- }
1728
- resolveResultWaiters(sessionId) {
1729
- const waiters = this.resultWaiters.get(sessionId);
1730
- if (!waiters) return;
1731
- this.resultWaiters.delete(sessionId);
1732
- for (const waiter of waiters) waiter();
1733
- }
1734
- /**
1735
1532
  * 获取指定会话的最近一条用户消息 ID
1736
1533
  *
1737
1534
  * 用于构建回退按钮的 action value。
@@ -1761,16 +1558,16 @@ var SessionProcessManager = class {
1761
1558
  async ensureProcessForRewind(sessionId) {
1762
1559
  const existing = this.processes.get(sessionId);
1763
1560
  if (existing && existing.status === "running") {
1764
- log$33.info("ensureProcessForRewind: 进程已在运行中,直接使用", { sessionId });
1561
+ log$34.info("ensureProcessForRewind: 进程已在运行中,直接使用", { sessionId });
1765
1562
  return existing;
1766
1563
  }
1767
1564
  const cwd = this.sessionCwdCache.get(sessionId);
1768
1565
  if (!cwd) {
1769
- log$33.warn("ensureProcessForRewind: 无法获取会话 cwd", { sessionId });
1566
+ log$34.warn("ensureProcessForRewind: 无法获取会话 cwd", { sessionId });
1770
1567
  return;
1771
1568
  }
1772
1569
  if (existing) this.cleanup(sessionId);
1773
- log$33.info("ensureProcessForRewind: 启动临时进程用于 rewind", {
1570
+ log$34.info("ensureProcessForRewind: 启动临时进程用于 rewind", {
1774
1571
  sessionId,
1775
1572
  cwd
1776
1573
  });
@@ -1781,7 +1578,7 @@ var SessionProcessManager = class {
1781
1578
  }, { skipInitialMessage: true });
1782
1579
  const proc = this.processes.get(sessionId);
1783
1580
  if (!proc || proc.status !== "running") {
1784
- log$33.warn("ensureProcessForRewind: 进程启动失败", {
1581
+ log$34.warn("ensureProcessForRewind: 进程启动失败", {
1785
1582
  sessionId,
1786
1583
  status: proc?.status
1787
1584
  });
@@ -1803,11 +1600,11 @@ var SessionProcessManager = class {
1803
1600
  async stopProcess(sessionId) {
1804
1601
  const proc = this.processes.get(sessionId);
1805
1602
  if (!proc) {
1806
- log$33.warn("尝试停止不存在的进程", { sessionId });
1603
+ log$34.warn("尝试停止不存在的进程", { sessionId });
1807
1604
  return;
1808
1605
  }
1809
1606
  if (proc.status === "stopped" || proc.status === "crashed") {
1810
- log$33.info("进程已经停止,直接清理", {
1607
+ log$34.info("进程已经停止,直接清理", {
1811
1608
  sessionId,
1812
1609
  status: proc.status
1813
1610
  });
@@ -1815,7 +1612,7 @@ var SessionProcessManager = class {
1815
1612
  return;
1816
1613
  }
1817
1614
  proc.status = "stopping";
1818
- log$33.info("正在停止 CC 常驻进程", {
1615
+ log$34.info("正在停止 CC 常驻进程", {
1819
1616
  sessionId,
1820
1617
  pid: proc.childProcess.pid
1821
1618
  });
@@ -1831,7 +1628,7 @@ var SessionProcessManager = class {
1831
1628
  const child = proc.childProcess;
1832
1629
  const forceKillTimer = setTimeout(() => {
1833
1630
  if (!child.killed) {
1834
- log$33.warn("CC 常驻进程未在规定时间内退出,强制终止", { sessionId });
1631
+ log$34.warn("CC 常驻进程未在规定时间内退出,强制终止", { sessionId });
1835
1632
  child.kill("SIGKILL");
1836
1633
  }
1837
1634
  }, GRACEFUL_SHUTDOWN_MS);
@@ -1851,9 +1648,9 @@ var SessionProcessManager = class {
1851
1648
  async stopAll() {
1852
1649
  const sessionIds = Array.from(this.processes.keys());
1853
1650
  if (sessionIds.length === 0) return;
1854
- log$33.info("正在停止所有 CC 常驻进程", { count: sessionIds.length });
1651
+ log$34.info("正在停止所有 CC 常驻进程", { count: sessionIds.length });
1855
1652
  await Promise.all(sessionIds.map((id) => this.stopProcess(id)));
1856
- log$33.info("所有 CC 常驻进程已停止");
1653
+ log$34.info("所有 CC 常驻进程已停止");
1857
1654
  }
1858
1655
  /**
1859
1656
  * 重置空闲计时器
@@ -1863,7 +1660,7 @@ var SessionProcessManager = class {
1863
1660
  if (existing) clearTimeout(existing);
1864
1661
  if (IDLE_TIMEOUT_MS <= 0) return;
1865
1662
  const timer = setTimeout(() => {
1866
- log$33.info("CC 常驻进程空闲超时,自动停止", {
1663
+ log$34.info("CC 常驻进程空闲超时,自动停止", {
1867
1664
  sessionId,
1868
1665
  timeoutMs: IDLE_TIMEOUT_MS
1869
1666
  });
@@ -1880,9 +1677,6 @@ var SessionProcessManager = class {
1880
1677
  this.processes.delete(sessionId);
1881
1678
  this.activeCallbacks.delete(sessionId);
1882
1679
  this.lastUserMessageIds.delete(sessionId);
1883
- this.lastInputTokens.delete(sessionId);
1884
- this.resultWaiters.delete(sessionId);
1885
- this.compactRetryAfter.delete(sessionId);
1886
1680
  for (const [reqId, pending] of this.pendingControlRequests) if (pending.sessionId === sessionId) {
1887
1681
  clearTimeout(pending.timer);
1888
1682
  this.pendingControlRequests.delete(reqId);
@@ -1894,7 +1688,7 @@ var SessionProcessManager = class {
1894
1688
  this.idleTimers.delete(sessionId);
1895
1689
  }
1896
1690
  cleanupMcpConfig(sessionId);
1897
- log$33.debug("已清理常驻进程状态", { sessionId });
1691
+ log$34.debug("已清理常驻进程状态", { sessionId });
1898
1692
  }
1899
1693
  /**
1900
1694
  * 将 cwd 编码为 CC 的 projects 子目录名
@@ -1923,7 +1717,7 @@ var SessionProcessManager = class {
1923
1717
  const cwdEncoded = this.encodeCwdForCC(cwd);
1924
1718
  const sessionFile = join(homedir(), ".claude", "projects", cwdEncoded, `${claudeSessionId}.jsonl`);
1925
1719
  const exists = existsSync(sessionFile);
1926
- log$33.debug("检查 CC session 文件", {
1720
+ log$34.debug("检查 CC session 文件", {
1927
1721
  cwd,
1928
1722
  cwdEncoded,
1929
1723
  claudeSessionId,
@@ -1932,7 +1726,7 @@ var SessionProcessManager = class {
1932
1726
  });
1933
1727
  return exists;
1934
1728
  } catch (err) {
1935
- log$33.warn("检查 CC session 文件失败,默认为首次创建", { error: err instanceof Error ? err.message : String(err) });
1729
+ log$34.warn("检查 CC session 文件失败,默认为首次创建", { error: err instanceof Error ? err.message : String(err) });
1936
1730
  return false;
1937
1731
  }
1938
1732
  }
@@ -1947,7 +1741,7 @@ var SessionProcessManager = class {
1947
1741
  *
1948
1742
  * 这是 LarkPal 的默认 AI 引擎适配器。
1949
1743
  */
1950
- const log$32 = larkLogger("runtime/claude-code-adapter");
1744
+ const log$33 = larkLogger("runtime/claude-code-adapter");
1951
1745
  var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1952
1746
  name = "claude-code";
1953
1747
  processManager;
@@ -1963,7 +1757,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1963
1757
  constructor() {
1964
1758
  this.processManager = new SessionProcessManager();
1965
1759
  ClaudeCodeAdapter._instance = this;
1966
- log$32.info("ClaudeCodeAdapter 初始化完成");
1760
+ log$33.info("ClaudeCodeAdapter 初始化完成");
1967
1761
  }
1968
1762
  /**
1969
1763
  * 获取底层 SessionProcessManager 实例
@@ -1976,7 +1770,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1976
1770
  }
1977
1771
  async executePrompt(config, callbacks) {
1978
1772
  this.lastRuntimeConfigs.set(config.sessionId, config);
1979
- log$32.info("executePrompt via ClaudeCodeAdapter", {
1773
+ log$33.info("executePrompt via ClaudeCodeAdapter", {
1980
1774
  sessionId: config.sessionId,
1981
1775
  cwd: config.cwd,
1982
1776
  promptLength: config.prompt.length,
@@ -1985,11 +1779,11 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
1985
1779
  await this.processManager.executePrompt(config, callbacks);
1986
1780
  }
1987
1781
  async stopProcess(sessionId) {
1988
- log$32.info("stopProcess via ClaudeCodeAdapter", { sessionId });
1782
+ log$33.info("stopProcess via ClaudeCodeAdapter", { sessionId });
1989
1783
  await this.processManager.stopProcess(sessionId);
1990
1784
  }
1991
1785
  async stopAll() {
1992
- log$32.info("stopAll via ClaudeCodeAdapter");
1786
+ log$33.info("stopAll via ClaudeCodeAdapter");
1993
1787
  await this.processManager.stopAll();
1994
1788
  }
1995
1789
  getProcessInfo(sessionId) {
@@ -2025,7 +1819,7 @@ var ClaudeCodeAdapter = class ClaudeCodeAdapter {
2025
1819
  };
2026
1820
  //#endregion
2027
1821
  //#region src/codex-runtime/app-server-client.ts
2028
- const log$31 = larkLogger("codex-runtime/app-server-client");
1822
+ const log$32 = larkLogger("codex-runtime/app-server-client");
2029
1823
  const DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
2030
1824
  var CodexAppServerClient = class extends EventEmitter {
2031
1825
  sessionId;
@@ -2056,7 +1850,7 @@ var CodexAppServerClient = class extends EventEmitter {
2056
1850
  "pipe"
2057
1851
  ]
2058
1852
  });
2059
- log$31.info("Codex app-server 进程已启动", {
1853
+ log$32.info("Codex app-server 进程已启动", {
2060
1854
  sessionId: this.sessionId,
2061
1855
  cwd: this.cwd,
2062
1856
  pid: this.childProcess.pid,
@@ -2135,14 +1929,14 @@ var CodexAppServerClient = class extends EventEmitter {
2135
1929
  bindStreams() {
2136
1930
  if (this.childProcess.stdout) createInterface({ input: this.childProcess.stdout }).on("line", (line) => this.handleLine(line));
2137
1931
  if (this.childProcess.stderr) createInterface({ input: this.childProcess.stderr }).on("line", (line) => {
2138
- log$31.warn("Codex app-server stderr", {
1932
+ log$32.warn("Codex app-server stderr", {
2139
1933
  sessionId: this.sessionId,
2140
1934
  line: line.slice(0, 500)
2141
1935
  });
2142
1936
  this.emit("stderr", line);
2143
1937
  });
2144
1938
  this.childProcess.on("error", (error) => {
2145
- log$31.error("Codex app-server 进程错误", {
1939
+ log$32.error("Codex app-server 进程错误", {
2146
1940
  sessionId: this.sessionId,
2147
1941
  error: error.message
2148
1942
  });
@@ -2151,7 +1945,7 @@ var CodexAppServerClient = class extends EventEmitter {
2151
1945
  });
2152
1946
  this.childProcess.on("close", (code, signal) => {
2153
1947
  this.closed = true;
2154
- log$31.info("Codex app-server 进程退出", {
1948
+ log$32.info("Codex app-server 进程退出", {
2155
1949
  sessionId: this.sessionId,
2156
1950
  code,
2157
1951
  signal
@@ -2168,7 +1962,7 @@ var CodexAppServerClient = class extends EventEmitter {
2168
1962
  message = JSON.parse(trimmed);
2169
1963
  } catch (err) {
2170
1964
  const error = err instanceof Error ? err : new Error(String(err));
2171
- log$31.warn("Codex app-server JSONL 解析失败", {
1965
+ log$32.warn("Codex app-server JSONL 解析失败", {
2172
1966
  sessionId: this.sessionId,
2173
1967
  error: error.message,
2174
1968
  line: trimmed.slice(0, 200)
@@ -2188,7 +1982,7 @@ var CodexAppServerClient = class extends EventEmitter {
2188
1982
  }
2189
1983
  handleResponse(message) {
2190
1984
  if (message.id == null) {
2191
- log$31.warn("Codex app-server 返回无 id 错误响应", {
1985
+ log$32.warn("Codex app-server 返回无 id 错误响应", {
2192
1986
  sessionId: this.sessionId,
2193
1987
  message
2194
1988
  });
@@ -2197,7 +1991,7 @@ var CodexAppServerClient = class extends EventEmitter {
2197
1991
  const idKey = String(message.id);
2198
1992
  const pending = this.pending.get(idKey);
2199
1993
  if (!pending) {
2200
- log$31.warn("Codex app-server 返回未知请求响应", {
1994
+ log$32.warn("Codex app-server 返回未知请求响应", {
2201
1995
  sessionId: this.sessionId,
2202
1996
  id: message.id
2203
1997
  });
@@ -2243,7 +2037,7 @@ function isNotification(message) {
2243
2037
  }
2244
2038
  //#endregion
2245
2039
  //#region src/runtime/codex-app-server-adapter.ts
2246
- const log$30 = larkLogger("runtime/codex-app-server-adapter");
2040
+ const log$31 = larkLogger("runtime/codex-app-server-adapter");
2247
2041
  const STATE_PATH = join(homedir(), ".larkpal", "runtime", "codex-app-server-threads.json");
2248
2042
  const REQUEST_TIMEOUT_MS = 12e4;
2249
2043
  var CodexAppServerAdapter = class {
@@ -2269,14 +2063,14 @@ var CodexAppServerAdapter = class {
2269
2063
  extraArgs: params.extraArgs,
2270
2064
  requestTimeoutMs: REQUEST_TIMEOUT_MS
2271
2065
  }));
2272
- log$30.info("CodexAppServerAdapter 初始化完成", { statePath: this.statePath });
2066
+ log$31.info("CodexAppServerAdapter 初始化完成", { statePath: this.statePath });
2273
2067
  }
2274
2068
  async executePrompt(config, callbacks) {
2275
2069
  await this.loadState();
2276
2070
  const { sessionId } = config;
2277
2071
  const prevLock = this.sessionLocks.get(sessionId);
2278
2072
  if (prevLock) {
2279
- log$30.info("等待上一条 Codex 消息处理完成", { sessionId });
2073
+ log$31.info("等待上一条 Codex 消息处理完成", { sessionId });
2280
2074
  await prevLock;
2281
2075
  }
2282
2076
  const completion = this.createCompletionPromise(sessionId);
@@ -2307,7 +2101,7 @@ var CodexAppServerAdapter = class {
2307
2101
  if (config.transcriptMode === "ephemeral") {
2308
2102
  const record = this.processes.get(sessionId);
2309
2103
  if (record) await this.closeRecord(sessionId, record).catch((err) => {
2310
- log$30.warn("ephemeral Codex app-server session 清理失败", {
2104
+ log$31.warn("ephemeral Codex app-server session 清理失败", {
2311
2105
  sessionId,
2312
2106
  error: err instanceof Error ? err.message : String(err)
2313
2107
  });
@@ -2322,12 +2116,12 @@ var CodexAppServerAdapter = class {
2322
2116
  async stopProcess(sessionId) {
2323
2117
  const record = this.processes.get(sessionId);
2324
2118
  if (!record) {
2325
- log$30.warn("尝试停止不存在的 Codex app-server session", { sessionId });
2119
+ log$31.warn("尝试停止不存在的 Codex app-server session", { sessionId });
2326
2120
  return;
2327
2121
  }
2328
2122
  if (this.busySessions.has(sessionId) && record.process.currentTurnId) {
2329
2123
  this.abortedSessions.add(sessionId);
2330
- log$30.info("发送 Codex turn/interrupt", {
2124
+ log$31.info("发送 Codex turn/interrupt", {
2331
2125
  sessionId,
2332
2126
  threadId: record.process.threadId,
2333
2127
  turnId: record.process.currentTurnId
@@ -2414,7 +2208,7 @@ var CodexAppServerAdapter = class {
2414
2208
  if (!isEphemeral) await this.setThreadId(config.sessionId, resumed.thread.id, config.cwd);
2415
2209
  return;
2416
2210
  } catch (err) {
2417
- log$30.warn("Codex thread/resume 失败,将新建 thread", {
2211
+ log$31.warn("Codex thread/resume 失败,将新建 thread", {
2418
2212
  sessionId: config.sessionId,
2419
2213
  threadId: storedThreadId,
2420
2214
  error: err instanceof Error ? err.message : String(err)
@@ -2480,7 +2274,7 @@ var CodexAppServerAdapter = class {
2480
2274
  this.handleErrorNotification(sessionId, message.params);
2481
2275
  break;
2482
2276
  default:
2483
- log$30.debug("忽略 Codex app-server notification", {
2277
+ log$31.debug("忽略 Codex app-server notification", {
2484
2278
  sessionId,
2485
2279
  method: message.method
2486
2280
  });
@@ -2508,7 +2302,7 @@ var CodexAppServerAdapter = class {
2508
2302
  handleMcpProgress(sessionId, params) {
2509
2303
  const toolName = this.lookupToolName(sessionId, params.itemId) ?? "mcp";
2510
2304
  this.activeCallbacks.get(sessionId)?.onToolProgress?.(toolName, 0);
2511
- log$30.debug("Codex MCP tool progress", {
2305
+ log$31.debug("Codex MCP tool progress", {
2512
2306
  sessionId,
2513
2307
  itemId: params.itemId,
2514
2308
  message: params.message
@@ -2538,7 +2332,7 @@ var CodexAppServerAdapter = class {
2538
2332
  this.resolveCompletion(sessionId);
2539
2333
  }
2540
2334
  async handleServerRequest(client, request) {
2541
- log$30.warn("Codex app-server server request 默认拒绝", {
2335
+ log$31.warn("Codex app-server server request 默认拒绝", {
2542
2336
  method: request.method,
2543
2337
  id: request.id
2544
2338
  });
@@ -2704,10 +2498,10 @@ var CodexAppServerAdapter = class {
2704
2498
  ];
2705
2499
  }
2706
2500
  logUnsupportedConfig(config) {
2707
- if (config.llmConfig) log$30.warn("Codex app-server v1 暂不支持 per-request llmConfig,已忽略", { sessionId: config.sessionId });
2708
- if (config.maxBudgetUsd != null) log$30.warn("Codex app-server v1 暂不支持 maxBudgetUsd,已忽略", { sessionId: config.sessionId });
2709
- if (config.maxTurns != null) log$30.warn("Codex app-server v1 暂不支持 maxTurns,已忽略", { sessionId: config.sessionId });
2710
- if (config.policy?.allowedTools || config.policy?.disallowedTools) log$30.warn("Codex app-server v1 暂不强制执行 tool allow/deny policy,已忽略", { sessionId: config.sessionId });
2501
+ if (config.llmConfig) log$31.warn("Codex app-server v1 暂不支持 per-request llmConfig,已忽略", { sessionId: config.sessionId });
2502
+ if (config.maxBudgetUsd != null) log$31.warn("Codex app-server v1 暂不支持 maxBudgetUsd,已忽略", { sessionId: config.sessionId });
2503
+ if (config.maxTurns != null) log$31.warn("Codex app-server v1 暂不支持 maxTurns,已忽略", { sessionId: config.sessionId });
2504
+ if (config.policy?.allowedTools || config.policy?.disallowedTools) log$31.warn("Codex app-server v1 暂不强制执行 tool allow/deny policy,已忽略", { sessionId: config.sessionId });
2711
2505
  }
2712
2506
  createCompletionPromise(sessionId) {
2713
2507
  return new Promise((resolve) => {
@@ -2723,7 +2517,7 @@ var CodexAppServerAdapter = class {
2723
2517
  async closeRecord(sessionId, record) {
2724
2518
  record.process.status = "stopping";
2725
2519
  if (record.process.threadId) await record.client.request("thread/unsubscribe", { threadId: record.process.threadId }).catch((err) => {
2726
- log$30.warn("Codex thread/unsubscribe 失败", {
2520
+ log$31.warn("Codex thread/unsubscribe 失败", {
2727
2521
  sessionId,
2728
2522
  threadId: record.process.threadId,
2729
2523
  error: err instanceof Error ? err.message : String(err)
@@ -2756,7 +2550,7 @@ var CodexAppServerAdapter = class {
2756
2550
  const parsed = JSON.parse(raw);
2757
2551
  for (const [sessionId, entry] of Object.entries(parsed.sessions ?? {})) if (entry.threadId) this.threadIds.set(sessionId, entry.threadId);
2758
2552
  } catch (err) {
2759
- log$30.warn("Codex thread state 加载失败,将从空状态继续", {
2553
+ log$31.warn("Codex thread state 加载失败,将从空状态继续", {
2760
2554
  path: this.statePath,
2761
2555
  error: err instanceof Error ? err.message : String(err)
2762
2556
  });
@@ -2824,18 +2618,18 @@ function getLarkpalMcpServerCommand() {
2824
2618
  *
2825
2619
  * 仅当 LARKPAL_RUNTIME=larkpal-agent 时使用。
2826
2620
  */
2827
- const log$29 = larkLogger("runtime/larkpal-agent-factory");
2621
+ const log$30 = larkLogger("runtime/larkpal-agent-factory");
2828
2622
  const AGENT_MD_PATH = join(homedir(), ".claude", "CLAUDE.md");
2829
2623
  async function loadSystemPrompt() {
2830
2624
  try {
2831
2625
  const content = await readFile(AGENT_MD_PATH, "utf-8");
2832
- log$29.info("systemPrompt 加载成功", {
2626
+ log$30.info("systemPrompt 加载成功", {
2833
2627
  path: AGENT_MD_PATH,
2834
2628
  length: content.length
2835
2629
  });
2836
2630
  return content;
2837
2631
  } catch {
2838
- log$29.warn("systemPrompt 文件不存在,使用默认", { path: AGENT_MD_PATH });
2632
+ log$30.warn("systemPrompt 文件不存在,使用默认", { path: AGENT_MD_PATH });
2839
2633
  return "你是一个 AI 助手,帮助用户完成飞书知识管理任务。";
2840
2634
  }
2841
2635
  }
@@ -2951,7 +2745,7 @@ async function createLarkpalAgentAdapter() {
2951
2745
  const scenarioManifestDirs = loadScenarioManifestDirs();
2952
2746
  const scenarioProviderConfig = loadScenarioProviderConfig(scenarioManifestDirs);
2953
2747
  const mcpTimeoutMs = loadAgentMcpTimeoutMs();
2954
- log$29.info("创建 LarkpalAgentAdapter", {
2748
+ log$30.info("创建 LarkpalAgentAdapter", {
2955
2749
  baseURL: llm.baseURL,
2956
2750
  model: llm.defaultModel,
2957
2751
  maxTurns: defaultMaxTurns,
@@ -2967,26 +2761,30 @@ async function createLarkpalAgentAdapter() {
2967
2761
  defaultMaxTurns,
2968
2762
  registry,
2969
2763
  scenarioManifestDirs,
2970
- scenarioProviderConfig
2764
+ scenarioProviderConfig,
2765
+ visualRuntime: {
2766
+ maxBatchSize: parseInt(process.env.LARKPAL_AGENT_VISUAL_MAX_BATCH_SIZE || "3", 10),
2767
+ allowedDirs: [process.env.LARK_CLI_WORKDIR || "/tmp/ai-knowledge-lark-cli", "/tmp"].filter(Boolean)
2768
+ }
2971
2769
  });
2972
2770
  const mcpServerUrl = process.env.LARKPAL_AGENT_MCP_SERVER_URL;
2973
2771
  if (mcpServerUrl) try {
2974
2772
  const toolCount = await registry.connectMcpServer(mcpServerUrl, { timeoutMs: mcpTimeoutMs });
2975
2773
  const tools = registry.getAll();
2976
2774
  adapter.registerTools(tools);
2977
- log$29.info("MCP 工具注册完成", {
2775
+ log$30.info("MCP 工具注册完成", {
2978
2776
  serverUrl: mcpServerUrl,
2979
2777
  timeoutMs: mcpTimeoutMs,
2980
2778
  toolCount,
2981
2779
  toolNames: tools.map((t) => t.name)
2982
2780
  });
2983
2781
  } catch (err) {
2984
- log$29.error("MCP Server 连接失败,Agent 将无法使用远程工具", {
2782
+ log$30.error("MCP Server 连接失败,Agent 将无法使用远程工具", {
2985
2783
  serverUrl: mcpServerUrl,
2986
2784
  error: err?.message
2987
2785
  });
2988
2786
  }
2989
- else log$29.warn("未配置 LARKPAL_AGENT_MCP_SERVER_URL,Agent 无远程工具可用");
2787
+ else log$30.warn("未配置 LARKPAL_AGENT_MCP_SERVER_URL,Agent 无远程工具可用");
2990
2788
  return adapter;
2991
2789
  }
2992
2790
  //#endregion
@@ -3323,6 +3121,7 @@ function createChatAuthRouter() {
3323
3121
  //#endregion
3324
3122
  //#region src/gateway/agent-context.ts
3325
3123
  const SENSITIVE_KEY_PATTERN = /(authorization|cookie|token|api[-_]?key|secret|password|credential)/i;
3124
+ const SAFE_KEY_PATTERN = /^(file[-_]?token|drive[-_]?file[-_]?token|doc[-_]?token|sheet[-_]?token|wiki[-_]?token|node[-_]?token)$/i;
3326
3125
  const MAX_SUMMARY_STRING_LENGTH = 200;
3327
3126
  const PROTECTED_CONTEXT_HEADERS = new Set([
3328
3127
  "x-tenant-key",
@@ -3445,7 +3244,7 @@ function summarizeForAudit(value) {
3445
3244
  };
3446
3245
  if (typeof value === "object") {
3447
3246
  const result = {};
3448
- for (const [key, entry] of Object.entries(value)) if (SENSITIVE_KEY_PATTERN.test(key)) result[key] = "[redacted]";
3247
+ for (const [key, entry] of Object.entries(value)) if (SENSITIVE_KEY_PATTERN.test(key) && !SAFE_KEY_PATTERN.test(key)) result[key] = "[redacted]";
3449
3248
  else result[key] = summarizeForAudit(entry);
3450
3249
  return result;
3451
3250
  }
@@ -3551,7 +3350,8 @@ function buildExecutionPolicy(policy, limits) {
3551
3350
  maxTurns: limits.maxTurns ?? policy?.maxTurns,
3552
3351
  maxToolCalls: policy?.maxToolCalls,
3553
3352
  maxRuntimeMs: policy?.maxRuntimeMs,
3554
- maxBudgetUsd: limits.maxBudgetUsd ?? policy?.maxBudgetUsd
3353
+ maxBudgetUsd: limits.maxBudgetUsd ?? policy?.maxBudgetUsd,
3354
+ turnPolicy: policy?.turnPolicy
3555
3355
  };
3556
3356
  }
3557
3357
  function buildUserContext$1(identity) {
@@ -3619,7 +3419,7 @@ function summarizeString(value) {
3619
3419
  * POST /api/chat/sessions — 创建新会话
3620
3420
  * DELETE /api/chat/sessions/:id — 删除会话
3621
3421
  */
3622
- const log$28 = larkLogger("chat/stream");
3422
+ const log$29 = larkLogger("chat/stream");
3623
3423
  const chatRunsBySession = /* @__PURE__ */ new Map();
3624
3424
  /** 统一写 SSE 格式数据到响应流 */
3625
3425
  function sendSSE(res, event, data) {
@@ -3630,7 +3430,7 @@ function isChatSessionRunning(sessionId, processManager) {
3630
3430
  return getActiveChatRun(sessionId) != null || (processManager.isSessionBusy?.(sessionId) ?? false);
3631
3431
  }
3632
3432
  function logChatAudit(event) {
3633
- log$28.info("[stream] Agent audit event", { ...event });
3433
+ log$29.info("[stream] Agent audit event", { ...event });
3634
3434
  }
3635
3435
  function prepareSSE(res) {
3636
3436
  res.setHeader("Content-Type", "text/event-stream");
@@ -3655,7 +3455,8 @@ function serializeChatRun(run) {
3655
3455
  completedAt: run.completedAt,
3656
3456
  finalStatus: run.finalStatus,
3657
3457
  error: run.error,
3658
- textLength: run.text.length
3458
+ textLength: run.text.length,
3459
+ finalText: run.status === "running" ? void 0 : run.text || void 0
3659
3460
  };
3660
3461
  }
3661
3462
  function isRunVisibleToUser(run, params) {
@@ -3665,7 +3466,7 @@ function addRunClient(req, res, run) {
3665
3466
  run.clients.add(res);
3666
3467
  req.on("close", () => {
3667
3468
  run.clients.delete(res);
3668
- log$28.info("[stream] 客户端断开连接", {
3469
+ log$29.info("[stream] 客户端断开连接", {
3669
3470
  sessionId: run.sessionId,
3670
3471
  runId: run.runId
3671
3472
  });
@@ -3674,12 +3475,26 @@ function addRunClient(req, res, run) {
3674
3475
  function sendRunStatus(res, run) {
3675
3476
  sendSSE(res, "run-status", serializeChatRun(run));
3676
3477
  }
3478
+ function extractRuntimeEventFinalText(event) {
3479
+ const direct = event.finalText;
3480
+ if (typeof direct === "string" && direct.trim()) return direct;
3481
+ const terminal = event.terminal;
3482
+ if (terminal && typeof terminal === "object" && !Array.isArray(terminal)) {
3483
+ const text = terminal.finalText;
3484
+ if (typeof text === "string" && text.trim()) return text;
3485
+ }
3486
+ const data = event.data;
3487
+ if (data && typeof data === "object" && !Array.isArray(data)) {
3488
+ const text = data.finalText;
3489
+ if (typeof text === "string" && text.trim()) return text;
3490
+ }
3491
+ }
3677
3492
  function broadcastRunEvent(run, event, data) {
3678
3493
  for (const client of Array.from(run.clients)) try {
3679
3494
  sendSSE(client, event, data);
3680
3495
  } catch (err) {
3681
3496
  run.clients.delete(client);
3682
- log$28.warn("[stream] SSE 写入失败,移除客户端", {
3497
+ log$29.warn("[stream] SSE 写入失败,移除客户端", {
3683
3498
  sessionId: run.sessionId,
3684
3499
  runId: run.runId,
3685
3500
  error: err instanceof Error ? err.message : String(err)
@@ -3699,7 +3514,7 @@ function queueRunMessage(run, messageStore, message, label) {
3699
3514
  run.persistQueue = run.persistQueue.catch(() => void 0).then(async () => {
3700
3515
  await messageStore.appendMessage(message);
3701
3516
  }).catch((err) => {
3702
- log$28.error(label, {
3517
+ log$29.error(label, {
3703
3518
  sessionId: run.sessionId,
3704
3519
  runId: run.runId,
3705
3520
  error: err instanceof Error ? err.message : String(err)
@@ -3764,7 +3579,7 @@ function createChatRouter(config) {
3764
3579
  addRunClient(req, res, activeRun);
3765
3580
  sendSSE(res, "session", { sessionId });
3766
3581
  sendRunStatus(res, activeRun);
3767
- log$28.info("[stream] 已重新连接到运行中的 run", {
3582
+ log$29.info("[stream] 已重新连接到运行中的 run", {
3768
3583
  sessionId,
3769
3584
  runId: activeRun.runId
3770
3585
  });
@@ -3777,7 +3592,7 @@ function createChatRouter(config) {
3777
3592
  const scenarioId = getScenarioId(body.options?.scenarioId, body.options?.metadata);
3778
3593
  const attachments = getRunInputValue(body.attachments, body.options?.attachments, body.options?.metadata?.attachments);
3779
3594
  const sourceArtifacts = getRunInputValue(body.sourceArtifacts, body.source_artifacts, body.options?.sourceArtifacts, body.options?.source_artifacts, body.options?.metadata?.sourceArtifacts, body.options?.metadata?.source_artifacts);
3780
- log$28.info("[stream] 收到流式对话请求", {
3595
+ log$29.info("[stream] 收到流式对话请求", {
3781
3596
  userId,
3782
3597
  sessionId,
3783
3598
  requestId,
@@ -3794,13 +3609,13 @@ function createChatRouter(config) {
3794
3609
  tenantKey,
3795
3610
  channel: "web"
3796
3611
  })).id;
3797
- log$28.info("[stream] 自动创建新会话", {
3612
+ log$29.info("[stream] 自动创建新会话", {
3798
3613
  userId: openId,
3799
3614
  sessionId
3800
3615
  });
3801
3616
  }
3802
3617
  if (isChatSessionRunning(sessionId, processManager)) {
3803
- log$28.warn("[stream] session 正在被非 Web Chat run 占用", {
3618
+ log$29.warn("[stream] session 正在被非 Web Chat run 占用", {
3804
3619
  tenantKey,
3805
3620
  userId,
3806
3621
  sessionId
@@ -3811,6 +3626,7 @@ function createChatRouter(config) {
3811
3626
  res.end();
3812
3627
  return;
3813
3628
  }
3629
+ const runtimePrompt = enrichPromptWithSourceArtifacts(body.prompt, sourceArtifacts);
3814
3630
  const runtimeConfig = buildAgentRuntimeConfig({
3815
3631
  requestId,
3816
3632
  traceId,
@@ -3818,7 +3634,7 @@ function createChatRouter(config) {
3818
3634
  conversationId: sessionId,
3819
3635
  entrypoint: "chat",
3820
3636
  scenarioId,
3821
- prompt: body.prompt,
3637
+ prompt: runtimePrompt,
3822
3638
  maxTurns: body.options?.maxTurns,
3823
3639
  maxBudgetUsd: body.options?.maxBudgetUsd,
3824
3640
  model: process.env.CLAUDE_MODEL || void 0,
@@ -3898,7 +3714,7 @@ function createChatRouter(config) {
3898
3714
  });
3899
3715
  } catch (err) {
3900
3716
  const errorMessage = err instanceof Error ? err.message : String(err);
3901
- log$28.error("[stream] 保存用户消息失败", {
3717
+ log$29.error("[stream] 保存用户消息失败", {
3902
3718
  sessionId,
3903
3719
  error: errorMessage
3904
3720
  });
@@ -3911,9 +3727,16 @@ function createChatRouter(config) {
3911
3727
  addRunClient(req, res, run);
3912
3728
  sendSSE(res, "session", { sessionId });
3913
3729
  sendRunStatus(res, run);
3730
+ const heartbeatTimer = setInterval(() => {
3731
+ if (run.status !== "running" || run.clients.size === 0) {
3732
+ clearInterval(heartbeatTimer);
3733
+ return;
3734
+ }
3735
+ broadcastRunEvent(run, "heartbeat", { ts: Date.now() });
3736
+ }, 15e3);
3914
3737
  const handleBlockedEvent = (event) => {
3915
3738
  const sanitized = sanitizeBlockedEvent(event);
3916
- log$28.warn("[stream] Agent blocked event", { ...sanitized });
3739
+ log$29.warn("[stream] Agent blocked event", { ...sanitized });
3917
3740
  if (runtimeConfig.requestContext) logChatAudit(buildAuditEvent(runtimeConfig.requestContext, sanitized.type, {
3918
3741
  toolName: sanitized.name,
3919
3742
  inputSummary: sanitized.inputSummary,
@@ -3944,6 +3767,8 @@ function createChatRouter(config) {
3944
3767
  };
3945
3768
  const handleRuntimeEvent = (event) => {
3946
3769
  const eventSummary = summarizeForAudit(event);
3770
+ const runtimeFinalText = extractRuntimeEventFinalText(event);
3771
+ if (runtimeFinalText && !run.text.trim()) run.text = runtimeFinalText;
3947
3772
  if (runtimeConfig.requestContext) logChatAudit(buildAuditEvent(runtimeConfig.requestContext, "runtime_event", {
3948
3773
  resultSummary: eventSummary,
3949
3774
  metadata: buildRuntimeEventAuditMetadata(event)
@@ -4042,7 +3867,8 @@ function createChatRouter(config) {
4042
3867
  broadcastRunEvent(run, "turn-end", { stopReason });
4043
3868
  },
4044
3869
  onResult: async (result) => {
4045
- const finalText = run.text || result.result || "";
3870
+ const finalText = run.text.trim() ? run.text : result.result || "";
3871
+ run.text = finalText;
4046
3872
  await markRunTerminal(result.isError ? "failed" : "completed", { finalStatus: result.subtype });
4047
3873
  await queueRunMessage(run, messageStore, {
4048
3874
  sessionId,
@@ -4070,8 +3896,9 @@ function createChatRouter(config) {
4070
3896
  totalCostUsd: result.totalCostUsd,
4071
3897
  numTurns: result.numTurns
4072
3898
  });
3899
+ clearInterval(heartbeatTimer);
4073
3900
  endRunClients(run);
4074
- log$28.info("[stream] 对话完成", {
3901
+ log$29.info("[stream] 对话完成", {
4075
3902
  sessionId,
4076
3903
  runId: run.runId,
4077
3904
  result: result.subtype,
@@ -4093,7 +3920,7 @@ function createChatRouter(config) {
4093
3920
  }));
4094
3921
  },
4095
3922
  onError: async (error) => {
4096
- log$28.error("[stream] Runtime 执行出错", {
3923
+ log$29.error("[stream] Runtime 执行出错", {
4097
3924
  sessionId,
4098
3925
  runId: run.runId,
4099
3926
  error: error.message
@@ -4130,6 +3957,7 @@ function createChatRouter(config) {
4130
3957
  runId: run.runId,
4131
3958
  message: error.message
4132
3959
  });
3960
+ clearInterval(heartbeatTimer);
4133
3961
  endRunClients(run);
4134
3962
  },
4135
3963
  onAuditEvent: (event) => {
@@ -4179,7 +4007,7 @@ function createChatRouter(config) {
4179
4007
  scenarioId: runtimeConfig.runContext?.scenarioId,
4180
4008
  ...buildRunInputAuditMetadata(runtimeConfig)
4181
4009
  } }));
4182
- log$28.info("[stream] 开始执行 prompt", {
4010
+ log$29.info("[stream] 开始执行 prompt", {
4183
4011
  sessionId,
4184
4012
  runId: run.runId,
4185
4013
  cwd: runtimeConfig.cwd
@@ -4187,7 +4015,7 @@ function createChatRouter(config) {
4187
4015
  await processManager.executePrompt(runtimeConfig, callbacks);
4188
4016
  if (run.status === "running") {
4189
4017
  const errorMessage = "Runtime completed without a final result event";
4190
- log$28.error("[stream] executePrompt 未返回终态回调", {
4018
+ log$29.error("[stream] executePrompt 未返回终态回调", {
4191
4019
  sessionId,
4192
4020
  runId: run.runId
4193
4021
  });
@@ -4199,11 +4027,12 @@ function createChatRouter(config) {
4199
4027
  runId: run.runId,
4200
4028
  message: errorMessage
4201
4029
  });
4030
+ clearInterval(heartbeatTimer);
4202
4031
  endRunClients(run);
4203
4032
  }
4204
4033
  } catch (err) {
4205
4034
  const errorMessage = err instanceof Error ? err.message : String(err);
4206
- log$28.error("[stream] executePrompt 异常", {
4035
+ log$29.error("[stream] executePrompt 异常", {
4207
4036
  sessionId,
4208
4037
  runId: run.runId,
4209
4038
  error: errorMessage
@@ -4220,7 +4049,7 @@ function createChatRouter(config) {
4220
4049
  }
4221
4050
  const cursor = req.query.cursor;
4222
4051
  const limit = parseInt(req.query.limit, 10) || 50;
4223
- log$28.info("[history] 查询会话历史", {
4052
+ log$29.info("[history] 查询会话历史", {
4224
4053
  userId: chatUser.userId,
4225
4054
  sessionId,
4226
4055
  cursor,
@@ -4245,7 +4074,7 @@ function createChatRouter(config) {
4245
4074
  });
4246
4075
  } catch (err) {
4247
4076
  const errorMessage = err instanceof Error ? err.message : String(err);
4248
- log$28.error("[history] 查询历史消息失败", {
4077
+ log$29.error("[history] 查询历史消息失败", {
4249
4078
  sessionId,
4250
4079
  error: errorMessage
4251
4080
  });
@@ -4280,13 +4109,13 @@ function createChatRouter(config) {
4280
4109
  });
4281
4110
  router.get("/api/chat/sessions", chatAuthMiddleware, async (_req, res) => {
4282
4111
  const chatUser = res.locals.chatUser;
4283
- log$28.info("[sessions] 列出用户会话", { userId: chatUser.userId });
4112
+ log$29.info("[sessions] 列出用户会话", { userId: chatUser.userId });
4284
4113
  try {
4285
4114
  const sessions = await messageStore.listSessions(chatUser.openId);
4286
4115
  res.json({ sessions });
4287
4116
  } catch (err) {
4288
4117
  const errorMessage = err instanceof Error ? err.message : String(err);
4289
- log$28.error("[sessions] 列出会话失败", {
4118
+ log$29.error("[sessions] 列出会话失败", {
4290
4119
  userId: chatUser.userId,
4291
4120
  error: errorMessage
4292
4121
  });
@@ -4296,7 +4125,7 @@ function createChatRouter(config) {
4296
4125
  router.post("/api/chat/sessions", chatAuthMiddleware, async (req, res) => {
4297
4126
  const chatUser = res.locals.chatUser;
4298
4127
  const body = req.body;
4299
- log$28.info("[sessions] 创建新会话", {
4128
+ log$29.info("[sessions] 创建新会话", {
4300
4129
  userId: chatUser.userId,
4301
4130
  title: body.title
4302
4131
  });
@@ -4311,7 +4140,7 @@ function createChatRouter(config) {
4311
4140
  res.json({ session });
4312
4141
  } catch (err) {
4313
4142
  const errorMessage = err instanceof Error ? err.message : String(err);
4314
- log$28.error("[sessions] 创建会话失败", {
4143
+ log$29.error("[sessions] 创建会话失败", {
4315
4144
  userId: chatUser.userId,
4316
4145
  error: errorMessage
4317
4146
  });
@@ -4321,7 +4150,7 @@ function createChatRouter(config) {
4321
4150
  router.delete("/api/chat/sessions/:id", chatAuthMiddleware, async (req, res) => {
4322
4151
  const chatUser = res.locals.chatUser;
4323
4152
  const sessionId = String(req.params.id);
4324
- log$28.info("[sessions] 删除会话", {
4153
+ log$29.info("[sessions] 删除会话", {
4325
4154
  userId: chatUser.userId,
4326
4155
  sessionId
4327
4156
  });
@@ -4331,7 +4160,7 @@ function createChatRouter(config) {
4331
4160
  res.json({ success: true });
4332
4161
  } catch (err) {
4333
4162
  const errorMessage = err instanceof Error ? err.message : String(err);
4334
- log$28.error("[sessions] 删除会话失败", {
4163
+ log$29.error("[sessions] 删除会话失败", {
4335
4164
  sessionId,
4336
4165
  error: errorMessage
4337
4166
  });
@@ -4340,6 +4169,34 @@ function createChatRouter(config) {
4340
4169
  });
4341
4170
  return router;
4342
4171
  }
4172
+ /**
4173
+ * 将 sourceArtifacts 中的文件信息(fileToken、fileName 等)注入到用户 prompt 中,
4174
+ * 使 LLM 能看到实际的 file token 值并直接用于下载。
4175
+ *
4176
+ * 如果没有 sourceArtifacts 或无法提取有效信息,返回原始 prompt。
4177
+ */
4178
+ function enrichPromptWithSourceArtifacts(prompt, sourceArtifacts) {
4179
+ if (!sourceArtifacts) return prompt;
4180
+ const lines = [];
4181
+ const extractInfo = (artifact) => {
4182
+ if (!artifact || typeof artifact !== "object") return;
4183
+ const record = artifact;
4184
+ const fileToken = record.fileToken ?? record.file_token ?? record.driveFileToken ?? record.drive_file_token;
4185
+ const fileName = record.fileName ?? record.file_name ?? record.name ?? record.title;
4186
+ const fileType = record.fileType ?? record.file_type ?? record.type ?? record.mimeType;
4187
+ const fileUrl = record.fileUrl ?? record.file_url ?? record.url ?? record.uri;
4188
+ if (fileToken || fileName || fileUrl) {
4189
+ if (fileName) lines.push(`- fileName: ${String(fileName)}`);
4190
+ if (fileToken) lines.push(`- fileToken: ${String(fileToken)}`);
4191
+ if (fileType) lines.push(`- fileType: ${String(fileType)}`);
4192
+ if (fileUrl) lines.push(`- fileUrl: ${String(fileUrl)}`);
4193
+ }
4194
+ };
4195
+ if (Array.isArray(sourceArtifacts)) for (const item of sourceArtifacts) extractInfo(item);
4196
+ else extractInfo(sourceArtifacts);
4197
+ if (lines.length === 0) return prompt;
4198
+ return `${prompt}\n\n[Attached Source File]\n${lines.join("\n")}`;
4199
+ }
4343
4200
  //#endregion
4344
4201
  //#region src/core/app-info-sync.ts
4345
4202
  /**
@@ -4357,7 +4214,7 @@ function createChatRouter(config) {
4357
4214
  * - GET /open-apis/application/v6/applications/me?lang=zh_cn(需要 application:application:self_manage 权限,返回完整信息)
4358
4215
  * 如果后者无权限则 fallback 到前者
4359
4216
  */
4360
- const log$27 = larkLogger("core/app-info-sync");
4217
+ const log$28 = larkLogger("core/app-info-sync");
4361
4218
  /**
4362
4219
  * 从飞书获取应用信息
4363
4220
  *
@@ -4368,7 +4225,7 @@ async function fetchAppInfo(credentials) {
4368
4225
  const { appId, appSecret } = credentials;
4369
4226
  const token = await getTenantAccessToken(appId, appSecret);
4370
4227
  if (!token) {
4371
- log$27.error("获取 tenant_access_token 失败,无法同步应用信息");
4228
+ log$28.error("获取 tenant_access_token 失败,无法同步应用信息");
4372
4229
  return null;
4373
4230
  }
4374
4231
  const fullInfo = await fetchFromApplicationApi(token);
@@ -4379,13 +4236,13 @@ async function fetchAppInfo(credentials) {
4379
4236
  async function fetchFromApplicationApi(token) {
4380
4237
  try {
4381
4238
  const data = await (await fetch("https://open.feishu.cn/open-apis/application/v6/applications/me?lang=zh_cn", { headers: { Authorization: `Bearer ${token}` } })).json();
4382
- log$27.info("application/v6 API 响应", {
4239
+ log$28.info("application/v6 API 响应", {
4383
4240
  code: data.code,
4384
4241
  msg: data.msg,
4385
4242
  hasApp: !!data.data?.app
4386
4243
  });
4387
4244
  if (data.code !== 0 || !data.data?.app) {
4388
- log$27.warn("application/v6 API 返回非零或无数据,将 fallback", {
4245
+ log$28.warn("application/v6 API 返回非零或无数据,将 fallback", {
4389
4246
  code: data.code,
4390
4247
  msg: data.msg
4391
4248
  });
@@ -4400,7 +4257,7 @@ async function fetchFromApplicationApi(token) {
4400
4257
  helpDocUrl: zhInfo?.help_use
4401
4258
  };
4402
4259
  } catch (err) {
4403
- log$27.warn("application/v6 API 请求异常", { error: err instanceof Error ? err.message : String(err) });
4260
+ log$28.warn("application/v6 API 请求异常", { error: err instanceof Error ? err.message : String(err) });
4404
4261
  return null;
4405
4262
  }
4406
4263
  }
@@ -4408,13 +4265,13 @@ async function fetchFromApplicationApi(token) {
4408
4265
  async function fetchFromBotApi(token) {
4409
4266
  try {
4410
4267
  const data = await (await fetch("https://open.feishu.cn/open-apis/bot/v3/info", { headers: { Authorization: `Bearer ${token}` } })).json();
4411
- log$27.info("bot/v3/info API 响应", {
4268
+ log$28.info("bot/v3/info API 响应", {
4412
4269
  code: data.code,
4413
4270
  msg: data.msg,
4414
4271
  hasBot: !!data.bot
4415
4272
  });
4416
4273
  if (data.code !== 0 || !data.bot) {
4417
- log$27.error("bot/v3/info API 失败", {
4274
+ log$28.error("bot/v3/info API 失败", {
4418
4275
  code: data.code,
4419
4276
  msg: data.msg
4420
4277
  });
@@ -4425,7 +4282,7 @@ async function fetchFromBotApi(token) {
4425
4282
  avatarUrl: data.bot.avatar_url
4426
4283
  };
4427
4284
  } catch (err) {
4428
- log$27.error("bot/v3/info API 请求异常", { error: err instanceof Error ? err.message : String(err) });
4285
+ log$28.error("bot/v3/info API 请求异常", { error: err instanceof Error ? err.message : String(err) });
4429
4286
  return null;
4430
4287
  }
4431
4288
  }
@@ -4441,7 +4298,7 @@ async function getTenantAccessToken(appId, appSecret) {
4441
4298
  })
4442
4299
  })).json();
4443
4300
  if (data.code !== 0 || !data.tenant_access_token) {
4444
- log$27.error("获取 tenant_access_token 失败", {
4301
+ log$28.error("获取 tenant_access_token 失败", {
4445
4302
  code: data.code,
4446
4303
  msg: data.msg
4447
4304
  });
@@ -4449,7 +4306,7 @@ async function getTenantAccessToken(appId, appSecret) {
4449
4306
  }
4450
4307
  return data.tenant_access_token;
4451
4308
  } catch (err) {
4452
- log$27.error("获取 tenant_access_token 异常", { error: err instanceof Error ? err.message : String(err) });
4309
+ log$28.error("获取 tenant_access_token 异常", { error: err instanceof Error ? err.message : String(err) });
4453
4310
  return null;
4454
4311
  }
4455
4312
  }
@@ -4487,10 +4344,10 @@ function parseDocTokenFromUrl(url) {
4487
4344
  async function fetchDocContent(docUrl, accessToken) {
4488
4345
  const parsed = parseDocTokenFromUrl(docUrl);
4489
4346
  if (!parsed) {
4490
- log$27.warn("无法从 URL 中解析文档 token", { url: docUrl });
4347
+ log$28.warn("无法从 URL 中解析文档 token", { url: docUrl });
4491
4348
  return null;
4492
4349
  }
4493
- log$27.info("开始读取人设文档", {
4350
+ log$28.info("开始读取人设文档", {
4494
4351
  url: docUrl,
4495
4352
  type: parsed.type,
4496
4353
  token: parsed.token
@@ -4498,18 +4355,18 @@ async function fetchDocContent(docUrl, accessToken) {
4498
4355
  let docToken = parsed.token;
4499
4356
  if (parsed.type === "wiki") {
4500
4357
  const realToken = await resolveWikiNodeToDocToken(parsed.token, accessToken);
4501
- if (!realToken) log$27.warn("wiki 节点解析失败,尝试直接使用 token 读取");
4358
+ if (!realToken) log$28.warn("wiki 节点解析失败,尝试直接使用 token 读取");
4502
4359
  else docToken = realToken;
4503
4360
  }
4504
4361
  try {
4505
4362
  const data = await (await fetch(`https://open.feishu.cn/open-apis/docx/v1/documents/${docToken}/raw_content`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
4506
- log$27.info("文档 raw_content API 响应", {
4363
+ log$28.info("文档 raw_content API 响应", {
4507
4364
  code: data.code,
4508
4365
  msg: data.msg,
4509
4366
  contentLength: data.data?.content?.length
4510
4367
  });
4511
4368
  if (data.code !== 0 || !data.data?.content) {
4512
- log$27.warn("读取文档内容失败", {
4369
+ log$28.warn("读取文档内容失败", {
4513
4370
  code: data.code,
4514
4371
  msg: data.msg,
4515
4372
  docToken
@@ -4518,7 +4375,7 @@ async function fetchDocContent(docUrl, accessToken) {
4518
4375
  }
4519
4376
  return data.data.content.trim();
4520
4377
  } catch (err) {
4521
- log$27.error("读取文档内容异常", {
4378
+ log$28.error("读取文档内容异常", {
4522
4379
  error: err instanceof Error ? err.message : String(err),
4523
4380
  docToken
4524
4381
  });
@@ -4529,7 +4386,7 @@ async function fetchDocContent(docUrl, accessToken) {
4529
4386
  async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
4530
4387
  try {
4531
4388
  const data = await (await fetch(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token=${wikiToken}`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
4532
- log$27.info("wiki get_node API 响应", {
4389
+ log$28.info("wiki get_node API 响应", {
4533
4390
  code: data.code,
4534
4391
  msg: data.msg,
4535
4392
  objType: data.data?.node?.obj_type
@@ -4537,7 +4394,7 @@ async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
4537
4394
  if (data.code !== 0 || !data.data?.node?.obj_token) return null;
4538
4395
  return data.data.node.obj_token;
4539
4396
  } catch (err) {
4540
- log$27.warn("wiki get_node 请求异常", { error: err instanceof Error ? err.message : String(err) });
4397
+ log$28.warn("wiki get_node 请求异常", { error: err instanceof Error ? err.message : String(err) });
4541
4398
  return null;
4542
4399
  }
4543
4400
  }
@@ -4559,7 +4416,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4559
4416
  const infoBlock = buildAppInfoBlock(appInfo);
4560
4417
  if (!existsSync(claudeMdPath)) {
4561
4418
  await writeFile(claudeMdPath, infoBlock + "\n\n" + getDefaultClaudeMdBody(), "utf-8");
4562
- log$27.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
4419
+ log$28.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
4563
4420
  return;
4564
4421
  }
4565
4422
  let content = await readFile(claudeMdPath, "utf-8");
@@ -4571,7 +4428,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4571
4428
  content = before + infoBlock + after;
4572
4429
  } else content = infoBlock + "\n\n" + content;
4573
4430
  await writeFile(claudeMdPath, content, "utf-8");
4574
- log$27.info("CLAUDE.md 应用信息已同步", {
4431
+ log$28.info("CLAUDE.md 应用信息已同步", {
4575
4432
  appName: appInfo.appName,
4576
4433
  hasDescription: !!appInfo.description,
4577
4434
  hasAvatar: !!appInfo.avatarUrl
@@ -4586,7 +4443,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4586
4443
  async function syncPersonaDocToClaudeMd(personaContent) {
4587
4444
  const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
4588
4445
  if (!existsSync(claudeMdPath)) {
4589
- log$27.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
4446
+ log$28.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
4590
4447
  return;
4591
4448
  }
4592
4449
  let content = await readFile(claudeMdPath, "utf-8");
@@ -4607,7 +4464,7 @@ async function syncPersonaDocToClaudeMd(personaContent) {
4607
4464
  content = before + personaBlock + after;
4608
4465
  } else content = content.trimEnd() + "\n\n" + personaBlock + "\n";
4609
4466
  await writeFile(claudeMdPath, content, "utf-8");
4610
- log$27.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
4467
+ log$28.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
4611
4468
  }
4612
4469
  /** 构建应用信息标记区块 */
4613
4470
  function buildAppInfoBlock(appInfo) {
@@ -4699,24 +4556,24 @@ function getDefaultClaudeMdBody() {
4699
4556
  * @returns 同步后的应用信息,如果失败返回 null
4700
4557
  */
4701
4558
  async function syncAppInfo(credentials) {
4702
- log$27.info("开始同步应用信息", { appId: credentials.appId });
4559
+ log$28.info("开始同步应用信息", { appId: credentials.appId });
4703
4560
  const appInfo = await fetchAppInfo(credentials);
4704
4561
  if (!appInfo) {
4705
- log$27.warn("获取应用信息失败,跳过同步");
4562
+ log$28.warn("获取应用信息失败,跳过同步");
4706
4563
  return null;
4707
4564
  }
4708
4565
  await syncAppInfoToClaudeMd(appInfo);
4709
4566
  if (appInfo.helpDocUrl) {
4710
- log$27.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
4567
+ log$28.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
4711
4568
  const token = await getTenantAccessToken(credentials.appId, credentials.appSecret);
4712
4569
  if (token) {
4713
4570
  const personaContent = await fetchDocContent(appInfo.helpDocUrl, token);
4714
4571
  if (personaContent) await syncPersonaDocToClaudeMd(personaContent);
4715
- else log$27.warn("人设文档内容为空或读取失败,跳过同步");
4572
+ else log$28.warn("人设文档内容为空或读取失败,跳过同步");
4716
4573
  }
4717
4574
  }
4718
4575
  await installSyncSkill();
4719
- log$27.info("应用信息同步完成", {
4576
+ log$28.info("应用信息同步完成", {
4720
4577
  appName: appInfo.appName,
4721
4578
  description: appInfo.description?.substring(0, 50),
4722
4579
  hasPersonaDoc: !!appInfo.helpDocUrl
@@ -4734,7 +4591,7 @@ async function installSyncSkill() {
4734
4591
  if ((await readFile(SYNC_SKILL_PATH, "utf-8")).includes(`skill-version: ${SYNC_SKILL_VERSION}`)) return;
4735
4592
  }
4736
4593
  await writeFile(SYNC_SKILL_PATH, SYNC_SKILL_CONTENT, "utf-8");
4737
- log$27.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
4594
+ log$28.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
4738
4595
  }
4739
4596
  const SYNC_SKILL_CONTENT = `---
4740
4597
  skill-version: ${SYNC_SKILL_VERSION}
@@ -5730,7 +5587,7 @@ function createHooksRouter() {
5730
5587
  }
5731
5588
  //#endregion
5732
5589
  //#region src/memory/store.ts
5733
- const log$26 = larkLogger("memory/store");
5590
+ const log$27 = larkLogger("memory/store");
5734
5591
  const MEMORY_VERSION = 1;
5735
5592
  const MAX_TEAMMATE_RECENT = 3;
5736
5593
  const MAX_LONG_TERM = 100;
@@ -5756,7 +5613,7 @@ var MemoryStore = class MemoryStore {
5756
5613
  const raw = await readFile(path, "utf-8");
5757
5614
  return normalizeMemoryFile(JSON.parse(raw));
5758
5615
  } catch (err) {
5759
- log$26.warn("memory file load failed, using empty state", {
5616
+ log$27.warn("memory file load failed, using empty state", {
5760
5617
  cwd,
5761
5618
  path,
5762
5619
  error: err instanceof Error ? err.message : String(err)
@@ -6252,7 +6109,7 @@ function createMcpCallbackRouter() {
6252
6109
  * 提供 /api/scheduled-tasks 的 RESTful CRUD 接口。
6253
6110
  * 每个请求和响应都记录详细日志,便于后续错误排查。
6254
6111
  */
6255
- const log$25 = larkLogger("gateway/scheduler-handler");
6112
+ const log$26 = larkLogger("gateway/scheduler-handler");
6256
6113
  /**
6257
6114
  * 创建定时任务路由
6258
6115
  *
@@ -6263,16 +6120,16 @@ function createSchedulerRouter(taskManager) {
6263
6120
  const router = Router();
6264
6121
  router.get("/api/scheduled-tasks", (req, res) => {
6265
6122
  const requestId = req.headers["x-request-id"];
6266
- log$25.info("请求列出所有定时任务", { requestId });
6123
+ log$26.info("请求列出所有定时任务", { requestId });
6267
6124
  try {
6268
6125
  const tasks = taskManager.listTasks();
6269
- log$25.info("返回定时任务列表", {
6126
+ log$26.info("返回定时任务列表", {
6270
6127
  requestId,
6271
6128
  count: tasks.length
6272
6129
  });
6273
6130
  res.json({ tasks });
6274
6131
  } catch (err) {
6275
- log$25.error("列出定时任务失败", {
6132
+ log$26.error("列出定时任务失败", {
6276
6133
  requestId,
6277
6134
  error: String(err)
6278
6135
  });
@@ -6285,13 +6142,13 @@ function createSchedulerRouter(taskManager) {
6285
6142
  router.post("/api/scheduled-tasks", (req, res) => {
6286
6143
  const requestId = req.headers["x-request-id"];
6287
6144
  const body = req.body;
6288
- log$25.info("请求创建定时任务", {
6145
+ log$26.info("请求创建定时任务", {
6289
6146
  requestId,
6290
6147
  body: JSON.stringify(body)
6291
6148
  });
6292
6149
  const { name, cron: cronExpr, session_id, cwd, prompt, example_output, constraints } = body;
6293
6150
  if (!name || typeof name !== "string") {
6294
- log$25.warn("创建任务参数缺失: name", { requestId });
6151
+ log$26.warn("创建任务参数缺失: name", { requestId });
6295
6152
  res.status(400).json({
6296
6153
  error: "参数错误",
6297
6154
  detail: "name 为必填字符串"
@@ -6299,7 +6156,7 @@ function createSchedulerRouter(taskManager) {
6299
6156
  return;
6300
6157
  }
6301
6158
  if (!cronExpr || typeof cronExpr !== "string") {
6302
- log$25.warn("创建任务参数缺失: cron", { requestId });
6159
+ log$26.warn("创建任务参数缺失: cron", { requestId });
6303
6160
  res.status(400).json({
6304
6161
  error: "参数错误",
6305
6162
  detail: "cron 为必填字符串"
@@ -6307,7 +6164,7 @@ function createSchedulerRouter(taskManager) {
6307
6164
  return;
6308
6165
  }
6309
6166
  if (!session_id || typeof session_id !== "string") {
6310
- log$25.warn("创建任务参数缺失: session_id", { requestId });
6167
+ log$26.warn("创建任务参数缺失: session_id", { requestId });
6311
6168
  res.status(400).json({
6312
6169
  error: "参数错误",
6313
6170
  detail: "session_id 为必填字符串"
@@ -6315,7 +6172,7 @@ function createSchedulerRouter(taskManager) {
6315
6172
  return;
6316
6173
  }
6317
6174
  if (!cwd || typeof cwd !== "string") {
6318
- log$25.warn("创建任务参数缺失: cwd", { requestId });
6175
+ log$26.warn("创建任务参数缺失: cwd", { requestId });
6319
6176
  res.status(400).json({
6320
6177
  error: "参数错误",
6321
6178
  detail: "cwd 为必填字符串"
@@ -6323,7 +6180,7 @@ function createSchedulerRouter(taskManager) {
6323
6180
  return;
6324
6181
  }
6325
6182
  if (!prompt || typeof prompt !== "string") {
6326
- log$25.warn("创建任务参数缺失: prompt", { requestId });
6183
+ log$26.warn("创建任务参数缺失: prompt", { requestId });
6327
6184
  res.status(400).json({
6328
6185
  error: "参数错误",
6329
6186
  detail: "prompt 为必填字符串"
@@ -6331,7 +6188,7 @@ function createSchedulerRouter(taskManager) {
6331
6188
  return;
6332
6189
  }
6333
6190
  if (!cron.validate(cronExpr)) {
6334
- log$25.warn("cron 表达式不合法", {
6191
+ log$26.warn("cron 表达式不合法", {
6335
6192
  requestId,
6336
6193
  cron: cronExpr
6337
6194
  });
@@ -6352,7 +6209,7 @@ function createSchedulerRouter(taskManager) {
6352
6209
  constraints
6353
6210
  };
6354
6211
  const task = taskManager.createTask(params);
6355
- log$25.info("定时任务创建成功", {
6212
+ log$26.info("定时任务创建成功", {
6356
6213
  requestId,
6357
6214
  taskId: task.id,
6358
6215
  name: task.name,
@@ -6360,7 +6217,7 @@ function createSchedulerRouter(taskManager) {
6360
6217
  });
6361
6218
  res.status(201).json({ task });
6362
6219
  } catch (err) {
6363
- log$25.error("创建定时任务失败", {
6220
+ log$26.error("创建定时任务失败", {
6364
6221
  requestId,
6365
6222
  error: String(err)
6366
6223
  });
@@ -6374,13 +6231,13 @@ function createSchedulerRouter(taskManager) {
6374
6231
  const requestId = req.headers["x-request-id"];
6375
6232
  const id = String(req.params.id);
6376
6233
  const body = req.body;
6377
- log$25.info("请求更新定时任务", {
6234
+ log$26.info("请求更新定时任务", {
6378
6235
  requestId,
6379
6236
  taskId: id,
6380
6237
  body: JSON.stringify(body)
6381
6238
  });
6382
6239
  if (!taskManager.getTask(id)) {
6383
- log$25.warn("更新的任务不存在", {
6240
+ log$26.warn("更新的任务不存在", {
6384
6241
  requestId,
6385
6242
  taskId: id
6386
6243
  });
@@ -6392,7 +6249,7 @@ function createSchedulerRouter(taskManager) {
6392
6249
  }
6393
6250
  if (body.cron !== void 0) {
6394
6251
  if (typeof body.cron !== "string" || !cron.validate(body.cron)) {
6395
- log$25.warn("更新的 cron 表达式不合法", {
6252
+ log$26.warn("更新的 cron 表达式不合法", {
6396
6253
  requestId,
6397
6254
  taskId: id,
6398
6255
  cron: body.cron
@@ -6413,7 +6270,7 @@ function createSchedulerRouter(taskManager) {
6413
6270
  if (body.constraints !== void 0) updates.constraints = body.constraints;
6414
6271
  if (body.enabled !== void 0) updates.enabled = body.enabled;
6415
6272
  const task = taskManager.updateTask(id, updates);
6416
- log$25.info("定时任务更新成功", {
6273
+ log$26.info("定时任务更新成功", {
6417
6274
  requestId,
6418
6275
  taskId: task.id,
6419
6276
  name: task.name,
@@ -6421,7 +6278,7 @@ function createSchedulerRouter(taskManager) {
6421
6278
  });
6422
6279
  res.json({ task });
6423
6280
  } catch (err) {
6424
- log$25.error("更新定时任务失败", {
6281
+ log$26.error("更新定时任务失败", {
6425
6282
  requestId,
6426
6283
  taskId: id,
6427
6284
  error: String(err)
@@ -6435,12 +6292,12 @@ function createSchedulerRouter(taskManager) {
6435
6292
  router.delete("/api/scheduled-tasks/:id", (req, res) => {
6436
6293
  const requestId = req.headers["x-request-id"];
6437
6294
  const id = String(req.params.id);
6438
- log$25.info("请求删除定时任务", {
6295
+ log$26.info("请求删除定时任务", {
6439
6296
  requestId,
6440
6297
  taskId: id
6441
6298
  });
6442
6299
  if (!taskManager.getTask(id)) {
6443
- log$25.warn("删除的任务不存在", {
6300
+ log$26.warn("删除的任务不存在", {
6444
6301
  requestId,
6445
6302
  taskId: id
6446
6303
  });
@@ -6452,7 +6309,7 @@ function createSchedulerRouter(taskManager) {
6452
6309
  }
6453
6310
  try {
6454
6311
  const deleted = taskManager.deleteTask(id);
6455
- log$25.info("定时任务删除结果", {
6312
+ log$26.info("定时任务删除结果", {
6456
6313
  requestId,
6457
6314
  taskId: id,
6458
6315
  deleted
@@ -6462,7 +6319,7 @@ function createSchedulerRouter(taskManager) {
6462
6319
  deleted: true
6463
6320
  });
6464
6321
  } catch (err) {
6465
- log$25.error("删除定时任务失败", {
6322
+ log$26.error("删除定时任务失败", {
6466
6323
  requestId,
6467
6324
  taskId: id,
6468
6325
  error: String(err)
@@ -7019,7 +6876,7 @@ function createGatewayServer(config) {
7019
6876
  *
7020
6877
  * 持久化文件: /workspace/config/scheduled-tasks.json
7021
6878
  */
7022
- const log$24 = larkLogger("gateway/scheduler");
6879
+ const log$25 = larkLogger("gateway/scheduler");
7023
6880
  /** 持久化文件路径 */
7024
6881
  const PERSIST_PATH = "/workspace/config/scheduled-tasks.json";
7025
6882
  /**
@@ -7037,7 +6894,7 @@ var ScheduledTaskManager = class {
7037
6894
  processManager;
7038
6895
  constructor(processManager) {
7039
6896
  this.processManager = processManager;
7040
- log$24.info("ScheduledTaskManager 已创建");
6897
+ log$25.info("ScheduledTaskManager 已创建");
7041
6898
  }
7042
6899
  /**
7043
6900
  * 创建定时任务
@@ -7057,7 +6914,7 @@ var ScheduledTaskManager = class {
7057
6914
  enabled: true,
7058
6915
  created_at: (/* @__PURE__ */ new Date()).toISOString()
7059
6916
  };
7060
- log$24.info("创建定时任务", {
6917
+ log$25.info("创建定时任务", {
7061
6918
  taskId: task.id,
7062
6919
  name: task.name,
7063
6920
  cron: task.cron,
@@ -7093,7 +6950,7 @@ var ScheduledTaskManager = class {
7093
6950
  if (updates.example_output !== void 0) task.example_output = updates.example_output;
7094
6951
  if (updates.constraints !== void 0) task.constraints = updates.constraints;
7095
6952
  if (updates.enabled !== void 0) task.enabled = updates.enabled;
7096
- log$24.info("更新定时任务", {
6953
+ log$25.info("更新定时任务", {
7097
6954
  taskId: id,
7098
6955
  name: task.name,
7099
6956
  cronChanged: oldCron !== task.cron,
@@ -7115,10 +6972,10 @@ var ScheduledTaskManager = class {
7115
6972
  deleteTask(id) {
7116
6973
  const task = this.tasks.get(id);
7117
6974
  if (!task) {
7118
- log$24.warn("尝试删除不存在的任务", { taskId: id });
6975
+ log$25.warn("尝试删除不存在的任务", { taskId: id });
7119
6976
  return false;
7120
6977
  }
7121
- log$24.info("删除定时任务", {
6978
+ log$25.info("删除定时任务", {
7122
6979
  taskId: id,
7123
6980
  name: task.name
7124
6981
  });
@@ -7133,28 +6990,28 @@ var ScheduledTaskManager = class {
7133
6990
  * 启动时调用,读取 JSON 文件并为每个 enabled 的任务注册 cron。
7134
6991
  */
7135
6992
  async loadFromDisk() {
7136
- log$24.info("从磁盘加载定时任务", { path: PERSIST_PATH });
6993
+ log$25.info("从磁盘加载定时任务", { path: PERSIST_PATH });
7137
6994
  try {
7138
6995
  const content = await readFile(PERSIST_PATH, "utf-8");
7139
6996
  const data = JSON.parse(content);
7140
6997
  if (!Array.isArray(data)) {
7141
- log$24.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
6998
+ log$25.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
7142
6999
  return;
7143
7000
  }
7144
7001
  for (const task of data) {
7145
7002
  this.tasks.set(task.id, task);
7146
7003
  if (task.enabled) this.registerCronJob(task);
7147
7004
  }
7148
- log$24.info("定时任务加载完成", {
7005
+ log$25.info("定时任务加载完成", {
7149
7006
  total: data.length,
7150
7007
  enabled: data.filter((t) => t.enabled).length
7151
7008
  });
7152
7009
  } catch (err) {
7153
7010
  if (err.code === "ENOENT") {
7154
- log$24.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
7011
+ log$25.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
7155
7012
  return;
7156
7013
  }
7157
- log$24.error("加载定时任务失败", {
7014
+ log$25.error("加载定时任务失败", {
7158
7015
  path: PERSIST_PATH,
7159
7016
  error: String(err)
7160
7017
  });
@@ -7170,12 +7027,12 @@ var ScheduledTaskManager = class {
7170
7027
  try {
7171
7028
  await mkdir(dirname(PERSIST_PATH), { recursive: true });
7172
7029
  await writeFile(PERSIST_PATH, JSON.stringify(tasks, null, 2), "utf-8");
7173
- log$24.info("定时任务已持久化到磁盘", {
7030
+ log$25.info("定时任务已持久化到磁盘", {
7174
7031
  path: PERSIST_PATH,
7175
7032
  count: tasks.length
7176
7033
  });
7177
7034
  } catch (err) {
7178
- log$24.error("持久化定时任务失败", {
7035
+ log$25.error("持久化定时任务失败", {
7179
7036
  path: PERSIST_PATH,
7180
7037
  error: String(err)
7181
7038
  });
@@ -7188,13 +7045,13 @@ var ScheduledTaskManager = class {
7188
7045
  */
7189
7046
  registerCronJob(task) {
7190
7047
  this.unregisterCronJob(task.id);
7191
- log$24.info("注册 cron 调度", {
7048
+ log$25.info("注册 cron 调度", {
7192
7049
  taskId: task.id,
7193
7050
  name: task.name,
7194
7051
  cron: task.cron
7195
7052
  });
7196
7053
  const job = cron.schedule(task.cron, () => {
7197
- log$24.info("cron 触发任务执行", {
7054
+ log$25.info("cron 触发任务执行", {
7198
7055
  taskId: task.id,
7199
7056
  name: task.name
7200
7057
  });
@@ -7210,7 +7067,7 @@ var ScheduledTaskManager = class {
7210
7067
  if (job) {
7211
7068
  job.stop();
7212
7069
  this.cronJobs.delete(taskId);
7213
- log$24.info("已注销 cron 调度", { taskId });
7070
+ log$25.info("已注销 cron 调度", { taskId });
7214
7071
  }
7215
7072
  }
7216
7073
  /**
@@ -7220,10 +7077,10 @@ var ScheduledTaskManager = class {
7220
7077
  */
7221
7078
  stopAll() {
7222
7079
  const count = this.cronJobs.size;
7223
- log$24.info("停止所有 cron 调度", { count });
7080
+ log$25.info("停止所有 cron 调度", { count });
7224
7081
  for (const [taskId, job] of this.cronJobs) {
7225
7082
  job.stop();
7226
- log$24.debug("已停止 cron 调度", { taskId });
7083
+ log$25.debug("已停止 cron 调度", { taskId });
7227
7084
  }
7228
7085
  this.cronJobs.clear();
7229
7086
  }
@@ -7237,17 +7094,17 @@ var ScheduledTaskManager = class {
7237
7094
  async executeTask(taskId) {
7238
7095
  const task = this.tasks.get(taskId);
7239
7096
  if (!task) {
7240
- log$24.warn("任务执行时未找到任务", { taskId });
7097
+ log$25.warn("任务执行时未找到任务", { taskId });
7241
7098
  return;
7242
7099
  }
7243
7100
  if (!task.enabled) {
7244
- log$24.info("任务已禁用,跳过执行", {
7101
+ log$25.info("任务已禁用,跳过执行", {
7245
7102
  taskId,
7246
7103
  name: task.name
7247
7104
  });
7248
7105
  return;
7249
7106
  }
7250
- log$24.info("开始执行定时任务", {
7107
+ log$25.info("开始执行定时任务", {
7251
7108
  taskId: task.id,
7252
7109
  name: task.name,
7253
7110
  sessionId: task.session_id,
@@ -7268,7 +7125,7 @@ var ScheduledTaskManager = class {
7268
7125
  onResult: (result) => {
7269
7126
  const summary = result.result ?? resultText;
7270
7127
  task.last_result = summary.slice(0, 2e3);
7271
- log$24.info("定时任务执行完成", {
7128
+ log$25.info("定时任务执行完成", {
7272
7129
  taskId: task.id,
7273
7130
  name: task.name,
7274
7131
  subtype: result.subtype,
@@ -7281,7 +7138,7 @@ var ScheduledTaskManager = class {
7281
7138
  },
7282
7139
  onError: (error) => {
7283
7140
  task.last_result = `执行失败: ${error.message}`;
7284
- log$24.error("定时任务执行失败", {
7141
+ log$25.error("定时任务执行失败", {
7285
7142
  taskId: task.id,
7286
7143
  name: task.name,
7287
7144
  error: error.message
@@ -7293,7 +7150,7 @@ var ScheduledTaskManager = class {
7293
7150
  await this.processManager.executePrompt(config, callbacks);
7294
7151
  } catch (err) {
7295
7152
  task.last_result = `执行异常: ${String(err)}`;
7296
- log$24.error("定时任务执行异常", {
7153
+ log$25.error("定时任务执行异常", {
7297
7154
  taskId: task.id,
7298
7155
  name: task.name,
7299
7156
  error: String(err)
@@ -7322,17 +7179,18 @@ var ScheduledTaskManager = class {
7322
7179
  * - 会话元数据:{workspaceRoot}/.chat-history/sessions/{userId}.json
7323
7180
  * - 消息数据:{workspaceRoot}/.chat-history/messages/{sessionId}.json
7324
7181
  */
7325
- const log$23 = larkLogger("chat/message-store");
7182
+ const log$24 = larkLogger("chat/message-store");
7326
7183
  var SessionMessageStore = class {
7327
7184
  baseDir;
7328
7185
  sessionsDir;
7329
7186
  messagesDir;
7187
+ jsonCache = /* @__PURE__ */ new Map();
7330
7188
  constructor(workspaceRoot) {
7331
7189
  const root = workspaceRoot || resolveWorkspaceRoot();
7332
7190
  this.baseDir = getWorkspaceChatHistoryRoot(root);
7333
7191
  this.sessionsDir = join(this.baseDir, "sessions");
7334
7192
  this.messagesDir = join(this.baseDir, "messages");
7335
- log$23.info("初始化消息存储", { baseDir: this.baseDir });
7193
+ log$24.info("初始化消息存储", { baseDir: this.baseDir });
7336
7194
  }
7337
7195
  /**
7338
7196
  * 创建新会话
@@ -7345,7 +7203,7 @@ var SessionMessageStore = class {
7345
7203
  updatedAt: now,
7346
7204
  messageCount: 0
7347
7205
  };
7348
- log$23.info("创建会话", {
7206
+ log$24.info("创建会话", {
7349
7207
  sessionId: fullSession.id,
7350
7208
  userId: fullSession.userId
7351
7209
  });
@@ -7358,7 +7216,7 @@ var SessionMessageStore = class {
7358
7216
  * 获取指定会话
7359
7217
  */
7360
7218
  async getSession(sessionId) {
7361
- log$23.debug("获取会话", { sessionId });
7219
+ log$24.debug("获取会话", { sessionId });
7362
7220
  try {
7363
7221
  const files = await this.listSessionFiles();
7364
7222
  for (const file of files) {
@@ -7368,7 +7226,7 @@ var SessionMessageStore = class {
7368
7226
  if (found) return found;
7369
7227
  }
7370
7228
  } catch (err) {
7371
- log$23.error("获取会话失败", {
7229
+ log$24.error("获取会话失败", {
7372
7230
  sessionId,
7373
7231
  error: String(err)
7374
7232
  });
@@ -7379,14 +7237,14 @@ var SessionMessageStore = class {
7379
7237
  * 列出指定用户的所有会话
7380
7238
  */
7381
7239
  async listSessions(userId) {
7382
- log$23.debug("列出用户会话", { userId });
7240
+ log$24.debug("列出用户会话", { userId });
7383
7241
  return await this.readUserSessions(userId);
7384
7242
  }
7385
7243
  /**
7386
7244
  * 删除指定会话(同时删除消息文件)
7387
7245
  */
7388
7246
  async deleteSession(sessionId) {
7389
- log$23.info("删除会话", { sessionId });
7247
+ log$24.info("删除会话", { sessionId });
7390
7248
  try {
7391
7249
  const files = await this.listSessionFiles();
7392
7250
  for (const file of files) {
@@ -7401,9 +7259,9 @@ var SessionMessageStore = class {
7401
7259
  }
7402
7260
  const msgFile = this.getMessageFilePath(sessionId);
7403
7261
  await this.writeJsonFile(msgFile, []);
7404
- log$23.info("会话删除完成", { sessionId });
7262
+ log$24.info("会话删除完成", { sessionId });
7405
7263
  } catch (err) {
7406
- log$23.error("删除会话失败", {
7264
+ log$24.error("删除会话失败", {
7407
7265
  sessionId,
7408
7266
  error: String(err)
7409
7267
  });
@@ -7420,7 +7278,7 @@ var SessionMessageStore = class {
7420
7278
  id: v4(),
7421
7279
  timestamp: Date.now()
7422
7280
  };
7423
- log$23.info("追加消息", {
7281
+ log$24.info("追加消息", {
7424
7282
  messageId: fullMessage.id,
7425
7283
  sessionId: fullMessage.sessionId,
7426
7284
  role: fullMessage.role
@@ -7431,9 +7289,9 @@ var SessionMessageStore = class {
7431
7289
  messages.push(fullMessage);
7432
7290
  await this.writeJsonFile(msgFile, messages);
7433
7291
  await this.updateSessionMeta(fullMessage.sessionId, messages.length);
7434
- log$23.debug("消息追加完成", { messageId: fullMessage.id });
7292
+ log$24.debug("消息追加完成", { messageId: fullMessage.id });
7435
7293
  } catch (err) {
7436
- log$23.error("追加消息失败", {
7294
+ log$24.error("追加消息失败", {
7437
7295
  sessionId: fullMessage.sessionId,
7438
7296
  error: String(err)
7439
7297
  });
@@ -7448,7 +7306,7 @@ var SessionMessageStore = class {
7448
7306
  async getMessages(sessionId, options) {
7449
7307
  const limit = options?.limit ?? 50;
7450
7308
  const cursor = options?.cursor;
7451
- log$23.debug("获取消息列表", {
7309
+ log$24.debug("获取消息列表", {
7452
7310
  sessionId,
7453
7311
  cursor,
7454
7312
  limit
@@ -7467,7 +7325,7 @@ var SessionMessageStore = class {
7467
7325
  nextCursor: startIdx + limit < allMessages.length ? sliced[sliced.length - 1]?.id : void 0
7468
7326
  };
7469
7327
  } catch (err) {
7470
- log$23.error("获取消息列表失败", {
7328
+ log$24.error("获取消息列表失败", {
7471
7329
  sessionId,
7472
7330
  error: String(err)
7473
7331
  });
@@ -7478,12 +7336,12 @@ var SessionMessageStore = class {
7478
7336
  * 获取会话消息总数
7479
7337
  */
7480
7338
  async getMessageCount(sessionId) {
7481
- log$23.debug("获取消息数量", { sessionId });
7339
+ log$24.debug("获取消息数量", { sessionId });
7482
7340
  try {
7483
7341
  const msgFile = this.getMessageFilePath(sessionId);
7484
7342
  return (await this.readJsonFile(msgFile) || []).length;
7485
7343
  } catch (err) {
7486
- log$23.error("获取消息数量失败", {
7344
+ log$24.error("获取消息数量失败", {
7487
7345
  sessionId,
7488
7346
  error: String(err)
7489
7347
  });
@@ -7534,7 +7392,7 @@ var SessionMessageStore = class {
7534
7392
  }
7535
7393
  }
7536
7394
  } catch (err) {
7537
- log$23.warn("更新会话元数据失败", {
7395
+ log$24.warn("更新会话元数据失败", {
7538
7396
  sessionId,
7539
7397
  error: String(err)
7540
7398
  });
@@ -7549,7 +7407,7 @@ var SessionMessageStore = class {
7549
7407
  await mkdir(this.sessionsDir, { recursive: true });
7550
7408
  return (await readdir(this.sessionsDir)).filter((e) => e.endsWith(".json")).map((e) => join(this.sessionsDir, e));
7551
7409
  } catch (err) {
7552
- log$23.error("列出会话索引文件失败", { error: String(err) });
7410
+ log$24.error("列出会话索引文件失败", { error: String(err) });
7553
7411
  return [];
7554
7412
  }
7555
7413
  }
@@ -7559,13 +7417,19 @@ var SessionMessageStore = class {
7559
7417
  async readJsonFile(filePath) {
7560
7418
  try {
7561
7419
  const raw = await readFile(filePath, "utf-8");
7562
- return JSON.parse(raw);
7420
+ const parsed = JSON.parse(raw);
7421
+ this.jsonCache.set(filePath, parsed);
7422
+ return parsed;
7563
7423
  } catch (err) {
7564
7424
  if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return null;
7565
- log$23.error("读取 JSON 文件失败", {
7425
+ log$24.error("读取 JSON 文件失败", {
7566
7426
  filePath,
7567
7427
  error: String(err)
7568
7428
  });
7429
+ if (this.jsonCache.has(filePath)) {
7430
+ log$24.warn("读取 JSON 文件失败,使用内存中的上一份完整快照", { filePath });
7431
+ return this.jsonCache.get(filePath);
7432
+ }
7569
7433
  return null;
7570
7434
  }
7571
7435
  }
@@ -7573,18 +7437,39 @@ var SessionMessageStore = class {
7573
7437
  * 通用 JSON 文件写入(自动创建目录)
7574
7438
  */
7575
7439
  async writeJsonFile(filePath, data) {
7440
+ const dir = dirname(filePath);
7441
+ const tmpPath = join(dir, `.${basename(filePath)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
7576
7442
  try {
7577
- const { dirname } = await import("node:path");
7578
- await mkdir(dirname(filePath), { recursive: true });
7579
- await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
7443
+ await mkdir(dir, { recursive: true });
7444
+ const handle = await open(tmpPath, "w");
7445
+ try {
7446
+ await handle.writeFile(JSON.stringify(data, null, 2), "utf-8");
7447
+ await handle.sync();
7448
+ } finally {
7449
+ await handle.close();
7450
+ }
7451
+ await rename(tmpPath, filePath);
7452
+ this.jsonCache.set(filePath, data);
7453
+ await this.fsyncDirectory(dir);
7580
7454
  } catch (err) {
7581
- log$23.error("写入 JSON 文件失败", {
7455
+ log$24.error("写入 JSON 文件失败", {
7582
7456
  filePath,
7583
7457
  error: String(err)
7584
7458
  });
7459
+ await unlink(tmpPath).catch(() => void 0);
7585
7460
  throw err;
7586
7461
  }
7587
7462
  }
7463
+ async fsyncDirectory(dir) {
7464
+ try {
7465
+ const handle = await open(dir, "r");
7466
+ try {
7467
+ await handle.sync();
7468
+ } finally {
7469
+ await handle.close();
7470
+ }
7471
+ } catch {}
7472
+ }
7588
7473
  };
7589
7474
  //#endregion
7590
7475
  //#region src/messaging/inbound/user-name-cache-store.ts
@@ -7859,7 +7744,7 @@ function getUserAgent() {
7859
7744
  * - `LarkClient.fromCredentials(credentials)` — ephemeral instance (not cached)
7860
7745
  * - `LarkClient.fromProvider(provider, opts)` — from ICredentialProvider (not cached)
7861
7746
  */
7862
- const log$21 = larkLogger("core/lark-client");
7747
+ const log$22 = larkLogger("core/lark-client");
7863
7748
  const GLOBAL_LARK_USER_AGENT_KEY = "LARK_USER_AGENT";
7864
7749
  function installGlobalUserAgent() {
7865
7750
  globalThis[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();
@@ -7951,7 +7836,7 @@ var LarkClient = class LarkClient {
7951
7836
  const existing = cache.get(account.accountId);
7952
7837
  if (existing && existing.account.appId === account.appId && credentialsEqual(existing.account.appSecret, account.appSecret)) return existing;
7953
7838
  if (existing) {
7954
- log$21.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
7839
+ log$22.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
7955
7840
  existing.dispose();
7956
7841
  }
7957
7842
  const instance = new LarkClient(account);
@@ -7994,7 +7879,7 @@ var LarkClient = class LarkClient {
7994
7879
  static fromProvider(provider, opts) {
7995
7880
  const appId = provider.getAppId();
7996
7881
  const appSecret = provider.getAppSecret();
7997
- log$21.info("通过 ICredentialProvider 创建 LarkClient", {
7882
+ log$22.info("通过 ICredentialProvider 创建 LarkClient", {
7998
7883
  appId,
7999
7884
  accountId: opts?.accountId ?? "default",
8000
7885
  brand: opts?.brand ?? "feishu"
@@ -8030,7 +7915,7 @@ var LarkClient = class LarkClient {
8030
7915
  get sdk() {
8031
7916
  if (!this._sdk) {
8032
7917
  const { appId, appSecret } = this.requireCredentials();
8033
- log$21.info("创建 Lark SDK 客户端实例", {
7918
+ log$22.info("创建 Lark SDK 客户端实例", {
8034
7919
  accountId: this.accountId,
8035
7920
  appId,
8036
7921
  brand: this.account.brand
@@ -8045,7 +7930,7 @@ var LarkClient = class LarkClient {
8045
7930
  if (sdkAny.httpInstance?.interceptors) {
8046
7931
  const accountId = this.accountId;
8047
7932
  sdkAny.httpInstance.interceptors.request.use((req) => {
8048
- log$21.debug("飞书 API 请求", {
7933
+ log$22.debug("飞书 API 请求", {
8049
7934
  accountId,
8050
7935
  method: req.method,
8051
7936
  url: req.url,
@@ -8055,7 +7940,7 @@ var LarkClient = class LarkClient {
8055
7940
  return req;
8056
7941
  }, void 0, { synchronous: true });
8057
7942
  sdkAny.httpInstance.interceptors.response.use((res) => {
8058
- log$21.debug("飞书 API 响应", {
7943
+ log$22.debug("飞书 API 响应", {
8059
7944
  accountId,
8060
7945
  code: res?.code,
8061
7946
  msg: res?.msg,
@@ -8063,7 +7948,7 @@ var LarkClient = class LarkClient {
8063
7948
  });
8064
7949
  return res;
8065
7950
  }, (err) => {
8066
- log$21.error("飞书 API 请求失败", {
7951
+ log$22.error("飞书 API 请求失败", {
8067
7952
  accountId,
8068
7953
  error: err instanceof Error ? err.message : String(err),
8069
7954
  url: err?.config?.url
@@ -8150,7 +8035,7 @@ var LarkClient = class LarkClient {
8150
8035
  dispatcher.register(handlers);
8151
8036
  const { appId, appSecret } = this.requireCredentials();
8152
8037
  if (this._wsClient) {
8153
- log$21.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
8038
+ log$22.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
8154
8039
  try {
8155
8040
  this._wsClient.close({ force: true });
8156
8041
  } catch {}
@@ -8167,7 +8052,7 @@ var LarkClient = class LarkClient {
8167
8052
  wsClientAny.handleEventData = (data) => {
8168
8053
  const msgType = data.headers?.find?.((h) => h.key === "type")?.value;
8169
8054
  const eventType = data.headers?.find?.((h) => h.key === "event_type")?.value;
8170
- log$21.info("WS 收到原始事件", {
8055
+ log$22.info("WS 收到原始事件", {
8171
8056
  accountId: this.accountId,
8172
8057
  msgType,
8173
8058
  eventType,
@@ -8193,14 +8078,14 @@ var LarkClient = class LarkClient {
8193
8078
  /** Disconnect WebSocket but keep instance in cache. */
8194
8079
  disconnect() {
8195
8080
  if (this._wsClient) {
8196
- log$21.info(`disconnecting WebSocket`, { accountId: this.accountId });
8081
+ log$22.info(`disconnecting WebSocket`, { accountId: this.accountId });
8197
8082
  try {
8198
8083
  this._wsClient.close({ force: true });
8199
8084
  } catch {}
8200
8085
  }
8201
8086
  this._wsClient = null;
8202
8087
  if (this.messageDedup) {
8203
- log$21.info(`disposing message dedup`, {
8088
+ log$22.info(`disposing message dedup`, {
8204
8089
  accountId: this.accountId,
8205
8090
  size: this.messageDedup.size
8206
8091
  });
@@ -8506,7 +8391,7 @@ function sortTraceValue(value) {
8506
8391
  }
8507
8392
  //#endregion
8508
8393
  //#region src/card/cc-stream-bridge.ts
8509
- const log$20 = larkLogger("card/cc-stream-bridge");
8394
+ const log$21 = larkLogger("card/cc-stream-bridge");
8510
8395
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
8511
8396
  /**
8512
8397
  * CCStreamBridge — 将 CC 流事件桥接到 StreamingCardController
@@ -8531,7 +8416,7 @@ var CCStreamBridge = class {
8531
8416
  sessionKey: options?.sessionKey
8532
8417
  };
8533
8418
  if (this.options.sessionKey) startToolUseTraceRun(this.options.sessionKey);
8534
- log$20.info("CCStreamBridge 初始化", {
8419
+ log$21.info("CCStreamBridge 初始化", {
8535
8420
  autoCompleteOnTurnEnd: this.options.autoCompleteOnTurnEnd,
8536
8421
  sessionKey: this.options.sessionKey ?? "(none)"
8537
8422
  });
@@ -8539,7 +8424,7 @@ var CCStreamBridge = class {
8539
8424
  /** 文本增量 → 累积后调用 controller.onPartialReply */
8540
8425
  onTextDelta(text) {
8541
8426
  this.accumulatedText += text;
8542
- log$20.debug("textDelta 事件", {
8427
+ log$21.debug("textDelta 事件", {
8543
8428
  deltaLen: text.length,
8544
8429
  totalLen: this.accumulatedText.length
8545
8430
  });
@@ -8548,7 +8433,7 @@ var CCStreamBridge = class {
8548
8433
  /** 思考增量 → 累积后调用 controller.onReasoningStream */
8549
8434
  onThinkingDelta(text) {
8550
8435
  this.accumulatedThinkingText += text;
8551
- log$20.debug("thinkingDelta 事件", {
8436
+ log$21.debug("thinkingDelta 事件", {
8552
8437
  deltaLen: text.length,
8553
8438
  totalLen: this.accumulatedThinkingText.length
8554
8439
  });
@@ -8560,7 +8445,7 @@ var CCStreamBridge = class {
8560
8445
  const displayName = getToolDisplayName(toolName);
8561
8446
  const toolParams = typeof _toolInput === "object" && _toolInput != null ? _toolInput : void 0;
8562
8447
  const hasParams = toolParams && Object.keys(toolParams).length > 0;
8563
- log$20.info("toolUseStart 事件", {
8448
+ log$21.info("toolUseStart 事件", {
8564
8449
  toolName,
8565
8450
  displayName,
8566
8451
  activeToolsCount: this.activeTools.size,
@@ -8573,7 +8458,7 @@ var CCStreamBridge = class {
8573
8458
  toolName,
8574
8459
  toolParams
8575
8460
  })) {
8576
- log$20.info("toolUseStart: 更新已有步骤的 params", { toolName });
8461
+ log$21.info("toolUseStart: 更新已有步骤的 params", { toolName });
8577
8462
  this.controller.onToolStart({
8578
8463
  name: toolName,
8579
8464
  phase: "start"
@@ -8597,7 +8482,7 @@ var CCStreamBridge = class {
8597
8482
  const toolName = this.activeTools.get(toolUseId) ?? this.lastToolName ?? "unknown";
8598
8483
  const displayName = getToolDisplayName(toolName);
8599
8484
  this.activeTools.delete(toolUseId);
8600
- log$20.info("toolResult 事件", {
8485
+ log$21.info("toolResult 事件", {
8601
8486
  toolUseId,
8602
8487
  toolName,
8603
8488
  displayName,
@@ -8612,7 +8497,7 @@ var CCStreamBridge = class {
8612
8497
  }
8613
8498
  /** 工具执行进度 → 记录日志 */
8614
8499
  onToolProgress(toolName, elapsedSeconds) {
8615
- log$20.debug("toolProgress 事件", {
8500
+ log$21.debug("toolProgress 事件", {
8616
8501
  toolName,
8617
8502
  displayName: getToolDisplayName(toolName),
8618
8503
  elapsedSeconds
@@ -8620,7 +8505,7 @@ var CCStreamBridge = class {
8620
8505
  }
8621
8506
  /** 轮次结束 → 仅在 end_turn 时标记完成 + 触发 onIdle */
8622
8507
  onTurnEnd(stopReason) {
8623
- log$20.info("turnEnd 事件", {
8508
+ log$21.info("turnEnd 事件", {
8624
8509
  stopReason,
8625
8510
  accumulatedTextLen: this.accumulatedText.length,
8626
8511
  accumulatedThinkingTextLen: this.accumulatedThinkingText.length
@@ -8628,7 +8513,7 @@ var CCStreamBridge = class {
8628
8513
  if (this.options.autoCompleteOnTurnEnd && stopReason === "end_turn") {
8629
8514
  const trimmedText = this.accumulatedText.trim();
8630
8515
  if (trimmedText === CC_INTERNAL_PLACEHOLDER) {
8631
- log$20.info("检测到 CC 内部占位消息,静默丢弃", {
8516
+ log$21.info("检测到 CC 内部占位消息,静默丢弃", {
8632
8517
  text: trimmedText.slice(0, 50),
8633
8518
  sessionKey: this.options.sessionKey
8634
8519
  });
@@ -8636,7 +8521,7 @@ var CCStreamBridge = class {
8636
8521
  return;
8637
8522
  }
8638
8523
  if (trimmedText === "") {
8639
- log$20.info("turnEnd 时文本为空,延迟到 onResult 兜底处理", { sessionKey: this.options.sessionKey });
8524
+ log$21.info("turnEnd 时文本为空,延迟到 onResult 兜底处理", { sessionKey: this.options.sessionKey });
8640
8525
  return;
8641
8526
  }
8642
8527
  this.controller.markFullyComplete();
@@ -8645,7 +8530,7 @@ var CCStreamBridge = class {
8645
8530
  }
8646
8531
  /** 最终结果 → 兜底最终化 + 错误处理 */
8647
8532
  onResult(data) {
8648
- log$20.info("result 事件", {
8533
+ log$21.info("result 事件", {
8649
8534
  subtype: data.subtype,
8650
8535
  isError: data.isError,
8651
8536
  durationMs: data.durationMs,
@@ -8654,7 +8539,7 @@ var CCStreamBridge = class {
8654
8539
  resultLen: data.result?.length ?? 0
8655
8540
  });
8656
8541
  if (data.aborted) {
8657
- log$20.info("运行时报告用户主动中断,按正常完成处理", {
8542
+ log$21.info("运行时报告用户主动中断,按正常完成处理", {
8658
8543
  subtype: data.subtype,
8659
8544
  sessionId: this.options.sessionKey
8660
8545
  });
@@ -8665,7 +8550,7 @@ var CCStreamBridge = class {
8665
8550
  const pm = ClaudeCodeAdapter.getInstance()?.getProcessManager();
8666
8551
  const sessionId = this.options.sessionKey;
8667
8552
  if (sessionId && pm ? pm.consumeAborted(sessionId) : false) {
8668
- log$20.info("用户主动中断,按正常完成处理", {
8553
+ log$21.info("用户主动中断,按正常完成处理", {
8669
8554
  subtype: data.subtype,
8670
8555
  sessionId
8671
8556
  });
@@ -8674,14 +8559,14 @@ var CCStreamBridge = class {
8674
8559
  this.controller.onIdle();
8675
8560
  } else {
8676
8561
  const errorMessage = data.result ?? `CC 执行失败: ${data.subtype}`;
8677
- log$20.error("CC 执行返回错误", {
8562
+ log$21.error("CC 执行返回错误", {
8678
8563
  subtype: data.subtype,
8679
8564
  errorMessage: errorMessage.slice(0, 200)
8680
8565
  });
8681
8566
  this.controller.onError(new Error(errorMessage), { kind: "cc-execution" });
8682
8567
  }
8683
8568
  } else if (!this.accumulatedText.trim()) if (data.result) {
8684
- log$20.info("result 兜底:使用 result.result 作为最终回复", {
8569
+ log$21.info("result 兜底:使用 result.result 作为最终回复", {
8685
8570
  resultLen: data.result.length,
8686
8571
  sessionKey: this.options.sessionKey
8687
8572
  });
@@ -8689,7 +8574,7 @@ var CCStreamBridge = class {
8689
8574
  this.controller.markFullyComplete();
8690
8575
  this.controller.onIdle();
8691
8576
  } else {
8692
- log$20.info("result 兜底:无文本且无 result,abort 卡片", { sessionKey: this.options.sessionKey });
8577
+ log$21.info("result 兜底:无文本且无 result,abort 卡片", { sessionKey: this.options.sessionKey });
8693
8578
  this.controller.abortCard();
8694
8579
  }
8695
8580
  else {
@@ -8703,7 +8588,7 @@ var CCStreamBridge = class {
8703
8588
  * 内部复用上面的直接调用方法。
8704
8589
  */
8705
8590
  bindParser(parser) {
8706
- log$20.info("绑定 CCStreamParser 事件到卡片控制器");
8591
+ log$21.info("绑定 CCStreamParser 事件到卡片控制器");
8707
8592
  parser.on("textDelta", (text) => this.onTextDelta(text));
8708
8593
  parser.on("thinkingDelta", (text) => this.onThinkingDelta(text));
8709
8594
  parser.on("toolUseStart", (toolName, toolInput) => this.onToolUseStart(toolName, toolInput));
@@ -8711,7 +8596,7 @@ var CCStreamBridge = class {
8711
8596
  parser.on("toolProgress", (toolName, elapsedSeconds) => this.onToolProgress(toolName, elapsedSeconds));
8712
8597
  parser.on("turnEnd", (stopReason) => this.onTurnEnd(stopReason));
8713
8598
  parser.on("result", (data) => this.onResult(data));
8714
- log$20.info("CCStreamParser 事件绑定完成");
8599
+ log$21.info("CCStreamParser 事件绑定完成");
8715
8600
  }
8716
8601
  };
8717
8602
  //#endregion
@@ -10730,7 +10615,7 @@ function resolveLarkSdk(cfg, accountId) {
10730
10615
  if (cached) return cached.sdk;
10731
10616
  return LarkClient.fromCfg(cfg, accountId).sdk;
10732
10617
  }
10733
- const log$19 = larkLogger("card/cardkit");
10618
+ const log$20 = larkLogger("card/cardkit");
10734
10619
  /**
10735
10620
  * 记录 CardKit API 响应日志,检测错误码并抛出异常。
10736
10621
  *
@@ -10740,13 +10625,13 @@ const log$19 = larkLogger("card/cardkit");
10740
10625
  function logCardKitResponse(params) {
10741
10626
  const { resp, api, context } = params;
10742
10627
  const { code, msg } = resp;
10743
- log$19.info(`cardkit ${api} response`, {
10628
+ log$20.info(`cardkit ${api} response`, {
10744
10629
  code,
10745
10630
  msg,
10746
10631
  context
10747
10632
  });
10748
10633
  if (code && code !== 0) {
10749
- log$19.warn(`cardkit ${api} FAILED`, {
10634
+ log$20.warn(`cardkit ${api} FAILED`, {
10750
10635
  code,
10751
10636
  msg,
10752
10637
  context,
@@ -11157,7 +11042,7 @@ function validateLocalMediaRoots(filePath, localRoots) {
11157
11042
  * Feishu messages, uploading media to the Feishu IM storage, and
11158
11043
  * sending image / file messages to chats.
11159
11044
  */
11160
- const log$18 = larkLogger("outbound/media");
11045
+ const log$19 = larkLogger("outbound/media");
11161
11046
  /**
11162
11047
  * Upload an image to Feishu IM storage.
11163
11048
  *
@@ -11225,7 +11110,7 @@ async function validateRemoteUrl(raw) {
11225
11110
  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}"`);
11226
11111
  } catch (err) {
11227
11112
  if (err instanceof Error && err.message.includes("SSRF protection")) throw err;
11228
- log$18.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
11113
+ log$19.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
11229
11114
  }
11230
11115
  }
11231
11116
  /**
@@ -11243,21 +11128,21 @@ async function fetchMediaBuffer(urlOrPath, localRoots) {
11243
11128
  if (localRoots !== void 0) validateLocalMediaRoots(filePath, localRoots);
11244
11129
  else throw new Error(`[feishu-media] Local file access denied for "${filePath}": mediaLocalRoots is not configured. Configure mediaLocalRoots to explicitly allow local file access.`);
11245
11130
  const buf = fs.readFileSync(filePath);
11246
- log$18.debug(`local file read: "${filePath}", ${buf.length} bytes`);
11131
+ log$19.debug(`local file read: "${filePath}", ${buf.length} bytes`);
11247
11132
  return buf;
11248
11133
  }
11249
11134
  await validateRemoteUrl(raw);
11250
11135
  const FETCH_TIMEOUT_MS = 3e4;
11251
- log$18.info(`fetching remote media: ${raw}`);
11136
+ log$19.info(`fetching remote media: ${raw}`);
11252
11137
  const response = await fetch(raw, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
11253
11138
  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.`);
11254
11139
  const arrayBuffer = await response.arrayBuffer();
11255
- log$18.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
11140
+ log$19.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
11256
11141
  return Buffer.from(arrayBuffer);
11257
11142
  }
11258
11143
  //#endregion
11259
11144
  //#region src/card/image-resolver.ts
11260
- const log$17 = larkLogger("card/image-resolver");
11145
+ const log$18 = larkLogger("card/image-resolver");
11261
11146
  /** Matches complete markdown image syntax: `![alt](value)` */
11262
11147
  const IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
11263
11148
  var ImageResolver = class {
@@ -11303,14 +11188,14 @@ var ImageResolver = class {
11303
11188
  async resolveImagesAwait(text, timeoutMs) {
11304
11189
  this.resolveImages(text);
11305
11190
  if (this.pending.size > 0) {
11306
- log$17.info("resolveImagesAwait: waiting for uploads", {
11191
+ log$18.info("resolveImagesAwait: waiting for uploads", {
11307
11192
  count: this.pending.size,
11308
11193
  timeoutMs
11309
11194
  });
11310
11195
  const allUploads = Promise.all(this.pending.values());
11311
11196
  const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
11312
11197
  await Promise.race([allUploads, timeout]);
11313
- if (this.pending.size > 0) log$17.warn("resolveImagesAwait: timed out with pending uploads", { remaining: this.pending.size });
11198
+ if (this.pending.size > 0) log$18.warn("resolveImagesAwait: timed out with pending uploads", { remaining: this.pending.size });
11314
11199
  }
11315
11200
  return this.resolveImages(text);
11316
11201
  }
@@ -11320,7 +11205,7 @@ var ImageResolver = class {
11320
11205
  }
11321
11206
  async doUpload(url) {
11322
11207
  try {
11323
- log$17.info("uploading image", { url });
11208
+ log$18.info("uploading image", { url });
11324
11209
  const buffer = await fetchRemoteImageBuffer(url);
11325
11210
  const { imageKey } = await uploadImageLark({
11326
11211
  cfg: this.cfg,
@@ -11328,7 +11213,7 @@ var ImageResolver = class {
11328
11213
  imageType: "message",
11329
11214
  accountId: this.accountId
11330
11215
  });
11331
- log$17.info("image uploaded", {
11216
+ log$18.info("image uploaded", {
11332
11217
  url,
11333
11218
  imageKey
11334
11219
  });
@@ -11337,7 +11222,7 @@ var ImageResolver = class {
11337
11222
  this.onImageResolved();
11338
11223
  return imageKey;
11339
11224
  } catch (err) {
11340
- log$17.warn("image upload failed", {
11225
+ log$18.warn("image upload failed", {
11341
11226
  url,
11342
11227
  error: String(err)
11343
11228
  });
@@ -11358,7 +11243,7 @@ var ImageResolver = class {
11358
11243
  * Encapsulates the terminateDueToUnavailable / shouldSkipForUnavailable
11359
11244
  * logic previously scattered as closures in reply-dispatcher.ts.
11360
11245
  */
11361
- const log$16 = larkLogger("card/unavailable-guard");
11246
+ const log$17 = larkLogger("card/unavailable-guard");
11362
11247
  var UnavailableGuard = class {
11363
11248
  terminated = false;
11364
11249
  replyToMessageId;
@@ -11411,7 +11296,7 @@ var UnavailableGuard = class {
11411
11296
  this.terminated = true;
11412
11297
  this.onTerminate();
11413
11298
  const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? "unknown";
11414
- log$16.warn("reply pipeline terminated by unavailable message", {
11299
+ log$17.warn("reply pipeline terminated by unavailable message", {
11415
11300
  source,
11416
11301
  apiCode,
11417
11302
  messageId: affectedMessageId
@@ -11448,7 +11333,7 @@ function getActiveCard(sessionId) {
11448
11333
  * Delegates throttling to FlushController and message-unavailable
11449
11334
  * detection to UnavailableGuard.
11450
11335
  */
11451
- const log$15 = larkLogger("card/streaming");
11336
+ const log$16 = larkLogger("card/streaming");
11452
11337
  var StreamingCardController = class StreamingCardController {
11453
11338
  phase = "idle";
11454
11339
  cardKit = {
@@ -11536,7 +11421,7 @@ var StreamingCardController = class StreamingCardController {
11536
11421
  }
11537
11422
  }
11538
11423
  if (!entry) {
11539
- log$15.debug("footer metrics lookup: session entry missing", {
11424
+ log$16.debug("footer metrics lookup: session entry missing", {
11540
11425
  sessionKey: this.deps.sessionKey,
11541
11426
  candidateKeys,
11542
11427
  storePath,
@@ -11554,7 +11439,7 @@ var StreamingCardController = class StreamingCardController {
11554
11439
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
11555
11440
  model: typeof entry.model === "string" ? entry.model : void 0
11556
11441
  };
11557
- log$15.debug("footer metrics lookup: session entry found", {
11442
+ log$16.debug("footer metrics lookup: session entry found", {
11558
11443
  sessionKey: this.deps.sessionKey,
11559
11444
  matchedKey,
11560
11445
  storePath,
@@ -11579,7 +11464,7 @@ var StreamingCardController = class StreamingCardController {
11579
11464
  }
11580
11465
  }
11581
11466
  if (!entry) {
11582
- log$15.debug("footer metrics lookup: session entry missing", {
11467
+ log$16.debug("footer metrics lookup: session entry missing", {
11583
11468
  sessionKey: this.deps.sessionKey,
11584
11469
  candidateKeys,
11585
11470
  storePath,
@@ -11597,7 +11482,7 @@ var StreamingCardController = class StreamingCardController {
11597
11482
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
11598
11483
  model: typeof entry.model === "string" ? entry.model : void 0
11599
11484
  };
11600
- log$15.debug("footer metrics lookup: session entry found", {
11485
+ log$16.debug("footer metrics lookup: session entry found", {
11601
11486
  sessionKey: this.deps.sessionKey,
11602
11487
  matchedKey,
11603
11488
  storePath,
@@ -11605,7 +11490,7 @@ var StreamingCardController = class StreamingCardController {
11605
11490
  });
11606
11491
  return metrics;
11607
11492
  } catch (err) {
11608
- log$15.warn("footer metrics lookup failed", {
11493
+ log$16.warn("footer metrics lookup failed", {
11609
11494
  error: String(err),
11610
11495
  sessionKey: this.deps.sessionKey
11611
11496
  });
@@ -11712,7 +11597,7 @@ var StreamingCardController = class StreamingCardController {
11712
11597
  const from = this.phase;
11713
11598
  if (from === to) return false;
11714
11599
  if (!PHASE_TRANSITIONS[from].has(to)) {
11715
- log$15.warn("phase transition rejected", {
11600
+ log$16.warn("phase transition rejected", {
11716
11601
  from,
11717
11602
  to,
11718
11603
  source
@@ -11720,7 +11605,7 @@ var StreamingCardController = class StreamingCardController {
11720
11605
  return false;
11721
11606
  }
11722
11607
  this.phase = to;
11723
- log$15.info("phase transition", {
11608
+ log$16.info("phase transition", {
11724
11609
  from,
11725
11610
  to,
11726
11611
  source,
@@ -11840,7 +11725,7 @@ var StreamingCardController = class StreamingCardController {
11840
11725
  this.reasoning.dirty = true;
11841
11726
  }
11842
11727
  const text = split.answerText ?? stripReasoningTags(rawText);
11843
- log$15.debug("onPartialReply", { len: text.length });
11728
+ log$16.debug("onPartialReply", { len: text.length });
11844
11729
  if (!text) return;
11845
11730
  this.captureToolUseElapsed();
11846
11731
  if (!this.reasoning.reasoningStartTime) this.reasoning.reasoningStartTime = Date.now();
@@ -11852,7 +11737,7 @@ var StreamingCardController = class StreamingCardController {
11852
11737
  this.text.lastPartialText = text;
11853
11738
  this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + "\n\n" + text : text;
11854
11739
  if (!this.text.streamingPrefix && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {
11855
- log$15.debug("onPartialReply: buffering NO_REPLY prefix");
11740
+ log$16.debug("onPartialReply: buffering NO_REPLY prefix");
11856
11741
  return;
11857
11742
  }
11858
11743
  await this.ensureCardCreated();
@@ -11862,7 +11747,7 @@ var StreamingCardController = class StreamingCardController {
11862
11747
  }
11863
11748
  async onError(err, info) {
11864
11749
  if (this.guard.terminate("onError", err)) return;
11865
- log$15.error(`${info.kind} reply failed`, { error: String(err) });
11750
+ log$16.error(`${info.kind} reply failed`, { error: String(err) });
11866
11751
  this.captureToolUseElapsed();
11867
11752
  this.finalizeCard("onError", "error");
11868
11753
  await this.flush.waitForFlush();
@@ -11918,7 +11803,7 @@ var StreamingCardController = class StreamingCardController {
11918
11803
  if (this.cardKit.cardMessageId) {
11919
11804
  const isNoReplyLeak = !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
11920
11805
  const displayText = this.text.completedText || (isNoReplyLeak ? "" : this.text.accumulatedText) || "Done.";
11921
- if (!this.text.completedText && !this.text.accumulatedText) log$15.warn("reply completed without visible text, using empty-reply fallback");
11806
+ if (!this.text.completedText && !this.text.accumulatedText) log$16.warn("reply completed without visible text, using empty-reply fallback");
11922
11807
  const resolvedDisplayText = await this.imageResolver.resolveImagesAwait(displayText, 15e3);
11923
11808
  const idleToolUseDisplay = this.computeToolUseDisplay();
11924
11809
  const terminalContent = prepareTerminalCardContent({
@@ -11946,7 +11831,7 @@ var StreamingCardController = class StreamingCardController {
11946
11831
  const rewindSessionId = this.deps.sessionKey;
11947
11832
  const rewindMessageId = ClaudeCodeAdapter.getInstance()?.getProcessManager()?.getLastUserMessageId(this.deps.sessionKey);
11948
11833
  const rewindHasFileChanges = this.hasFileChangingTools(idleToolUseDisplay?.steps);
11949
- log$15.info("onIdle: rewind button conditions", {
11834
+ log$16.info("onIdle: rewind button conditions", {
11950
11835
  sessionId: rewindSessionId,
11951
11836
  messageId: rewindMessageId,
11952
11837
  hasFileChanges: rewindHasFileChanges,
@@ -11959,7 +11844,7 @@ var StreamingCardController = class StreamingCardController {
11959
11844
  if (idleEffectiveCardId) {
11960
11845
  const seqBeforeClose = this.cardKit.cardKitSequence;
11961
11846
  this.cardKit.cardKitSequence += 1;
11962
- log$15.info("onIdle: closing streaming mode", {
11847
+ log$16.info("onIdle: closing streaming mode", {
11963
11848
  attempt,
11964
11849
  seqBefore: seqBeforeClose,
11965
11850
  seqAfter: this.cardKit.cardKitSequence
@@ -11973,7 +11858,7 @@ var StreamingCardController = class StreamingCardController {
11973
11858
  });
11974
11859
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
11975
11860
  this.cardKit.cardKitSequence += 1;
11976
- log$15.info("onIdle: updating final card", {
11861
+ log$16.info("onIdle: updating final card", {
11977
11862
  attempt,
11978
11863
  seqBefore: seqBeforeUpdate,
11979
11864
  seqAfter: this.cardKit.cardKitSequence
@@ -11991,33 +11876,33 @@ var StreamingCardController = class StreamingCardController {
11991
11876
  card: completeCard,
11992
11877
  accountId: this.deps.accountId
11993
11878
  });
11994
- log$15.info("reply completed, card finalized", {
11879
+ log$16.info("reply completed, card finalized", {
11995
11880
  elapsedMs: this.elapsed(),
11996
11881
  isCardKit: !!idleEffectiveCardId,
11997
11882
  attempt
11998
11883
  });
11999
11884
  break;
12000
11885
  } catch (retryErr) {
12001
- log$15.warn("final card update attempt failed", {
11886
+ log$16.warn("final card update attempt failed", {
12002
11887
  attempt,
12003
11888
  maxRetries: MAX_FINAL_UPDATE_RETRIES,
12004
11889
  error: String(retryErr)
12005
11890
  });
12006
11891
  if (attempt < MAX_FINAL_UPDATE_RETRIES) await new Promise((resolve) => setTimeout(resolve, FINAL_UPDATE_RETRY_DELAY_MS * attempt));
12007
- else log$15.error("final card update exhausted all retries, card may remain in streaming state", {
11892
+ else log$16.error("final card update exhausted all retries, card may remain in streaming state", {
12008
11893
  cardId: idleEffectiveCardId,
12009
11894
  messageId: this.cardKit.cardMessageId
12010
11895
  });
12011
11896
  }
12012
11897
  }
12013
11898
  } catch (err) {
12014
- log$15.error("final card update failed (outer)", { error: String(err) });
11899
+ log$16.error("final card update failed (outer)", { error: String(err) });
12015
11900
  } finally {
12016
11901
  clearToolUseTraceRun(this.deps.sessionKey);
12017
11902
  }
12018
11903
  }
12019
11904
  markFullyComplete() {
12020
- log$15.debug("markFullyComplete", {
11905
+ log$16.debug("markFullyComplete", {
12021
11906
  completedTextLen: this.text.completedText.length,
12022
11907
  accumulatedTextLen: this.text.accumulatedText.length
12023
11908
  });
@@ -12056,7 +11941,7 @@ var StreamingCardController = class StreamingCardController {
12056
11941
  footerMetrics
12057
11942
  });
12058
11943
  await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, "abortCard");
12059
- log$15.info("abortCard completed", { effectiveCardId });
11944
+ log$16.info("abortCard completed", { effectiveCardId });
12060
11945
  } else if (this.cardKit.cardMessageId) {
12061
11946
  const abortCard = buildCardContent("complete", {
12062
11947
  text: terminalContent.text,
@@ -12077,10 +11962,10 @@ var StreamingCardController = class StreamingCardController {
12077
11962
  card: abortCard,
12078
11963
  accountId: this.deps.accountId
12079
11964
  });
12080
- log$15.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
11965
+ log$16.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
12081
11966
  }
12082
11967
  } catch (err) {
12083
- log$15.warn("abortCard failed", { error: String(err) });
11968
+ log$16.warn("abortCard failed", { error: String(err) });
12084
11969
  } finally {
12085
11970
  clearToolUseTraceRun(this.deps.sessionKey);
12086
11971
  }
@@ -12095,7 +11980,7 @@ var StreamingCardController = class StreamingCardController {
12095
11980
  if (effectiveCardId) {
12096
11981
  const seqBeforeClose = this.cardKit.cardKitSequence;
12097
11982
  this.cardKit.cardKitSequence += 1;
12098
- log$15.info("replaceWithClarificationCard: closing streaming mode", {
11983
+ log$16.info("replaceWithClarificationCard: closing streaming mode", {
12099
11984
  cardId: effectiveCardId,
12100
11985
  seqBefore: seqBeforeClose,
12101
11986
  seqAfter: this.cardKit.cardKitSequence
@@ -12116,7 +12001,7 @@ var StreamingCardController = class StreamingCardController {
12116
12001
  sequence: this.cardKit.cardKitSequence,
12117
12002
  accountId: this.deps.accountId
12118
12003
  });
12119
- log$15.info("replaceWithClarificationCard: existing card replaced", {
12004
+ log$16.info("replaceWithClarificationCard: existing card replaced", {
12120
12005
  cardId: effectiveCardId,
12121
12006
  seqBefore: seqBeforeUpdate,
12122
12007
  seqAfter: this.cardKit.cardKitSequence
@@ -12139,7 +12024,7 @@ var StreamingCardController = class StreamingCardController {
12139
12024
  this.cardKit.cardKitCardId = cardId;
12140
12025
  this.cardKit.originalCardKitCardId = cardId;
12141
12026
  this.cardKit.cardMessageId = result.messageId;
12142
- log$15.info("replaceWithClarificationCard: new clarification card sent", {
12027
+ log$16.info("replaceWithClarificationCard: new clarification card sent", {
12143
12028
  cardId,
12144
12029
  messageId: result.messageId
12145
12030
  });
@@ -12148,7 +12033,7 @@ var StreamingCardController = class StreamingCardController {
12148
12033
  clearToolUseTraceRun(this.deps.sessionKey);
12149
12034
  return true;
12150
12035
  } catch (err) {
12151
- log$15.error("replaceWithClarificationCard failed", { error: String(err) });
12036
+ log$16.error("replaceWithClarificationCard failed", { error: String(err) });
12152
12037
  return false;
12153
12038
  }
12154
12039
  }
@@ -12162,10 +12047,10 @@ var StreamingCardController = class StreamingCardController {
12162
12047
  async forceCloseStreaming() {
12163
12048
  const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
12164
12049
  if (!effectiveCardId && !this.cardKit.cardMessageId) {
12165
- log$15.warn("forceCloseStreaming: no card to close");
12050
+ log$16.warn("forceCloseStreaming: no card to close");
12166
12051
  return;
12167
12052
  }
12168
- log$15.info("forceCloseStreaming: 强制终态化卡片", {
12053
+ log$16.info("forceCloseStreaming: 强制终态化卡片", {
12169
12054
  cardId: effectiveCardId,
12170
12055
  messageId: this.cardKit.cardMessageId,
12171
12056
  phase: this.phase
@@ -12215,10 +12100,10 @@ var StreamingCardController = class StreamingCardController {
12215
12100
  card: completeCard,
12216
12101
  accountId: this.deps.accountId
12217
12102
  });
12218
- log$15.info("forceCloseStreaming: 卡片已强制终态化");
12103
+ log$16.info("forceCloseStreaming: 卡片已强制终态化");
12219
12104
  if (!this.isTerminalPhase) this.finalizeCard("forceCloseStreaming", "abort");
12220
12105
  } catch (err) {
12221
- log$15.error("forceCloseStreaming failed", { error: String(err) });
12106
+ log$16.error("forceCloseStreaming failed", { error: String(err) });
12222
12107
  } finally {
12223
12108
  unregisterActiveCard(this.deps.sessionKey);
12224
12109
  clearToolUseTraceRun(this.deps.sessionKey);
@@ -12242,7 +12127,7 @@ var StreamingCardController = class StreamingCardController {
12242
12127
  this.disposeShutdownHook = null;
12243
12128
  return;
12244
12129
  }
12245
- log$15.info("reusing placeholder card", {
12130
+ log$16.info("reusing placeholder card", {
12246
12131
  cardId: this.deps.placeholderCardId,
12247
12132
  messageId: this.deps.placeholderMessageId
12248
12133
  });
@@ -12266,7 +12151,7 @@ var StreamingCardController = class StreamingCardController {
12266
12151
  accountId: this.deps.accountId
12267
12152
  });
12268
12153
  if (this.isStaleCreate(epoch)) {
12269
- log$15.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
12154
+ log$16.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
12270
12155
  epoch,
12271
12156
  phase: this.phase
12272
12157
  });
@@ -12277,7 +12162,7 @@ var StreamingCardController = class StreamingCardController {
12277
12162
  this.cardKit.originalCardKitCardId = cId;
12278
12163
  this.cardKit.cardKitSequence = 1;
12279
12164
  this.disposeShutdownHook = registerShutdownHook(`streaming-card:${cId}`, () => this.abortCard());
12280
- log$15.info("created CardKit entity", {
12165
+ log$16.info("created CardKit entity", {
12281
12166
  cardId: cId,
12282
12167
  initialSequence: this.cardKit.cardKitSequence
12283
12168
  });
@@ -12290,7 +12175,7 @@ var StreamingCardController = class StreamingCardController {
12290
12175
  accountId: this.deps.accountId
12291
12176
  });
12292
12177
  if (this.isStaleCreate(epoch)) {
12293
- log$15.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
12178
+ log$16.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
12294
12179
  epoch,
12295
12180
  phase: this.phase
12296
12181
  });
@@ -12305,13 +12190,13 @@ var StreamingCardController = class StreamingCardController {
12305
12190
  this.disposeShutdownHook = null;
12306
12191
  return;
12307
12192
  }
12308
- log$15.info("sent CardKit card", { messageId: result.messageId });
12193
+ log$16.info("sent CardKit card", { messageId: result.messageId });
12309
12194
  } else throw new Error("card.create returned empty card_id");
12310
12195
  } catch (cardKitErr) {
12311
12196
  if (this.isStaleCreate(epoch)) return;
12312
12197
  if (this.guard.terminate("ensureCardCreated.cardkitFlow", cardKitErr)) return;
12313
12198
  const apiDetail = extractApiDetail(cardKitErr);
12314
- log$15.warn("CardKit flow failed, falling back to IM", { apiDetail });
12199
+ log$16.warn("CardKit flow failed, falling back to IM", { apiDetail });
12315
12200
  this.cardKit.cardKitCardId = null;
12316
12201
  this.cardKit.originalCardKitCardId = null;
12317
12202
  const fallbackCard = buildCardContent("streaming", { showToolUse: this.deps.toolUseDisplay.showToolUse });
@@ -12324,7 +12209,7 @@ var StreamingCardController = class StreamingCardController {
12324
12209
  accountId: this.deps.accountId
12325
12210
  });
12326
12211
  if (this.isStaleCreate(epoch)) {
12327
- log$15.info("ensureCardCreated: stale epoch after IM fallback send, bailing out", {
12212
+ log$16.info("ensureCardCreated: stale epoch after IM fallback send, bailing out", {
12328
12213
  epoch,
12329
12214
  phase: this.phase
12330
12215
  });
@@ -12333,12 +12218,12 @@ var StreamingCardController = class StreamingCardController {
12333
12218
  this.cardKit.cardMessageId = result.messageId;
12334
12219
  this.flush.setCardMessageReady(true);
12335
12220
  if (!this.transition("streaming", "ensureCardCreated.imFallback")) return;
12336
- log$15.info("sent fallback IM card", { messageId: result.messageId });
12221
+ log$16.info("sent fallback IM card", { messageId: result.messageId });
12337
12222
  }
12338
12223
  } catch (err) {
12339
12224
  if (this.isStaleCreate(epoch)) return;
12340
12225
  if (this.guard.terminate("ensureCardCreated.outer", err)) return;
12341
- log$15.warn("thinking card failed, falling back to static", { error: String(err) });
12226
+ log$16.warn("thinking card failed, falling back to static", { error: String(err) });
12342
12227
  this.transition("creation_failed", "ensureCardCreated.outer", "creation_failed");
12343
12228
  }
12344
12229
  })();
@@ -12347,10 +12232,10 @@ var StreamingCardController = class StreamingCardController {
12347
12232
  async performFlush() {
12348
12233
  if (!this.cardKit.cardMessageId || this.isTerminalPhase) return;
12349
12234
  if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {
12350
- log$15.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
12235
+ log$16.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
12351
12236
  return;
12352
12237
  }
12353
- log$15.debug("flushCardUpdate: enter", {
12238
+ log$16.debug("flushCardUpdate: enter", {
12354
12239
  seq: this.cardKit.cardKitSequence,
12355
12240
  isCardKit: !!this.cardKit.cardKitCardId
12356
12241
  });
@@ -12372,7 +12257,7 @@ var StreamingCardController = class StreamingCardController {
12372
12257
  reasoningText: this.reasoning.accumulatedReasoningText || void 0
12373
12258
  });
12374
12259
  this.cardKit.cardKitSequence += 1;
12375
- log$15.debug("flushCardUpdate: full card update (dirty)", {
12260
+ log$16.debug("flushCardUpdate: full card update (dirty)", {
12376
12261
  seq: this.cardKit.cardKitSequence,
12377
12262
  stepCount: display?.stepCount,
12378
12263
  hasReasoning: !!this.reasoning.accumulatedReasoningText
@@ -12388,7 +12273,7 @@ var StreamingCardController = class StreamingCardController {
12388
12273
  } else if (resolvedText !== this.text.lastFlushedText) {
12389
12274
  const prevSeq = this.cardKit.cardKitSequence;
12390
12275
  this.cardKit.cardKitSequence += 1;
12391
- log$15.debug("flushCardUpdate: answer seq bump", {
12276
+ log$16.debug("flushCardUpdate: answer seq bump", {
12392
12277
  seqBefore: prevSeq,
12393
12278
  seqAfter: this.cardKit.cardKitSequence
12394
12279
  });
@@ -12403,7 +12288,7 @@ var StreamingCardController = class StreamingCardController {
12403
12288
  this.text.lastFlushedText = resolvedText;
12404
12289
  }
12405
12290
  } else {
12406
- log$15.debug("flushCardUpdate: IM patch fallback");
12291
+ log$16.debug("flushCardUpdate: IM patch fallback");
12407
12292
  const flushDisplay = this.computeToolUseDisplay();
12408
12293
  const card = buildCardContent("streaming", {
12409
12294
  text: this.reasoning.isReasoningPhase ? "" : resolvedText,
@@ -12423,31 +12308,31 @@ var StreamingCardController = class StreamingCardController {
12423
12308
  if (this.guard.terminate("flushCardUpdate", err)) return;
12424
12309
  const apiCode = extractLarkApiCode(err);
12425
12310
  if (isCardRateLimitError(err)) {
12426
- log$15.info("flushCardUpdate: rate limited (230020), skipping", { seq: this.cardKit.cardKitSequence });
12311
+ log$16.info("flushCardUpdate: rate limited (230020), skipping", { seq: this.cardKit.cardKitSequence });
12427
12312
  return;
12428
12313
  }
12429
12314
  if (isCardTableLimitError(err)) {
12430
- log$15.warn("flushCardUpdate: card table limit exceeded (230099/11310), disabling CardKit streaming", { seq: this.cardKit.cardKitSequence });
12315
+ log$16.warn("flushCardUpdate: card table limit exceeded (230099/11310), disabling CardKit streaming", { seq: this.cardKit.cardKitSequence });
12431
12316
  this.cardKit.cardKitCardId = null;
12432
12317
  return;
12433
12318
  }
12434
12319
  if (apiCode === 300317 && this.cardKit.cardKitCardId) {
12435
12320
  const oldSeq = this.cardKit.cardKitSequence;
12436
12321
  this.cardKit.cardKitSequence += 100;
12437
- log$15.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
12322
+ log$16.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
12438
12323
  oldSeq,
12439
12324
  newSeq: this.cardKit.cardKitSequence
12440
12325
  });
12441
12326
  return;
12442
12327
  }
12443
12328
  const apiDetail = extractApiDetail(err);
12444
- log$15.error("card stream update failed", {
12329
+ log$16.error("card stream update failed", {
12445
12330
  apiCode,
12446
12331
  seq: this.cardKit.cardKitSequence,
12447
12332
  apiDetail
12448
12333
  });
12449
12334
  if (this.cardKit.cardKitCardId) {
12450
- log$15.warn("disabling CardKit streaming, falling back to im.message.patch");
12335
+ log$16.warn("disabling CardKit streaming, falling back to im.message.patch");
12451
12336
  this.cardKit.cardKitCardId = null;
12452
12337
  }
12453
12338
  }
@@ -12472,7 +12357,7 @@ var StreamingCardController = class StreamingCardController {
12472
12357
  if (!this.cardKit.cardKitCardId || this.isTerminalPhase) return;
12473
12358
  try {
12474
12359
  const display = this.computeToolUseDisplay();
12475
- log$15.info("updateToolUseStatus", {
12360
+ log$16.info("updateToolUseStatus", {
12476
12361
  hasDisplay: !!display,
12477
12362
  stepCount: display?.stepCount,
12478
12363
  steps: display?.steps?.map((s) => `${s.title}:${s.status}`).join(", ")
@@ -12494,7 +12379,7 @@ var StreamingCardController = class StreamingCardController {
12494
12379
  accountId: this.deps.accountId
12495
12380
  });
12496
12381
  } catch (err) {
12497
- log$15.debug("updateToolUseStatus failed", { error: String(err) });
12382
+ log$16.debug("updateToolUseStatus failed", { error: String(err) });
12498
12383
  }
12499
12384
  }
12500
12385
  finalizeCard(source, reason) {
@@ -12505,7 +12390,7 @@ var StreamingCardController = class StreamingCardController {
12505
12390
  const from = this.phase;
12506
12391
  this.phase = "completed";
12507
12392
  this._terminalReason = "normal";
12508
- log$15.info("phase transition", {
12393
+ log$16.info("phase transition", {
12509
12394
  from,
12510
12395
  to: this.phase,
12511
12396
  source: "replaceWithClarificationCard",
@@ -12519,7 +12404,7 @@ var StreamingCardController = class StreamingCardController {
12519
12404
  async closeStreamingAndUpdate(cardId, card, label) {
12520
12405
  const seqBeforeClose = this.cardKit.cardKitSequence;
12521
12406
  this.cardKit.cardKitSequence += 1;
12522
- log$15.info(`${label}: closing streaming mode`, {
12407
+ log$16.info(`${label}: closing streaming mode`, {
12523
12408
  seqBefore: seqBeforeClose,
12524
12409
  seqAfter: this.cardKit.cardKitSequence
12525
12410
  });
@@ -12532,7 +12417,7 @@ var StreamingCardController = class StreamingCardController {
12532
12417
  });
12533
12418
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
12534
12419
  this.cardKit.cardKitSequence += 1;
12535
- log$15.info(`${label}: updating card`, {
12420
+ log$16.info(`${label}: updating card`, {
12536
12421
  seqBefore: seqBeforeUpdate,
12537
12422
  seqAfter: this.cardKit.cardKitSequence
12538
12423
  });
@@ -12566,7 +12451,7 @@ function extractApiDetail(err) {
12566
12451
  }
12567
12452
  //#endregion
12568
12453
  //#region src/messaging/format-for-cc.ts
12569
- const log$14 = larkLogger("messaging/format-for-cc");
12454
+ const log$15 = larkLogger("messaging/format-for-cc");
12570
12455
  function safeParseJSON(raw) {
12571
12456
  try {
12572
12457
  return JSON.parse(raw);
@@ -12622,7 +12507,7 @@ function extractPostText(rawContent) {
12622
12507
  */
12623
12508
  function formatContentByType(ctx) {
12624
12509
  const { contentType, content, resources } = ctx;
12625
- log$14.debug("formatContentByType 开始格式化", {
12510
+ log$15.debug("formatContentByType 开始格式化", {
12626
12511
  contentType,
12627
12512
  contentLength: content?.length ?? 0,
12628
12513
  resourceCount: resources?.length ?? 0
@@ -12664,7 +12549,7 @@ function formatContentByType(ctx) {
12664
12549
  case "todo":
12665
12550
  case "vote": return content;
12666
12551
  default:
12667
- log$14.warn("遇到不支持的消息类型", { contentType });
12552
+ log$15.warn("遇到不支持的消息类型", { contentType });
12668
12553
  return `[不支持的消息类型: ${contentType}]`;
12669
12554
  }
12670
12555
  }
@@ -12707,7 +12592,7 @@ function formatMessageTime(createTime) {
12707
12592
  * @returns 格式化后的纯文本字符串
12708
12593
  */
12709
12594
  function formatForCC(ctx, isGroup) {
12710
- log$14.info("formatForCC 开始处理", {
12595
+ log$15.info("formatForCC 开始处理", {
12711
12596
  messageId: ctx.messageId,
12712
12597
  contentType: ctx.contentType,
12713
12598
  chatType: ctx.chatType,
@@ -12723,7 +12608,7 @@ function formatForCC(ctx, isGroup) {
12723
12608
  if (isGroup && ctx.senderName) parts.push(`[${ctx.senderName}]`);
12724
12609
  if (ctx.threadId) parts.push("[话题回复]");
12725
12610
  const result = (parts.length > 0 ? parts.join(" ") + " " : "") + formattedContent;
12726
- log$14.info("formatForCC 完成", {
12611
+ log$15.info("formatForCC 完成", {
12727
12612
  messageId: ctx.messageId,
12728
12613
  resultLength: result.length,
12729
12614
  hasTime: !!timeStr,
@@ -12731,6 +12616,41 @@ function formatForCC(ctx, isGroup) {
12731
12616
  });
12732
12617
  return result;
12733
12618
  }
12619
+ function formatPromptMetadata(ctx) {
12620
+ const senderOpenId = ctx.rawSender.sender_id.open_id || ctx.senderId;
12621
+ const senderUserId = ctx.rawSender.sender_id.user_id;
12622
+ const fields = [
12623
+ `message_id=${ctx.messageId}`,
12624
+ `chat_id=${ctx.chatId}`,
12625
+ `chat_type=${ctx.chatType}`,
12626
+ `sender_open_id=${senderOpenId}`
12627
+ ];
12628
+ if (senderUserId) fields.push(`sender_user_id=${senderUserId}`);
12629
+ if (ctx.senderName) fields.push(`sender_name=${ctx.senderName}`);
12630
+ if (ctx.parentId) fields.push(`reply_to_message_id=${ctx.parentId}`);
12631
+ if (ctx.rootId) fields.push(`root_message_id=${ctx.rootId}`);
12632
+ if (ctx.threadId) fields.push(`thread_id=${ctx.threadId}`);
12633
+ const mentionedUsers = ctx.mentions.filter((mention) => !mention.isBot && mention.openId).map((mention) => `${mention.name || mention.openId}(open_id=${mention.openId})`);
12634
+ if (mentionedUsers.length > 0) fields.push(`mentioned_users=${mentionedUsers.join(", ")}`);
12635
+ return `[消息元数据] ${fields.join("; ")}`;
12636
+ }
12637
+ /**
12638
+ * Build the full text prompt sent to the agent runtime.
12639
+ *
12640
+ * Quoted/replied-to content is intentionally injected outside the parser:
12641
+ * resolving it requires Feishu API calls and can fail independently from
12642
+ * parsing the current event.
12643
+ */
12644
+ function formatPromptForCC(ctx, isGroup, quotedContent) {
12645
+ let currentMessage = formatForCC(ctx, isGroup);
12646
+ if (isGroup && ctx.threadId) currentMessage = `[群聊话题] ${currentMessage}`;
12647
+ else if (isGroup) currentMessage = `[群聊] ${currentMessage}`;
12648
+ else currentMessage = `[私聊] ${currentMessage}`;
12649
+ const quote = quotedContent?.trim();
12650
+ const metadata = formatPromptMetadata(ctx);
12651
+ if (!quote) return `${metadata}\n${currentMessage}`;
12652
+ return `${metadata}\n\n[引用/回复消息上下文]\n${quote}\n\n[当前消息]\n${currentMessage}`;
12653
+ }
12734
12654
  //#endregion
12735
12655
  //#region src/card/interactive-cards.ts
12736
12656
  /**
@@ -12746,7 +12666,7 @@ function formatForCC(ctx, isGroup) {
12746
12666
  * - CC 输出文本后处理(检测特殊标记)
12747
12667
  * - 操作 ID 生命周期管理
12748
12668
  */
12749
- const log$13 = larkLogger("card/interactive-cards");
12669
+ const log$14 = larkLogger("card/interactive-cards");
12750
12670
  const pendingOperations = /* @__PURE__ */ new Map();
12751
12671
  const OPERATION_TTL_MS = 1800 * 1e3;
12752
12672
  setInterval(() => {
@@ -12844,7 +12764,7 @@ function buildAuthCard(ctx) {
12844
12764
  scopes: ctx.scopes
12845
12765
  }
12846
12766
  });
12847
- log$13.info("已创建授权卡片操作", {
12767
+ log$14.info("已创建授权卡片操作", {
12848
12768
  operationId,
12849
12769
  scopes: ctx.scopes,
12850
12770
  sessionId: ctx.sessionId
@@ -12882,7 +12802,7 @@ function buildAskCard(ctx, phaseIndex = 0) {
12882
12802
  freeTextAnswers: {}
12883
12803
  }
12884
12804
  });
12885
- log$13.info("已创建提问卡片操作", {
12805
+ log$14.info("已创建提问卡片操作", {
12886
12806
  operationId,
12887
12807
  phaseId: phase?.id,
12888
12808
  phaseIndex,
@@ -13047,7 +12967,7 @@ function toggleAskSelection(operationId, phaseId, value) {
13047
12967
  else next = current[0] === value ? [] : [value];
13048
12968
  if (next.length > 0) op.askContext.answers[phaseId] = next;
13049
12969
  else delete op.askContext.answers[phaseId];
13050
- log$13.info("更新提问卡片勾选状态", {
12970
+ log$14.info("更新提问卡片勾选状态", {
13051
12971
  operationId,
13052
12972
  phaseId,
13053
12973
  selectedValues: next
@@ -13063,7 +12983,7 @@ function setAskFreeTextAnswer(operationId, phaseId, value) {
13063
12983
  const text = normalizeFreeText(value);
13064
12984
  if (text) op.askContext.freeTextAnswers[phaseId] = text;
13065
12985
  else delete op.askContext.freeTextAnswers[phaseId];
13066
- log$13.info("更新提问卡片自由文本", {
12986
+ log$14.info("更新提问卡片自由文本", {
13067
12987
  operationId,
13068
12988
  phaseId,
13069
12989
  hasText: text.length > 0
@@ -13307,7 +13227,7 @@ async function trySendClarificationFallback(params) {
13307
13227
  }
13308
13228
  //#endregion
13309
13229
  //#region src/messaging/inbound/dispatch-cc.ts
13310
- const log$12 = larkLogger("inbound/dispatch-cc");
13230
+ const log$13 = larkLogger("inbound/dispatch-cc");
13311
13231
  function buildFeishuRuntimeConfig(params) {
13312
13232
  const { ctx, account, route } = params;
13313
13233
  const senderUserId = ctx.rawSender.sender_id.user_id;
@@ -13364,7 +13284,7 @@ async function dispatchToCC(params) {
13364
13284
  const ccModel = params.model;
13365
13285
  const maxTurns = params.maxTurns;
13366
13286
  const maxBudgetUsd = params.maxBudgetUsd;
13367
- log$12.info("dispatchToCC 开始", {
13287
+ log$13.info("dispatchToCC 开始", {
13368
13288
  msgId,
13369
13289
  chatId,
13370
13290
  chatType,
@@ -13377,12 +13297,12 @@ async function dispatchToCC(params) {
13377
13297
  threadId,
13378
13298
  userId: ctx.senderId
13379
13299
  });
13380
- log$12.info("会话路由解析完成", {
13300
+ log$13.info("会话路由解析完成", {
13381
13301
  sessionId: route.sessionId,
13382
13302
  cwd: route.cwd,
13383
13303
  type: route.type
13384
13304
  });
13385
- if (params.userContext) log$12.info("用户上下文已注入", {
13305
+ if (params.userContext) log$13.info("用户上下文已注入", {
13386
13306
  userId: params.userContext.userId,
13387
13307
  isTenantIdentity: params.userContext.isTenantIdentity,
13388
13308
  credentialDir: params.userContext.credentialDir
@@ -13391,10 +13311,7 @@ async function dispatchToCC(params) {
13391
13311
  const isGroup = chatType === "group";
13392
13312
  const replyInThread = !!threadId;
13393
13313
  const replyToMessageId = threadId ? void 0 : ctx.messageId;
13394
- let textPrompt = formatForCC(ctx, isGroup);
13395
- if (isGroup && replyInThread) textPrompt = `[群聊话题] ${textPrompt}`;
13396
- else if (isGroup) textPrompt = `[群聊] ${textPrompt}`;
13397
- else textPrompt = `[私聊] ${textPrompt}`;
13314
+ const textPrompt = formatPromptForCC(ctx, isGroup, params.quotedContent);
13398
13315
  const imageResources = ctx.resources.filter((r) => r.type === "image");
13399
13316
  let prompt = textPrompt;
13400
13317
  if (imageResources.length > 0) {
@@ -13421,7 +13338,7 @@ async function dispatchToCC(params) {
13421
13338
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13422
13339
  buffer = Buffer.concat(chunks);
13423
13340
  } else {
13424
- log$12.warn("图片资源下载返回未知格式,跳过", {
13341
+ log$13.warn("图片资源下载返回未知格式,跳过", {
13425
13342
  fileKey: imgRes.fileKey,
13426
13343
  responseType: typeof response
13427
13344
  });
@@ -13429,7 +13346,7 @@ async function dispatchToCC(params) {
13429
13346
  }
13430
13347
  if (resp.headers?.["content-type"]) mediaType = resp.headers["content-type"];
13431
13348
  } else {
13432
- log$12.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
13349
+ log$13.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
13433
13350
  continue;
13434
13351
  }
13435
13352
  const imgFileName = `${imgRes.fileKey}.png`;
@@ -13439,13 +13356,13 @@ async function dispatchToCC(params) {
13439
13356
  const { mkdir, writeFile } = await import("node:fs/promises");
13440
13357
  await mkdir(filesDir, { recursive: true });
13441
13358
  await writeFile(imgFilePath, buffer);
13442
- log$12.info("图片已保存到工作目录", {
13359
+ log$13.info("图片已保存到工作目录", {
13443
13360
  fileKey: imgRes.fileKey,
13444
13361
  path: imgFilePath,
13445
13362
  sizeBytes: buffer.length
13446
13363
  });
13447
13364
  } catch (saveErr) {
13448
- log$12.warn("图片保存到工作目录失败", {
13365
+ log$13.warn("图片保存到工作目录失败", {
13449
13366
  fileKey: imgRes.fileKey,
13450
13367
  path: imgFilePath,
13451
13368
  error: saveErr instanceof Error ? saveErr.message : String(saveErr)
@@ -13460,13 +13377,13 @@ async function dispatchToCC(params) {
13460
13377
  data: base64Data
13461
13378
  }
13462
13379
  });
13463
- log$12.info("图片资源下载成功", {
13380
+ log$13.info("图片资源下载成功", {
13464
13381
  fileKey: imgRes.fileKey,
13465
13382
  mediaType,
13466
13383
  sizeBytes: buffer.length
13467
13384
  });
13468
13385
  } catch (err) {
13469
- log$12.warn("图片资源下载失败,跳过", {
13386
+ log$13.warn("图片资源下载失败,跳过", {
13470
13387
  fileKey: imgRes.fileKey,
13471
13388
  error: err instanceof Error ? err.message : String(err)
13472
13389
  });
@@ -13476,7 +13393,7 @@ async function dispatchToCC(params) {
13476
13393
  type: "text",
13477
13394
  text: textPrompt
13478
13395
  }, ...imageBlocks];
13479
- log$12.info("已构建多模态 prompt", {
13396
+ log$13.info("已构建多模态 prompt", {
13480
13397
  textLength: textPrompt.length,
13481
13398
  imageCount: imageBlocks.length
13482
13399
  });
@@ -13484,12 +13401,12 @@ async function dispatchToCC(params) {
13484
13401
  const textWithoutImageRefs = textPrompt.replace(/!\[image\]\([^)]*\)/g, "").replace(/\[图片\]\s*/g, "").trim();
13485
13402
  if (textWithoutImageRefs.length > 0) {
13486
13403
  prompt = textPrompt.replace(/!\[image\]\([^)]*\)/g, "[图片加载失败]");
13487
- log$12.info("图片下载失败但保留文字内容继续调度", {
13404
+ log$13.info("图片下载失败但保留文字内容继续调度", {
13488
13405
  messageId: ctx.messageId,
13489
13406
  textLength: textWithoutImageRefs.length
13490
13407
  });
13491
13408
  } else {
13492
- log$12.warn("纯图片消息且图片全部下载失败,中止调度", {
13409
+ log$13.warn("纯图片消息且图片全部下载失败,中止调度", {
13493
13410
  messageId: ctx.messageId,
13494
13411
  expectedImages: imageResources.length
13495
13412
  });
@@ -13511,7 +13428,7 @@ async function dispatchToCC(params) {
13511
13428
  await fsMkdir(filesDir, { recursive: true });
13512
13429
  let updatedTextPrompt = typeof prompt === "string" ? prompt : prompt[0].text;
13513
13430
  for (const res of attachmentResources) try {
13514
- log$12.info("开始下载非图片附件", {
13431
+ log$13.info("开始下载非图片附件", {
13515
13432
  type: res.type,
13516
13433
  fileKey: res.fileKey,
13517
13434
  fileName: res.fileName,
@@ -13525,7 +13442,7 @@ async function dispatchToCC(params) {
13525
13442
  },
13526
13443
  params: { type: "file" }
13527
13444
  });
13528
- log$12.info("非图片附件 API 响应已收到", {
13445
+ log$13.info("非图片附件 API 响应已收到", {
13529
13446
  type: res.type,
13530
13447
  fileKey: res.fileKey,
13531
13448
  responseType: typeof response,
@@ -13544,7 +13461,7 @@ async function dispatchToCC(params) {
13544
13461
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13545
13462
  buffer = Buffer.concat(chunks);
13546
13463
  } else {
13547
- log$12.warn("非图片附件下载返回未知格式,跳过", {
13464
+ log$13.warn("非图片附件下载返回未知格式,跳过", {
13548
13465
  type: res.type,
13549
13466
  fileKey: res.fileKey,
13550
13467
  responseType: typeof response
@@ -13552,7 +13469,7 @@ async function dispatchToCC(params) {
13552
13469
  continue;
13553
13470
  }
13554
13471
  } else {
13555
- log$12.warn("非图片附件下载返回空,跳过", {
13472
+ log$13.warn("非图片附件下载返回空,跳过", {
13556
13473
  type: res.type,
13557
13474
  fileKey: res.fileKey
13558
13475
  });
@@ -13575,7 +13492,7 @@ async function dispatchToCC(params) {
13575
13492
  }
13576
13493
  const filePath = path.join(filesDir, savedFileName);
13577
13494
  await fsWriteFile(filePath, buffer);
13578
- log$12.info("非图片附件已保存到工作目录", {
13495
+ log$13.info("非图片附件已保存到工作目录", {
13579
13496
  type: res.type,
13580
13497
  fileKey: res.fileKey,
13581
13498
  savedFileName,
@@ -13609,7 +13526,7 @@ async function dispatchToCC(params) {
13609
13526
  }
13610
13527
  }
13611
13528
  } catch (err) {
13612
- log$12.warn("非图片附件下载失败,替换为降级描述", {
13529
+ log$13.warn("非图片附件下载失败,替换为降级描述", {
13613
13530
  type: res.type,
13614
13531
  fileKey: res.fileKey,
13615
13532
  error: err instanceof Error ? err.message : String(err)
@@ -13628,12 +13545,12 @@ async function dispatchToCC(params) {
13628
13545
  const textBlock = prompt.find((b) => b.type === "text");
13629
13546
  if (textBlock) textBlock.text = updatedTextPrompt;
13630
13547
  }
13631
- log$12.info("非图片附件处理完成", {
13548
+ log$13.info("非图片附件处理完成", {
13632
13549
  totalAttachments: attachmentResources.length,
13633
13550
  promptLength: typeof prompt === "string" ? prompt.length : prompt.find((b) => b.type === "text")?.text?.length
13634
13551
  });
13635
13552
  }
13636
- log$12.info("消息格式化完成", {
13553
+ log$13.info("消息格式化完成", {
13637
13554
  promptLength: typeof prompt === "string" ? prompt.length : prompt.length,
13638
13555
  isMultimodal: Array.isArray(prompt),
13639
13556
  isGroup
@@ -13665,19 +13582,19 @@ async function dispatchToCC(params) {
13665
13582
  };
13666
13583
  const cardController = new StreamingCardController(cardDeps);
13667
13584
  registerActiveCard(route.sessionId, cardController);
13668
- log$12.info("StreamingCardController 已创建", {
13585
+ log$13.info("StreamingCardController 已创建", {
13669
13586
  sessionId: route.sessionId,
13670
13587
  chatId,
13671
13588
  replyToMessageId: cardDeps.replyToMessageId
13672
13589
  });
13673
13590
  cardController.ensureCardCreated().catch((err) => {
13674
- log$12.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
13591
+ log$13.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
13675
13592
  });
13676
13593
  let promptForRuntime = prompt;
13677
13594
  try {
13678
13595
  promptForRuntime = await new MemoryContextBuilder().injectPrompt(route.cwd, prompt, promptToText(prompt));
13679
13596
  } catch (err) {
13680
- log$12.warn("memory context 注入失败,继续使用原始 prompt", {
13597
+ log$13.warn("memory context 注入失败,继续使用原始 prompt", {
13681
13598
  sessionId: route.sessionId,
13682
13599
  error: err instanceof Error ? err.message : String(err)
13683
13600
  });
@@ -13698,7 +13615,7 @@ async function dispatchToCC(params) {
13698
13615
  });
13699
13616
  } catch (err) {
13700
13617
  const errorMessage = err instanceof Error ? err.message : String(err);
13701
- log$12.error("飞书事件 run context 构造失败,终止调度", {
13618
+ log$13.error("飞书事件 run context 构造失败,终止调度", {
13702
13619
  sessionId: route.sessionId,
13703
13620
  messageId: msgId,
13704
13621
  error: errorMessage
@@ -13716,7 +13633,7 @@ async function dispatchToCC(params) {
13716
13633
  channel: "feishu-bot",
13717
13634
  title: userContent.slice(0, 30) + (userContent.length > 30 ? "..." : "")
13718
13635
  }).catch((err) => {
13719
- log$12.warn("创建飞书端会话记录失败", { error: String(err) });
13636
+ log$13.warn("创建飞书端会话记录失败", { error: String(err) });
13720
13637
  });
13721
13638
  params.messageStore.appendMessage({
13722
13639
  sessionId: storeSessionId,
@@ -13730,14 +13647,14 @@ async function dispatchToCC(params) {
13730
13647
  scenarioId: runtimeConfig.runContext?.scenarioId
13731
13648
  }
13732
13649
  }).catch((err) => {
13733
- log$12.warn("记录飞书用户消息到 store 失败", { error: String(err) });
13650
+ log$13.warn("记录飞书用户消息到 store 失败", { error: String(err) });
13734
13651
  });
13735
13652
  }
13736
13653
  const bridge = new CCStreamBridge(cardController, {
13737
13654
  autoCompleteOnTurnEnd: true,
13738
13655
  sessionKey: route.sessionId
13739
13656
  });
13740
- log$12.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
13657
+ log$13.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
13741
13658
  let feishuAccumText = "";
13742
13659
  let usedAskUserTool = false;
13743
13660
  let pendingEndTurn = null;
@@ -13759,12 +13676,12 @@ async function dispatchToCC(params) {
13759
13676
  ...metadata
13760
13677
  }
13761
13678
  }).catch((err) => {
13762
- log$12.warn("记录飞书 assistant 消息到 store 失败", { error: String(err) });
13679
+ log$13.warn("记录飞书 assistant 消息到 store 失败", { error: String(err) });
13763
13680
  });
13764
13681
  };
13765
13682
  const appendRuntimeEventMessage = (event) => {
13766
13683
  const eventSummary = summarizeForAudit(event);
13767
- log$12.info("CC onRuntimeEvent", {
13684
+ log$13.info("CC onRuntimeEvent", {
13768
13685
  sessionId: route.sessionId,
13769
13686
  type: event.type,
13770
13687
  traceId: runtimeConfig.runContext?.traceId,
@@ -13802,11 +13719,11 @@ async function dispatchToCC(params) {
13802
13719
  finalStatus: event.finalStatus
13803
13720
  }
13804
13721
  }).catch((err) => {
13805
- log$12.warn("记录飞书 runtime event 到 store 失败", { error: String(err) });
13722
+ log$13.warn("记录飞书 runtime event 到 store 失败", { error: String(err) });
13806
13723
  });
13807
13724
  };
13808
13725
  const handleResult = async (result) => {
13809
- log$12.info("CC onResult", {
13726
+ log$13.info("CC onResult", {
13810
13727
  sessionId: route.sessionId,
13811
13728
  subtype: result.subtype,
13812
13729
  isError: result.isError,
@@ -13834,13 +13751,13 @@ async function dispatchToCC(params) {
13834
13751
  })
13835
13752
  });
13836
13753
  } catch (err) {
13837
- log$12.warn("clarification gate: 兜底处理失败,回退到普通回复", {
13754
+ log$13.warn("clarification gate: 兜底处理失败,回退到普通回复", {
13838
13755
  sessionId: route.sessionId,
13839
13756
  error: err instanceof Error ? err.message : String(err)
13840
13757
  });
13841
13758
  }
13842
13759
  if (clarificationFallback.handled) {
13843
- log$12.info("clarification gate: 普通文本提问已转为提问卡片", {
13760
+ log$13.info("clarification gate: 普通文本提问已转为提问卡片", {
13844
13761
  sessionId: route.sessionId,
13845
13762
  operationId: clarificationFallback.operationId,
13846
13763
  reason: clarificationFallback.reason,
@@ -13848,7 +13765,7 @@ async function dispatchToCC(params) {
13848
13765
  });
13849
13766
  return;
13850
13767
  }
13851
- if (clarificationFallback.matched) log$12.warn("clarification gate: 提问卡片替换失败,回退到普通回复", {
13768
+ if (clarificationFallback.matched) log$13.warn("clarification gate: 提问卡片替换失败,回退到普通回复", {
13852
13769
  sessionId: route.sessionId,
13853
13770
  operationId: clarificationFallback.operationId
13854
13771
  });
@@ -13861,7 +13778,7 @@ async function dispatchToCC(params) {
13861
13778
  };
13862
13779
  const handleBlockedEvent = (event) => {
13863
13780
  const sanitized = sanitizeBlockedEvent(event);
13864
- log$12.warn("CC onBlockedEvent", {
13781
+ log$13.warn("CC onBlockedEvent", {
13865
13782
  ...sanitized,
13866
13783
  traceId: runtimeConfig.runContext?.traceId,
13867
13784
  scenarioId: runtimeConfig.runContext?.scenarioId
@@ -13869,7 +13786,7 @@ async function dispatchToCC(params) {
13869
13786
  };
13870
13787
  const callbacks = {
13871
13788
  onTextDelta: (text) => {
13872
- log$12.debug("CC onTextDelta", {
13789
+ log$13.debug("CC onTextDelta", {
13873
13790
  sessionId: route.sessionId,
13874
13791
  deltaLen: text.length
13875
13792
  });
@@ -13877,14 +13794,14 @@ async function dispatchToCC(params) {
13877
13794
  feishuAccumText += text;
13878
13795
  },
13879
13796
  onThinkingDelta: (text) => {
13880
- log$12.debug("CC onThinkingDelta", {
13797
+ log$13.debug("CC onThinkingDelta", {
13881
13798
  sessionId: route.sessionId,
13882
13799
  deltaLen: text.length
13883
13800
  });
13884
13801
  bridge.onThinkingDelta(text);
13885
13802
  },
13886
13803
  onToolUseStart: (toolName, toolInput) => {
13887
- log$12.info("CC onToolUseStart", {
13804
+ log$13.info("CC onToolUseStart", {
13888
13805
  sessionId: route.sessionId,
13889
13806
  toolName
13890
13807
  });
@@ -13892,7 +13809,7 @@ async function dispatchToCC(params) {
13892
13809
  bridge.onToolUseStart(toolName, toolInput);
13893
13810
  },
13894
13811
  onToolResult: (toolUseId, result) => {
13895
- log$12.info("CC onToolResult", {
13812
+ log$13.info("CC onToolResult", {
13896
13813
  sessionId: route.sessionId,
13897
13814
  toolUseId,
13898
13815
  traceId: runtimeConfig.runContext?.traceId,
@@ -13901,7 +13818,7 @@ async function dispatchToCC(params) {
13901
13818
  bridge.onToolResult(toolUseId);
13902
13819
  },
13903
13820
  onToolProgress: (toolName, elapsedSeconds) => {
13904
- log$12.debug("CC onToolProgress", {
13821
+ log$13.debug("CC onToolProgress", {
13905
13822
  sessionId: route.sessionId,
13906
13823
  toolName,
13907
13824
  elapsedSeconds
@@ -13909,7 +13826,7 @@ async function dispatchToCC(params) {
13909
13826
  bridge.onToolProgress(toolName, elapsedSeconds);
13910
13827
  },
13911
13828
  onTurnEnd: (stopReason) => {
13912
- log$12.info("CC onTurnEnd", {
13829
+ log$13.info("CC onTurnEnd", {
13913
13830
  sessionId: route.sessionId,
13914
13831
  stopReason
13915
13832
  });
@@ -13921,14 +13838,14 @@ async function dispatchToCC(params) {
13921
13838
  },
13922
13839
  onResult: (result) => {
13923
13840
  resultHandlingPromise = handleResult(result).catch((err) => {
13924
- log$12.error("CC onResult 收尾处理失败", {
13841
+ log$13.error("CC onResult 收尾处理失败", {
13925
13842
  sessionId: route.sessionId,
13926
13843
  error: err instanceof Error ? err.message : String(err)
13927
13844
  });
13928
13845
  });
13929
13846
  },
13930
13847
  onError: (error) => {
13931
- log$12.error("CC 进程错误", {
13848
+ log$13.error("CC 进程错误", {
13932
13849
  sessionId: route.sessionId,
13933
13850
  traceId: runtimeConfig.runContext?.traceId,
13934
13851
  error: error.message
@@ -13936,7 +13853,7 @@ async function dispatchToCC(params) {
13936
13853
  cardController.onError(error, { kind: "cc-process" });
13937
13854
  },
13938
13855
  onAuditEvent: (event) => {
13939
- log$12.info("CC onAuditEvent", {
13856
+ log$13.info("CC onAuditEvent", {
13940
13857
  ...event,
13941
13858
  inputSummary: summarizeForAudit(event.inputSummary),
13942
13859
  resultSummary: summarizeForAudit(event.resultSummary)
@@ -13947,7 +13864,7 @@ async function dispatchToCC(params) {
13947
13864
  onBlocked: handleBlockedEvent,
13948
13865
  onBlockedEvent: handleBlockedEvent
13949
13866
  };
13950
- log$12.info("开始执行 CC 进程", {
13867
+ log$13.info("开始执行 CC 进程", {
13951
13868
  sessionId: route.sessionId,
13952
13869
  cwd: route.cwd,
13953
13870
  promptLength: prompt.length,
@@ -13970,11 +13887,11 @@ async function dispatchToCC(params) {
13970
13887
  onMcpCallback.on(`callback:${route.sessionId}`, mcpCallbackListener);
13971
13888
  try {
13972
13889
  await processManager.executePrompt(runtimeConfig, callbacks);
13973
- log$12.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
13890
+ log$13.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
13974
13891
  if (resultHandlingPromise) await resultHandlingPromise;
13975
13892
  } catch (err) {
13976
13893
  const errorMessage = err instanceof Error ? err.message : String(err);
13977
- log$12.error("CC 进程 executePrompt 调用异常", {
13894
+ log$13.error("CC 进程 executePrompt 调用异常", {
13978
13895
  sessionId: route.sessionId,
13979
13896
  error: errorMessage
13980
13897
  });
@@ -13998,7 +13915,7 @@ async function dispatchToCC(params) {
13998
13915
  */
13999
13916
  async function dispatchTeammateEval(params) {
14000
13917
  const { chatId, prompt, account, sessionRouter, processManager } = params;
14001
- log$12.info("dispatchTeammateEval 开始", {
13918
+ log$13.info("dispatchTeammateEval 开始", {
14002
13919
  chatId,
14003
13920
  promptLength: prompt.length
14004
13921
  });
@@ -14011,7 +13928,7 @@ async function dispatchTeammateEval(params) {
14011
13928
  userName: void 0
14012
13929
  });
14013
13930
  if (processManager.isSessionBusy?.(route.sessionId)) {
14014
- log$12.info("dispatchTeammateEval 跳过:主 session 正在执行", {
13931
+ log$13.info("dispatchTeammateEval 跳过:主 session 正在执行", {
14015
13932
  chatId,
14016
13933
  sessionId: route.sessionId
14017
13934
  });
@@ -14074,7 +13991,7 @@ async function dispatchTeammateEval(params) {
14074
13991
  }
14075
13992
  });
14076
13993
  } catch (err) {
14077
- log$12.error("teammate run context 构造失败,跳过评估", {
13994
+ log$13.error("teammate run context 构造失败,跳过评估", {
14078
13995
  chatId,
14079
13996
  error: err instanceof Error ? err.message : String(err)
14080
13997
  });
@@ -14098,7 +14015,7 @@ async function dispatchTeammateEval(params) {
14098
14015
  sourceRefs: params.sourceRefs ?? (params.requestId ? [params.requestId] : [])
14099
14016
  });
14100
14017
  if (evalDecision.decision === "NO_REPLY") {
14101
- log$12.info("teammate evaluator 决定静默", {
14018
+ log$13.info("teammate evaluator 决定静默", {
14102
14019
  chatId,
14103
14020
  evalSessionId,
14104
14021
  summary: evalDecision.summary.slice(0, 120)
@@ -14132,7 +14049,7 @@ async function dispatchTeammateEval(params) {
14132
14049
  const confirmReply = () => {
14133
14050
  if (confirmed) return;
14134
14051
  confirmed = true;
14135
- log$12.info("teammate 确认回复,创建流式卡片", {
14052
+ log$13.info("teammate 确认回复,创建流式卡片", {
14136
14053
  chatId,
14137
14054
  thinkingLen: thinkingBuffer.length,
14138
14055
  textLen: fullTextBuffer.length
@@ -14189,7 +14106,7 @@ async function dispatchTeammateEval(params) {
14189
14106
  fullTextBuffer += text;
14190
14107
  if (fullTextBuffer.trimStart().startsWith(NO_REPLY_TOKEN)) {
14191
14108
  silenced = true;
14192
- log$12.info("teammate 前缀检测: 检测到 NO_REPLY 首输出,立即静默", {
14109
+ log$13.info("teammate 前缀检测: 检测到 NO_REPLY 首输出,立即静默", {
14193
14110
  chatId,
14194
14111
  bufferLen: fullTextBuffer.length
14195
14112
  });
@@ -14222,7 +14139,7 @@ async function dispatchTeammateEval(params) {
14222
14139
  toolName: currentToolName,
14223
14140
  toolCallId: toolUseId
14224
14141
  });
14225
- log$12.info("teammate onToolResult", {
14142
+ log$13.info("teammate onToolResult", {
14226
14143
  chatId,
14227
14144
  toolUseId,
14228
14145
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14240,7 +14157,7 @@ async function dispatchTeammateEval(params) {
14240
14157
  onTurnEnd: (stopReason) => {
14241
14158
  finalStopReason = stopReason;
14242
14159
  if (stopReason === "tool_use") {
14243
- log$12.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
14160
+ log$13.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
14244
14161
  if (confirmed && bridge) bridge.onTurnEnd(stopReason);
14245
14162
  return;
14246
14163
  }
@@ -14248,7 +14165,7 @@ async function dispatchTeammateEval(params) {
14248
14165
  const trimmed = fullTextBuffer.trim();
14249
14166
  if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed.endsWith(NO_REPLY_TOKEN) || trimmed === "") {
14250
14167
  silenced = true;
14251
- log$12.info("teammate turnEnd 最终判断: 静默", {
14168
+ log$13.info("teammate turnEnd 最终判断: 静默", {
14252
14169
  chatId,
14253
14170
  trimmed: trimmed.slice(0, 60)
14254
14171
  });
@@ -14263,7 +14180,7 @@ async function dispatchTeammateEval(params) {
14263
14180
  resolve();
14264
14181
  },
14265
14182
  onError: (error) => {
14266
- log$12.error("teammate 评估 CC 进程错误", {
14183
+ log$13.error("teammate 评估 CC 进程错误", {
14267
14184
  chatId,
14268
14185
  traceId: mainRuntimeConfig.runContext?.traceId,
14269
14186
  error: error.message
@@ -14271,14 +14188,14 @@ async function dispatchTeammateEval(params) {
14271
14188
  resolve();
14272
14189
  },
14273
14190
  onAuditEvent: (event) => {
14274
- log$12.info("teammate onAuditEvent", {
14191
+ log$13.info("teammate onAuditEvent", {
14275
14192
  ...event,
14276
14193
  inputSummary: summarizeForAudit(event.inputSummary),
14277
14194
  resultSummary: summarizeForAudit(event.resultSummary)
14278
14195
  });
14279
14196
  },
14280
14197
  onRuntimeEvent: (event) => {
14281
- log$12.info("teammate onRuntimeEvent", {
14198
+ log$13.info("teammate onRuntimeEvent", {
14282
14199
  type: event.type,
14283
14200
  chatId,
14284
14201
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14289,7 +14206,7 @@ async function dispatchTeammateEval(params) {
14289
14206
  });
14290
14207
  },
14291
14208
  onScenarioEvent: (event) => {
14292
- log$12.info("teammate onScenarioEvent", {
14209
+ log$13.info("teammate onScenarioEvent", {
14293
14210
  type: event.type,
14294
14211
  chatId,
14295
14212
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14301,28 +14218,28 @@ async function dispatchTeammateEval(params) {
14301
14218
  },
14302
14219
  onBlocked: (event) => {
14303
14220
  const sanitized = sanitizeBlockedEvent(event);
14304
- log$12.warn("teammate onBlocked", {
14221
+ log$13.warn("teammate onBlocked", {
14305
14222
  ...sanitized,
14306
14223
  traceId: mainRuntimeConfig.runContext?.traceId
14307
14224
  });
14308
14225
  },
14309
14226
  onBlockedEvent: (event) => {
14310
14227
  const sanitized = sanitizeBlockedEvent(event);
14311
- log$12.warn("teammate onBlockedEvent", {
14228
+ log$13.warn("teammate onBlockedEvent", {
14312
14229
  ...sanitized,
14313
14230
  traceId: mainRuntimeConfig.runContext?.traceId
14314
14231
  });
14315
14232
  }
14316
14233
  };
14317
14234
  processManager.executePrompt(mainRuntimeConfig, callbacks).catch((err) => {
14318
- log$12.error("teammate executePrompt 异常", {
14235
+ log$13.error("teammate executePrompt 异常", {
14319
14236
  chatId,
14320
14237
  error: err instanceof Error ? err.message : String(err)
14321
14238
  });
14322
14239
  resolve();
14323
14240
  });
14324
14241
  });
14325
- log$12.info("dispatchTeammateEval 完成", {
14242
+ log$13.info("dispatchTeammateEval 完成", {
14326
14243
  chatId,
14327
14244
  confirmed,
14328
14245
  silenced,
@@ -14333,7 +14250,7 @@ async function dispatchTeammateEval(params) {
14333
14250
  return { replied: confirmed };
14334
14251
  } catch (err) {
14335
14252
  const errorMessage = err instanceof Error ? err.message : String(err);
14336
- log$12.error("dispatchTeammateEval 异常", {
14253
+ log$13.error("dispatchTeammateEval 异常", {
14337
14254
  chatId,
14338
14255
  error: errorMessage
14339
14256
  });
@@ -14357,7 +14274,7 @@ async function runTeammateDecisionEval(params) {
14357
14274
  resolve();
14358
14275
  },
14359
14276
  onError: (error) => {
14360
- log$12.error("teammate evaluator 执行错误", {
14277
+ log$13.error("teammate evaluator 执行错误", {
14361
14278
  chatId,
14362
14279
  sessionId: runtimeConfig.sessionId,
14363
14280
  error: error.message
@@ -14365,21 +14282,21 @@ async function runTeammateDecisionEval(params) {
14365
14282
  resolve();
14366
14283
  },
14367
14284
  onToolUseStart: (toolName, toolInput) => {
14368
- log$12.info("teammate evaluator toolUseStart", {
14285
+ log$13.info("teammate evaluator toolUseStart", {
14369
14286
  chatId,
14370
14287
  toolName,
14371
14288
  toolInput: summarizeForAudit(toolInput)
14372
14289
  });
14373
14290
  },
14374
14291
  onToolResult: (toolUseId, result) => {
14375
- log$12.info("teammate evaluator toolResult", {
14292
+ log$13.info("teammate evaluator toolResult", {
14376
14293
  chatId,
14377
14294
  toolUseId,
14378
14295
  result: summarizeForAudit(result)
14379
14296
  });
14380
14297
  }
14381
14298
  }).catch((err) => {
14382
- log$12.error("teammate evaluator executePrompt 异常", {
14299
+ log$13.error("teammate evaluator executePrompt 异常", {
14383
14300
  chatId,
14384
14301
  sessionId: runtimeConfig.sessionId,
14385
14302
  error: err instanceof Error ? err.message : String(err)
@@ -14394,7 +14311,7 @@ async function runTeammateDecisionEval(params) {
14394
14311
  const decision = trimmed !== "" && trimmed !== placeholderText && firstToken === "HANDOFF_TO_MAIN" ? "HANDOFF_TO_MAIN" : "NO_REPLY";
14395
14312
  const cleanedSummary = trimmed.replace(/^(HANDOFF_TO_MAIN|NO_REPLY)\b[\s::-]*/i, "").trim();
14396
14313
  const summary = malformed ? "NO_REPLY (malformed evaluator output)" : cleanedSummary.slice(0, 600) || `${decision} (thinking ${thinkingLength} chars)`;
14397
- log$12.info("teammate evaluator 完成", {
14314
+ log$13.info("teammate evaluator 完成", {
14398
14315
  chatId,
14399
14316
  sessionId: runtimeConfig.sessionId,
14400
14317
  decision,
@@ -14452,7 +14369,7 @@ async function handleMcpToolCallback(req, ctx) {
14452
14369
  try {
14453
14370
  if (req.tool === "ask_user") {
14454
14371
  const params = req.params;
14455
- log$12.info("[MCP] 收到 ask_user 回调,准备发送提问卡片", {
14372
+ log$13.info("[MCP] 收到 ask_user 回调,准备发送提问卡片", {
14456
14373
  requestId: req.requestId,
14457
14374
  sessionId: req.sessionId,
14458
14375
  question: params.question?.slice(0, 50)
@@ -14465,14 +14382,14 @@ async function handleMcpToolCallback(req, ctx) {
14465
14382
  });
14466
14383
  mcpOperationMap.set(operationId, req.requestId);
14467
14384
  await sendInteractiveCardViaCardKit(card, chatId, account, replyInThread, threadId);
14468
- log$12.info("[MCP] 提问卡片已发送", {
14385
+ log$13.info("[MCP] 提问卡片已发送", {
14469
14386
  requestId: req.requestId,
14470
14387
  operationId,
14471
14388
  chatId
14472
14389
  });
14473
14390
  } else if (req.tool === "request_permission") {
14474
14391
  const params = req.params;
14475
- log$12.info("[MCP] 收到 request_permission 回调,准备发送授权卡片", {
14392
+ log$13.info("[MCP] 收到 request_permission 回调,准备发送授权卡片", {
14476
14393
  requestId: req.requestId,
14477
14394
  scopes: params.scopes
14478
14395
  });
@@ -14486,14 +14403,14 @@ async function handleMcpToolCallback(req, ctx) {
14486
14403
  });
14487
14404
  mcpOperationMap.set(operationId, req.requestId);
14488
14405
  await sendInteractiveCardViaCardKit(card, chatId, account, replyInThread, threadId);
14489
- log$12.info("[MCP] 授权卡片已发送", {
14406
+ log$13.info("[MCP] 授权卡片已发送", {
14490
14407
  requestId: req.requestId,
14491
14408
  operationId,
14492
14409
  chatId
14493
14410
  });
14494
14411
  }
14495
14412
  } catch (err) {
14496
- log$12.error("[MCP] 交互卡片发送失败", {
14413
+ log$13.error("[MCP] 交互卡片发送失败", {
14497
14414
  requestId: req.requestId,
14498
14415
  tool: req.tool,
14499
14416
  error: err instanceof Error ? err.message : String(err)
@@ -14518,7 +14435,7 @@ async function sendInteractiveCardViaCardKit(card, chatId, account, replyInThrea
14518
14435
  } });
14519
14436
  const cardId = createResp?.data?.card_id;
14520
14437
  if (!cardId) throw new Error(`CardKit card.create 失败: code=${createResp?.code}, msg=${createResp?.msg}`);
14521
- log$12.info("[MCP] CardKit 卡片实体已创建", { cardId });
14438
+ log$13.info("[MCP] CardKit 卡片实体已创建", { cardId });
14522
14439
  const contentPayload = JSON.stringify({
14523
14440
  type: "card",
14524
14441
  data: { card_id: cardId }
@@ -16048,7 +15965,7 @@ const convertLocation = (raw) => {
16048
15965
  * injected via callbacks in `ConvertContext`. Callers are responsible
16049
15966
  * for creating the appropriate callbacks (UAT / TAT / event push).
16050
15967
  */
16051
- const log$11 = larkLogger("converters/merge-forward");
15968
+ const log$12 = larkLogger("converters/merge-forward");
16052
15969
  /**
16053
15970
  * Recursively expand a merge_forward message.
16054
15971
  *
@@ -16063,7 +15980,7 @@ const log$11 = larkLogger("converters/merge-forward");
16063
15980
  const convertMergeForward = async (_raw, ctx) => {
16064
15981
  const { accountId, messageId, resolveUserName, batchResolveNames, fetchSubMessages, convertMessageContent } = ctx;
16065
15982
  if (!fetchSubMessages) {
16066
- log$11.warn("fetchSubMessages 回调未注入,无法展开合并转发消息", {
15983
+ log$12.warn("fetchSubMessages 回调未注入,无法展开合并转发消息", {
16067
15984
  messageId,
16068
15985
  accountId
16069
15986
  });
@@ -16081,23 +15998,23 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
16081
15998
  let items;
16082
15999
  try {
16083
16000
  items = await fetchSubMessages(messageId);
16084
- log$11.info("fetchSubMessages 成功", {
16001
+ log$12.info("fetchSubMessages 成功", {
16085
16002
  messageId,
16086
16003
  itemCount: items.length
16087
16004
  });
16088
16005
  } catch (error) {
16089
- log$11.error("fetch sub-messages failed", {
16006
+ log$12.error("fetch sub-messages failed", {
16090
16007
  messageId,
16091
16008
  error: error instanceof Error ? error.message : String(error)
16092
16009
  });
16093
16010
  return "<forwarded_messages/>";
16094
16011
  }
16095
16012
  if (items.length === 0) {
16096
- log$11.warn("fetchSubMessages 返回空 items", { messageId });
16013
+ log$12.warn("fetchSubMessages 返回空 items", { messageId });
16097
16014
  return "<forwarded_messages/>";
16098
16015
  }
16099
16016
  const childrenMap = buildChildrenMap(items, messageId);
16100
- log$11.info("buildChildrenMap 完成", {
16017
+ log$12.info("buildChildrenMap 完成", {
16101
16018
  messageId,
16102
16019
  parentKeys: [...childrenMap.keys()],
16103
16020
  childCounts: [...childrenMap.entries()].map(([k, v]) => `${k}:${v.length}`)
@@ -16106,7 +16023,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
16106
16023
  if (senderIds.length > 0 && batchResolveNames) try {
16107
16024
  await batchResolveNames(senderIds);
16108
16025
  } catch (err) {
16109
- log$11.debug("batchResolveNames failed (best-effort)", { error: err instanceof Error ? err.message : String(err) });
16026
+ log$12.debug("batchResolveNames failed (best-effort)", { error: err instanceof Error ? err.message : String(err) });
16110
16027
  }
16111
16028
  return formatSubTree(messageId, childrenMap, accountId, resolveUserName, convertContent);
16112
16029
  }
@@ -16186,7 +16103,7 @@ async function formatSubTree(parentId, childrenMap, accountId, resolveUserName,
16186
16103
  const indented = indentLines(content, " ");
16187
16104
  parts.push(`[${timestamp}] ${displayName}:\n${indented}`);
16188
16105
  } catch (err) {
16189
- log$11.warn("failed to convert sub-message", {
16106
+ log$12.warn("failed to convert sub-message", {
16190
16107
  messageId: item.message_id,
16191
16108
  msgType: item.msg_type ?? "unknown",
16192
16109
  error: err instanceof Error ? err.message : String(err)
@@ -16400,7 +16317,7 @@ async function convertMessageContent(raw, messageType, ctx) {
16400
16317
  }
16401
16318
  //#endregion
16402
16319
  //#region src/messaging/inbound/parse-io.ts
16403
- const log$10 = larkLogger("inbound/parse-io");
16320
+ const log$11 = larkLogger("inbound/parse-io");
16404
16321
  /**
16405
16322
  * 对 interactive 消息,通过 TAT 调用 API 获取完整 v2 卡片内容。
16406
16323
  * 事件推送的 content 可能不包含 json_card,API 调用可返回完整的 raw_card_content。
@@ -16420,7 +16337,7 @@ async function fetchCardContent(messageId, larkClient) {
16420
16337
  }
16421
16338
  }))?.data?.items?.[0]?.body?.content ?? void 0;
16422
16339
  } catch (err) {
16423
- log$10.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);
16340
+ log$11.warn(`fetchCardContent failed for ${messageId}: ${err instanceof Error ? err.message : String(err)}`);
16424
16341
  return;
16425
16342
  }
16426
16343
  }
@@ -16435,7 +16352,7 @@ async function fetchCardContent(messageId, larkClient) {
16435
16352
  */
16436
16353
  function createFetchSubMessages(larkClient) {
16437
16354
  return async (msgId) => {
16438
- log$10.info("fetchSubMessages 请求", {
16355
+ log$11.info("fetchSubMessages 请求", {
16439
16356
  msgId,
16440
16357
  url: `/open-apis/im/v1/messages/${msgId}`
16441
16358
  });
@@ -16447,7 +16364,7 @@ function createFetchSubMessages(larkClient) {
16447
16364
  card_msg_content_type: "raw_card_content"
16448
16365
  }
16449
16366
  });
16450
- log$10.info("fetchSubMessages 响应", {
16367
+ log$11.info("fetchSubMessages 响应", {
16451
16368
  msgId,
16452
16369
  code: response?.code,
16453
16370
  msg: response?.msg,
@@ -16464,11 +16381,11 @@ function createFetchSubMessages(larkClient) {
16464
16381
  * the account and log function.
16465
16382
  */
16466
16383
  function createParseResolveNames(account) {
16467
- return createBatchResolveNames(account, (...args) => log$10.info(args.map(String).join(" ")));
16384
+ return createBatchResolveNames(account, (...args) => log$11.info(args.map(String).join(" ")));
16468
16385
  }
16469
16386
  //#endregion
16470
16387
  //#region src/messaging/inbound/parse.ts
16471
- const log$9 = larkLogger("inbound/parse");
16388
+ const log$10 = larkLogger("inbound/parse");
16472
16389
  /**
16473
16390
  * Parse a raw Feishu message event into a normalised MessageContext.
16474
16391
  *
@@ -16518,7 +16435,7 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
16518
16435
  const fullContent = await fetchCardContent(event.message.message_id, larkClient);
16519
16436
  if (fullContent) {
16520
16437
  effectiveContent = fullContent;
16521
- log$9.info("replaced interactive content with full v2 card data");
16438
+ log$10.info("replaced interactive content with full v2 card data");
16522
16439
  }
16523
16440
  }
16524
16441
  const convertCtx = {
@@ -16558,6 +16475,219 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
16558
16475
  };
16559
16476
  }
16560
16477
  //#endregion
16478
+ //#region src/messaging/shared/message-lookup.ts
16479
+ const log$9 = larkLogger("shared/message-lookup");
16480
+ /**
16481
+ * Retrieve a single message by its ID from the Feishu IM API.
16482
+ *
16483
+ * Returns a normalised {@link FeishuMessageInfo} object, or `null` if the
16484
+ * message cannot be found or the API returns an error.
16485
+ *
16486
+ * @param params.cfg - Plugin configuration with Feishu credentials.
16487
+ * @param params.messageId - The message ID to fetch.
16488
+ * @param params.accountId - Optional account identifier for multi-account setups.
16489
+ */
16490
+ async function getMessageFeishu(params) {
16491
+ const { cfg, messageId, accountId, expandForward } = params;
16492
+ const larkClient = LarkClient.fromCfg(cfg, accountId);
16493
+ const sdk = larkClient.sdk;
16494
+ try {
16495
+ const requestOpts = {
16496
+ method: "GET",
16497
+ url: `/open-apis/im/v1/messages/mget`,
16498
+ params: {
16499
+ message_ids: messageId,
16500
+ user_id_type: "open_id",
16501
+ card_msg_content_type: "raw_card_content"
16502
+ }
16503
+ };
16504
+ const items = (await sdk.request(requestOpts))?.data?.items;
16505
+ if (!items || items.length === 0) {
16506
+ log$9.info(`getMessageFeishu: no items returned for ${messageId}`);
16507
+ return null;
16508
+ }
16509
+ const expandCtx = expandForward ? {
16510
+ cfg,
16511
+ accountId,
16512
+ fetchSubMessages: async (msgId) => {
16513
+ const res = await larkClient.sdk.request({
16514
+ method: "GET",
16515
+ url: `/open-apis/im/v1/messages/${msgId}`,
16516
+ params: {
16517
+ user_id_type: "open_id",
16518
+ card_msg_content_type: "raw_card_content"
16519
+ }
16520
+ });
16521
+ if (res?.code !== 0) throw new Error(`API error: code=${res?.code} msg=${res?.msg}`);
16522
+ return res?.data?.items ?? [];
16523
+ },
16524
+ batchResolveNames: createBatchResolveNames(getLarkAccount(cfg, accountId), (...args) => log$9.info(args.map(String).join(" ")))
16525
+ } : void 0;
16526
+ return await parseMessageItem(items[0], messageId, expandCtx);
16527
+ } catch (error) {
16528
+ log$9.error(`get message failed (${messageId}): ${error instanceof Error ? error.message : String(error)}`);
16529
+ return null;
16530
+ }
16531
+ }
16532
+ /**
16533
+ * Parse a single message item from the Feishu IM API response into a
16534
+ * normalised {@link FeishuMessageInfo}.
16535
+ *
16536
+ * Content parsing is delegated to the shared converter system so that
16537
+ * every message-type mapping is defined in exactly one place.
16538
+ */
16539
+ async function parseMessageItem(msg, fallbackMessageId, expandCtx) {
16540
+ const msgType = msg.msg_type ?? "text";
16541
+ const rawContent = msg.body?.content ?? "{}";
16542
+ const messageId = msg.message_id ?? fallbackMessageId;
16543
+ const acctId = expandCtx?.accountId;
16544
+ const { content } = await convertMessageContent(rawContent, msgType, {
16545
+ ...buildConvertContextFromItem(msg, fallbackMessageId, acctId),
16546
+ cfg: expandCtx?.cfg,
16547
+ accountId: acctId,
16548
+ fetchSubMessages: expandCtx?.fetchSubMessages,
16549
+ batchResolveNames: expandCtx?.batchResolveNames
16550
+ });
16551
+ const senderId = msg.sender?.id ?? void 0;
16552
+ const senderType = msg.sender?.sender_type ?? void 0;
16553
+ const senderName = senderId && acctId ? getUserNameCache(acctId).get(senderId) : void 0;
16554
+ return {
16555
+ messageId,
16556
+ chatId: msg.chat_id ?? "",
16557
+ chatType: msg.chat_type ?? void 0,
16558
+ senderId,
16559
+ senderName,
16560
+ senderType,
16561
+ content,
16562
+ contentType: msgType,
16563
+ createTime: msg.create_time ? parseInt(String(msg.create_time), 10) : void 0,
16564
+ threadId: msg.thread_id || void 0
16565
+ };
16566
+ }
16567
+ //#endregion
16568
+ //#region src/messaging/inbound/enrich.ts
16569
+ /** 从飞书 IM API 获取指定消息的 parent_id 和 root_id 字段 */
16570
+ async function getMessageParentId(params) {
16571
+ const { messageId, account, accountScopedCfg, log } = params;
16572
+ try {
16573
+ const items = (await LarkClient.fromCfg(accountScopedCfg, account.accountId).sdk.request({
16574
+ method: "GET",
16575
+ url: `/open-apis/im/v1/messages/mget`,
16576
+ params: {
16577
+ message_ids: messageId,
16578
+ user_id_type: "open_id"
16579
+ }
16580
+ }))?.data?.items;
16581
+ if (!items || items.length === 0) {
16582
+ log(`feishu[${account.accountId}]: getMessageParentId: no items returned for ${messageId}`);
16583
+ return null;
16584
+ }
16585
+ const item = items[0];
16586
+ log(`feishu[${account.accountId}]: getMessageParentId(${messageId}): parent_id=${item.parent_id || "none"}, root_id=${item.root_id || "none"}`);
16587
+ return {
16588
+ parentId: item.parent_id || void 0,
16589
+ rootId: item.root_id || void 0
16590
+ };
16591
+ } catch (error) {
16592
+ log(`feishu[${account.accountId}]: getMessageParentId failed (${messageId}): ${error instanceof Error ? error.message : String(error)}`);
16593
+ return null;
16594
+ }
16595
+ }
16596
+ /** 将消息格式化为带缩进层级的回复链文本行 */
16597
+ function formatChainMessage(msg, depth) {
16598
+ const prefix = `[message_id=${msg.messageId}]`;
16599
+ const text = msg.senderName ? `${prefix} ${msg.senderName}: ${msg.content}` : `${prefix} ${msg.content}`;
16600
+ if (depth === 0) return text;
16601
+ return `${" ".repeat(depth)}↳ ${text}`;
16602
+ }
16603
+ /**
16604
+ * 递归解析回复链上下文,支持 rootId 追溯和 N 层 parentId 递归。
16605
+ *
16606
+ * 解析逻辑:
16607
+ * 1. 如果存在 rootId 且与 parentId 不同,先获取话题根消息作为链的起点
16608
+ * 2. 从 parentId 开始,递归向上追溯每条消息的 parentId,最多追溯 maxQuoteDepth 层
16609
+ * 3. 对结果去重后按时间顺序(最旧在前)输出完整的对话链上下文
16610
+ *
16611
+ * 输出格式:
16612
+ * ```
16613
+ * [回复链上下文]:
16614
+ * [message_id=xxx] senderName: content
16615
+ * ↳ [message_id=yyy] senderName2: content2
16616
+ * ↳ [message_id=zzz] senderName3: content3
16617
+ * ```
16618
+ */
16619
+ async function resolveQuotedContent(params) {
16620
+ const { ctx, accountScopedCfg, account, log, maxQuoteDepth = 3 } = params;
16621
+ if (!ctx.parentId) return void 0;
16622
+ log(`feishu[${account.accountId}]: resolveQuotedContent 开始, parentId=${ctx.parentId}, rootId=${ctx.rootId || "none"}, maxDepth=${maxQuoteDepth}`);
16623
+ const chainMessages = [];
16624
+ const visited = /* @__PURE__ */ new Set();
16625
+ let currentMsgId = ctx.parentId;
16626
+ let depth = 0;
16627
+ while (currentMsgId && depth < maxQuoteDepth) {
16628
+ if (visited.has(currentMsgId)) {
16629
+ log(`feishu[${account.accountId}]: 检测到循环引用, messageId=${currentMsgId}, 停止递归`);
16630
+ break;
16631
+ }
16632
+ visited.add(currentMsgId);
16633
+ try {
16634
+ const msg = await getMessageFeishu({
16635
+ cfg: accountScopedCfg,
16636
+ messageId: currentMsgId,
16637
+ accountId: account.accountId,
16638
+ expandForward: true
16639
+ });
16640
+ if (!msg) {
16641
+ log(`feishu[${account.accountId}]: 回复链第 ${depth + 1} 层消息获取为空, messageId=${currentMsgId}, 停止递归`);
16642
+ break;
16643
+ }
16644
+ log(`feishu[${account.accountId}]: 回复链第 ${depth + 1} 层: messageId=${currentMsgId}, content=${msg.content?.slice(0, 80)}`);
16645
+ chainMessages.push(msg);
16646
+ const parentInfo = await getMessageParentId({
16647
+ messageId: currentMsgId,
16648
+ account,
16649
+ accountScopedCfg,
16650
+ log
16651
+ });
16652
+ if (!parentInfo?.parentId) {
16653
+ log(`feishu[${account.accountId}]: 第 ${depth + 1} 层消息无 parentId, 到达链顶端`);
16654
+ break;
16655
+ }
16656
+ currentMsgId = parentInfo.parentId;
16657
+ depth++;
16658
+ } catch (err) {
16659
+ log(`feishu[${account.accountId}]: 回复链第 ${depth + 1} 层获取失败, messageId=${currentMsgId}: ${String(err)}, 停止递归`);
16660
+ break;
16661
+ }
16662
+ }
16663
+ if (depth >= maxQuoteDepth) log(`feishu[${account.accountId}]: 已达到最大递归深度 ${maxQuoteDepth}, 停止追溯`);
16664
+ if (ctx.rootId && ctx.rootId !== ctx.parentId && !visited.has(ctx.rootId)) try {
16665
+ const rootMsg = await getMessageFeishu({
16666
+ cfg: accountScopedCfg,
16667
+ messageId: ctx.rootId,
16668
+ accountId: account.accountId,
16669
+ expandForward: true
16670
+ });
16671
+ if (rootMsg) {
16672
+ log(`feishu[${account.accountId}]: 获取到话题根消息: rootId=${ctx.rootId}, content=${rootMsg.content?.slice(0, 80)}`);
16673
+ chainMessages.push(rootMsg);
16674
+ visited.add(ctx.rootId);
16675
+ } else log(`feishu[${account.accountId}]: 话题根消息获取为空, rootId=${ctx.rootId}`);
16676
+ } catch (err) {
16677
+ log(`feishu[${account.accountId}]: 话题根消息获取失败, rootId=${ctx.rootId}: ${String(err)}`);
16678
+ }
16679
+ if (chainMessages.length === 0) {
16680
+ log(`feishu[${account.accountId}]: 回复链解析完成, 未获取到任何消息`);
16681
+ return;
16682
+ }
16683
+ chainMessages.reverse();
16684
+ const lines = ["[回复链上下文]:"];
16685
+ for (let i = 0; i < chainMessages.length; i++) lines.push(formatChainMessage(chainMessages[i], i));
16686
+ const result = lines.join("\n");
16687
+ log(`feishu[${account.accountId}]: 回复链解析完成, 共 ${chainMessages.length} 条消息`);
16688
+ return result;
16689
+ }
16690
+ //#endregion
16561
16691
  //#region src/messaging/inbound/dedup.ts
16562
16692
  const DEFAULT_TTL_MS = 720 * 60 * 1e3;
16563
16693
  const DEFAULT_MAX_ENTRIES = 5e3;
@@ -18187,6 +18317,30 @@ async function main() {
18187
18317
  brand: "feishu",
18188
18318
  config: {}
18189
18319
  };
18320
+ const accountScopedCfg = { channels: { feishu: {
18321
+ ...account.config,
18322
+ appId: account.appId,
18323
+ appSecret: account.appSecret,
18324
+ domain: account.brand
18325
+ } } };
18326
+ const resolveQuotedContentForMessage = async (ctx) => {
18327
+ if (!ctx.parentId) return void 0;
18328
+ try {
18329
+ return await resolveQuotedContent({
18330
+ ctx,
18331
+ accountScopedCfg,
18332
+ account,
18333
+ log: (...args) => logger.info("resolveQuotedContent", { detail: args.map(String).join(" ") })
18334
+ });
18335
+ } catch (err) {
18336
+ logger.warn("引用消息解析失败,继续仅处理当前消息", {
18337
+ msgId: ctx.messageId,
18338
+ parentId: ctx.parentId,
18339
+ error: err instanceof Error ? err.message : String(err)
18340
+ });
18341
+ return;
18342
+ }
18343
+ };
18190
18344
  const larkClient = LarkClient.fromAccount(account);
18191
18345
  const probeResult = await larkClient.probe();
18192
18346
  if (probeResult.ok) logger.info("Bot 身份探测成功", {
@@ -18325,7 +18479,7 @@ async function main() {
18325
18479
  });
18326
18480
  if (nameResult.name) parsed.senderName = nameResult.name;
18327
18481
  } catch {}
18328
- const formattedText = formatForCC(parsed, true);
18482
+ const formattedText = formatPromptForCC(parsed, true, await resolveQuotedContentForMessage(parsed));
18329
18483
  teammateBuffer.push(parsed.chatId, {
18330
18484
  formattedText,
18331
18485
  bufferedAt: Date.now(),
@@ -18377,6 +18531,7 @@ async function main() {
18377
18531
  workspaceRoot
18378
18532
  });
18379
18533
  await ensureUserDirectory(userContext);
18534
+ const quotedContent = await resolveQuotedContentForMessage(parsed);
18380
18535
  await dispatchToCC({
18381
18536
  ctx: parsed,
18382
18537
  account,
@@ -18386,7 +18541,8 @@ async function main() {
18386
18541
  userContext,
18387
18542
  placeholderCardId,
18388
18543
  placeholderMessageId,
18389
- messageStore
18544
+ messageStore,
18545
+ quotedContent
18390
18546
  });
18391
18547
  if (threadId) markBotInvolvedInThread(threadId);
18392
18548
  } catch (err) {