@vibe-lark/larkpal 0.1.57 → 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 +834 -695
  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,43 +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 无远程工具可用");
2990
- try {
2991
- const { loadSkillsFromDir, registerSkills } = await import("@vibe-lark/larkpal-agent");
2992
- const { fileURLToPath } = await import("node:url");
2993
- const skillsDir = join(fileURLToPath(import.meta.resolve("@vibe-lark/larkpal-agent")).replace(/\/dist\/index\.js$/, ""), "src", "skills", "definitions");
2994
- const skills = await loadSkillsFromDir(skillsDir);
2995
- if (skills.length > 0) {
2996
- registerSkills(registry, skills, llm);
2997
- const skillTools = registry.getAll().filter((t) => t.name.startsWith("skill:"));
2998
- adapter.registerTools(skillTools);
2999
- log$29.info("Skill Meta-Tools 注册完成", {
3000
- skillCount: skills.length,
3001
- skillNames: skills.map((s) => s.name)
3002
- });
3003
- } else log$29.info("未找到 Skill 定义文件", { skillsDir });
3004
- } catch (err) {
3005
- log$29.warn("Skill 加载失败(非阻塞)", { error: err?.message });
3006
- }
2787
+ else log$30.warn("未配置 LARKPAL_AGENT_MCP_SERVER_URL,Agent 无远程工具可用");
3007
2788
  return adapter;
3008
2789
  }
3009
2790
  //#endregion
@@ -3340,6 +3121,7 @@ function createChatAuthRouter() {
3340
3121
  //#endregion
3341
3122
  //#region src/gateway/agent-context.ts
3342
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;
3343
3125
  const MAX_SUMMARY_STRING_LENGTH = 200;
3344
3126
  const PROTECTED_CONTEXT_HEADERS = new Set([
3345
3127
  "x-tenant-key",
@@ -3462,7 +3244,7 @@ function summarizeForAudit(value) {
3462
3244
  };
3463
3245
  if (typeof value === "object") {
3464
3246
  const result = {};
3465
- 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]";
3466
3248
  else result[key] = summarizeForAudit(entry);
3467
3249
  return result;
3468
3250
  }
@@ -3568,7 +3350,8 @@ function buildExecutionPolicy(policy, limits) {
3568
3350
  maxTurns: limits.maxTurns ?? policy?.maxTurns,
3569
3351
  maxToolCalls: policy?.maxToolCalls,
3570
3352
  maxRuntimeMs: policy?.maxRuntimeMs,
3571
- maxBudgetUsd: limits.maxBudgetUsd ?? policy?.maxBudgetUsd
3353
+ maxBudgetUsd: limits.maxBudgetUsd ?? policy?.maxBudgetUsd,
3354
+ turnPolicy: policy?.turnPolicy
3572
3355
  };
3573
3356
  }
3574
3357
  function buildUserContext$1(identity) {
@@ -3636,7 +3419,7 @@ function summarizeString(value) {
3636
3419
  * POST /api/chat/sessions — 创建新会话
3637
3420
  * DELETE /api/chat/sessions/:id — 删除会话
3638
3421
  */
3639
- const log$28 = larkLogger("chat/stream");
3422
+ const log$29 = larkLogger("chat/stream");
3640
3423
  const chatRunsBySession = /* @__PURE__ */ new Map();
3641
3424
  /** 统一写 SSE 格式数据到响应流 */
3642
3425
  function sendSSE(res, event, data) {
@@ -3647,7 +3430,7 @@ function isChatSessionRunning(sessionId, processManager) {
3647
3430
  return getActiveChatRun(sessionId) != null || (processManager.isSessionBusy?.(sessionId) ?? false);
3648
3431
  }
3649
3432
  function logChatAudit(event) {
3650
- log$28.info("[stream] Agent audit event", { ...event });
3433
+ log$29.info("[stream] Agent audit event", { ...event });
3651
3434
  }
3652
3435
  function prepareSSE(res) {
3653
3436
  res.setHeader("Content-Type", "text/event-stream");
@@ -3672,7 +3455,8 @@ function serializeChatRun(run) {
3672
3455
  completedAt: run.completedAt,
3673
3456
  finalStatus: run.finalStatus,
3674
3457
  error: run.error,
3675
- textLength: run.text.length
3458
+ textLength: run.text.length,
3459
+ finalText: run.status === "running" ? void 0 : run.text || void 0
3676
3460
  };
3677
3461
  }
3678
3462
  function isRunVisibleToUser(run, params) {
@@ -3682,7 +3466,7 @@ function addRunClient(req, res, run) {
3682
3466
  run.clients.add(res);
3683
3467
  req.on("close", () => {
3684
3468
  run.clients.delete(res);
3685
- log$28.info("[stream] 客户端断开连接", {
3469
+ log$29.info("[stream] 客户端断开连接", {
3686
3470
  sessionId: run.sessionId,
3687
3471
  runId: run.runId
3688
3472
  });
@@ -3691,12 +3475,26 @@ function addRunClient(req, res, run) {
3691
3475
  function sendRunStatus(res, run) {
3692
3476
  sendSSE(res, "run-status", serializeChatRun(run));
3693
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
+ }
3694
3492
  function broadcastRunEvent(run, event, data) {
3695
3493
  for (const client of Array.from(run.clients)) try {
3696
3494
  sendSSE(client, event, data);
3697
3495
  } catch (err) {
3698
3496
  run.clients.delete(client);
3699
- log$28.warn("[stream] SSE 写入失败,移除客户端", {
3497
+ log$29.warn("[stream] SSE 写入失败,移除客户端", {
3700
3498
  sessionId: run.sessionId,
3701
3499
  runId: run.runId,
3702
3500
  error: err instanceof Error ? err.message : String(err)
@@ -3716,7 +3514,7 @@ function queueRunMessage(run, messageStore, message, label) {
3716
3514
  run.persistQueue = run.persistQueue.catch(() => void 0).then(async () => {
3717
3515
  await messageStore.appendMessage(message);
3718
3516
  }).catch((err) => {
3719
- log$28.error(label, {
3517
+ log$29.error(label, {
3720
3518
  sessionId: run.sessionId,
3721
3519
  runId: run.runId,
3722
3520
  error: err instanceof Error ? err.message : String(err)
@@ -3781,7 +3579,7 @@ function createChatRouter(config) {
3781
3579
  addRunClient(req, res, activeRun);
3782
3580
  sendSSE(res, "session", { sessionId });
3783
3581
  sendRunStatus(res, activeRun);
3784
- log$28.info("[stream] 已重新连接到运行中的 run", {
3582
+ log$29.info("[stream] 已重新连接到运行中的 run", {
3785
3583
  sessionId,
3786
3584
  runId: activeRun.runId
3787
3585
  });
@@ -3794,7 +3592,7 @@ function createChatRouter(config) {
3794
3592
  const scenarioId = getScenarioId(body.options?.scenarioId, body.options?.metadata);
3795
3593
  const attachments = getRunInputValue(body.attachments, body.options?.attachments, body.options?.metadata?.attachments);
3796
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);
3797
- log$28.info("[stream] 收到流式对话请求", {
3595
+ log$29.info("[stream] 收到流式对话请求", {
3798
3596
  userId,
3799
3597
  sessionId,
3800
3598
  requestId,
@@ -3811,13 +3609,13 @@ function createChatRouter(config) {
3811
3609
  tenantKey,
3812
3610
  channel: "web"
3813
3611
  })).id;
3814
- log$28.info("[stream] 自动创建新会话", {
3612
+ log$29.info("[stream] 自动创建新会话", {
3815
3613
  userId: openId,
3816
3614
  sessionId
3817
3615
  });
3818
3616
  }
3819
3617
  if (isChatSessionRunning(sessionId, processManager)) {
3820
- log$28.warn("[stream] session 正在被非 Web Chat run 占用", {
3618
+ log$29.warn("[stream] session 正在被非 Web Chat run 占用", {
3821
3619
  tenantKey,
3822
3620
  userId,
3823
3621
  sessionId
@@ -3828,6 +3626,7 @@ function createChatRouter(config) {
3828
3626
  res.end();
3829
3627
  return;
3830
3628
  }
3629
+ const runtimePrompt = enrichPromptWithSourceArtifacts(body.prompt, sourceArtifacts);
3831
3630
  const runtimeConfig = buildAgentRuntimeConfig({
3832
3631
  requestId,
3833
3632
  traceId,
@@ -3835,7 +3634,7 @@ function createChatRouter(config) {
3835
3634
  conversationId: sessionId,
3836
3635
  entrypoint: "chat",
3837
3636
  scenarioId,
3838
- prompt: body.prompt,
3637
+ prompt: runtimePrompt,
3839
3638
  maxTurns: body.options?.maxTurns,
3840
3639
  maxBudgetUsd: body.options?.maxBudgetUsd,
3841
3640
  model: process.env.CLAUDE_MODEL || void 0,
@@ -3915,7 +3714,7 @@ function createChatRouter(config) {
3915
3714
  });
3916
3715
  } catch (err) {
3917
3716
  const errorMessage = err instanceof Error ? err.message : String(err);
3918
- log$28.error("[stream] 保存用户消息失败", {
3717
+ log$29.error("[stream] 保存用户消息失败", {
3919
3718
  sessionId,
3920
3719
  error: errorMessage
3921
3720
  });
@@ -3928,9 +3727,16 @@ function createChatRouter(config) {
3928
3727
  addRunClient(req, res, run);
3929
3728
  sendSSE(res, "session", { sessionId });
3930
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);
3931
3737
  const handleBlockedEvent = (event) => {
3932
3738
  const sanitized = sanitizeBlockedEvent(event);
3933
- log$28.warn("[stream] Agent blocked event", { ...sanitized });
3739
+ log$29.warn("[stream] Agent blocked event", { ...sanitized });
3934
3740
  if (runtimeConfig.requestContext) logChatAudit(buildAuditEvent(runtimeConfig.requestContext, sanitized.type, {
3935
3741
  toolName: sanitized.name,
3936
3742
  inputSummary: sanitized.inputSummary,
@@ -3961,6 +3767,8 @@ function createChatRouter(config) {
3961
3767
  };
3962
3768
  const handleRuntimeEvent = (event) => {
3963
3769
  const eventSummary = summarizeForAudit(event);
3770
+ const runtimeFinalText = extractRuntimeEventFinalText(event);
3771
+ if (runtimeFinalText && !run.text.trim()) run.text = runtimeFinalText;
3964
3772
  if (runtimeConfig.requestContext) logChatAudit(buildAuditEvent(runtimeConfig.requestContext, "runtime_event", {
3965
3773
  resultSummary: eventSummary,
3966
3774
  metadata: buildRuntimeEventAuditMetadata(event)
@@ -4059,7 +3867,9 @@ function createChatRouter(config) {
4059
3867
  broadcastRunEvent(run, "turn-end", { stopReason });
4060
3868
  },
4061
3869
  onResult: async (result) => {
4062
- const finalText = run.text || result.result || "";
3870
+ const finalText = run.text.trim() ? run.text : result.result || "";
3871
+ run.text = finalText;
3872
+ await markRunTerminal(result.isError ? "failed" : "completed", { finalStatus: result.subtype });
4063
3873
  await queueRunMessage(run, messageStore, {
4064
3874
  sessionId,
4065
3875
  role: "assistant",
@@ -4077,7 +3887,6 @@ function createChatRouter(config) {
4077
3887
  isError: result.isError
4078
3888
  }
4079
3889
  }, "[stream] 保存 assistant 消息失败");
4080
- await markRunTerminal(result.isError ? "failed" : "completed", { finalStatus: result.subtype });
4081
3890
  broadcastRunEvent(run, "done", {
4082
3891
  sessionId,
4083
3892
  runId: run.runId,
@@ -4087,8 +3896,9 @@ function createChatRouter(config) {
4087
3896
  totalCostUsd: result.totalCostUsd,
4088
3897
  numTurns: result.numTurns
4089
3898
  });
3899
+ clearInterval(heartbeatTimer);
4090
3900
  endRunClients(run);
4091
- log$28.info("[stream] 对话完成", {
3901
+ log$29.info("[stream] 对话完成", {
4092
3902
  sessionId,
4093
3903
  runId: run.runId,
4094
3904
  result: result.subtype,
@@ -4110,7 +3920,7 @@ function createChatRouter(config) {
4110
3920
  }));
4111
3921
  },
4112
3922
  onError: async (error) => {
4113
- log$28.error("[stream] Runtime 执行出错", {
3923
+ log$29.error("[stream] Runtime 执行出错", {
4114
3924
  sessionId,
4115
3925
  runId: run.runId,
4116
3926
  error: error.message
@@ -4147,6 +3957,7 @@ function createChatRouter(config) {
4147
3957
  runId: run.runId,
4148
3958
  message: error.message
4149
3959
  });
3960
+ clearInterval(heartbeatTimer);
4150
3961
  endRunClients(run);
4151
3962
  },
4152
3963
  onAuditEvent: (event) => {
@@ -4196,7 +4007,7 @@ function createChatRouter(config) {
4196
4007
  scenarioId: runtimeConfig.runContext?.scenarioId,
4197
4008
  ...buildRunInputAuditMetadata(runtimeConfig)
4198
4009
  } }));
4199
- log$28.info("[stream] 开始执行 prompt", {
4010
+ log$29.info("[stream] 开始执行 prompt", {
4200
4011
  sessionId,
4201
4012
  runId: run.runId,
4202
4013
  cwd: runtimeConfig.cwd
@@ -4204,7 +4015,7 @@ function createChatRouter(config) {
4204
4015
  await processManager.executePrompt(runtimeConfig, callbacks);
4205
4016
  if (run.status === "running") {
4206
4017
  const errorMessage = "Runtime completed without a final result event";
4207
- log$28.error("[stream] executePrompt 未返回终态回调", {
4018
+ log$29.error("[stream] executePrompt 未返回终态回调", {
4208
4019
  sessionId,
4209
4020
  runId: run.runId
4210
4021
  });
@@ -4216,11 +4027,12 @@ function createChatRouter(config) {
4216
4027
  runId: run.runId,
4217
4028
  message: errorMessage
4218
4029
  });
4030
+ clearInterval(heartbeatTimer);
4219
4031
  endRunClients(run);
4220
4032
  }
4221
4033
  } catch (err) {
4222
4034
  const errorMessage = err instanceof Error ? err.message : String(err);
4223
- log$28.error("[stream] executePrompt 异常", {
4035
+ log$29.error("[stream] executePrompt 异常", {
4224
4036
  sessionId,
4225
4037
  runId: run.runId,
4226
4038
  error: errorMessage
@@ -4237,7 +4049,7 @@ function createChatRouter(config) {
4237
4049
  }
4238
4050
  const cursor = req.query.cursor;
4239
4051
  const limit = parseInt(req.query.limit, 10) || 50;
4240
- log$28.info("[history] 查询会话历史", {
4052
+ log$29.info("[history] 查询会话历史", {
4241
4053
  userId: chatUser.userId,
4242
4054
  sessionId,
4243
4055
  cursor,
@@ -4262,7 +4074,7 @@ function createChatRouter(config) {
4262
4074
  });
4263
4075
  } catch (err) {
4264
4076
  const errorMessage = err instanceof Error ? err.message : String(err);
4265
- log$28.error("[history] 查询历史消息失败", {
4077
+ log$29.error("[history] 查询历史消息失败", {
4266
4078
  sessionId,
4267
4079
  error: errorMessage
4268
4080
  });
@@ -4297,13 +4109,13 @@ function createChatRouter(config) {
4297
4109
  });
4298
4110
  router.get("/api/chat/sessions", chatAuthMiddleware, async (_req, res) => {
4299
4111
  const chatUser = res.locals.chatUser;
4300
- log$28.info("[sessions] 列出用户会话", { userId: chatUser.userId });
4112
+ log$29.info("[sessions] 列出用户会话", { userId: chatUser.userId });
4301
4113
  try {
4302
4114
  const sessions = await messageStore.listSessions(chatUser.openId);
4303
4115
  res.json({ sessions });
4304
4116
  } catch (err) {
4305
4117
  const errorMessage = err instanceof Error ? err.message : String(err);
4306
- log$28.error("[sessions] 列出会话失败", {
4118
+ log$29.error("[sessions] 列出会话失败", {
4307
4119
  userId: chatUser.userId,
4308
4120
  error: errorMessage
4309
4121
  });
@@ -4313,7 +4125,7 @@ function createChatRouter(config) {
4313
4125
  router.post("/api/chat/sessions", chatAuthMiddleware, async (req, res) => {
4314
4126
  const chatUser = res.locals.chatUser;
4315
4127
  const body = req.body;
4316
- log$28.info("[sessions] 创建新会话", {
4128
+ log$29.info("[sessions] 创建新会话", {
4317
4129
  userId: chatUser.userId,
4318
4130
  title: body.title
4319
4131
  });
@@ -4328,7 +4140,7 @@ function createChatRouter(config) {
4328
4140
  res.json({ session });
4329
4141
  } catch (err) {
4330
4142
  const errorMessage = err instanceof Error ? err.message : String(err);
4331
- log$28.error("[sessions] 创建会话失败", {
4143
+ log$29.error("[sessions] 创建会话失败", {
4332
4144
  userId: chatUser.userId,
4333
4145
  error: errorMessage
4334
4146
  });
@@ -4338,7 +4150,7 @@ function createChatRouter(config) {
4338
4150
  router.delete("/api/chat/sessions/:id", chatAuthMiddleware, async (req, res) => {
4339
4151
  const chatUser = res.locals.chatUser;
4340
4152
  const sessionId = String(req.params.id);
4341
- log$28.info("[sessions] 删除会话", {
4153
+ log$29.info("[sessions] 删除会话", {
4342
4154
  userId: chatUser.userId,
4343
4155
  sessionId
4344
4156
  });
@@ -4348,7 +4160,7 @@ function createChatRouter(config) {
4348
4160
  res.json({ success: true });
4349
4161
  } catch (err) {
4350
4162
  const errorMessage = err instanceof Error ? err.message : String(err);
4351
- log$28.error("[sessions] 删除会话失败", {
4163
+ log$29.error("[sessions] 删除会话失败", {
4352
4164
  sessionId,
4353
4165
  error: errorMessage
4354
4166
  });
@@ -4357,6 +4169,34 @@ function createChatRouter(config) {
4357
4169
  });
4358
4170
  return router;
4359
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
+ }
4360
4200
  //#endregion
4361
4201
  //#region src/core/app-info-sync.ts
4362
4202
  /**
@@ -4374,7 +4214,7 @@ function createChatRouter(config) {
4374
4214
  * - GET /open-apis/application/v6/applications/me?lang=zh_cn(需要 application:application:self_manage 权限,返回完整信息)
4375
4215
  * 如果后者无权限则 fallback 到前者
4376
4216
  */
4377
- const log$27 = larkLogger("core/app-info-sync");
4217
+ const log$28 = larkLogger("core/app-info-sync");
4378
4218
  /**
4379
4219
  * 从飞书获取应用信息
4380
4220
  *
@@ -4385,7 +4225,7 @@ async function fetchAppInfo(credentials) {
4385
4225
  const { appId, appSecret } = credentials;
4386
4226
  const token = await getTenantAccessToken(appId, appSecret);
4387
4227
  if (!token) {
4388
- log$27.error("获取 tenant_access_token 失败,无法同步应用信息");
4228
+ log$28.error("获取 tenant_access_token 失败,无法同步应用信息");
4389
4229
  return null;
4390
4230
  }
4391
4231
  const fullInfo = await fetchFromApplicationApi(token);
@@ -4396,13 +4236,13 @@ async function fetchAppInfo(credentials) {
4396
4236
  async function fetchFromApplicationApi(token) {
4397
4237
  try {
4398
4238
  const data = await (await fetch("https://open.feishu.cn/open-apis/application/v6/applications/me?lang=zh_cn", { headers: { Authorization: `Bearer ${token}` } })).json();
4399
- log$27.info("application/v6 API 响应", {
4239
+ log$28.info("application/v6 API 响应", {
4400
4240
  code: data.code,
4401
4241
  msg: data.msg,
4402
4242
  hasApp: !!data.data?.app
4403
4243
  });
4404
4244
  if (data.code !== 0 || !data.data?.app) {
4405
- log$27.warn("application/v6 API 返回非零或无数据,将 fallback", {
4245
+ log$28.warn("application/v6 API 返回非零或无数据,将 fallback", {
4406
4246
  code: data.code,
4407
4247
  msg: data.msg
4408
4248
  });
@@ -4417,7 +4257,7 @@ async function fetchFromApplicationApi(token) {
4417
4257
  helpDocUrl: zhInfo?.help_use
4418
4258
  };
4419
4259
  } catch (err) {
4420
- 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) });
4421
4261
  return null;
4422
4262
  }
4423
4263
  }
@@ -4425,13 +4265,13 @@ async function fetchFromApplicationApi(token) {
4425
4265
  async function fetchFromBotApi(token) {
4426
4266
  try {
4427
4267
  const data = await (await fetch("https://open.feishu.cn/open-apis/bot/v3/info", { headers: { Authorization: `Bearer ${token}` } })).json();
4428
- log$27.info("bot/v3/info API 响应", {
4268
+ log$28.info("bot/v3/info API 响应", {
4429
4269
  code: data.code,
4430
4270
  msg: data.msg,
4431
4271
  hasBot: !!data.bot
4432
4272
  });
4433
4273
  if (data.code !== 0 || !data.bot) {
4434
- log$27.error("bot/v3/info API 失败", {
4274
+ log$28.error("bot/v3/info API 失败", {
4435
4275
  code: data.code,
4436
4276
  msg: data.msg
4437
4277
  });
@@ -4442,7 +4282,7 @@ async function fetchFromBotApi(token) {
4442
4282
  avatarUrl: data.bot.avatar_url
4443
4283
  };
4444
4284
  } catch (err) {
4445
- 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) });
4446
4286
  return null;
4447
4287
  }
4448
4288
  }
@@ -4458,7 +4298,7 @@ async function getTenantAccessToken(appId, appSecret) {
4458
4298
  })
4459
4299
  })).json();
4460
4300
  if (data.code !== 0 || !data.tenant_access_token) {
4461
- log$27.error("获取 tenant_access_token 失败", {
4301
+ log$28.error("获取 tenant_access_token 失败", {
4462
4302
  code: data.code,
4463
4303
  msg: data.msg
4464
4304
  });
@@ -4466,7 +4306,7 @@ async function getTenantAccessToken(appId, appSecret) {
4466
4306
  }
4467
4307
  return data.tenant_access_token;
4468
4308
  } catch (err) {
4469
- 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) });
4470
4310
  return null;
4471
4311
  }
4472
4312
  }
@@ -4504,10 +4344,10 @@ function parseDocTokenFromUrl(url) {
4504
4344
  async function fetchDocContent(docUrl, accessToken) {
4505
4345
  const parsed = parseDocTokenFromUrl(docUrl);
4506
4346
  if (!parsed) {
4507
- log$27.warn("无法从 URL 中解析文档 token", { url: docUrl });
4347
+ log$28.warn("无法从 URL 中解析文档 token", { url: docUrl });
4508
4348
  return null;
4509
4349
  }
4510
- log$27.info("开始读取人设文档", {
4350
+ log$28.info("开始读取人设文档", {
4511
4351
  url: docUrl,
4512
4352
  type: parsed.type,
4513
4353
  token: parsed.token
@@ -4515,18 +4355,18 @@ async function fetchDocContent(docUrl, accessToken) {
4515
4355
  let docToken = parsed.token;
4516
4356
  if (parsed.type === "wiki") {
4517
4357
  const realToken = await resolveWikiNodeToDocToken(parsed.token, accessToken);
4518
- if (!realToken) log$27.warn("wiki 节点解析失败,尝试直接使用 token 读取");
4358
+ if (!realToken) log$28.warn("wiki 节点解析失败,尝试直接使用 token 读取");
4519
4359
  else docToken = realToken;
4520
4360
  }
4521
4361
  try {
4522
4362
  const data = await (await fetch(`https://open.feishu.cn/open-apis/docx/v1/documents/${docToken}/raw_content`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
4523
- log$27.info("文档 raw_content API 响应", {
4363
+ log$28.info("文档 raw_content API 响应", {
4524
4364
  code: data.code,
4525
4365
  msg: data.msg,
4526
4366
  contentLength: data.data?.content?.length
4527
4367
  });
4528
4368
  if (data.code !== 0 || !data.data?.content) {
4529
- log$27.warn("读取文档内容失败", {
4369
+ log$28.warn("读取文档内容失败", {
4530
4370
  code: data.code,
4531
4371
  msg: data.msg,
4532
4372
  docToken
@@ -4535,7 +4375,7 @@ async function fetchDocContent(docUrl, accessToken) {
4535
4375
  }
4536
4376
  return data.data.content.trim();
4537
4377
  } catch (err) {
4538
- log$27.error("读取文档内容异常", {
4378
+ log$28.error("读取文档内容异常", {
4539
4379
  error: err instanceof Error ? err.message : String(err),
4540
4380
  docToken
4541
4381
  });
@@ -4546,7 +4386,7 @@ async function fetchDocContent(docUrl, accessToken) {
4546
4386
  async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
4547
4387
  try {
4548
4388
  const data = await (await fetch(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node?token=${wikiToken}`, { headers: { Authorization: `Bearer ${accessToken}` } })).json();
4549
- log$27.info("wiki get_node API 响应", {
4389
+ log$28.info("wiki get_node API 响应", {
4550
4390
  code: data.code,
4551
4391
  msg: data.msg,
4552
4392
  objType: data.data?.node?.obj_type
@@ -4554,7 +4394,7 @@ async function resolveWikiNodeToDocToken(wikiToken, accessToken) {
4554
4394
  if (data.code !== 0 || !data.data?.node?.obj_token) return null;
4555
4395
  return data.data.node.obj_token;
4556
4396
  } catch (err) {
4557
- 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) });
4558
4398
  return null;
4559
4399
  }
4560
4400
  }
@@ -4576,7 +4416,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4576
4416
  const infoBlock = buildAppInfoBlock(appInfo);
4577
4417
  if (!existsSync(claudeMdPath)) {
4578
4418
  await writeFile(claudeMdPath, infoBlock + "\n\n" + getDefaultClaudeMdBody(), "utf-8");
4579
- log$27.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
4419
+ log$28.info("CLAUDE.md 已创建(含应用信息)", { appName: appInfo.appName });
4580
4420
  return;
4581
4421
  }
4582
4422
  let content = await readFile(claudeMdPath, "utf-8");
@@ -4588,7 +4428,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4588
4428
  content = before + infoBlock + after;
4589
4429
  } else content = infoBlock + "\n\n" + content;
4590
4430
  await writeFile(claudeMdPath, content, "utf-8");
4591
- log$27.info("CLAUDE.md 应用信息已同步", {
4431
+ log$28.info("CLAUDE.md 应用信息已同步", {
4592
4432
  appName: appInfo.appName,
4593
4433
  hasDescription: !!appInfo.description,
4594
4434
  hasAvatar: !!appInfo.avatarUrl
@@ -4603,7 +4443,7 @@ async function syncAppInfoToClaudeMd(appInfo) {
4603
4443
  async function syncPersonaDocToClaudeMd(personaContent) {
4604
4444
  const claudeMdPath = join(homedir(), ".claude", "CLAUDE.md");
4605
4445
  if (!existsSync(claudeMdPath)) {
4606
- log$27.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
4446
+ log$28.warn("CLAUDE.md 不存在,无法同步人设文档(需先同步应用信息)");
4607
4447
  return;
4608
4448
  }
4609
4449
  let content = await readFile(claudeMdPath, "utf-8");
@@ -4624,7 +4464,7 @@ async function syncPersonaDocToClaudeMd(personaContent) {
4624
4464
  content = before + personaBlock + after;
4625
4465
  } else content = content.trimEnd() + "\n\n" + personaBlock + "\n";
4626
4466
  await writeFile(claudeMdPath, content, "utf-8");
4627
- log$27.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
4467
+ log$28.info("CLAUDE.md 人设文档已同步", { contentLength: personaContent.length });
4628
4468
  }
4629
4469
  /** 构建应用信息标记区块 */
4630
4470
  function buildAppInfoBlock(appInfo) {
@@ -4716,24 +4556,24 @@ function getDefaultClaudeMdBody() {
4716
4556
  * @returns 同步后的应用信息,如果失败返回 null
4717
4557
  */
4718
4558
  async function syncAppInfo(credentials) {
4719
- log$27.info("开始同步应用信息", { appId: credentials.appId });
4559
+ log$28.info("开始同步应用信息", { appId: credentials.appId });
4720
4560
  const appInfo = await fetchAppInfo(credentials);
4721
4561
  if (!appInfo) {
4722
- log$27.warn("获取应用信息失败,跳过同步");
4562
+ log$28.warn("获取应用信息失败,跳过同步");
4723
4563
  return null;
4724
4564
  }
4725
4565
  await syncAppInfoToClaudeMd(appInfo);
4726
4566
  if (appInfo.helpDocUrl) {
4727
- log$27.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
4567
+ log$28.info("检测到帮助文档 URL,尝试同步人设文档", { helpDocUrl: appInfo.helpDocUrl });
4728
4568
  const token = await getTenantAccessToken(credentials.appId, credentials.appSecret);
4729
4569
  if (token) {
4730
4570
  const personaContent = await fetchDocContent(appInfo.helpDocUrl, token);
4731
4571
  if (personaContent) await syncPersonaDocToClaudeMd(personaContent);
4732
- else log$27.warn("人设文档内容为空或读取失败,跳过同步");
4572
+ else log$28.warn("人设文档内容为空或读取失败,跳过同步");
4733
4573
  }
4734
4574
  }
4735
4575
  await installSyncSkill();
4736
- log$27.info("应用信息同步完成", {
4576
+ log$28.info("应用信息同步完成", {
4737
4577
  appName: appInfo.appName,
4738
4578
  description: appInfo.description?.substring(0, 50),
4739
4579
  hasPersonaDoc: !!appInfo.helpDocUrl
@@ -4751,7 +4591,7 @@ async function installSyncSkill() {
4751
4591
  if ((await readFile(SYNC_SKILL_PATH, "utf-8")).includes(`skill-version: ${SYNC_SKILL_VERSION}`)) return;
4752
4592
  }
4753
4593
  await writeFile(SYNC_SKILL_PATH, SYNC_SKILL_CONTENT, "utf-8");
4754
- log$27.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
4594
+ log$28.info("sync-app-info 技能已安装", { path: SYNC_SKILL_PATH });
4755
4595
  }
4756
4596
  const SYNC_SKILL_CONTENT = `---
4757
4597
  skill-version: ${SYNC_SKILL_VERSION}
@@ -5747,7 +5587,7 @@ function createHooksRouter() {
5747
5587
  }
5748
5588
  //#endregion
5749
5589
  //#region src/memory/store.ts
5750
- const log$26 = larkLogger("memory/store");
5590
+ const log$27 = larkLogger("memory/store");
5751
5591
  const MEMORY_VERSION = 1;
5752
5592
  const MAX_TEAMMATE_RECENT = 3;
5753
5593
  const MAX_LONG_TERM = 100;
@@ -5773,7 +5613,7 @@ var MemoryStore = class MemoryStore {
5773
5613
  const raw = await readFile(path, "utf-8");
5774
5614
  return normalizeMemoryFile(JSON.parse(raw));
5775
5615
  } catch (err) {
5776
- log$26.warn("memory file load failed, using empty state", {
5616
+ log$27.warn("memory file load failed, using empty state", {
5777
5617
  cwd,
5778
5618
  path,
5779
5619
  error: err instanceof Error ? err.message : String(err)
@@ -6269,7 +6109,7 @@ function createMcpCallbackRouter() {
6269
6109
  * 提供 /api/scheduled-tasks 的 RESTful CRUD 接口。
6270
6110
  * 每个请求和响应都记录详细日志,便于后续错误排查。
6271
6111
  */
6272
- const log$25 = larkLogger("gateway/scheduler-handler");
6112
+ const log$26 = larkLogger("gateway/scheduler-handler");
6273
6113
  /**
6274
6114
  * 创建定时任务路由
6275
6115
  *
@@ -6280,16 +6120,16 @@ function createSchedulerRouter(taskManager) {
6280
6120
  const router = Router();
6281
6121
  router.get("/api/scheduled-tasks", (req, res) => {
6282
6122
  const requestId = req.headers["x-request-id"];
6283
- log$25.info("请求列出所有定时任务", { requestId });
6123
+ log$26.info("请求列出所有定时任务", { requestId });
6284
6124
  try {
6285
6125
  const tasks = taskManager.listTasks();
6286
- log$25.info("返回定时任务列表", {
6126
+ log$26.info("返回定时任务列表", {
6287
6127
  requestId,
6288
6128
  count: tasks.length
6289
6129
  });
6290
6130
  res.json({ tasks });
6291
6131
  } catch (err) {
6292
- log$25.error("列出定时任务失败", {
6132
+ log$26.error("列出定时任务失败", {
6293
6133
  requestId,
6294
6134
  error: String(err)
6295
6135
  });
@@ -6302,13 +6142,13 @@ function createSchedulerRouter(taskManager) {
6302
6142
  router.post("/api/scheduled-tasks", (req, res) => {
6303
6143
  const requestId = req.headers["x-request-id"];
6304
6144
  const body = req.body;
6305
- log$25.info("请求创建定时任务", {
6145
+ log$26.info("请求创建定时任务", {
6306
6146
  requestId,
6307
6147
  body: JSON.stringify(body)
6308
6148
  });
6309
6149
  const { name, cron: cronExpr, session_id, cwd, prompt, example_output, constraints } = body;
6310
6150
  if (!name || typeof name !== "string") {
6311
- log$25.warn("创建任务参数缺失: name", { requestId });
6151
+ log$26.warn("创建任务参数缺失: name", { requestId });
6312
6152
  res.status(400).json({
6313
6153
  error: "参数错误",
6314
6154
  detail: "name 为必填字符串"
@@ -6316,7 +6156,7 @@ function createSchedulerRouter(taskManager) {
6316
6156
  return;
6317
6157
  }
6318
6158
  if (!cronExpr || typeof cronExpr !== "string") {
6319
- log$25.warn("创建任务参数缺失: cron", { requestId });
6159
+ log$26.warn("创建任务参数缺失: cron", { requestId });
6320
6160
  res.status(400).json({
6321
6161
  error: "参数错误",
6322
6162
  detail: "cron 为必填字符串"
@@ -6324,7 +6164,7 @@ function createSchedulerRouter(taskManager) {
6324
6164
  return;
6325
6165
  }
6326
6166
  if (!session_id || typeof session_id !== "string") {
6327
- log$25.warn("创建任务参数缺失: session_id", { requestId });
6167
+ log$26.warn("创建任务参数缺失: session_id", { requestId });
6328
6168
  res.status(400).json({
6329
6169
  error: "参数错误",
6330
6170
  detail: "session_id 为必填字符串"
@@ -6332,7 +6172,7 @@ function createSchedulerRouter(taskManager) {
6332
6172
  return;
6333
6173
  }
6334
6174
  if (!cwd || typeof cwd !== "string") {
6335
- log$25.warn("创建任务参数缺失: cwd", { requestId });
6175
+ log$26.warn("创建任务参数缺失: cwd", { requestId });
6336
6176
  res.status(400).json({
6337
6177
  error: "参数错误",
6338
6178
  detail: "cwd 为必填字符串"
@@ -6340,7 +6180,7 @@ function createSchedulerRouter(taskManager) {
6340
6180
  return;
6341
6181
  }
6342
6182
  if (!prompt || typeof prompt !== "string") {
6343
- log$25.warn("创建任务参数缺失: prompt", { requestId });
6183
+ log$26.warn("创建任务参数缺失: prompt", { requestId });
6344
6184
  res.status(400).json({
6345
6185
  error: "参数错误",
6346
6186
  detail: "prompt 为必填字符串"
@@ -6348,7 +6188,7 @@ function createSchedulerRouter(taskManager) {
6348
6188
  return;
6349
6189
  }
6350
6190
  if (!cron.validate(cronExpr)) {
6351
- log$25.warn("cron 表达式不合法", {
6191
+ log$26.warn("cron 表达式不合法", {
6352
6192
  requestId,
6353
6193
  cron: cronExpr
6354
6194
  });
@@ -6369,7 +6209,7 @@ function createSchedulerRouter(taskManager) {
6369
6209
  constraints
6370
6210
  };
6371
6211
  const task = taskManager.createTask(params);
6372
- log$25.info("定时任务创建成功", {
6212
+ log$26.info("定时任务创建成功", {
6373
6213
  requestId,
6374
6214
  taskId: task.id,
6375
6215
  name: task.name,
@@ -6377,7 +6217,7 @@ function createSchedulerRouter(taskManager) {
6377
6217
  });
6378
6218
  res.status(201).json({ task });
6379
6219
  } catch (err) {
6380
- log$25.error("创建定时任务失败", {
6220
+ log$26.error("创建定时任务失败", {
6381
6221
  requestId,
6382
6222
  error: String(err)
6383
6223
  });
@@ -6391,13 +6231,13 @@ function createSchedulerRouter(taskManager) {
6391
6231
  const requestId = req.headers["x-request-id"];
6392
6232
  const id = String(req.params.id);
6393
6233
  const body = req.body;
6394
- log$25.info("请求更新定时任务", {
6234
+ log$26.info("请求更新定时任务", {
6395
6235
  requestId,
6396
6236
  taskId: id,
6397
6237
  body: JSON.stringify(body)
6398
6238
  });
6399
6239
  if (!taskManager.getTask(id)) {
6400
- log$25.warn("更新的任务不存在", {
6240
+ log$26.warn("更新的任务不存在", {
6401
6241
  requestId,
6402
6242
  taskId: id
6403
6243
  });
@@ -6409,7 +6249,7 @@ function createSchedulerRouter(taskManager) {
6409
6249
  }
6410
6250
  if (body.cron !== void 0) {
6411
6251
  if (typeof body.cron !== "string" || !cron.validate(body.cron)) {
6412
- log$25.warn("更新的 cron 表达式不合法", {
6252
+ log$26.warn("更新的 cron 表达式不合法", {
6413
6253
  requestId,
6414
6254
  taskId: id,
6415
6255
  cron: body.cron
@@ -6430,7 +6270,7 @@ function createSchedulerRouter(taskManager) {
6430
6270
  if (body.constraints !== void 0) updates.constraints = body.constraints;
6431
6271
  if (body.enabled !== void 0) updates.enabled = body.enabled;
6432
6272
  const task = taskManager.updateTask(id, updates);
6433
- log$25.info("定时任务更新成功", {
6273
+ log$26.info("定时任务更新成功", {
6434
6274
  requestId,
6435
6275
  taskId: task.id,
6436
6276
  name: task.name,
@@ -6438,7 +6278,7 @@ function createSchedulerRouter(taskManager) {
6438
6278
  });
6439
6279
  res.json({ task });
6440
6280
  } catch (err) {
6441
- log$25.error("更新定时任务失败", {
6281
+ log$26.error("更新定时任务失败", {
6442
6282
  requestId,
6443
6283
  taskId: id,
6444
6284
  error: String(err)
@@ -6452,12 +6292,12 @@ function createSchedulerRouter(taskManager) {
6452
6292
  router.delete("/api/scheduled-tasks/:id", (req, res) => {
6453
6293
  const requestId = req.headers["x-request-id"];
6454
6294
  const id = String(req.params.id);
6455
- log$25.info("请求删除定时任务", {
6295
+ log$26.info("请求删除定时任务", {
6456
6296
  requestId,
6457
6297
  taskId: id
6458
6298
  });
6459
6299
  if (!taskManager.getTask(id)) {
6460
- log$25.warn("删除的任务不存在", {
6300
+ log$26.warn("删除的任务不存在", {
6461
6301
  requestId,
6462
6302
  taskId: id
6463
6303
  });
@@ -6469,7 +6309,7 @@ function createSchedulerRouter(taskManager) {
6469
6309
  }
6470
6310
  try {
6471
6311
  const deleted = taskManager.deleteTask(id);
6472
- log$25.info("定时任务删除结果", {
6312
+ log$26.info("定时任务删除结果", {
6473
6313
  requestId,
6474
6314
  taskId: id,
6475
6315
  deleted
@@ -6479,7 +6319,7 @@ function createSchedulerRouter(taskManager) {
6479
6319
  deleted: true
6480
6320
  });
6481
6321
  } catch (err) {
6482
- log$25.error("删除定时任务失败", {
6322
+ log$26.error("删除定时任务失败", {
6483
6323
  requestId,
6484
6324
  taskId: id,
6485
6325
  error: String(err)
@@ -7036,7 +6876,7 @@ function createGatewayServer(config) {
7036
6876
  *
7037
6877
  * 持久化文件: /workspace/config/scheduled-tasks.json
7038
6878
  */
7039
- const log$24 = larkLogger("gateway/scheduler");
6879
+ const log$25 = larkLogger("gateway/scheduler");
7040
6880
  /** 持久化文件路径 */
7041
6881
  const PERSIST_PATH = "/workspace/config/scheduled-tasks.json";
7042
6882
  /**
@@ -7054,7 +6894,7 @@ var ScheduledTaskManager = class {
7054
6894
  processManager;
7055
6895
  constructor(processManager) {
7056
6896
  this.processManager = processManager;
7057
- log$24.info("ScheduledTaskManager 已创建");
6897
+ log$25.info("ScheduledTaskManager 已创建");
7058
6898
  }
7059
6899
  /**
7060
6900
  * 创建定时任务
@@ -7074,7 +6914,7 @@ var ScheduledTaskManager = class {
7074
6914
  enabled: true,
7075
6915
  created_at: (/* @__PURE__ */ new Date()).toISOString()
7076
6916
  };
7077
- log$24.info("创建定时任务", {
6917
+ log$25.info("创建定时任务", {
7078
6918
  taskId: task.id,
7079
6919
  name: task.name,
7080
6920
  cron: task.cron,
@@ -7110,7 +6950,7 @@ var ScheduledTaskManager = class {
7110
6950
  if (updates.example_output !== void 0) task.example_output = updates.example_output;
7111
6951
  if (updates.constraints !== void 0) task.constraints = updates.constraints;
7112
6952
  if (updates.enabled !== void 0) task.enabled = updates.enabled;
7113
- log$24.info("更新定时任务", {
6953
+ log$25.info("更新定时任务", {
7114
6954
  taskId: id,
7115
6955
  name: task.name,
7116
6956
  cronChanged: oldCron !== task.cron,
@@ -7132,10 +6972,10 @@ var ScheduledTaskManager = class {
7132
6972
  deleteTask(id) {
7133
6973
  const task = this.tasks.get(id);
7134
6974
  if (!task) {
7135
- log$24.warn("尝试删除不存在的任务", { taskId: id });
6975
+ log$25.warn("尝试删除不存在的任务", { taskId: id });
7136
6976
  return false;
7137
6977
  }
7138
- log$24.info("删除定时任务", {
6978
+ log$25.info("删除定时任务", {
7139
6979
  taskId: id,
7140
6980
  name: task.name
7141
6981
  });
@@ -7150,28 +6990,28 @@ var ScheduledTaskManager = class {
7150
6990
  * 启动时调用,读取 JSON 文件并为每个 enabled 的任务注册 cron。
7151
6991
  */
7152
6992
  async loadFromDisk() {
7153
- log$24.info("从磁盘加载定时任务", { path: PERSIST_PATH });
6993
+ log$25.info("从磁盘加载定时任务", { path: PERSIST_PATH });
7154
6994
  try {
7155
6995
  const content = await readFile(PERSIST_PATH, "utf-8");
7156
6996
  const data = JSON.parse(content);
7157
6997
  if (!Array.isArray(data)) {
7158
- log$24.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
6998
+ log$25.warn("持久化文件格式异常,跳过加载", { path: PERSIST_PATH });
7159
6999
  return;
7160
7000
  }
7161
7001
  for (const task of data) {
7162
7002
  this.tasks.set(task.id, task);
7163
7003
  if (task.enabled) this.registerCronJob(task);
7164
7004
  }
7165
- log$24.info("定时任务加载完成", {
7005
+ log$25.info("定时任务加载完成", {
7166
7006
  total: data.length,
7167
7007
  enabled: data.filter((t) => t.enabled).length
7168
7008
  });
7169
7009
  } catch (err) {
7170
7010
  if (err.code === "ENOENT") {
7171
- log$24.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
7011
+ log$25.info("持久化文件不存在,跳过加载(首次启动)", { path: PERSIST_PATH });
7172
7012
  return;
7173
7013
  }
7174
- log$24.error("加载定时任务失败", {
7014
+ log$25.error("加载定时任务失败", {
7175
7015
  path: PERSIST_PATH,
7176
7016
  error: String(err)
7177
7017
  });
@@ -7187,12 +7027,12 @@ var ScheduledTaskManager = class {
7187
7027
  try {
7188
7028
  await mkdir(dirname(PERSIST_PATH), { recursive: true });
7189
7029
  await writeFile(PERSIST_PATH, JSON.stringify(tasks, null, 2), "utf-8");
7190
- log$24.info("定时任务已持久化到磁盘", {
7030
+ log$25.info("定时任务已持久化到磁盘", {
7191
7031
  path: PERSIST_PATH,
7192
7032
  count: tasks.length
7193
7033
  });
7194
7034
  } catch (err) {
7195
- log$24.error("持久化定时任务失败", {
7035
+ log$25.error("持久化定时任务失败", {
7196
7036
  path: PERSIST_PATH,
7197
7037
  error: String(err)
7198
7038
  });
@@ -7205,13 +7045,13 @@ var ScheduledTaskManager = class {
7205
7045
  */
7206
7046
  registerCronJob(task) {
7207
7047
  this.unregisterCronJob(task.id);
7208
- log$24.info("注册 cron 调度", {
7048
+ log$25.info("注册 cron 调度", {
7209
7049
  taskId: task.id,
7210
7050
  name: task.name,
7211
7051
  cron: task.cron
7212
7052
  });
7213
7053
  const job = cron.schedule(task.cron, () => {
7214
- log$24.info("cron 触发任务执行", {
7054
+ log$25.info("cron 触发任务执行", {
7215
7055
  taskId: task.id,
7216
7056
  name: task.name
7217
7057
  });
@@ -7227,7 +7067,7 @@ var ScheduledTaskManager = class {
7227
7067
  if (job) {
7228
7068
  job.stop();
7229
7069
  this.cronJobs.delete(taskId);
7230
- log$24.info("已注销 cron 调度", { taskId });
7070
+ log$25.info("已注销 cron 调度", { taskId });
7231
7071
  }
7232
7072
  }
7233
7073
  /**
@@ -7237,10 +7077,10 @@ var ScheduledTaskManager = class {
7237
7077
  */
7238
7078
  stopAll() {
7239
7079
  const count = this.cronJobs.size;
7240
- log$24.info("停止所有 cron 调度", { count });
7080
+ log$25.info("停止所有 cron 调度", { count });
7241
7081
  for (const [taskId, job] of this.cronJobs) {
7242
7082
  job.stop();
7243
- log$24.debug("已停止 cron 调度", { taskId });
7083
+ log$25.debug("已停止 cron 调度", { taskId });
7244
7084
  }
7245
7085
  this.cronJobs.clear();
7246
7086
  }
@@ -7254,17 +7094,17 @@ var ScheduledTaskManager = class {
7254
7094
  async executeTask(taskId) {
7255
7095
  const task = this.tasks.get(taskId);
7256
7096
  if (!task) {
7257
- log$24.warn("任务执行时未找到任务", { taskId });
7097
+ log$25.warn("任务执行时未找到任务", { taskId });
7258
7098
  return;
7259
7099
  }
7260
7100
  if (!task.enabled) {
7261
- log$24.info("任务已禁用,跳过执行", {
7101
+ log$25.info("任务已禁用,跳过执行", {
7262
7102
  taskId,
7263
7103
  name: task.name
7264
7104
  });
7265
7105
  return;
7266
7106
  }
7267
- log$24.info("开始执行定时任务", {
7107
+ log$25.info("开始执行定时任务", {
7268
7108
  taskId: task.id,
7269
7109
  name: task.name,
7270
7110
  sessionId: task.session_id,
@@ -7285,7 +7125,7 @@ var ScheduledTaskManager = class {
7285
7125
  onResult: (result) => {
7286
7126
  const summary = result.result ?? resultText;
7287
7127
  task.last_result = summary.slice(0, 2e3);
7288
- log$24.info("定时任务执行完成", {
7128
+ log$25.info("定时任务执行完成", {
7289
7129
  taskId: task.id,
7290
7130
  name: task.name,
7291
7131
  subtype: result.subtype,
@@ -7298,7 +7138,7 @@ var ScheduledTaskManager = class {
7298
7138
  },
7299
7139
  onError: (error) => {
7300
7140
  task.last_result = `执行失败: ${error.message}`;
7301
- log$24.error("定时任务执行失败", {
7141
+ log$25.error("定时任务执行失败", {
7302
7142
  taskId: task.id,
7303
7143
  name: task.name,
7304
7144
  error: error.message
@@ -7310,7 +7150,7 @@ var ScheduledTaskManager = class {
7310
7150
  await this.processManager.executePrompt(config, callbacks);
7311
7151
  } catch (err) {
7312
7152
  task.last_result = `执行异常: ${String(err)}`;
7313
- log$24.error("定时任务执行异常", {
7153
+ log$25.error("定时任务执行异常", {
7314
7154
  taskId: task.id,
7315
7155
  name: task.name,
7316
7156
  error: String(err)
@@ -7339,17 +7179,18 @@ var ScheduledTaskManager = class {
7339
7179
  * - 会话元数据:{workspaceRoot}/.chat-history/sessions/{userId}.json
7340
7180
  * - 消息数据:{workspaceRoot}/.chat-history/messages/{sessionId}.json
7341
7181
  */
7342
- const log$23 = larkLogger("chat/message-store");
7182
+ const log$24 = larkLogger("chat/message-store");
7343
7183
  var SessionMessageStore = class {
7344
7184
  baseDir;
7345
7185
  sessionsDir;
7346
7186
  messagesDir;
7187
+ jsonCache = /* @__PURE__ */ new Map();
7347
7188
  constructor(workspaceRoot) {
7348
7189
  const root = workspaceRoot || resolveWorkspaceRoot();
7349
7190
  this.baseDir = getWorkspaceChatHistoryRoot(root);
7350
7191
  this.sessionsDir = join(this.baseDir, "sessions");
7351
7192
  this.messagesDir = join(this.baseDir, "messages");
7352
- log$23.info("初始化消息存储", { baseDir: this.baseDir });
7193
+ log$24.info("初始化消息存储", { baseDir: this.baseDir });
7353
7194
  }
7354
7195
  /**
7355
7196
  * 创建新会话
@@ -7362,7 +7203,7 @@ var SessionMessageStore = class {
7362
7203
  updatedAt: now,
7363
7204
  messageCount: 0
7364
7205
  };
7365
- log$23.info("创建会话", {
7206
+ log$24.info("创建会话", {
7366
7207
  sessionId: fullSession.id,
7367
7208
  userId: fullSession.userId
7368
7209
  });
@@ -7375,7 +7216,7 @@ var SessionMessageStore = class {
7375
7216
  * 获取指定会话
7376
7217
  */
7377
7218
  async getSession(sessionId) {
7378
- log$23.debug("获取会话", { sessionId });
7219
+ log$24.debug("获取会话", { sessionId });
7379
7220
  try {
7380
7221
  const files = await this.listSessionFiles();
7381
7222
  for (const file of files) {
@@ -7385,7 +7226,7 @@ var SessionMessageStore = class {
7385
7226
  if (found) return found;
7386
7227
  }
7387
7228
  } catch (err) {
7388
- log$23.error("获取会话失败", {
7229
+ log$24.error("获取会话失败", {
7389
7230
  sessionId,
7390
7231
  error: String(err)
7391
7232
  });
@@ -7396,14 +7237,14 @@ var SessionMessageStore = class {
7396
7237
  * 列出指定用户的所有会话
7397
7238
  */
7398
7239
  async listSessions(userId) {
7399
- log$23.debug("列出用户会话", { userId });
7240
+ log$24.debug("列出用户会话", { userId });
7400
7241
  return await this.readUserSessions(userId);
7401
7242
  }
7402
7243
  /**
7403
7244
  * 删除指定会话(同时删除消息文件)
7404
7245
  */
7405
7246
  async deleteSession(sessionId) {
7406
- log$23.info("删除会话", { sessionId });
7247
+ log$24.info("删除会话", { sessionId });
7407
7248
  try {
7408
7249
  const files = await this.listSessionFiles();
7409
7250
  for (const file of files) {
@@ -7418,9 +7259,9 @@ var SessionMessageStore = class {
7418
7259
  }
7419
7260
  const msgFile = this.getMessageFilePath(sessionId);
7420
7261
  await this.writeJsonFile(msgFile, []);
7421
- log$23.info("会话删除完成", { sessionId });
7262
+ log$24.info("会话删除完成", { sessionId });
7422
7263
  } catch (err) {
7423
- log$23.error("删除会话失败", {
7264
+ log$24.error("删除会话失败", {
7424
7265
  sessionId,
7425
7266
  error: String(err)
7426
7267
  });
@@ -7437,7 +7278,7 @@ var SessionMessageStore = class {
7437
7278
  id: v4(),
7438
7279
  timestamp: Date.now()
7439
7280
  };
7440
- log$23.info("追加消息", {
7281
+ log$24.info("追加消息", {
7441
7282
  messageId: fullMessage.id,
7442
7283
  sessionId: fullMessage.sessionId,
7443
7284
  role: fullMessage.role
@@ -7448,9 +7289,9 @@ var SessionMessageStore = class {
7448
7289
  messages.push(fullMessage);
7449
7290
  await this.writeJsonFile(msgFile, messages);
7450
7291
  await this.updateSessionMeta(fullMessage.sessionId, messages.length);
7451
- log$23.debug("消息追加完成", { messageId: fullMessage.id });
7292
+ log$24.debug("消息追加完成", { messageId: fullMessage.id });
7452
7293
  } catch (err) {
7453
- log$23.error("追加消息失败", {
7294
+ log$24.error("追加消息失败", {
7454
7295
  sessionId: fullMessage.sessionId,
7455
7296
  error: String(err)
7456
7297
  });
@@ -7465,7 +7306,7 @@ var SessionMessageStore = class {
7465
7306
  async getMessages(sessionId, options) {
7466
7307
  const limit = options?.limit ?? 50;
7467
7308
  const cursor = options?.cursor;
7468
- log$23.debug("获取消息列表", {
7309
+ log$24.debug("获取消息列表", {
7469
7310
  sessionId,
7470
7311
  cursor,
7471
7312
  limit
@@ -7484,7 +7325,7 @@ var SessionMessageStore = class {
7484
7325
  nextCursor: startIdx + limit < allMessages.length ? sliced[sliced.length - 1]?.id : void 0
7485
7326
  };
7486
7327
  } catch (err) {
7487
- log$23.error("获取消息列表失败", {
7328
+ log$24.error("获取消息列表失败", {
7488
7329
  sessionId,
7489
7330
  error: String(err)
7490
7331
  });
@@ -7495,12 +7336,12 @@ var SessionMessageStore = class {
7495
7336
  * 获取会话消息总数
7496
7337
  */
7497
7338
  async getMessageCount(sessionId) {
7498
- log$23.debug("获取消息数量", { sessionId });
7339
+ log$24.debug("获取消息数量", { sessionId });
7499
7340
  try {
7500
7341
  const msgFile = this.getMessageFilePath(sessionId);
7501
7342
  return (await this.readJsonFile(msgFile) || []).length;
7502
7343
  } catch (err) {
7503
- log$23.error("获取消息数量失败", {
7344
+ log$24.error("获取消息数量失败", {
7504
7345
  sessionId,
7505
7346
  error: String(err)
7506
7347
  });
@@ -7551,7 +7392,7 @@ var SessionMessageStore = class {
7551
7392
  }
7552
7393
  }
7553
7394
  } catch (err) {
7554
- log$23.warn("更新会话元数据失败", {
7395
+ log$24.warn("更新会话元数据失败", {
7555
7396
  sessionId,
7556
7397
  error: String(err)
7557
7398
  });
@@ -7566,7 +7407,7 @@ var SessionMessageStore = class {
7566
7407
  await mkdir(this.sessionsDir, { recursive: true });
7567
7408
  return (await readdir(this.sessionsDir)).filter((e) => e.endsWith(".json")).map((e) => join(this.sessionsDir, e));
7568
7409
  } catch (err) {
7569
- log$23.error("列出会话索引文件失败", { error: String(err) });
7410
+ log$24.error("列出会话索引文件失败", { error: String(err) });
7570
7411
  return [];
7571
7412
  }
7572
7413
  }
@@ -7576,13 +7417,19 @@ var SessionMessageStore = class {
7576
7417
  async readJsonFile(filePath) {
7577
7418
  try {
7578
7419
  const raw = await readFile(filePath, "utf-8");
7579
- return JSON.parse(raw);
7420
+ const parsed = JSON.parse(raw);
7421
+ this.jsonCache.set(filePath, parsed);
7422
+ return parsed;
7580
7423
  } catch (err) {
7581
7424
  if (err && typeof err === "object" && "code" in err && err.code === "ENOENT") return null;
7582
- log$23.error("读取 JSON 文件失败", {
7425
+ log$24.error("读取 JSON 文件失败", {
7583
7426
  filePath,
7584
7427
  error: String(err)
7585
7428
  });
7429
+ if (this.jsonCache.has(filePath)) {
7430
+ log$24.warn("读取 JSON 文件失败,使用内存中的上一份完整快照", { filePath });
7431
+ return this.jsonCache.get(filePath);
7432
+ }
7586
7433
  return null;
7587
7434
  }
7588
7435
  }
@@ -7590,18 +7437,39 @@ var SessionMessageStore = class {
7590
7437
  * 通用 JSON 文件写入(自动创建目录)
7591
7438
  */
7592
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`);
7593
7442
  try {
7594
- const { dirname } = await import("node:path");
7595
- await mkdir(dirname(filePath), { recursive: true });
7596
- 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);
7597
7454
  } catch (err) {
7598
- log$23.error("写入 JSON 文件失败", {
7455
+ log$24.error("写入 JSON 文件失败", {
7599
7456
  filePath,
7600
7457
  error: String(err)
7601
7458
  });
7459
+ await unlink(tmpPath).catch(() => void 0);
7602
7460
  throw err;
7603
7461
  }
7604
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
+ }
7605
7473
  };
7606
7474
  //#endregion
7607
7475
  //#region src/messaging/inbound/user-name-cache-store.ts
@@ -7876,7 +7744,7 @@ function getUserAgent() {
7876
7744
  * - `LarkClient.fromCredentials(credentials)` — ephemeral instance (not cached)
7877
7745
  * - `LarkClient.fromProvider(provider, opts)` — from ICredentialProvider (not cached)
7878
7746
  */
7879
- const log$21 = larkLogger("core/lark-client");
7747
+ const log$22 = larkLogger("core/lark-client");
7880
7748
  const GLOBAL_LARK_USER_AGENT_KEY = "LARK_USER_AGENT";
7881
7749
  function installGlobalUserAgent() {
7882
7750
  globalThis[GLOBAL_LARK_USER_AGENT_KEY] = getUserAgent();
@@ -7968,7 +7836,7 @@ var LarkClient = class LarkClient {
7968
7836
  const existing = cache.get(account.accountId);
7969
7837
  if (existing && existing.account.appId === account.appId && credentialsEqual(existing.account.appSecret, account.appSecret)) return existing;
7970
7838
  if (existing) {
7971
- log$21.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
7839
+ log$22.info(`credentials changed, disposing stale instance`, { accountId: account.accountId });
7972
7840
  existing.dispose();
7973
7841
  }
7974
7842
  const instance = new LarkClient(account);
@@ -8011,7 +7879,7 @@ var LarkClient = class LarkClient {
8011
7879
  static fromProvider(provider, opts) {
8012
7880
  const appId = provider.getAppId();
8013
7881
  const appSecret = provider.getAppSecret();
8014
- log$21.info("通过 ICredentialProvider 创建 LarkClient", {
7882
+ log$22.info("通过 ICredentialProvider 创建 LarkClient", {
8015
7883
  appId,
8016
7884
  accountId: opts?.accountId ?? "default",
8017
7885
  brand: opts?.brand ?? "feishu"
@@ -8047,7 +7915,7 @@ var LarkClient = class LarkClient {
8047
7915
  get sdk() {
8048
7916
  if (!this._sdk) {
8049
7917
  const { appId, appSecret } = this.requireCredentials();
8050
- log$21.info("创建 Lark SDK 客户端实例", {
7918
+ log$22.info("创建 Lark SDK 客户端实例", {
8051
7919
  accountId: this.accountId,
8052
7920
  appId,
8053
7921
  brand: this.account.brand
@@ -8062,7 +7930,7 @@ var LarkClient = class LarkClient {
8062
7930
  if (sdkAny.httpInstance?.interceptors) {
8063
7931
  const accountId = this.accountId;
8064
7932
  sdkAny.httpInstance.interceptors.request.use((req) => {
8065
- log$21.debug("飞书 API 请求", {
7933
+ log$22.debug("飞书 API 请求", {
8066
7934
  accountId,
8067
7935
  method: req.method,
8068
7936
  url: req.url,
@@ -8072,7 +7940,7 @@ var LarkClient = class LarkClient {
8072
7940
  return req;
8073
7941
  }, void 0, { synchronous: true });
8074
7942
  sdkAny.httpInstance.interceptors.response.use((res) => {
8075
- log$21.debug("飞书 API 响应", {
7943
+ log$22.debug("飞书 API 响应", {
8076
7944
  accountId,
8077
7945
  code: res?.code,
8078
7946
  msg: res?.msg,
@@ -8080,7 +7948,7 @@ var LarkClient = class LarkClient {
8080
7948
  });
8081
7949
  return res;
8082
7950
  }, (err) => {
8083
- log$21.error("飞书 API 请求失败", {
7951
+ log$22.error("飞书 API 请求失败", {
8084
7952
  accountId,
8085
7953
  error: err instanceof Error ? err.message : String(err),
8086
7954
  url: err?.config?.url
@@ -8167,7 +8035,7 @@ var LarkClient = class LarkClient {
8167
8035
  dispatcher.register(handlers);
8168
8036
  const { appId, appSecret } = this.requireCredentials();
8169
8037
  if (this._wsClient) {
8170
- log$21.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
8038
+ log$22.warn(`closing previous WSClient before reconnect`, { accountId: this.accountId });
8171
8039
  try {
8172
8040
  this._wsClient.close({ force: true });
8173
8041
  } catch {}
@@ -8184,7 +8052,7 @@ var LarkClient = class LarkClient {
8184
8052
  wsClientAny.handleEventData = (data) => {
8185
8053
  const msgType = data.headers?.find?.((h) => h.key === "type")?.value;
8186
8054
  const eventType = data.headers?.find?.((h) => h.key === "event_type")?.value;
8187
- log$21.info("WS 收到原始事件", {
8055
+ log$22.info("WS 收到原始事件", {
8188
8056
  accountId: this.accountId,
8189
8057
  msgType,
8190
8058
  eventType,
@@ -8210,14 +8078,14 @@ var LarkClient = class LarkClient {
8210
8078
  /** Disconnect WebSocket but keep instance in cache. */
8211
8079
  disconnect() {
8212
8080
  if (this._wsClient) {
8213
- log$21.info(`disconnecting WebSocket`, { accountId: this.accountId });
8081
+ log$22.info(`disconnecting WebSocket`, { accountId: this.accountId });
8214
8082
  try {
8215
8083
  this._wsClient.close({ force: true });
8216
8084
  } catch {}
8217
8085
  }
8218
8086
  this._wsClient = null;
8219
8087
  if (this.messageDedup) {
8220
- log$21.info(`disposing message dedup`, {
8088
+ log$22.info(`disposing message dedup`, {
8221
8089
  accountId: this.accountId,
8222
8090
  size: this.messageDedup.size
8223
8091
  });
@@ -8523,7 +8391,7 @@ function sortTraceValue(value) {
8523
8391
  }
8524
8392
  //#endregion
8525
8393
  //#region src/card/cc-stream-bridge.ts
8526
- const log$20 = larkLogger("card/cc-stream-bridge");
8394
+ const log$21 = larkLogger("card/cc-stream-bridge");
8527
8395
  const CC_INTERNAL_PLACEHOLDER = "No response requested.";
8528
8396
  /**
8529
8397
  * CCStreamBridge — 将 CC 流事件桥接到 StreamingCardController
@@ -8548,7 +8416,7 @@ var CCStreamBridge = class {
8548
8416
  sessionKey: options?.sessionKey
8549
8417
  };
8550
8418
  if (this.options.sessionKey) startToolUseTraceRun(this.options.sessionKey);
8551
- log$20.info("CCStreamBridge 初始化", {
8419
+ log$21.info("CCStreamBridge 初始化", {
8552
8420
  autoCompleteOnTurnEnd: this.options.autoCompleteOnTurnEnd,
8553
8421
  sessionKey: this.options.sessionKey ?? "(none)"
8554
8422
  });
@@ -8556,7 +8424,7 @@ var CCStreamBridge = class {
8556
8424
  /** 文本增量 → 累积后调用 controller.onPartialReply */
8557
8425
  onTextDelta(text) {
8558
8426
  this.accumulatedText += text;
8559
- log$20.debug("textDelta 事件", {
8427
+ log$21.debug("textDelta 事件", {
8560
8428
  deltaLen: text.length,
8561
8429
  totalLen: this.accumulatedText.length
8562
8430
  });
@@ -8565,7 +8433,7 @@ var CCStreamBridge = class {
8565
8433
  /** 思考增量 → 累积后调用 controller.onReasoningStream */
8566
8434
  onThinkingDelta(text) {
8567
8435
  this.accumulatedThinkingText += text;
8568
- log$20.debug("thinkingDelta 事件", {
8436
+ log$21.debug("thinkingDelta 事件", {
8569
8437
  deltaLen: text.length,
8570
8438
  totalLen: this.accumulatedThinkingText.length
8571
8439
  });
@@ -8577,7 +8445,7 @@ var CCStreamBridge = class {
8577
8445
  const displayName = getToolDisplayName(toolName);
8578
8446
  const toolParams = typeof _toolInput === "object" && _toolInput != null ? _toolInput : void 0;
8579
8447
  const hasParams = toolParams && Object.keys(toolParams).length > 0;
8580
- log$20.info("toolUseStart 事件", {
8448
+ log$21.info("toolUseStart 事件", {
8581
8449
  toolName,
8582
8450
  displayName,
8583
8451
  activeToolsCount: this.activeTools.size,
@@ -8590,7 +8458,7 @@ var CCStreamBridge = class {
8590
8458
  toolName,
8591
8459
  toolParams
8592
8460
  })) {
8593
- log$20.info("toolUseStart: 更新已有步骤的 params", { toolName });
8461
+ log$21.info("toolUseStart: 更新已有步骤的 params", { toolName });
8594
8462
  this.controller.onToolStart({
8595
8463
  name: toolName,
8596
8464
  phase: "start"
@@ -8614,7 +8482,7 @@ var CCStreamBridge = class {
8614
8482
  const toolName = this.activeTools.get(toolUseId) ?? this.lastToolName ?? "unknown";
8615
8483
  const displayName = getToolDisplayName(toolName);
8616
8484
  this.activeTools.delete(toolUseId);
8617
- log$20.info("toolResult 事件", {
8485
+ log$21.info("toolResult 事件", {
8618
8486
  toolUseId,
8619
8487
  toolName,
8620
8488
  displayName,
@@ -8629,7 +8497,7 @@ var CCStreamBridge = class {
8629
8497
  }
8630
8498
  /** 工具执行进度 → 记录日志 */
8631
8499
  onToolProgress(toolName, elapsedSeconds) {
8632
- log$20.debug("toolProgress 事件", {
8500
+ log$21.debug("toolProgress 事件", {
8633
8501
  toolName,
8634
8502
  displayName: getToolDisplayName(toolName),
8635
8503
  elapsedSeconds
@@ -8637,7 +8505,7 @@ var CCStreamBridge = class {
8637
8505
  }
8638
8506
  /** 轮次结束 → 仅在 end_turn 时标记完成 + 触发 onIdle */
8639
8507
  onTurnEnd(stopReason) {
8640
- log$20.info("turnEnd 事件", {
8508
+ log$21.info("turnEnd 事件", {
8641
8509
  stopReason,
8642
8510
  accumulatedTextLen: this.accumulatedText.length,
8643
8511
  accumulatedThinkingTextLen: this.accumulatedThinkingText.length
@@ -8645,7 +8513,7 @@ var CCStreamBridge = class {
8645
8513
  if (this.options.autoCompleteOnTurnEnd && stopReason === "end_turn") {
8646
8514
  const trimmedText = this.accumulatedText.trim();
8647
8515
  if (trimmedText === CC_INTERNAL_PLACEHOLDER) {
8648
- log$20.info("检测到 CC 内部占位消息,静默丢弃", {
8516
+ log$21.info("检测到 CC 内部占位消息,静默丢弃", {
8649
8517
  text: trimmedText.slice(0, 50),
8650
8518
  sessionKey: this.options.sessionKey
8651
8519
  });
@@ -8653,7 +8521,7 @@ var CCStreamBridge = class {
8653
8521
  return;
8654
8522
  }
8655
8523
  if (trimmedText === "") {
8656
- log$20.info("turnEnd 时文本为空,延迟到 onResult 兜底处理", { sessionKey: this.options.sessionKey });
8524
+ log$21.info("turnEnd 时文本为空,延迟到 onResult 兜底处理", { sessionKey: this.options.sessionKey });
8657
8525
  return;
8658
8526
  }
8659
8527
  this.controller.markFullyComplete();
@@ -8662,7 +8530,7 @@ var CCStreamBridge = class {
8662
8530
  }
8663
8531
  /** 最终结果 → 兜底最终化 + 错误处理 */
8664
8532
  onResult(data) {
8665
- log$20.info("result 事件", {
8533
+ log$21.info("result 事件", {
8666
8534
  subtype: data.subtype,
8667
8535
  isError: data.isError,
8668
8536
  durationMs: data.durationMs,
@@ -8671,7 +8539,7 @@ var CCStreamBridge = class {
8671
8539
  resultLen: data.result?.length ?? 0
8672
8540
  });
8673
8541
  if (data.aborted) {
8674
- log$20.info("运行时报告用户主动中断,按正常完成处理", {
8542
+ log$21.info("运行时报告用户主动中断,按正常完成处理", {
8675
8543
  subtype: data.subtype,
8676
8544
  sessionId: this.options.sessionKey
8677
8545
  });
@@ -8682,7 +8550,7 @@ var CCStreamBridge = class {
8682
8550
  const pm = ClaudeCodeAdapter.getInstance()?.getProcessManager();
8683
8551
  const sessionId = this.options.sessionKey;
8684
8552
  if (sessionId && pm ? pm.consumeAborted(sessionId) : false) {
8685
- log$20.info("用户主动中断,按正常完成处理", {
8553
+ log$21.info("用户主动中断,按正常完成处理", {
8686
8554
  subtype: data.subtype,
8687
8555
  sessionId
8688
8556
  });
@@ -8691,14 +8559,14 @@ var CCStreamBridge = class {
8691
8559
  this.controller.onIdle();
8692
8560
  } else {
8693
8561
  const errorMessage = data.result ?? `CC 执行失败: ${data.subtype}`;
8694
- log$20.error("CC 执行返回错误", {
8562
+ log$21.error("CC 执行返回错误", {
8695
8563
  subtype: data.subtype,
8696
8564
  errorMessage: errorMessage.slice(0, 200)
8697
8565
  });
8698
8566
  this.controller.onError(new Error(errorMessage), { kind: "cc-execution" });
8699
8567
  }
8700
8568
  } else if (!this.accumulatedText.trim()) if (data.result) {
8701
- log$20.info("result 兜底:使用 result.result 作为最终回复", {
8569
+ log$21.info("result 兜底:使用 result.result 作为最终回复", {
8702
8570
  resultLen: data.result.length,
8703
8571
  sessionKey: this.options.sessionKey
8704
8572
  });
@@ -8706,7 +8574,7 @@ var CCStreamBridge = class {
8706
8574
  this.controller.markFullyComplete();
8707
8575
  this.controller.onIdle();
8708
8576
  } else {
8709
- log$20.info("result 兜底:无文本且无 result,abort 卡片", { sessionKey: this.options.sessionKey });
8577
+ log$21.info("result 兜底:无文本且无 result,abort 卡片", { sessionKey: this.options.sessionKey });
8710
8578
  this.controller.abortCard();
8711
8579
  }
8712
8580
  else {
@@ -8720,7 +8588,7 @@ var CCStreamBridge = class {
8720
8588
  * 内部复用上面的直接调用方法。
8721
8589
  */
8722
8590
  bindParser(parser) {
8723
- log$20.info("绑定 CCStreamParser 事件到卡片控制器");
8591
+ log$21.info("绑定 CCStreamParser 事件到卡片控制器");
8724
8592
  parser.on("textDelta", (text) => this.onTextDelta(text));
8725
8593
  parser.on("thinkingDelta", (text) => this.onThinkingDelta(text));
8726
8594
  parser.on("toolUseStart", (toolName, toolInput) => this.onToolUseStart(toolName, toolInput));
@@ -8728,7 +8596,7 @@ var CCStreamBridge = class {
8728
8596
  parser.on("toolProgress", (toolName, elapsedSeconds) => this.onToolProgress(toolName, elapsedSeconds));
8729
8597
  parser.on("turnEnd", (stopReason) => this.onTurnEnd(stopReason));
8730
8598
  parser.on("result", (data) => this.onResult(data));
8731
- log$20.info("CCStreamParser 事件绑定完成");
8599
+ log$21.info("CCStreamParser 事件绑定完成");
8732
8600
  }
8733
8601
  };
8734
8602
  //#endregion
@@ -10747,7 +10615,7 @@ function resolveLarkSdk(cfg, accountId) {
10747
10615
  if (cached) return cached.sdk;
10748
10616
  return LarkClient.fromCfg(cfg, accountId).sdk;
10749
10617
  }
10750
- const log$19 = larkLogger("card/cardkit");
10618
+ const log$20 = larkLogger("card/cardkit");
10751
10619
  /**
10752
10620
  * 记录 CardKit API 响应日志,检测错误码并抛出异常。
10753
10621
  *
@@ -10757,13 +10625,13 @@ const log$19 = larkLogger("card/cardkit");
10757
10625
  function logCardKitResponse(params) {
10758
10626
  const { resp, api, context } = params;
10759
10627
  const { code, msg } = resp;
10760
- log$19.info(`cardkit ${api} response`, {
10628
+ log$20.info(`cardkit ${api} response`, {
10761
10629
  code,
10762
10630
  msg,
10763
10631
  context
10764
10632
  });
10765
10633
  if (code && code !== 0) {
10766
- log$19.warn(`cardkit ${api} FAILED`, {
10634
+ log$20.warn(`cardkit ${api} FAILED`, {
10767
10635
  code,
10768
10636
  msg,
10769
10637
  context,
@@ -11174,7 +11042,7 @@ function validateLocalMediaRoots(filePath, localRoots) {
11174
11042
  * Feishu messages, uploading media to the Feishu IM storage, and
11175
11043
  * sending image / file messages to chats.
11176
11044
  */
11177
- const log$18 = larkLogger("outbound/media");
11045
+ const log$19 = larkLogger("outbound/media");
11178
11046
  /**
11179
11047
  * Upload an image to Feishu IM storage.
11180
11048
  *
@@ -11242,7 +11110,7 @@ async function validateRemoteUrl(raw) {
11242
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}"`);
11243
11111
  } catch (err) {
11244
11112
  if (err instanceof Error && err.message.includes("SSRF protection")) throw err;
11245
- log$18.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
11113
+ log$19.warn(`[feishu-media] DNS resolution failed for "${hostname}": ${err}`);
11246
11114
  }
11247
11115
  }
11248
11116
  /**
@@ -11260,21 +11128,21 @@ async function fetchMediaBuffer(urlOrPath, localRoots) {
11260
11128
  if (localRoots !== void 0) validateLocalMediaRoots(filePath, localRoots);
11261
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.`);
11262
11130
  const buf = fs.readFileSync(filePath);
11263
- log$18.debug(`local file read: "${filePath}", ${buf.length} bytes`);
11131
+ log$19.debug(`local file read: "${filePath}", ${buf.length} bytes`);
11264
11132
  return buf;
11265
11133
  }
11266
11134
  await validateRemoteUrl(raw);
11267
11135
  const FETCH_TIMEOUT_MS = 3e4;
11268
- log$18.info(`fetching remote media: ${raw}`);
11136
+ log$19.info(`fetching remote media: ${raw}`);
11269
11137
  const response = await fetch(raw, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
11270
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.`);
11271
11139
  const arrayBuffer = await response.arrayBuffer();
11272
- log$18.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
11140
+ log$19.debug(`remote media fetched: ${raw}, ${arrayBuffer.byteLength} bytes`);
11273
11141
  return Buffer.from(arrayBuffer);
11274
11142
  }
11275
11143
  //#endregion
11276
11144
  //#region src/card/image-resolver.ts
11277
- const log$17 = larkLogger("card/image-resolver");
11145
+ const log$18 = larkLogger("card/image-resolver");
11278
11146
  /** Matches complete markdown image syntax: `![alt](value)` */
11279
11147
  const IMAGE_RE = /!\[([^\]]*)\]\(([^)\s]+)\)/g;
11280
11148
  var ImageResolver = class {
@@ -11320,14 +11188,14 @@ var ImageResolver = class {
11320
11188
  async resolveImagesAwait(text, timeoutMs) {
11321
11189
  this.resolveImages(text);
11322
11190
  if (this.pending.size > 0) {
11323
- log$17.info("resolveImagesAwait: waiting for uploads", {
11191
+ log$18.info("resolveImagesAwait: waiting for uploads", {
11324
11192
  count: this.pending.size,
11325
11193
  timeoutMs
11326
11194
  });
11327
11195
  const allUploads = Promise.all(this.pending.values());
11328
11196
  const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
11329
11197
  await Promise.race([allUploads, timeout]);
11330
- 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 });
11331
11199
  }
11332
11200
  return this.resolveImages(text);
11333
11201
  }
@@ -11337,7 +11205,7 @@ var ImageResolver = class {
11337
11205
  }
11338
11206
  async doUpload(url) {
11339
11207
  try {
11340
- log$17.info("uploading image", { url });
11208
+ log$18.info("uploading image", { url });
11341
11209
  const buffer = await fetchRemoteImageBuffer(url);
11342
11210
  const { imageKey } = await uploadImageLark({
11343
11211
  cfg: this.cfg,
@@ -11345,7 +11213,7 @@ var ImageResolver = class {
11345
11213
  imageType: "message",
11346
11214
  accountId: this.accountId
11347
11215
  });
11348
- log$17.info("image uploaded", {
11216
+ log$18.info("image uploaded", {
11349
11217
  url,
11350
11218
  imageKey
11351
11219
  });
@@ -11354,7 +11222,7 @@ var ImageResolver = class {
11354
11222
  this.onImageResolved();
11355
11223
  return imageKey;
11356
11224
  } catch (err) {
11357
- log$17.warn("image upload failed", {
11225
+ log$18.warn("image upload failed", {
11358
11226
  url,
11359
11227
  error: String(err)
11360
11228
  });
@@ -11375,7 +11243,7 @@ var ImageResolver = class {
11375
11243
  * Encapsulates the terminateDueToUnavailable / shouldSkipForUnavailable
11376
11244
  * logic previously scattered as closures in reply-dispatcher.ts.
11377
11245
  */
11378
- const log$16 = larkLogger("card/unavailable-guard");
11246
+ const log$17 = larkLogger("card/unavailable-guard");
11379
11247
  var UnavailableGuard = class {
11380
11248
  terminated = false;
11381
11249
  replyToMessageId;
@@ -11428,7 +11296,7 @@ var UnavailableGuard = class {
11428
11296
  this.terminated = true;
11429
11297
  this.onTerminate();
11430
11298
  const affectedMessageId = fromError?.messageId ?? this.replyToMessageId ?? cardMessageId ?? "unknown";
11431
- log$16.warn("reply pipeline terminated by unavailable message", {
11299
+ log$17.warn("reply pipeline terminated by unavailable message", {
11432
11300
  source,
11433
11301
  apiCode,
11434
11302
  messageId: affectedMessageId
@@ -11465,7 +11333,7 @@ function getActiveCard(sessionId) {
11465
11333
  * Delegates throttling to FlushController and message-unavailable
11466
11334
  * detection to UnavailableGuard.
11467
11335
  */
11468
- const log$15 = larkLogger("card/streaming");
11336
+ const log$16 = larkLogger("card/streaming");
11469
11337
  var StreamingCardController = class StreamingCardController {
11470
11338
  phase = "idle";
11471
11339
  cardKit = {
@@ -11553,7 +11421,7 @@ var StreamingCardController = class StreamingCardController {
11553
11421
  }
11554
11422
  }
11555
11423
  if (!entry) {
11556
- log$15.debug("footer metrics lookup: session entry missing", {
11424
+ log$16.debug("footer metrics lookup: session entry missing", {
11557
11425
  sessionKey: this.deps.sessionKey,
11558
11426
  candidateKeys,
11559
11427
  storePath,
@@ -11571,7 +11439,7 @@ var StreamingCardController = class StreamingCardController {
11571
11439
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
11572
11440
  model: typeof entry.model === "string" ? entry.model : void 0
11573
11441
  };
11574
- log$15.debug("footer metrics lookup: session entry found", {
11442
+ log$16.debug("footer metrics lookup: session entry found", {
11575
11443
  sessionKey: this.deps.sessionKey,
11576
11444
  matchedKey,
11577
11445
  storePath,
@@ -11596,7 +11464,7 @@ var StreamingCardController = class StreamingCardController {
11596
11464
  }
11597
11465
  }
11598
11466
  if (!entry) {
11599
- log$15.debug("footer metrics lookup: session entry missing", {
11467
+ log$16.debug("footer metrics lookup: session entry missing", {
11600
11468
  sessionKey: this.deps.sessionKey,
11601
11469
  candidateKeys,
11602
11470
  storePath,
@@ -11614,7 +11482,7 @@ var StreamingCardController = class StreamingCardController {
11614
11482
  contextTokens: typeof entry.contextTokens === "number" ? entry.contextTokens : void 0,
11615
11483
  model: typeof entry.model === "string" ? entry.model : void 0
11616
11484
  };
11617
- log$15.debug("footer metrics lookup: session entry found", {
11485
+ log$16.debug("footer metrics lookup: session entry found", {
11618
11486
  sessionKey: this.deps.sessionKey,
11619
11487
  matchedKey,
11620
11488
  storePath,
@@ -11622,7 +11490,7 @@ var StreamingCardController = class StreamingCardController {
11622
11490
  });
11623
11491
  return metrics;
11624
11492
  } catch (err) {
11625
- log$15.warn("footer metrics lookup failed", {
11493
+ log$16.warn("footer metrics lookup failed", {
11626
11494
  error: String(err),
11627
11495
  sessionKey: this.deps.sessionKey
11628
11496
  });
@@ -11729,7 +11597,7 @@ var StreamingCardController = class StreamingCardController {
11729
11597
  const from = this.phase;
11730
11598
  if (from === to) return false;
11731
11599
  if (!PHASE_TRANSITIONS[from].has(to)) {
11732
- log$15.warn("phase transition rejected", {
11600
+ log$16.warn("phase transition rejected", {
11733
11601
  from,
11734
11602
  to,
11735
11603
  source
@@ -11737,7 +11605,7 @@ var StreamingCardController = class StreamingCardController {
11737
11605
  return false;
11738
11606
  }
11739
11607
  this.phase = to;
11740
- log$15.info("phase transition", {
11608
+ log$16.info("phase transition", {
11741
11609
  from,
11742
11610
  to,
11743
11611
  source,
@@ -11857,7 +11725,7 @@ var StreamingCardController = class StreamingCardController {
11857
11725
  this.reasoning.dirty = true;
11858
11726
  }
11859
11727
  const text = split.answerText ?? stripReasoningTags(rawText);
11860
- log$15.debug("onPartialReply", { len: text.length });
11728
+ log$16.debug("onPartialReply", { len: text.length });
11861
11729
  if (!text) return;
11862
11730
  this.captureToolUseElapsed();
11863
11731
  if (!this.reasoning.reasoningStartTime) this.reasoning.reasoningStartTime = Date.now();
@@ -11869,7 +11737,7 @@ var StreamingCardController = class StreamingCardController {
11869
11737
  this.text.lastPartialText = text;
11870
11738
  this.text.accumulatedText = this.text.streamingPrefix ? this.text.streamingPrefix + "\n\n" + text : text;
11871
11739
  if (!this.text.streamingPrefix && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim())) {
11872
- log$15.debug("onPartialReply: buffering NO_REPLY prefix");
11740
+ log$16.debug("onPartialReply: buffering NO_REPLY prefix");
11873
11741
  return;
11874
11742
  }
11875
11743
  await this.ensureCardCreated();
@@ -11879,7 +11747,7 @@ var StreamingCardController = class StreamingCardController {
11879
11747
  }
11880
11748
  async onError(err, info) {
11881
11749
  if (this.guard.terminate("onError", err)) return;
11882
- log$15.error(`${info.kind} reply failed`, { error: String(err) });
11750
+ log$16.error(`${info.kind} reply failed`, { error: String(err) });
11883
11751
  this.captureToolUseElapsed();
11884
11752
  this.finalizeCard("onError", "error");
11885
11753
  await this.flush.waitForFlush();
@@ -11935,7 +11803,7 @@ var StreamingCardController = class StreamingCardController {
11935
11803
  if (this.cardKit.cardMessageId) {
11936
11804
  const isNoReplyLeak = !this.text.completedText && SILENT_REPLY_TOKEN.startsWith(this.text.accumulatedText.trim());
11937
11805
  const displayText = this.text.completedText || (isNoReplyLeak ? "" : this.text.accumulatedText) || "Done.";
11938
- 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");
11939
11807
  const resolvedDisplayText = await this.imageResolver.resolveImagesAwait(displayText, 15e3);
11940
11808
  const idleToolUseDisplay = this.computeToolUseDisplay();
11941
11809
  const terminalContent = prepareTerminalCardContent({
@@ -11963,7 +11831,7 @@ var StreamingCardController = class StreamingCardController {
11963
11831
  const rewindSessionId = this.deps.sessionKey;
11964
11832
  const rewindMessageId = ClaudeCodeAdapter.getInstance()?.getProcessManager()?.getLastUserMessageId(this.deps.sessionKey);
11965
11833
  const rewindHasFileChanges = this.hasFileChangingTools(idleToolUseDisplay?.steps);
11966
- log$15.info("onIdle: rewind button conditions", {
11834
+ log$16.info("onIdle: rewind button conditions", {
11967
11835
  sessionId: rewindSessionId,
11968
11836
  messageId: rewindMessageId,
11969
11837
  hasFileChanges: rewindHasFileChanges,
@@ -11976,7 +11844,7 @@ var StreamingCardController = class StreamingCardController {
11976
11844
  if (idleEffectiveCardId) {
11977
11845
  const seqBeforeClose = this.cardKit.cardKitSequence;
11978
11846
  this.cardKit.cardKitSequence += 1;
11979
- log$15.info("onIdle: closing streaming mode", {
11847
+ log$16.info("onIdle: closing streaming mode", {
11980
11848
  attempt,
11981
11849
  seqBefore: seqBeforeClose,
11982
11850
  seqAfter: this.cardKit.cardKitSequence
@@ -11990,7 +11858,7 @@ var StreamingCardController = class StreamingCardController {
11990
11858
  });
11991
11859
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
11992
11860
  this.cardKit.cardKitSequence += 1;
11993
- log$15.info("onIdle: updating final card", {
11861
+ log$16.info("onIdle: updating final card", {
11994
11862
  attempt,
11995
11863
  seqBefore: seqBeforeUpdate,
11996
11864
  seqAfter: this.cardKit.cardKitSequence
@@ -12008,33 +11876,33 @@ var StreamingCardController = class StreamingCardController {
12008
11876
  card: completeCard,
12009
11877
  accountId: this.deps.accountId
12010
11878
  });
12011
- log$15.info("reply completed, card finalized", {
11879
+ log$16.info("reply completed, card finalized", {
12012
11880
  elapsedMs: this.elapsed(),
12013
11881
  isCardKit: !!idleEffectiveCardId,
12014
11882
  attempt
12015
11883
  });
12016
11884
  break;
12017
11885
  } catch (retryErr) {
12018
- log$15.warn("final card update attempt failed", {
11886
+ log$16.warn("final card update attempt failed", {
12019
11887
  attempt,
12020
11888
  maxRetries: MAX_FINAL_UPDATE_RETRIES,
12021
11889
  error: String(retryErr)
12022
11890
  });
12023
11891
  if (attempt < MAX_FINAL_UPDATE_RETRIES) await new Promise((resolve) => setTimeout(resolve, FINAL_UPDATE_RETRY_DELAY_MS * attempt));
12024
- 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", {
12025
11893
  cardId: idleEffectiveCardId,
12026
11894
  messageId: this.cardKit.cardMessageId
12027
11895
  });
12028
11896
  }
12029
11897
  }
12030
11898
  } catch (err) {
12031
- log$15.error("final card update failed (outer)", { error: String(err) });
11899
+ log$16.error("final card update failed (outer)", { error: String(err) });
12032
11900
  } finally {
12033
11901
  clearToolUseTraceRun(this.deps.sessionKey);
12034
11902
  }
12035
11903
  }
12036
11904
  markFullyComplete() {
12037
- log$15.debug("markFullyComplete", {
11905
+ log$16.debug("markFullyComplete", {
12038
11906
  completedTextLen: this.text.completedText.length,
12039
11907
  accumulatedTextLen: this.text.accumulatedText.length
12040
11908
  });
@@ -12073,7 +11941,7 @@ var StreamingCardController = class StreamingCardController {
12073
11941
  footerMetrics
12074
11942
  });
12075
11943
  await this.closeStreamingAndUpdate(effectiveCardId, abortCardContent, "abortCard");
12076
- log$15.info("abortCard completed", { effectiveCardId });
11944
+ log$16.info("abortCard completed", { effectiveCardId });
12077
11945
  } else if (this.cardKit.cardMessageId) {
12078
11946
  const abortCard = buildCardContent("complete", {
12079
11947
  text: terminalContent.text,
@@ -12094,10 +11962,10 @@ var StreamingCardController = class StreamingCardController {
12094
11962
  card: abortCard,
12095
11963
  accountId: this.deps.accountId
12096
11964
  });
12097
- log$15.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
11965
+ log$16.info("abortCard completed (IM fallback)", { messageId: this.cardKit.cardMessageId });
12098
11966
  }
12099
11967
  } catch (err) {
12100
- log$15.warn("abortCard failed", { error: String(err) });
11968
+ log$16.warn("abortCard failed", { error: String(err) });
12101
11969
  } finally {
12102
11970
  clearToolUseTraceRun(this.deps.sessionKey);
12103
11971
  }
@@ -12112,7 +11980,7 @@ var StreamingCardController = class StreamingCardController {
12112
11980
  if (effectiveCardId) {
12113
11981
  const seqBeforeClose = this.cardKit.cardKitSequence;
12114
11982
  this.cardKit.cardKitSequence += 1;
12115
- log$15.info("replaceWithClarificationCard: closing streaming mode", {
11983
+ log$16.info("replaceWithClarificationCard: closing streaming mode", {
12116
11984
  cardId: effectiveCardId,
12117
11985
  seqBefore: seqBeforeClose,
12118
11986
  seqAfter: this.cardKit.cardKitSequence
@@ -12133,7 +12001,7 @@ var StreamingCardController = class StreamingCardController {
12133
12001
  sequence: this.cardKit.cardKitSequence,
12134
12002
  accountId: this.deps.accountId
12135
12003
  });
12136
- log$15.info("replaceWithClarificationCard: existing card replaced", {
12004
+ log$16.info("replaceWithClarificationCard: existing card replaced", {
12137
12005
  cardId: effectiveCardId,
12138
12006
  seqBefore: seqBeforeUpdate,
12139
12007
  seqAfter: this.cardKit.cardKitSequence
@@ -12156,7 +12024,7 @@ var StreamingCardController = class StreamingCardController {
12156
12024
  this.cardKit.cardKitCardId = cardId;
12157
12025
  this.cardKit.originalCardKitCardId = cardId;
12158
12026
  this.cardKit.cardMessageId = result.messageId;
12159
- log$15.info("replaceWithClarificationCard: new clarification card sent", {
12027
+ log$16.info("replaceWithClarificationCard: new clarification card sent", {
12160
12028
  cardId,
12161
12029
  messageId: result.messageId
12162
12030
  });
@@ -12165,7 +12033,7 @@ var StreamingCardController = class StreamingCardController {
12165
12033
  clearToolUseTraceRun(this.deps.sessionKey);
12166
12034
  return true;
12167
12035
  } catch (err) {
12168
- log$15.error("replaceWithClarificationCard failed", { error: String(err) });
12036
+ log$16.error("replaceWithClarificationCard failed", { error: String(err) });
12169
12037
  return false;
12170
12038
  }
12171
12039
  }
@@ -12179,10 +12047,10 @@ var StreamingCardController = class StreamingCardController {
12179
12047
  async forceCloseStreaming() {
12180
12048
  const effectiveCardId = this.cardKit.cardKitCardId ?? this.cardKit.originalCardKitCardId;
12181
12049
  if (!effectiveCardId && !this.cardKit.cardMessageId) {
12182
- log$15.warn("forceCloseStreaming: no card to close");
12050
+ log$16.warn("forceCloseStreaming: no card to close");
12183
12051
  return;
12184
12052
  }
12185
- log$15.info("forceCloseStreaming: 强制终态化卡片", {
12053
+ log$16.info("forceCloseStreaming: 强制终态化卡片", {
12186
12054
  cardId: effectiveCardId,
12187
12055
  messageId: this.cardKit.cardMessageId,
12188
12056
  phase: this.phase
@@ -12232,10 +12100,10 @@ var StreamingCardController = class StreamingCardController {
12232
12100
  card: completeCard,
12233
12101
  accountId: this.deps.accountId
12234
12102
  });
12235
- log$15.info("forceCloseStreaming: 卡片已强制终态化");
12103
+ log$16.info("forceCloseStreaming: 卡片已强制终态化");
12236
12104
  if (!this.isTerminalPhase) this.finalizeCard("forceCloseStreaming", "abort");
12237
12105
  } catch (err) {
12238
- log$15.error("forceCloseStreaming failed", { error: String(err) });
12106
+ log$16.error("forceCloseStreaming failed", { error: String(err) });
12239
12107
  } finally {
12240
12108
  unregisterActiveCard(this.deps.sessionKey);
12241
12109
  clearToolUseTraceRun(this.deps.sessionKey);
@@ -12259,7 +12127,7 @@ var StreamingCardController = class StreamingCardController {
12259
12127
  this.disposeShutdownHook = null;
12260
12128
  return;
12261
12129
  }
12262
- log$15.info("reusing placeholder card", {
12130
+ log$16.info("reusing placeholder card", {
12263
12131
  cardId: this.deps.placeholderCardId,
12264
12132
  messageId: this.deps.placeholderMessageId
12265
12133
  });
@@ -12283,7 +12151,7 @@ var StreamingCardController = class StreamingCardController {
12283
12151
  accountId: this.deps.accountId
12284
12152
  });
12285
12153
  if (this.isStaleCreate(epoch)) {
12286
- log$15.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
12154
+ log$16.info("ensureCardCreated: stale epoch after createCardEntity, bailing out", {
12287
12155
  epoch,
12288
12156
  phase: this.phase
12289
12157
  });
@@ -12294,7 +12162,7 @@ var StreamingCardController = class StreamingCardController {
12294
12162
  this.cardKit.originalCardKitCardId = cId;
12295
12163
  this.cardKit.cardKitSequence = 1;
12296
12164
  this.disposeShutdownHook = registerShutdownHook(`streaming-card:${cId}`, () => this.abortCard());
12297
- log$15.info("created CardKit entity", {
12165
+ log$16.info("created CardKit entity", {
12298
12166
  cardId: cId,
12299
12167
  initialSequence: this.cardKit.cardKitSequence
12300
12168
  });
@@ -12307,7 +12175,7 @@ var StreamingCardController = class StreamingCardController {
12307
12175
  accountId: this.deps.accountId
12308
12176
  });
12309
12177
  if (this.isStaleCreate(epoch)) {
12310
- log$15.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
12178
+ log$16.info("ensureCardCreated: stale epoch after sendCardByCardId, bailing out", {
12311
12179
  epoch,
12312
12180
  phase: this.phase
12313
12181
  });
@@ -12322,13 +12190,13 @@ var StreamingCardController = class StreamingCardController {
12322
12190
  this.disposeShutdownHook = null;
12323
12191
  return;
12324
12192
  }
12325
- log$15.info("sent CardKit card", { messageId: result.messageId });
12193
+ log$16.info("sent CardKit card", { messageId: result.messageId });
12326
12194
  } else throw new Error("card.create returned empty card_id");
12327
12195
  } catch (cardKitErr) {
12328
12196
  if (this.isStaleCreate(epoch)) return;
12329
12197
  if (this.guard.terminate("ensureCardCreated.cardkitFlow", cardKitErr)) return;
12330
12198
  const apiDetail = extractApiDetail(cardKitErr);
12331
- log$15.warn("CardKit flow failed, falling back to IM", { apiDetail });
12199
+ log$16.warn("CardKit flow failed, falling back to IM", { apiDetail });
12332
12200
  this.cardKit.cardKitCardId = null;
12333
12201
  this.cardKit.originalCardKitCardId = null;
12334
12202
  const fallbackCard = buildCardContent("streaming", { showToolUse: this.deps.toolUseDisplay.showToolUse });
@@ -12341,7 +12209,7 @@ var StreamingCardController = class StreamingCardController {
12341
12209
  accountId: this.deps.accountId
12342
12210
  });
12343
12211
  if (this.isStaleCreate(epoch)) {
12344
- 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", {
12345
12213
  epoch,
12346
12214
  phase: this.phase
12347
12215
  });
@@ -12350,12 +12218,12 @@ var StreamingCardController = class StreamingCardController {
12350
12218
  this.cardKit.cardMessageId = result.messageId;
12351
12219
  this.flush.setCardMessageReady(true);
12352
12220
  if (!this.transition("streaming", "ensureCardCreated.imFallback")) return;
12353
- log$15.info("sent fallback IM card", { messageId: result.messageId });
12221
+ log$16.info("sent fallback IM card", { messageId: result.messageId });
12354
12222
  }
12355
12223
  } catch (err) {
12356
12224
  if (this.isStaleCreate(epoch)) return;
12357
12225
  if (this.guard.terminate("ensureCardCreated.outer", err)) return;
12358
- 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) });
12359
12227
  this.transition("creation_failed", "ensureCardCreated.outer", "creation_failed");
12360
12228
  }
12361
12229
  })();
@@ -12364,10 +12232,10 @@ var StreamingCardController = class StreamingCardController {
12364
12232
  async performFlush() {
12365
12233
  if (!this.cardKit.cardMessageId || this.isTerminalPhase) return;
12366
12234
  if (!this.cardKit.cardKitCardId && this.cardKit.originalCardKitCardId) {
12367
- log$15.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
12235
+ log$16.debug("performFlush: skipping (CardKit streaming disabled, awaiting final update)");
12368
12236
  return;
12369
12237
  }
12370
- log$15.debug("flushCardUpdate: enter", {
12238
+ log$16.debug("flushCardUpdate: enter", {
12371
12239
  seq: this.cardKit.cardKitSequence,
12372
12240
  isCardKit: !!this.cardKit.cardKitCardId
12373
12241
  });
@@ -12389,7 +12257,7 @@ var StreamingCardController = class StreamingCardController {
12389
12257
  reasoningText: this.reasoning.accumulatedReasoningText || void 0
12390
12258
  });
12391
12259
  this.cardKit.cardKitSequence += 1;
12392
- log$15.debug("flushCardUpdate: full card update (dirty)", {
12260
+ log$16.debug("flushCardUpdate: full card update (dirty)", {
12393
12261
  seq: this.cardKit.cardKitSequence,
12394
12262
  stepCount: display?.stepCount,
12395
12263
  hasReasoning: !!this.reasoning.accumulatedReasoningText
@@ -12405,7 +12273,7 @@ var StreamingCardController = class StreamingCardController {
12405
12273
  } else if (resolvedText !== this.text.lastFlushedText) {
12406
12274
  const prevSeq = this.cardKit.cardKitSequence;
12407
12275
  this.cardKit.cardKitSequence += 1;
12408
- log$15.debug("flushCardUpdate: answer seq bump", {
12276
+ log$16.debug("flushCardUpdate: answer seq bump", {
12409
12277
  seqBefore: prevSeq,
12410
12278
  seqAfter: this.cardKit.cardKitSequence
12411
12279
  });
@@ -12420,7 +12288,7 @@ var StreamingCardController = class StreamingCardController {
12420
12288
  this.text.lastFlushedText = resolvedText;
12421
12289
  }
12422
12290
  } else {
12423
- log$15.debug("flushCardUpdate: IM patch fallback");
12291
+ log$16.debug("flushCardUpdate: IM patch fallback");
12424
12292
  const flushDisplay = this.computeToolUseDisplay();
12425
12293
  const card = buildCardContent("streaming", {
12426
12294
  text: this.reasoning.isReasoningPhase ? "" : resolvedText,
@@ -12440,31 +12308,31 @@ var StreamingCardController = class StreamingCardController {
12440
12308
  if (this.guard.terminate("flushCardUpdate", err)) return;
12441
12309
  const apiCode = extractLarkApiCode(err);
12442
12310
  if (isCardRateLimitError(err)) {
12443
- 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 });
12444
12312
  return;
12445
12313
  }
12446
12314
  if (isCardTableLimitError(err)) {
12447
- 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 });
12448
12316
  this.cardKit.cardKitCardId = null;
12449
12317
  return;
12450
12318
  }
12451
12319
  if (apiCode === 300317 && this.cardKit.cardKitCardId) {
12452
12320
  const oldSeq = this.cardKit.cardKitSequence;
12453
12321
  this.cardKit.cardKitSequence += 100;
12454
- log$15.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
12322
+ log$16.warn("flushCardUpdate: sequence conflict (300317), jumping sequence", {
12455
12323
  oldSeq,
12456
12324
  newSeq: this.cardKit.cardKitSequence
12457
12325
  });
12458
12326
  return;
12459
12327
  }
12460
12328
  const apiDetail = extractApiDetail(err);
12461
- log$15.error("card stream update failed", {
12329
+ log$16.error("card stream update failed", {
12462
12330
  apiCode,
12463
12331
  seq: this.cardKit.cardKitSequence,
12464
12332
  apiDetail
12465
12333
  });
12466
12334
  if (this.cardKit.cardKitCardId) {
12467
- 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");
12468
12336
  this.cardKit.cardKitCardId = null;
12469
12337
  }
12470
12338
  }
@@ -12489,7 +12357,7 @@ var StreamingCardController = class StreamingCardController {
12489
12357
  if (!this.cardKit.cardKitCardId || this.isTerminalPhase) return;
12490
12358
  try {
12491
12359
  const display = this.computeToolUseDisplay();
12492
- log$15.info("updateToolUseStatus", {
12360
+ log$16.info("updateToolUseStatus", {
12493
12361
  hasDisplay: !!display,
12494
12362
  stepCount: display?.stepCount,
12495
12363
  steps: display?.steps?.map((s) => `${s.title}:${s.status}`).join(", ")
@@ -12511,7 +12379,7 @@ var StreamingCardController = class StreamingCardController {
12511
12379
  accountId: this.deps.accountId
12512
12380
  });
12513
12381
  } catch (err) {
12514
- log$15.debug("updateToolUseStatus failed", { error: String(err) });
12382
+ log$16.debug("updateToolUseStatus failed", { error: String(err) });
12515
12383
  }
12516
12384
  }
12517
12385
  finalizeCard(source, reason) {
@@ -12522,7 +12390,7 @@ var StreamingCardController = class StreamingCardController {
12522
12390
  const from = this.phase;
12523
12391
  this.phase = "completed";
12524
12392
  this._terminalReason = "normal";
12525
- log$15.info("phase transition", {
12393
+ log$16.info("phase transition", {
12526
12394
  from,
12527
12395
  to: this.phase,
12528
12396
  source: "replaceWithClarificationCard",
@@ -12536,7 +12404,7 @@ var StreamingCardController = class StreamingCardController {
12536
12404
  async closeStreamingAndUpdate(cardId, card, label) {
12537
12405
  const seqBeforeClose = this.cardKit.cardKitSequence;
12538
12406
  this.cardKit.cardKitSequence += 1;
12539
- log$15.info(`${label}: closing streaming mode`, {
12407
+ log$16.info(`${label}: closing streaming mode`, {
12540
12408
  seqBefore: seqBeforeClose,
12541
12409
  seqAfter: this.cardKit.cardKitSequence
12542
12410
  });
@@ -12549,7 +12417,7 @@ var StreamingCardController = class StreamingCardController {
12549
12417
  });
12550
12418
  const seqBeforeUpdate = this.cardKit.cardKitSequence;
12551
12419
  this.cardKit.cardKitSequence += 1;
12552
- log$15.info(`${label}: updating card`, {
12420
+ log$16.info(`${label}: updating card`, {
12553
12421
  seqBefore: seqBeforeUpdate,
12554
12422
  seqAfter: this.cardKit.cardKitSequence
12555
12423
  });
@@ -12583,7 +12451,7 @@ function extractApiDetail(err) {
12583
12451
  }
12584
12452
  //#endregion
12585
12453
  //#region src/messaging/format-for-cc.ts
12586
- const log$14 = larkLogger("messaging/format-for-cc");
12454
+ const log$15 = larkLogger("messaging/format-for-cc");
12587
12455
  function safeParseJSON(raw) {
12588
12456
  try {
12589
12457
  return JSON.parse(raw);
@@ -12639,7 +12507,7 @@ function extractPostText(rawContent) {
12639
12507
  */
12640
12508
  function formatContentByType(ctx) {
12641
12509
  const { contentType, content, resources } = ctx;
12642
- log$14.debug("formatContentByType 开始格式化", {
12510
+ log$15.debug("formatContentByType 开始格式化", {
12643
12511
  contentType,
12644
12512
  contentLength: content?.length ?? 0,
12645
12513
  resourceCount: resources?.length ?? 0
@@ -12681,7 +12549,7 @@ function formatContentByType(ctx) {
12681
12549
  case "todo":
12682
12550
  case "vote": return content;
12683
12551
  default:
12684
- log$14.warn("遇到不支持的消息类型", { contentType });
12552
+ log$15.warn("遇到不支持的消息类型", { contentType });
12685
12553
  return `[不支持的消息类型: ${contentType}]`;
12686
12554
  }
12687
12555
  }
@@ -12724,7 +12592,7 @@ function formatMessageTime(createTime) {
12724
12592
  * @returns 格式化后的纯文本字符串
12725
12593
  */
12726
12594
  function formatForCC(ctx, isGroup) {
12727
- log$14.info("formatForCC 开始处理", {
12595
+ log$15.info("formatForCC 开始处理", {
12728
12596
  messageId: ctx.messageId,
12729
12597
  contentType: ctx.contentType,
12730
12598
  chatType: ctx.chatType,
@@ -12740,7 +12608,7 @@ function formatForCC(ctx, isGroup) {
12740
12608
  if (isGroup && ctx.senderName) parts.push(`[${ctx.senderName}]`);
12741
12609
  if (ctx.threadId) parts.push("[话题回复]");
12742
12610
  const result = (parts.length > 0 ? parts.join(" ") + " " : "") + formattedContent;
12743
- log$14.info("formatForCC 完成", {
12611
+ log$15.info("formatForCC 完成", {
12744
12612
  messageId: ctx.messageId,
12745
12613
  resultLength: result.length,
12746
12614
  hasTime: !!timeStr,
@@ -12748,6 +12616,41 @@ function formatForCC(ctx, isGroup) {
12748
12616
  });
12749
12617
  return result;
12750
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
+ }
12751
12654
  //#endregion
12752
12655
  //#region src/card/interactive-cards.ts
12753
12656
  /**
@@ -12763,7 +12666,7 @@ function formatForCC(ctx, isGroup) {
12763
12666
  * - CC 输出文本后处理(检测特殊标记)
12764
12667
  * - 操作 ID 生命周期管理
12765
12668
  */
12766
- const log$13 = larkLogger("card/interactive-cards");
12669
+ const log$14 = larkLogger("card/interactive-cards");
12767
12670
  const pendingOperations = /* @__PURE__ */ new Map();
12768
12671
  const OPERATION_TTL_MS = 1800 * 1e3;
12769
12672
  setInterval(() => {
@@ -12861,7 +12764,7 @@ function buildAuthCard(ctx) {
12861
12764
  scopes: ctx.scopes
12862
12765
  }
12863
12766
  });
12864
- log$13.info("已创建授权卡片操作", {
12767
+ log$14.info("已创建授权卡片操作", {
12865
12768
  operationId,
12866
12769
  scopes: ctx.scopes,
12867
12770
  sessionId: ctx.sessionId
@@ -12899,7 +12802,7 @@ function buildAskCard(ctx, phaseIndex = 0) {
12899
12802
  freeTextAnswers: {}
12900
12803
  }
12901
12804
  });
12902
- log$13.info("已创建提问卡片操作", {
12805
+ log$14.info("已创建提问卡片操作", {
12903
12806
  operationId,
12904
12807
  phaseId: phase?.id,
12905
12808
  phaseIndex,
@@ -13064,7 +12967,7 @@ function toggleAskSelection(operationId, phaseId, value) {
13064
12967
  else next = current[0] === value ? [] : [value];
13065
12968
  if (next.length > 0) op.askContext.answers[phaseId] = next;
13066
12969
  else delete op.askContext.answers[phaseId];
13067
- log$13.info("更新提问卡片勾选状态", {
12970
+ log$14.info("更新提问卡片勾选状态", {
13068
12971
  operationId,
13069
12972
  phaseId,
13070
12973
  selectedValues: next
@@ -13080,7 +12983,7 @@ function setAskFreeTextAnswer(operationId, phaseId, value) {
13080
12983
  const text = normalizeFreeText(value);
13081
12984
  if (text) op.askContext.freeTextAnswers[phaseId] = text;
13082
12985
  else delete op.askContext.freeTextAnswers[phaseId];
13083
- log$13.info("更新提问卡片自由文本", {
12986
+ log$14.info("更新提问卡片自由文本", {
13084
12987
  operationId,
13085
12988
  phaseId,
13086
12989
  hasText: text.length > 0
@@ -13324,7 +13227,7 @@ async function trySendClarificationFallback(params) {
13324
13227
  }
13325
13228
  //#endregion
13326
13229
  //#region src/messaging/inbound/dispatch-cc.ts
13327
- const log$12 = larkLogger("inbound/dispatch-cc");
13230
+ const log$13 = larkLogger("inbound/dispatch-cc");
13328
13231
  function buildFeishuRuntimeConfig(params) {
13329
13232
  const { ctx, account, route } = params;
13330
13233
  const senderUserId = ctx.rawSender.sender_id.user_id;
@@ -13381,7 +13284,7 @@ async function dispatchToCC(params) {
13381
13284
  const ccModel = params.model;
13382
13285
  const maxTurns = params.maxTurns;
13383
13286
  const maxBudgetUsd = params.maxBudgetUsd;
13384
- log$12.info("dispatchToCC 开始", {
13287
+ log$13.info("dispatchToCC 开始", {
13385
13288
  msgId,
13386
13289
  chatId,
13387
13290
  chatType,
@@ -13394,12 +13297,12 @@ async function dispatchToCC(params) {
13394
13297
  threadId,
13395
13298
  userId: ctx.senderId
13396
13299
  });
13397
- log$12.info("会话路由解析完成", {
13300
+ log$13.info("会话路由解析完成", {
13398
13301
  sessionId: route.sessionId,
13399
13302
  cwd: route.cwd,
13400
13303
  type: route.type
13401
13304
  });
13402
- if (params.userContext) log$12.info("用户上下文已注入", {
13305
+ if (params.userContext) log$13.info("用户上下文已注入", {
13403
13306
  userId: params.userContext.userId,
13404
13307
  isTenantIdentity: params.userContext.isTenantIdentity,
13405
13308
  credentialDir: params.userContext.credentialDir
@@ -13408,10 +13311,7 @@ async function dispatchToCC(params) {
13408
13311
  const isGroup = chatType === "group";
13409
13312
  const replyInThread = !!threadId;
13410
13313
  const replyToMessageId = threadId ? void 0 : ctx.messageId;
13411
- let textPrompt = formatForCC(ctx, isGroup);
13412
- if (isGroup && replyInThread) textPrompt = `[群聊话题] ${textPrompt}`;
13413
- else if (isGroup) textPrompt = `[群聊] ${textPrompt}`;
13414
- else textPrompt = `[私聊] ${textPrompt}`;
13314
+ const textPrompt = formatPromptForCC(ctx, isGroup, params.quotedContent);
13415
13315
  const imageResources = ctx.resources.filter((r) => r.type === "image");
13416
13316
  let prompt = textPrompt;
13417
13317
  if (imageResources.length > 0) {
@@ -13438,7 +13338,7 @@ async function dispatchToCC(params) {
13438
13338
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13439
13339
  buffer = Buffer.concat(chunks);
13440
13340
  } else {
13441
- log$12.warn("图片资源下载返回未知格式,跳过", {
13341
+ log$13.warn("图片资源下载返回未知格式,跳过", {
13442
13342
  fileKey: imgRes.fileKey,
13443
13343
  responseType: typeof response
13444
13344
  });
@@ -13446,7 +13346,7 @@ async function dispatchToCC(params) {
13446
13346
  }
13447
13347
  if (resp.headers?.["content-type"]) mediaType = resp.headers["content-type"];
13448
13348
  } else {
13449
- log$12.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
13349
+ log$13.warn("图片资源下载返回空,跳过", { fileKey: imgRes.fileKey });
13450
13350
  continue;
13451
13351
  }
13452
13352
  const imgFileName = `${imgRes.fileKey}.png`;
@@ -13456,13 +13356,13 @@ async function dispatchToCC(params) {
13456
13356
  const { mkdir, writeFile } = await import("node:fs/promises");
13457
13357
  await mkdir(filesDir, { recursive: true });
13458
13358
  await writeFile(imgFilePath, buffer);
13459
- log$12.info("图片已保存到工作目录", {
13359
+ log$13.info("图片已保存到工作目录", {
13460
13360
  fileKey: imgRes.fileKey,
13461
13361
  path: imgFilePath,
13462
13362
  sizeBytes: buffer.length
13463
13363
  });
13464
13364
  } catch (saveErr) {
13465
- log$12.warn("图片保存到工作目录失败", {
13365
+ log$13.warn("图片保存到工作目录失败", {
13466
13366
  fileKey: imgRes.fileKey,
13467
13367
  path: imgFilePath,
13468
13368
  error: saveErr instanceof Error ? saveErr.message : String(saveErr)
@@ -13477,13 +13377,13 @@ async function dispatchToCC(params) {
13477
13377
  data: base64Data
13478
13378
  }
13479
13379
  });
13480
- log$12.info("图片资源下载成功", {
13380
+ log$13.info("图片资源下载成功", {
13481
13381
  fileKey: imgRes.fileKey,
13482
13382
  mediaType,
13483
13383
  sizeBytes: buffer.length
13484
13384
  });
13485
13385
  } catch (err) {
13486
- log$12.warn("图片资源下载失败,跳过", {
13386
+ log$13.warn("图片资源下载失败,跳过", {
13487
13387
  fileKey: imgRes.fileKey,
13488
13388
  error: err instanceof Error ? err.message : String(err)
13489
13389
  });
@@ -13493,7 +13393,7 @@ async function dispatchToCC(params) {
13493
13393
  type: "text",
13494
13394
  text: textPrompt
13495
13395
  }, ...imageBlocks];
13496
- log$12.info("已构建多模态 prompt", {
13396
+ log$13.info("已构建多模态 prompt", {
13497
13397
  textLength: textPrompt.length,
13498
13398
  imageCount: imageBlocks.length
13499
13399
  });
@@ -13501,12 +13401,12 @@ async function dispatchToCC(params) {
13501
13401
  const textWithoutImageRefs = textPrompt.replace(/!\[image\]\([^)]*\)/g, "").replace(/\[图片\]\s*/g, "").trim();
13502
13402
  if (textWithoutImageRefs.length > 0) {
13503
13403
  prompt = textPrompt.replace(/!\[image\]\([^)]*\)/g, "[图片加载失败]");
13504
- log$12.info("图片下载失败但保留文字内容继续调度", {
13404
+ log$13.info("图片下载失败但保留文字内容继续调度", {
13505
13405
  messageId: ctx.messageId,
13506
13406
  textLength: textWithoutImageRefs.length
13507
13407
  });
13508
13408
  } else {
13509
- log$12.warn("纯图片消息且图片全部下载失败,中止调度", {
13409
+ log$13.warn("纯图片消息且图片全部下载失败,中止调度", {
13510
13410
  messageId: ctx.messageId,
13511
13411
  expectedImages: imageResources.length
13512
13412
  });
@@ -13528,7 +13428,7 @@ async function dispatchToCC(params) {
13528
13428
  await fsMkdir(filesDir, { recursive: true });
13529
13429
  let updatedTextPrompt = typeof prompt === "string" ? prompt : prompt[0].text;
13530
13430
  for (const res of attachmentResources) try {
13531
- log$12.info("开始下载非图片附件", {
13431
+ log$13.info("开始下载非图片附件", {
13532
13432
  type: res.type,
13533
13433
  fileKey: res.fileKey,
13534
13434
  fileName: res.fileName,
@@ -13542,7 +13442,7 @@ async function dispatchToCC(params) {
13542
13442
  },
13543
13443
  params: { type: "file" }
13544
13444
  });
13545
- log$12.info("非图片附件 API 响应已收到", {
13445
+ log$13.info("非图片附件 API 响应已收到", {
13546
13446
  type: res.type,
13547
13447
  fileKey: res.fileKey,
13548
13448
  responseType: typeof response,
@@ -13561,7 +13461,7 @@ async function dispatchToCC(params) {
13561
13461
  for await (const chunk of readable) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
13562
13462
  buffer = Buffer.concat(chunks);
13563
13463
  } else {
13564
- log$12.warn("非图片附件下载返回未知格式,跳过", {
13464
+ log$13.warn("非图片附件下载返回未知格式,跳过", {
13565
13465
  type: res.type,
13566
13466
  fileKey: res.fileKey,
13567
13467
  responseType: typeof response
@@ -13569,7 +13469,7 @@ async function dispatchToCC(params) {
13569
13469
  continue;
13570
13470
  }
13571
13471
  } else {
13572
- log$12.warn("非图片附件下载返回空,跳过", {
13472
+ log$13.warn("非图片附件下载返回空,跳过", {
13573
13473
  type: res.type,
13574
13474
  fileKey: res.fileKey
13575
13475
  });
@@ -13592,7 +13492,7 @@ async function dispatchToCC(params) {
13592
13492
  }
13593
13493
  const filePath = path.join(filesDir, savedFileName);
13594
13494
  await fsWriteFile(filePath, buffer);
13595
- log$12.info("非图片附件已保存到工作目录", {
13495
+ log$13.info("非图片附件已保存到工作目录", {
13596
13496
  type: res.type,
13597
13497
  fileKey: res.fileKey,
13598
13498
  savedFileName,
@@ -13626,7 +13526,7 @@ async function dispatchToCC(params) {
13626
13526
  }
13627
13527
  }
13628
13528
  } catch (err) {
13629
- log$12.warn("非图片附件下载失败,替换为降级描述", {
13529
+ log$13.warn("非图片附件下载失败,替换为降级描述", {
13630
13530
  type: res.type,
13631
13531
  fileKey: res.fileKey,
13632
13532
  error: err instanceof Error ? err.message : String(err)
@@ -13645,12 +13545,12 @@ async function dispatchToCC(params) {
13645
13545
  const textBlock = prompt.find((b) => b.type === "text");
13646
13546
  if (textBlock) textBlock.text = updatedTextPrompt;
13647
13547
  }
13648
- log$12.info("非图片附件处理完成", {
13548
+ log$13.info("非图片附件处理完成", {
13649
13549
  totalAttachments: attachmentResources.length,
13650
13550
  promptLength: typeof prompt === "string" ? prompt.length : prompt.find((b) => b.type === "text")?.text?.length
13651
13551
  });
13652
13552
  }
13653
- log$12.info("消息格式化完成", {
13553
+ log$13.info("消息格式化完成", {
13654
13554
  promptLength: typeof prompt === "string" ? prompt.length : prompt.length,
13655
13555
  isMultimodal: Array.isArray(prompt),
13656
13556
  isGroup
@@ -13682,19 +13582,19 @@ async function dispatchToCC(params) {
13682
13582
  };
13683
13583
  const cardController = new StreamingCardController(cardDeps);
13684
13584
  registerActiveCard(route.sessionId, cardController);
13685
- log$12.info("StreamingCardController 已创建", {
13585
+ log$13.info("StreamingCardController 已创建", {
13686
13586
  sessionId: route.sessionId,
13687
13587
  chatId,
13688
13588
  replyToMessageId: cardDeps.replyToMessageId
13689
13589
  });
13690
13590
  cardController.ensureCardCreated().catch((err) => {
13691
- log$12.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
13591
+ log$13.warn("提前创建卡片失败(streaming 回调会重试)", { error: String(err) });
13692
13592
  });
13693
13593
  let promptForRuntime = prompt;
13694
13594
  try {
13695
13595
  promptForRuntime = await new MemoryContextBuilder().injectPrompt(route.cwd, prompt, promptToText(prompt));
13696
13596
  } catch (err) {
13697
- log$12.warn("memory context 注入失败,继续使用原始 prompt", {
13597
+ log$13.warn("memory context 注入失败,继续使用原始 prompt", {
13698
13598
  sessionId: route.sessionId,
13699
13599
  error: err instanceof Error ? err.message : String(err)
13700
13600
  });
@@ -13715,7 +13615,7 @@ async function dispatchToCC(params) {
13715
13615
  });
13716
13616
  } catch (err) {
13717
13617
  const errorMessage = err instanceof Error ? err.message : String(err);
13718
- log$12.error("飞书事件 run context 构造失败,终止调度", {
13618
+ log$13.error("飞书事件 run context 构造失败,终止调度", {
13719
13619
  sessionId: route.sessionId,
13720
13620
  messageId: msgId,
13721
13621
  error: errorMessage
@@ -13733,7 +13633,7 @@ async function dispatchToCC(params) {
13733
13633
  channel: "feishu-bot",
13734
13634
  title: userContent.slice(0, 30) + (userContent.length > 30 ? "..." : "")
13735
13635
  }).catch((err) => {
13736
- log$12.warn("创建飞书端会话记录失败", { error: String(err) });
13636
+ log$13.warn("创建飞书端会话记录失败", { error: String(err) });
13737
13637
  });
13738
13638
  params.messageStore.appendMessage({
13739
13639
  sessionId: storeSessionId,
@@ -13747,14 +13647,14 @@ async function dispatchToCC(params) {
13747
13647
  scenarioId: runtimeConfig.runContext?.scenarioId
13748
13648
  }
13749
13649
  }).catch((err) => {
13750
- log$12.warn("记录飞书用户消息到 store 失败", { error: String(err) });
13650
+ log$13.warn("记录飞书用户消息到 store 失败", { error: String(err) });
13751
13651
  });
13752
13652
  }
13753
13653
  const bridge = new CCStreamBridge(cardController, {
13754
13654
  autoCompleteOnTurnEnd: true,
13755
13655
  sessionKey: route.sessionId
13756
13656
  });
13757
- log$12.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
13657
+ log$13.info("CCStreamBridge 已创建", { sessionId: route.sessionId });
13758
13658
  let feishuAccumText = "";
13759
13659
  let usedAskUserTool = false;
13760
13660
  let pendingEndTurn = null;
@@ -13776,12 +13676,12 @@ async function dispatchToCC(params) {
13776
13676
  ...metadata
13777
13677
  }
13778
13678
  }).catch((err) => {
13779
- log$12.warn("记录飞书 assistant 消息到 store 失败", { error: String(err) });
13679
+ log$13.warn("记录飞书 assistant 消息到 store 失败", { error: String(err) });
13780
13680
  });
13781
13681
  };
13782
13682
  const appendRuntimeEventMessage = (event) => {
13783
13683
  const eventSummary = summarizeForAudit(event);
13784
- log$12.info("CC onRuntimeEvent", {
13684
+ log$13.info("CC onRuntimeEvent", {
13785
13685
  sessionId: route.sessionId,
13786
13686
  type: event.type,
13787
13687
  traceId: runtimeConfig.runContext?.traceId,
@@ -13819,11 +13719,11 @@ async function dispatchToCC(params) {
13819
13719
  finalStatus: event.finalStatus
13820
13720
  }
13821
13721
  }).catch((err) => {
13822
- log$12.warn("记录飞书 runtime event 到 store 失败", { error: String(err) });
13722
+ log$13.warn("记录飞书 runtime event 到 store 失败", { error: String(err) });
13823
13723
  });
13824
13724
  };
13825
13725
  const handleResult = async (result) => {
13826
- log$12.info("CC onResult", {
13726
+ log$13.info("CC onResult", {
13827
13727
  sessionId: route.sessionId,
13828
13728
  subtype: result.subtype,
13829
13729
  isError: result.isError,
@@ -13851,13 +13751,13 @@ async function dispatchToCC(params) {
13851
13751
  })
13852
13752
  });
13853
13753
  } catch (err) {
13854
- log$12.warn("clarification gate: 兜底处理失败,回退到普通回复", {
13754
+ log$13.warn("clarification gate: 兜底处理失败,回退到普通回复", {
13855
13755
  sessionId: route.sessionId,
13856
13756
  error: err instanceof Error ? err.message : String(err)
13857
13757
  });
13858
13758
  }
13859
13759
  if (clarificationFallback.handled) {
13860
- log$12.info("clarification gate: 普通文本提问已转为提问卡片", {
13760
+ log$13.info("clarification gate: 普通文本提问已转为提问卡片", {
13861
13761
  sessionId: route.sessionId,
13862
13762
  operationId: clarificationFallback.operationId,
13863
13763
  reason: clarificationFallback.reason,
@@ -13865,7 +13765,7 @@ async function dispatchToCC(params) {
13865
13765
  });
13866
13766
  return;
13867
13767
  }
13868
- if (clarificationFallback.matched) log$12.warn("clarification gate: 提问卡片替换失败,回退到普通回复", {
13768
+ if (clarificationFallback.matched) log$13.warn("clarification gate: 提问卡片替换失败,回退到普通回复", {
13869
13769
  sessionId: route.sessionId,
13870
13770
  operationId: clarificationFallback.operationId
13871
13771
  });
@@ -13878,7 +13778,7 @@ async function dispatchToCC(params) {
13878
13778
  };
13879
13779
  const handleBlockedEvent = (event) => {
13880
13780
  const sanitized = sanitizeBlockedEvent(event);
13881
- log$12.warn("CC onBlockedEvent", {
13781
+ log$13.warn("CC onBlockedEvent", {
13882
13782
  ...sanitized,
13883
13783
  traceId: runtimeConfig.runContext?.traceId,
13884
13784
  scenarioId: runtimeConfig.runContext?.scenarioId
@@ -13886,7 +13786,7 @@ async function dispatchToCC(params) {
13886
13786
  };
13887
13787
  const callbacks = {
13888
13788
  onTextDelta: (text) => {
13889
- log$12.debug("CC onTextDelta", {
13789
+ log$13.debug("CC onTextDelta", {
13890
13790
  sessionId: route.sessionId,
13891
13791
  deltaLen: text.length
13892
13792
  });
@@ -13894,14 +13794,14 @@ async function dispatchToCC(params) {
13894
13794
  feishuAccumText += text;
13895
13795
  },
13896
13796
  onThinkingDelta: (text) => {
13897
- log$12.debug("CC onThinkingDelta", {
13797
+ log$13.debug("CC onThinkingDelta", {
13898
13798
  sessionId: route.sessionId,
13899
13799
  deltaLen: text.length
13900
13800
  });
13901
13801
  bridge.onThinkingDelta(text);
13902
13802
  },
13903
13803
  onToolUseStart: (toolName, toolInput) => {
13904
- log$12.info("CC onToolUseStart", {
13804
+ log$13.info("CC onToolUseStart", {
13905
13805
  sessionId: route.sessionId,
13906
13806
  toolName
13907
13807
  });
@@ -13909,7 +13809,7 @@ async function dispatchToCC(params) {
13909
13809
  bridge.onToolUseStart(toolName, toolInput);
13910
13810
  },
13911
13811
  onToolResult: (toolUseId, result) => {
13912
- log$12.info("CC onToolResult", {
13812
+ log$13.info("CC onToolResult", {
13913
13813
  sessionId: route.sessionId,
13914
13814
  toolUseId,
13915
13815
  traceId: runtimeConfig.runContext?.traceId,
@@ -13918,7 +13818,7 @@ async function dispatchToCC(params) {
13918
13818
  bridge.onToolResult(toolUseId);
13919
13819
  },
13920
13820
  onToolProgress: (toolName, elapsedSeconds) => {
13921
- log$12.debug("CC onToolProgress", {
13821
+ log$13.debug("CC onToolProgress", {
13922
13822
  sessionId: route.sessionId,
13923
13823
  toolName,
13924
13824
  elapsedSeconds
@@ -13926,7 +13826,7 @@ async function dispatchToCC(params) {
13926
13826
  bridge.onToolProgress(toolName, elapsedSeconds);
13927
13827
  },
13928
13828
  onTurnEnd: (stopReason) => {
13929
- log$12.info("CC onTurnEnd", {
13829
+ log$13.info("CC onTurnEnd", {
13930
13830
  sessionId: route.sessionId,
13931
13831
  stopReason
13932
13832
  });
@@ -13938,14 +13838,14 @@ async function dispatchToCC(params) {
13938
13838
  },
13939
13839
  onResult: (result) => {
13940
13840
  resultHandlingPromise = handleResult(result).catch((err) => {
13941
- log$12.error("CC onResult 收尾处理失败", {
13841
+ log$13.error("CC onResult 收尾处理失败", {
13942
13842
  sessionId: route.sessionId,
13943
13843
  error: err instanceof Error ? err.message : String(err)
13944
13844
  });
13945
13845
  });
13946
13846
  },
13947
13847
  onError: (error) => {
13948
- log$12.error("CC 进程错误", {
13848
+ log$13.error("CC 进程错误", {
13949
13849
  sessionId: route.sessionId,
13950
13850
  traceId: runtimeConfig.runContext?.traceId,
13951
13851
  error: error.message
@@ -13953,7 +13853,7 @@ async function dispatchToCC(params) {
13953
13853
  cardController.onError(error, { kind: "cc-process" });
13954
13854
  },
13955
13855
  onAuditEvent: (event) => {
13956
- log$12.info("CC onAuditEvent", {
13856
+ log$13.info("CC onAuditEvent", {
13957
13857
  ...event,
13958
13858
  inputSummary: summarizeForAudit(event.inputSummary),
13959
13859
  resultSummary: summarizeForAudit(event.resultSummary)
@@ -13964,7 +13864,7 @@ async function dispatchToCC(params) {
13964
13864
  onBlocked: handleBlockedEvent,
13965
13865
  onBlockedEvent: handleBlockedEvent
13966
13866
  };
13967
- log$12.info("开始执行 CC 进程", {
13867
+ log$13.info("开始执行 CC 进程", {
13968
13868
  sessionId: route.sessionId,
13969
13869
  cwd: route.cwd,
13970
13870
  promptLength: prompt.length,
@@ -13987,11 +13887,11 @@ async function dispatchToCC(params) {
13987
13887
  onMcpCallback.on(`callback:${route.sessionId}`, mcpCallbackListener);
13988
13888
  try {
13989
13889
  await processManager.executePrompt(runtimeConfig, callbacks);
13990
- log$12.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
13890
+ log$13.info("CC 进程 executePrompt 调用完成", { sessionId: route.sessionId });
13991
13891
  if (resultHandlingPromise) await resultHandlingPromise;
13992
13892
  } catch (err) {
13993
13893
  const errorMessage = err instanceof Error ? err.message : String(err);
13994
- log$12.error("CC 进程 executePrompt 调用异常", {
13894
+ log$13.error("CC 进程 executePrompt 调用异常", {
13995
13895
  sessionId: route.sessionId,
13996
13896
  error: errorMessage
13997
13897
  });
@@ -14015,7 +13915,7 @@ async function dispatchToCC(params) {
14015
13915
  */
14016
13916
  async function dispatchTeammateEval(params) {
14017
13917
  const { chatId, prompt, account, sessionRouter, processManager } = params;
14018
- log$12.info("dispatchTeammateEval 开始", {
13918
+ log$13.info("dispatchTeammateEval 开始", {
14019
13919
  chatId,
14020
13920
  promptLength: prompt.length
14021
13921
  });
@@ -14028,7 +13928,7 @@ async function dispatchTeammateEval(params) {
14028
13928
  userName: void 0
14029
13929
  });
14030
13930
  if (processManager.isSessionBusy?.(route.sessionId)) {
14031
- log$12.info("dispatchTeammateEval 跳过:主 session 正在执行", {
13931
+ log$13.info("dispatchTeammateEval 跳过:主 session 正在执行", {
14032
13932
  chatId,
14033
13933
  sessionId: route.sessionId
14034
13934
  });
@@ -14091,7 +13991,7 @@ async function dispatchTeammateEval(params) {
14091
13991
  }
14092
13992
  });
14093
13993
  } catch (err) {
14094
- log$12.error("teammate run context 构造失败,跳过评估", {
13994
+ log$13.error("teammate run context 构造失败,跳过评估", {
14095
13995
  chatId,
14096
13996
  error: err instanceof Error ? err.message : String(err)
14097
13997
  });
@@ -14115,7 +14015,7 @@ async function dispatchTeammateEval(params) {
14115
14015
  sourceRefs: params.sourceRefs ?? (params.requestId ? [params.requestId] : [])
14116
14016
  });
14117
14017
  if (evalDecision.decision === "NO_REPLY") {
14118
- log$12.info("teammate evaluator 决定静默", {
14018
+ log$13.info("teammate evaluator 决定静默", {
14119
14019
  chatId,
14120
14020
  evalSessionId,
14121
14021
  summary: evalDecision.summary.slice(0, 120)
@@ -14149,7 +14049,7 @@ async function dispatchTeammateEval(params) {
14149
14049
  const confirmReply = () => {
14150
14050
  if (confirmed) return;
14151
14051
  confirmed = true;
14152
- log$12.info("teammate 确认回复,创建流式卡片", {
14052
+ log$13.info("teammate 确认回复,创建流式卡片", {
14153
14053
  chatId,
14154
14054
  thinkingLen: thinkingBuffer.length,
14155
14055
  textLen: fullTextBuffer.length
@@ -14206,7 +14106,7 @@ async function dispatchTeammateEval(params) {
14206
14106
  fullTextBuffer += text;
14207
14107
  if (fullTextBuffer.trimStart().startsWith(NO_REPLY_TOKEN)) {
14208
14108
  silenced = true;
14209
- log$12.info("teammate 前缀检测: 检测到 NO_REPLY 首输出,立即静默", {
14109
+ log$13.info("teammate 前缀检测: 检测到 NO_REPLY 首输出,立即静默", {
14210
14110
  chatId,
14211
14111
  bufferLen: fullTextBuffer.length
14212
14112
  });
@@ -14239,7 +14139,7 @@ async function dispatchTeammateEval(params) {
14239
14139
  toolName: currentToolName,
14240
14140
  toolCallId: toolUseId
14241
14141
  });
14242
- log$12.info("teammate onToolResult", {
14142
+ log$13.info("teammate onToolResult", {
14243
14143
  chatId,
14244
14144
  toolUseId,
14245
14145
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14257,7 +14157,7 @@ async function dispatchTeammateEval(params) {
14257
14157
  onTurnEnd: (stopReason) => {
14258
14158
  finalStopReason = stopReason;
14259
14159
  if (stopReason === "tool_use") {
14260
- log$12.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
14160
+ log$13.debug("teammate turnEnd: tool_use, 跳过最终判断", { chatId });
14261
14161
  if (confirmed && bridge) bridge.onTurnEnd(stopReason);
14262
14162
  return;
14263
14163
  }
@@ -14265,7 +14165,7 @@ async function dispatchTeammateEval(params) {
14265
14165
  const trimmed = fullTextBuffer.trim();
14266
14166
  if (trimmed === NO_REPLY_TOKEN || trimmed === CC_INTERNAL_PLACEHOLDER || trimmed.endsWith(NO_REPLY_TOKEN) || trimmed === "") {
14267
14167
  silenced = true;
14268
- log$12.info("teammate turnEnd 最终判断: 静默", {
14168
+ log$13.info("teammate turnEnd 最终判断: 静默", {
14269
14169
  chatId,
14270
14170
  trimmed: trimmed.slice(0, 60)
14271
14171
  });
@@ -14280,7 +14180,7 @@ async function dispatchTeammateEval(params) {
14280
14180
  resolve();
14281
14181
  },
14282
14182
  onError: (error) => {
14283
- log$12.error("teammate 评估 CC 进程错误", {
14183
+ log$13.error("teammate 评估 CC 进程错误", {
14284
14184
  chatId,
14285
14185
  traceId: mainRuntimeConfig.runContext?.traceId,
14286
14186
  error: error.message
@@ -14288,14 +14188,14 @@ async function dispatchTeammateEval(params) {
14288
14188
  resolve();
14289
14189
  },
14290
14190
  onAuditEvent: (event) => {
14291
- log$12.info("teammate onAuditEvent", {
14191
+ log$13.info("teammate onAuditEvent", {
14292
14192
  ...event,
14293
14193
  inputSummary: summarizeForAudit(event.inputSummary),
14294
14194
  resultSummary: summarizeForAudit(event.resultSummary)
14295
14195
  });
14296
14196
  },
14297
14197
  onRuntimeEvent: (event) => {
14298
- log$12.info("teammate onRuntimeEvent", {
14198
+ log$13.info("teammate onRuntimeEvent", {
14299
14199
  type: event.type,
14300
14200
  chatId,
14301
14201
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14306,7 +14206,7 @@ async function dispatchTeammateEval(params) {
14306
14206
  });
14307
14207
  },
14308
14208
  onScenarioEvent: (event) => {
14309
- log$12.info("teammate onScenarioEvent", {
14209
+ log$13.info("teammate onScenarioEvent", {
14310
14210
  type: event.type,
14311
14211
  chatId,
14312
14212
  traceId: mainRuntimeConfig.runContext?.traceId,
@@ -14318,28 +14218,28 @@ async function dispatchTeammateEval(params) {
14318
14218
  },
14319
14219
  onBlocked: (event) => {
14320
14220
  const sanitized = sanitizeBlockedEvent(event);
14321
- log$12.warn("teammate onBlocked", {
14221
+ log$13.warn("teammate onBlocked", {
14322
14222
  ...sanitized,
14323
14223
  traceId: mainRuntimeConfig.runContext?.traceId
14324
14224
  });
14325
14225
  },
14326
14226
  onBlockedEvent: (event) => {
14327
14227
  const sanitized = sanitizeBlockedEvent(event);
14328
- log$12.warn("teammate onBlockedEvent", {
14228
+ log$13.warn("teammate onBlockedEvent", {
14329
14229
  ...sanitized,
14330
14230
  traceId: mainRuntimeConfig.runContext?.traceId
14331
14231
  });
14332
14232
  }
14333
14233
  };
14334
14234
  processManager.executePrompt(mainRuntimeConfig, callbacks).catch((err) => {
14335
- log$12.error("teammate executePrompt 异常", {
14235
+ log$13.error("teammate executePrompt 异常", {
14336
14236
  chatId,
14337
14237
  error: err instanceof Error ? err.message : String(err)
14338
14238
  });
14339
14239
  resolve();
14340
14240
  });
14341
14241
  });
14342
- log$12.info("dispatchTeammateEval 完成", {
14242
+ log$13.info("dispatchTeammateEval 完成", {
14343
14243
  chatId,
14344
14244
  confirmed,
14345
14245
  silenced,
@@ -14350,7 +14250,7 @@ async function dispatchTeammateEval(params) {
14350
14250
  return { replied: confirmed };
14351
14251
  } catch (err) {
14352
14252
  const errorMessage = err instanceof Error ? err.message : String(err);
14353
- log$12.error("dispatchTeammateEval 异常", {
14253
+ log$13.error("dispatchTeammateEval 异常", {
14354
14254
  chatId,
14355
14255
  error: errorMessage
14356
14256
  });
@@ -14374,7 +14274,7 @@ async function runTeammateDecisionEval(params) {
14374
14274
  resolve();
14375
14275
  },
14376
14276
  onError: (error) => {
14377
- log$12.error("teammate evaluator 执行错误", {
14277
+ log$13.error("teammate evaluator 执行错误", {
14378
14278
  chatId,
14379
14279
  sessionId: runtimeConfig.sessionId,
14380
14280
  error: error.message
@@ -14382,21 +14282,21 @@ async function runTeammateDecisionEval(params) {
14382
14282
  resolve();
14383
14283
  },
14384
14284
  onToolUseStart: (toolName, toolInput) => {
14385
- log$12.info("teammate evaluator toolUseStart", {
14285
+ log$13.info("teammate evaluator toolUseStart", {
14386
14286
  chatId,
14387
14287
  toolName,
14388
14288
  toolInput: summarizeForAudit(toolInput)
14389
14289
  });
14390
14290
  },
14391
14291
  onToolResult: (toolUseId, result) => {
14392
- log$12.info("teammate evaluator toolResult", {
14292
+ log$13.info("teammate evaluator toolResult", {
14393
14293
  chatId,
14394
14294
  toolUseId,
14395
14295
  result: summarizeForAudit(result)
14396
14296
  });
14397
14297
  }
14398
14298
  }).catch((err) => {
14399
- log$12.error("teammate evaluator executePrompt 异常", {
14299
+ log$13.error("teammate evaluator executePrompt 异常", {
14400
14300
  chatId,
14401
14301
  sessionId: runtimeConfig.sessionId,
14402
14302
  error: err instanceof Error ? err.message : String(err)
@@ -14411,7 +14311,7 @@ async function runTeammateDecisionEval(params) {
14411
14311
  const decision = trimmed !== "" && trimmed !== placeholderText && firstToken === "HANDOFF_TO_MAIN" ? "HANDOFF_TO_MAIN" : "NO_REPLY";
14412
14312
  const cleanedSummary = trimmed.replace(/^(HANDOFF_TO_MAIN|NO_REPLY)\b[\s::-]*/i, "").trim();
14413
14313
  const summary = malformed ? "NO_REPLY (malformed evaluator output)" : cleanedSummary.slice(0, 600) || `${decision} (thinking ${thinkingLength} chars)`;
14414
- log$12.info("teammate evaluator 完成", {
14314
+ log$13.info("teammate evaluator 完成", {
14415
14315
  chatId,
14416
14316
  sessionId: runtimeConfig.sessionId,
14417
14317
  decision,
@@ -14469,7 +14369,7 @@ async function handleMcpToolCallback(req, ctx) {
14469
14369
  try {
14470
14370
  if (req.tool === "ask_user") {
14471
14371
  const params = req.params;
14472
- log$12.info("[MCP] 收到 ask_user 回调,准备发送提问卡片", {
14372
+ log$13.info("[MCP] 收到 ask_user 回调,准备发送提问卡片", {
14473
14373
  requestId: req.requestId,
14474
14374
  sessionId: req.sessionId,
14475
14375
  question: params.question?.slice(0, 50)
@@ -14482,14 +14382,14 @@ async function handleMcpToolCallback(req, ctx) {
14482
14382
  });
14483
14383
  mcpOperationMap.set(operationId, req.requestId);
14484
14384
  await sendInteractiveCardViaCardKit(card, chatId, account, replyInThread, threadId);
14485
- log$12.info("[MCP] 提问卡片已发送", {
14385
+ log$13.info("[MCP] 提问卡片已发送", {
14486
14386
  requestId: req.requestId,
14487
14387
  operationId,
14488
14388
  chatId
14489
14389
  });
14490
14390
  } else if (req.tool === "request_permission") {
14491
14391
  const params = req.params;
14492
- log$12.info("[MCP] 收到 request_permission 回调,准备发送授权卡片", {
14392
+ log$13.info("[MCP] 收到 request_permission 回调,准备发送授权卡片", {
14493
14393
  requestId: req.requestId,
14494
14394
  scopes: params.scopes
14495
14395
  });
@@ -14503,14 +14403,14 @@ async function handleMcpToolCallback(req, ctx) {
14503
14403
  });
14504
14404
  mcpOperationMap.set(operationId, req.requestId);
14505
14405
  await sendInteractiveCardViaCardKit(card, chatId, account, replyInThread, threadId);
14506
- log$12.info("[MCP] 授权卡片已发送", {
14406
+ log$13.info("[MCP] 授权卡片已发送", {
14507
14407
  requestId: req.requestId,
14508
14408
  operationId,
14509
14409
  chatId
14510
14410
  });
14511
14411
  }
14512
14412
  } catch (err) {
14513
- log$12.error("[MCP] 交互卡片发送失败", {
14413
+ log$13.error("[MCP] 交互卡片发送失败", {
14514
14414
  requestId: req.requestId,
14515
14415
  tool: req.tool,
14516
14416
  error: err instanceof Error ? err.message : String(err)
@@ -14535,7 +14435,7 @@ async function sendInteractiveCardViaCardKit(card, chatId, account, replyInThrea
14535
14435
  } });
14536
14436
  const cardId = createResp?.data?.card_id;
14537
14437
  if (!cardId) throw new Error(`CardKit card.create 失败: code=${createResp?.code}, msg=${createResp?.msg}`);
14538
- log$12.info("[MCP] CardKit 卡片实体已创建", { cardId });
14438
+ log$13.info("[MCP] CardKit 卡片实体已创建", { cardId });
14539
14439
  const contentPayload = JSON.stringify({
14540
14440
  type: "card",
14541
14441
  data: { card_id: cardId }
@@ -16065,7 +15965,7 @@ const convertLocation = (raw) => {
16065
15965
  * injected via callbacks in `ConvertContext`. Callers are responsible
16066
15966
  * for creating the appropriate callbacks (UAT / TAT / event push).
16067
15967
  */
16068
- const log$11 = larkLogger("converters/merge-forward");
15968
+ const log$12 = larkLogger("converters/merge-forward");
16069
15969
  /**
16070
15970
  * Recursively expand a merge_forward message.
16071
15971
  *
@@ -16080,7 +15980,7 @@ const log$11 = larkLogger("converters/merge-forward");
16080
15980
  const convertMergeForward = async (_raw, ctx) => {
16081
15981
  const { accountId, messageId, resolveUserName, batchResolveNames, fetchSubMessages, convertMessageContent } = ctx;
16082
15982
  if (!fetchSubMessages) {
16083
- log$11.warn("fetchSubMessages 回调未注入,无法展开合并转发消息", {
15983
+ log$12.warn("fetchSubMessages 回调未注入,无法展开合并转发消息", {
16084
15984
  messageId,
16085
15985
  accountId
16086
15986
  });
@@ -16098,23 +15998,23 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
16098
15998
  let items;
16099
15999
  try {
16100
16000
  items = await fetchSubMessages(messageId);
16101
- log$11.info("fetchSubMessages 成功", {
16001
+ log$12.info("fetchSubMessages 成功", {
16102
16002
  messageId,
16103
16003
  itemCount: items.length
16104
16004
  });
16105
16005
  } catch (error) {
16106
- log$11.error("fetch sub-messages failed", {
16006
+ log$12.error("fetch sub-messages failed", {
16107
16007
  messageId,
16108
16008
  error: error instanceof Error ? error.message : String(error)
16109
16009
  });
16110
16010
  return "<forwarded_messages/>";
16111
16011
  }
16112
16012
  if (items.length === 0) {
16113
- log$11.warn("fetchSubMessages 返回空 items", { messageId });
16013
+ log$12.warn("fetchSubMessages 返回空 items", { messageId });
16114
16014
  return "<forwarded_messages/>";
16115
16015
  }
16116
16016
  const childrenMap = buildChildrenMap(items, messageId);
16117
- log$11.info("buildChildrenMap 完成", {
16017
+ log$12.info("buildChildrenMap 完成", {
16118
16018
  messageId,
16119
16019
  parentKeys: [...childrenMap.keys()],
16120
16020
  childCounts: [...childrenMap.entries()].map(([k, v]) => `${k}:${v.length}`)
@@ -16123,7 +16023,7 @@ async function expand(accountId, messageId, resolveUserName, batchResolveNames,
16123
16023
  if (senderIds.length > 0 && batchResolveNames) try {
16124
16024
  await batchResolveNames(senderIds);
16125
16025
  } catch (err) {
16126
- 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) });
16127
16027
  }
16128
16028
  return formatSubTree(messageId, childrenMap, accountId, resolveUserName, convertContent);
16129
16029
  }
@@ -16203,7 +16103,7 @@ async function formatSubTree(parentId, childrenMap, accountId, resolveUserName,
16203
16103
  const indented = indentLines(content, " ");
16204
16104
  parts.push(`[${timestamp}] ${displayName}:\n${indented}`);
16205
16105
  } catch (err) {
16206
- log$11.warn("failed to convert sub-message", {
16106
+ log$12.warn("failed to convert sub-message", {
16207
16107
  messageId: item.message_id,
16208
16108
  msgType: item.msg_type ?? "unknown",
16209
16109
  error: err instanceof Error ? err.message : String(err)
@@ -16417,7 +16317,7 @@ async function convertMessageContent(raw, messageType, ctx) {
16417
16317
  }
16418
16318
  //#endregion
16419
16319
  //#region src/messaging/inbound/parse-io.ts
16420
- const log$10 = larkLogger("inbound/parse-io");
16320
+ const log$11 = larkLogger("inbound/parse-io");
16421
16321
  /**
16422
16322
  * 对 interactive 消息,通过 TAT 调用 API 获取完整 v2 卡片内容。
16423
16323
  * 事件推送的 content 可能不包含 json_card,API 调用可返回完整的 raw_card_content。
@@ -16437,7 +16337,7 @@ async function fetchCardContent(messageId, larkClient) {
16437
16337
  }
16438
16338
  }))?.data?.items?.[0]?.body?.content ?? void 0;
16439
16339
  } catch (err) {
16440
- 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)}`);
16441
16341
  return;
16442
16342
  }
16443
16343
  }
@@ -16452,7 +16352,7 @@ async function fetchCardContent(messageId, larkClient) {
16452
16352
  */
16453
16353
  function createFetchSubMessages(larkClient) {
16454
16354
  return async (msgId) => {
16455
- log$10.info("fetchSubMessages 请求", {
16355
+ log$11.info("fetchSubMessages 请求", {
16456
16356
  msgId,
16457
16357
  url: `/open-apis/im/v1/messages/${msgId}`
16458
16358
  });
@@ -16464,7 +16364,7 @@ function createFetchSubMessages(larkClient) {
16464
16364
  card_msg_content_type: "raw_card_content"
16465
16365
  }
16466
16366
  });
16467
- log$10.info("fetchSubMessages 响应", {
16367
+ log$11.info("fetchSubMessages 响应", {
16468
16368
  msgId,
16469
16369
  code: response?.code,
16470
16370
  msg: response?.msg,
@@ -16481,11 +16381,11 @@ function createFetchSubMessages(larkClient) {
16481
16381
  * the account and log function.
16482
16382
  */
16483
16383
  function createParseResolveNames(account) {
16484
- return createBatchResolveNames(account, (...args) => log$10.info(args.map(String).join(" ")));
16384
+ return createBatchResolveNames(account, (...args) => log$11.info(args.map(String).join(" ")));
16485
16385
  }
16486
16386
  //#endregion
16487
16387
  //#region src/messaging/inbound/parse.ts
16488
- const log$9 = larkLogger("inbound/parse");
16388
+ const log$10 = larkLogger("inbound/parse");
16489
16389
  /**
16490
16390
  * Parse a raw Feishu message event into a normalised MessageContext.
16491
16391
  *
@@ -16535,7 +16435,7 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
16535
16435
  const fullContent = await fetchCardContent(event.message.message_id, larkClient);
16536
16436
  if (fullContent) {
16537
16437
  effectiveContent = fullContent;
16538
- log$9.info("replaced interactive content with full v2 card data");
16438
+ log$10.info("replaced interactive content with full v2 card data");
16539
16439
  }
16540
16440
  }
16541
16441
  const convertCtx = {
@@ -16575,6 +16475,219 @@ async function parseMessageEvent(event, botOpenId, expandCtx) {
16575
16475
  };
16576
16476
  }
16577
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
16578
16691
  //#region src/messaging/inbound/dedup.ts
16579
16692
  const DEFAULT_TTL_MS = 720 * 60 * 1e3;
16580
16693
  const DEFAULT_MAX_ENTRIES = 5e3;
@@ -18204,6 +18317,30 @@ async function main() {
18204
18317
  brand: "feishu",
18205
18318
  config: {}
18206
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
+ };
18207
18344
  const larkClient = LarkClient.fromAccount(account);
18208
18345
  const probeResult = await larkClient.probe();
18209
18346
  if (probeResult.ok) logger.info("Bot 身份探测成功", {
@@ -18342,7 +18479,7 @@ async function main() {
18342
18479
  });
18343
18480
  if (nameResult.name) parsed.senderName = nameResult.name;
18344
18481
  } catch {}
18345
- const formattedText = formatForCC(parsed, true);
18482
+ const formattedText = formatPromptForCC(parsed, true, await resolveQuotedContentForMessage(parsed));
18346
18483
  teammateBuffer.push(parsed.chatId, {
18347
18484
  formattedText,
18348
18485
  bufferedAt: Date.now(),
@@ -18394,6 +18531,7 @@ async function main() {
18394
18531
  workspaceRoot
18395
18532
  });
18396
18533
  await ensureUserDirectory(userContext);
18534
+ const quotedContent = await resolveQuotedContentForMessage(parsed);
18397
18535
  await dispatchToCC({
18398
18536
  ctx: parsed,
18399
18537
  account,
@@ -18403,7 +18541,8 @@ async function main() {
18403
18541
  userContext,
18404
18542
  placeholderCardId,
18405
18543
  placeholderMessageId,
18406
- messageStore
18544
+ messageStore,
18545
+ quotedContent
18407
18546
  });
18408
18547
  if (threadId) markBotInvolvedInThread(threadId);
18409
18548
  } catch (err) {