engine7 7.1.112 → 7.1.113

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.
@@ -2445,14 +2445,14 @@ var init_live = __esm({
2445
2445
  }
2446
2446
  /** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
2447
2447
  async persistToFile(dotPath, val) {
2448
- const fs58 = await import("node:fs");
2448
+ const fs59 = await import("node:fs");
2449
2449
  const cfgPath = this.current?._configFilePath;
2450
- if (!cfgPath || !fs58.existsSync(cfgPath)) {
2450
+ if (!cfgPath || !fs59.existsSync(cfgPath)) {
2451
2451
  console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
2452
2452
  return;
2453
2453
  }
2454
2454
  try {
2455
- const raw = JSON.parse(fs58.readFileSync(cfgPath, "utf-8"));
2455
+ const raw = JSON.parse(fs59.readFileSync(cfgPath, "utf-8"));
2456
2456
  const keys = dotPath.split(".");
2457
2457
  let o = raw;
2458
2458
  for (let i = 0; i < keys.length - 1; i++) {
@@ -2460,7 +2460,7 @@ var init_live = __esm({
2460
2460
  o = o[keys[i]];
2461
2461
  }
2462
2462
  o[keys[keys.length - 1]] = val;
2463
- fs58.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
2463
+ fs59.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
2464
2464
  console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
2465
2465
  markSelfWrite();
2466
2466
  } catch (e) {
@@ -4004,7 +4004,7 @@ var init_skill_gate = __esm({
4004
4004
  DEVELOPMENT_HINTS = /代码|源码|编译|构建|测试|报错|错误|bug|修复|排查|调试|实现|开发|接口|脚本|函数|类型|依赖|部署|代码 review|code|build|test|debug|implement|fix|compile|typescript|javascript|python|git/i;
4005
4005
  TECHNICAL_OBJECTS = /代码|源码|仓库|项目|模块|插件|接口|服务|引擎|engine|voice[- ]?chat|docker|容器|llm|模型|延迟|工具|脚本|函数|依赖|部署|分支|commit|api/i;
4006
4006
  TECHNICAL_ACTIONS = /优化|瘦身|提速|迁移|重构|接线|收尾|改动|加上|删掉|换成|裁剪|升级|降级|修|接入|拆分|合并|调试|排查/i;
4007
- USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy"]);
4007
+ USER_CHANNELS = /* @__PURE__ */ new Set(["discord", "feishu", "wechat", "telegram", "cli", "console", "voice-chat", "deskBuddy", "ccBridge"]);
4008
4008
  loadedBySession = /* @__PURE__ */ new Map();
4009
4009
  MAX_TRACKED_SESSIONS = 100;
4010
4010
  }
@@ -4038,9 +4038,9 @@ function isAutoMemPath(absolutePath, workspace) {
4038
4038
  return normalizedPath.startsWith(getAutoMemPath(workspace));
4039
4039
  }
4040
4040
  async function ensureMemoryDirExists(memoryDir) {
4041
- const fs58 = await import("node:fs");
4041
+ const fs59 = await import("node:fs");
4042
4042
  try {
4043
- await fs58.promises.mkdir(memoryDir, { recursive: true });
4043
+ await fs59.promises.mkdir(memoryDir, { recursive: true });
4044
4044
  } catch (e) {
4045
4045
  const code = e?.code;
4046
4046
  if (code !== "EEXIST") {
@@ -6717,7 +6717,13 @@ var init_anthropic_provider = __esm({
6717
6717
  thinking: { type: "enabled", budget_tokens: thinkingBudget }
6718
6718
  } : params.disableThinking ? {
6719
6719
  // 显式关闭 thinking(智谱 GLM-5.1 需要传 disabled 才真正关闭)
6720
- thinking: { type: "disabled" },
6720
+ // 0928 修:MiniMax M3.1-Flash 系强制 adaptive,disabled 直接 400(码 2013,
6721
+ // 实证:proactive 后台调用换 M3.1 后 LLM UNREACHABLE)——该系降级发 adaptive
6722
+ ...params.model?.includes("M3.1-Flash") ? {
6723
+ thinking: { type: "adaptive" }
6724
+ } : {
6725
+ thinking: { type: "disabled" }
6726
+ },
6721
6727
  ...params.temperature !== void 0 ? { temperature: params.temperature } : {}
6722
6728
  } : {
6723
6729
  ...params.temperature !== void 0 ? { temperature: params.temperature } : {}
@@ -7786,7 +7792,10 @@ function loadConfig(configPath) {
7786
7792
  channels: agentDefaults.channels || [],
7787
7793
  extensions: agentDefaults.extensions,
7788
7794
  maxTurns: agentDefaults.maxTurns,
7789
- devMode: agentDefaults.devMode === true
7795
+ // ⚠️ **默认 true**(0928 翀哥拍板):开源项目,功能不按 license 分档。
7796
+ // 要收起来才显式配 `agents.defaults.devMode: false`。
7797
+ // 反过来写(=== true)时,任何没手配这一格的配置都会静默落进"基础引擎模式"。
7798
+ devMode: agentDefaults.devMode !== false
7790
7799
  };
7791
7800
  const rawSession = raw.session || {};
7792
7801
  const sessionScope = Object.keys(rawSession).length > 0 ? { ...rawSession } : void 0;
@@ -8353,7 +8362,7 @@ async function acquireLock(inboxPath) {
8353
8362
  for (let i = 0; i < LOCK_RETRIES; i++) {
8354
8363
  if (await isLockStale(lockPath2)) {
8355
8364
  try {
8356
- await import("node:fs/promises").then((fs58) => fs58.rm(lockPath2, { force: true }));
8365
+ await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
8357
8366
  } catch {
8358
8367
  }
8359
8368
  }
@@ -8361,7 +8370,7 @@ async function acquireLock(inboxPath) {
8361
8370
  await writeFile2(lockPath2, `${process.pid}-${Date.now()}`, { encoding: "utf-8", flag: "wx" });
8362
8371
  return async () => {
8363
8372
  try {
8364
- await import("node:fs/promises").then((fs58) => fs58.rm(lockPath2, { force: true }));
8373
+ await import("node:fs/promises").then((fs59) => fs59.rm(lockPath2, { force: true }));
8365
8374
  } catch {
8366
8375
  }
8367
8376
  };
@@ -10001,9 +10010,9 @@ Follow these principles when generating episodic memories:
10001
10010
  import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
10002
10011
  import { mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync6, existsSync as existsSync14, readdirSync as readdirSync2 } from "node:fs";
10003
10012
  import { join as join24 } from "node:path";
10004
- import { createHash } from "node:crypto";
10013
+ import { createHash as createHash2 } from "node:crypto";
10005
10014
  function contentHash(text) {
10006
- return createHash("sha256").update(text.replace(/\s+/g, "")).digest("hex").slice(0, 40);
10015
+ return createHash2("sha256").update(text.replace(/\s+/g, "")).digest("hex").slice(0, 40);
10007
10016
  }
10008
10017
  function mem7Root(stateDir) {
10009
10018
  return join24(stateDir, "mem7");
@@ -16719,11 +16728,11 @@ async function readLastConsolidatedAt(memoryDir) {
16719
16728
  }
16720
16729
  }
16721
16730
  async function tryAcquireConsolidationLock(memoryDir) {
16722
- const path60 = lockPath(memoryDir);
16731
+ const path61 = lockPath(memoryDir);
16723
16732
  let mtimeMs;
16724
16733
  let holderPid;
16725
16734
  try {
16726
- const [s, raw] = await Promise.all([stat3(path60), readFile5(path60, "utf8")]);
16735
+ const [s, raw] = await Promise.all([stat3(path61), readFile5(path61, "utf8")]);
16727
16736
  mtimeMs = s.mtimeMs;
16728
16737
  const parsed = parseInt(raw.trim(), 10);
16729
16738
  holderPid = Number.isFinite(parsed) ? parsed : void 0;
@@ -16736,10 +16745,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
16736
16745
  }
16737
16746
  }
16738
16747
  await mkdir3(memoryDir, { recursive: true });
16739
- await writeFile4(path60, String(process.pid));
16748
+ await writeFile4(path61, String(process.pid));
16740
16749
  let verify2;
16741
16750
  try {
16742
- verify2 = await readFile5(path60, "utf8");
16751
+ verify2 = await readFile5(path61, "utf8");
16743
16752
  } catch {
16744
16753
  return null;
16745
16754
  }
@@ -16747,15 +16756,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
16747
16756
  return mtimeMs ?? 0;
16748
16757
  }
16749
16758
  async function rollbackConsolidationLock(memoryDir, priorMtime) {
16750
- const path60 = lockPath(memoryDir);
16759
+ const path61 = lockPath(memoryDir);
16751
16760
  try {
16752
16761
  if (priorMtime === 0) {
16753
- await unlink(path60);
16762
+ await unlink(path61);
16754
16763
  return;
16755
16764
  }
16756
- await writeFile4(path60, "");
16765
+ await writeFile4(path61, "");
16757
16766
  const t = priorMtime / 1e3;
16758
- await utimes(path60, t, t);
16767
+ await utimes(path61, t, t);
16759
16768
  } catch (e) {
16760
16769
  console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
16761
16770
  }
@@ -17357,12 +17366,12 @@ async function executeAndDeliver(task, now, deps) {
17357
17366
  if (promptText.startsWith("@")) {
17358
17367
  let filePath = promptText.slice(1).trim();
17359
17368
  try {
17360
- const fs58 = await import("fs");
17361
- const path60 = await import("path");
17362
- if (!path60.isAbsolute(filePath)) {
17363
- filePath = path60.join(deps.sessions["config"].stateDir, filePath);
17369
+ const fs59 = await import("fs");
17370
+ const path61 = await import("path");
17371
+ if (!path61.isAbsolute(filePath)) {
17372
+ filePath = path61.join(deps.sessions["config"].stateDir, filePath);
17364
17373
  }
17365
- promptText = fs58.readFileSync(filePath, "utf-8");
17374
+ promptText = fs59.readFileSync(filePath, "utf-8");
17366
17375
  console.log(`[cron] Loaded prompt from ${filePath} (${promptText.length} chars)`);
17367
17376
  } catch (err) {
17368
17377
  throw new Error(`Prompt file not found: ${filePath}: ${err.message}`);
@@ -17400,16 +17409,16 @@ async function executeAndDeliver(task, now, deps) {
17400
17409
  let finalResult = result;
17401
17410
  if (task.postProcess) {
17402
17411
  try {
17403
- const path60 = await import("path");
17404
- const fs58 = await import("fs");
17412
+ const path61 = await import("path");
17413
+ const fs59 = await import("fs");
17405
17414
  let scriptPath = task.postProcess;
17406
- if (!path60.isAbsolute(scriptPath)) {
17407
- scriptPath = path60.join(deps.sessions["config"].stateDir, scriptPath);
17415
+ if (!path61.isAbsolute(scriptPath)) {
17416
+ scriptPath = path61.join(deps.sessions["config"].stateDir, scriptPath);
17408
17417
  }
17409
- const resultsDirTmp = path60.join(getStorageDir(), "results");
17410
- fs58.mkdirSync(resultsDirTmp, { recursive: true });
17411
- const inputFile = path60.join(resultsDirTmp, `${task.id}.input.txt`);
17412
- fs58.writeFileSync(inputFile, result, "utf-8");
17418
+ const resultsDirTmp = path61.join(getStorageDir(), "results");
17419
+ fs59.mkdirSync(resultsDirTmp, { recursive: true });
17420
+ const inputFile = path61.join(resultsDirTmp, `${task.id}.input.txt`);
17421
+ fs59.writeFileSync(inputFile, result, "utf-8");
17413
17422
  const { execFile: execFile5 } = await import("child_process");
17414
17423
  await new Promise((resolve12) => {
17415
17424
  execFile5("python", [scriptPath, "main", "--file", inputFile], {
@@ -17436,12 +17445,12 @@ async function executeAndDeliver(task, now, deps) {
17436
17445
  }
17437
17446
  }
17438
17447
  try {
17439
- const fs58 = await import("fs");
17440
- const path60 = await import("path");
17441
- const resultsDir = path60.join(getStorageDir(), "results");
17442
- fs58.mkdirSync(resultsDir, { recursive: true });
17443
- const resultFile = path60.join(resultsDir, `${task.id}.json`);
17444
- fs58.writeFileSync(resultFile, JSON.stringify({
17448
+ const fs59 = await import("fs");
17449
+ const path61 = await import("path");
17450
+ const resultsDir = path61.join(getStorageDir(), "results");
17451
+ fs59.mkdirSync(resultsDir, { recursive: true });
17452
+ const resultFile = path61.join(resultsDir, `${task.id}.json`);
17453
+ fs59.writeFileSync(resultFile, JSON.stringify({
17445
17454
  taskId: task.id,
17446
17455
  description: task.description,
17447
17456
  executedAt: now.toISOString(),
@@ -17771,30 +17780,30 @@ function registerCronTools() {
17771
17780
  }
17772
17781
  },
17773
17782
  handler: async (args) => {
17774
- const fs58 = await import("fs");
17775
- const path60 = await import("path");
17776
- const resultsDir = path60.join(getStorageDir(), "results");
17777
- if (!fs58.existsSync(resultsDir)) {
17783
+ const fs59 = await import("fs");
17784
+ const path61 = await import("path");
17785
+ const resultsDir = path61.join(getStorageDir(), "results");
17786
+ if (!fs59.existsSync(resultsDir)) {
17778
17787
  return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
17779
17788
  }
17780
17789
  if (args.task_id) {
17781
- const file2 = path60.join(resultsDir, `${args.task_id}.json`);
17782
- if (!fs58.existsSync(file2)) {
17790
+ const file2 = path61.join(resultsDir, `${args.task_id}.json`);
17791
+ if (!fs59.existsSync(file2)) {
17783
17792
  return { content: `\u4EFB\u52A1 ${args.task_id} \u6682\u65E0\u6267\u884C\u7ED3\u679C`, isError: true };
17784
17793
  }
17785
- const data = JSON.parse(fs58.readFileSync(file2, "utf-8"));
17794
+ const data = JSON.parse(fs59.readFileSync(file2, "utf-8"));
17786
17795
  return { content: `## ${data.description}
17787
17796
  \u6267\u884C\u65F6\u95F4: ${data.executedAt}
17788
17797
  \u7B2C${data.runCount}\u6B21\u6267\u884C
17789
17798
 
17790
17799
  ${data.result}` };
17791
17800
  }
17792
- const files = fs58.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
17801
+ const files = fs59.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
17793
17802
  if (files.length === 0) {
17794
17803
  return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
17795
17804
  }
17796
17805
  const results = files.map((f) => {
17797
- const data = JSON.parse(fs58.readFileSync(path60.join(resultsDir, f), "utf-8"));
17806
+ const data = JSON.parse(fs59.readFileSync(path61.join(resultsDir, f), "utf-8"));
17798
17807
  return `### ${data.description} (${data.taskId.slice(0, 8)})
17799
17808
  \u6267\u884C: ${data.executedAt} | \u7B2C${data.runCount}\u6B21
17800
17809
  ${data.result.slice(0, 500)}${data.result.length > 500 ? "..." : ""}`;
@@ -18469,10 +18478,10 @@ function ensureLoaded(workspace, configIds) {
18469
18478
  if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
18470
18479
  }
18471
18480
  }
18472
- const path60 = join44(workspace, ".reply-blocklist.json");
18481
+ const path61 = join44(workspace, ".reply-blocklist.json");
18473
18482
  try {
18474
- if (existsSync22(path60)) {
18475
- const raw = readFileSync32(path60, "utf-8");
18483
+ if (existsSync22(path61)) {
18484
+ const raw = readFileSync32(path61, "utf-8");
18476
18485
  const parsed = JSON.parse(raw);
18477
18486
  if (parsed.blockedUserIds) {
18478
18487
  for (const id of parsed.blockedUserIds) {
@@ -18488,9 +18497,9 @@ function ensureLoaded(workspace, configIds) {
18488
18497
  loaded = true;
18489
18498
  }
18490
18499
  function save(workspace) {
18491
- const path60 = join44(workspace, ".reply-blocklist.json");
18500
+ const path61 = join44(workspace, ".reply-blocklist.json");
18492
18501
  try {
18493
- writeFileSync10(path60, JSON.stringify(state, null, 2), "utf-8");
18502
+ writeFileSync10(path61, JSON.stringify(state, null, 2), "utf-8");
18494
18503
  } catch (err) {
18495
18504
  console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
18496
18505
  }
@@ -19060,15 +19069,15 @@ var read_exports = {};
19060
19069
  __export(read_exports, {
19061
19070
  readFileState: () => readFileState
19062
19071
  });
19063
- import * as fs51 from "node:fs";
19064
- import * as path50 from "node:path";
19072
+ import * as fs52 from "node:fs";
19073
+ import * as path51 from "node:path";
19065
19074
  function isBlockedDevicePath(filePath) {
19066
19075
  if (BLOCKED_DEVICE_PATHS.has(filePath)) return true;
19067
19076
  if (filePath.startsWith("/proc/") && (filePath.endsWith("/fd/0") || filePath.endsWith("/fd/1") || filePath.endsWith("/fd/2"))) return true;
19068
19077
  return false;
19069
19078
  }
19070
19079
  function checkReadLoop(filePath, offset, limit) {
19071
- const stat4 = fs51.statSync(filePath);
19080
+ const stat4 = fs52.statSync(filePath);
19072
19081
  const mtimeMs = stat4.mtimeMs;
19073
19082
  const prev = readHistory.get(filePath);
19074
19083
  if (prev && prev.offset === offset && prev.limit === limit && prev.mtimeMs === mtimeMs) {
@@ -19082,19 +19091,19 @@ function checkReadLoop(filePath, offset, limit) {
19082
19091
  return null;
19083
19092
  }
19084
19093
  function readFileContent(filePath) {
19085
- const fd = fs51.openSync(filePath, "r");
19094
+ const fd = fs52.openSync(filePath, "r");
19086
19095
  const bom = Buffer.alloc(2);
19087
- fs51.readSync(fd, bom, 0, 2, 0);
19088
- fs51.closeSync(fd);
19096
+ fs52.readSync(fd, bom, 0, 2, 0);
19097
+ fs52.closeSync(fd);
19089
19098
  let encoding = "utf8";
19090
19099
  if (bom[0] === 255 && bom[1] === 254) {
19091
19100
  encoding = "utf16le";
19092
19101
  }
19093
- const stat4 = fs51.statSync(filePath);
19102
+ const stat4 = fs52.statSync(filePath);
19094
19103
  if (stat4.size > MAX_FILE_SIZE) {
19095
19104
  throw new Error(`\u6587\u4EF6\u592A\u5927 (${(stat4.size / 1024).toFixed(1)}KB)\uFF0C\u8D85\u8FC7 ${MAX_FILE_SIZE / 1024}KB \u9650\u5236\u3002\u8BF7\u4F7F\u7528 offset + limit \u5206\u6BB5\u8BFB\u53D6\u3002`);
19096
19105
  }
19097
- const raw = fs51.readFileSync(filePath, encoding);
19106
+ const raw = fs52.readFileSync(filePath, encoding);
19098
19107
  const content = raw.toString().replaceAll("\r\n", "\n");
19099
19108
  return { content, encoding };
19100
19109
  }
@@ -19236,11 +19245,11 @@ Usage:
19236
19245
  } catch (e) {
19237
19246
  return { content: e.message, isError: true };
19238
19247
  }
19239
- if (!fs51.existsSync(filePath)) {
19248
+ if (!fs52.existsSync(filePath)) {
19240
19249
  return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
19241
19250
  }
19242
- const stat4 = fs51.statSync(filePath);
19243
- const baseName = path50.basename(filePath).toUpperCase();
19251
+ const stat4 = fs52.statSync(filePath);
19252
+ const baseName = path51.basename(filePath).toUpperCase();
19244
19253
  if (BLOCKED_BASENAMES.has(baseName)) {
19245
19254
  return { content: `\u8BBE\u5907\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6: ${filePath}`, isError: true };
19246
19255
  }
@@ -19248,11 +19257,11 @@ Usage:
19248
19257
  return { content: `\u8BBE\u5907\u6587\u4EF6\u4F1A\u963B\u585E\u6216\u4EA7\u751F\u65E0\u9650\u8F93\u51FA: ${filePath}`, isError: true };
19249
19258
  }
19250
19259
  if (stat4.isDirectory()) {
19251
- const entries2 = fs51.readdirSync(filePath);
19260
+ const entries2 = fs52.readdirSync(filePath);
19252
19261
  const items = entries2.map((e) => {
19253
- const full = path50.join(filePath, e);
19262
+ const full = path51.join(filePath, e);
19254
19263
  try {
19255
- const s = fs51.statSync(full);
19264
+ const s = fs52.statSync(full);
19256
19265
  return s.isDirectory() ? `${e}/` : e;
19257
19266
  } catch {
19258
19267
  return e;
@@ -19261,7 +19270,7 @@ Usage:
19261
19270
  return { content: `\u76EE\u5F55 (${entries2.length} \u9879):
19262
19271
  ${items.join("\n")}` };
19263
19272
  }
19264
- const ext = path50.extname(filePath).toLowerCase();
19273
+ const ext = path51.extname(filePath).toLowerCase();
19265
19274
  if (BINARY_EXTENSIONS.has(ext)) {
19266
19275
  return { content: `\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6 (${ext}): ${filePath}`, isError: true };
19267
19276
  }
@@ -19309,22 +19318,22 @@ ${result}` : result };
19309
19318
 
19310
19319
  // src/tools/write.ts
19311
19320
  var write_exports = {};
19312
- import * as fs52 from "node:fs";
19313
- import * as path51 from "node:path";
19321
+ import * as fs53 from "node:fs";
19322
+ import * as path52 from "node:path";
19314
19323
  function isBlockedPath(filePath) {
19315
19324
  return BLOCKED_PATTERNS.some((p) => p.test(filePath));
19316
19325
  }
19317
19326
  function atomicWrite(filePath, content) {
19318
19327
  const tmpPath = filePath + ".tmp." + Date.now() + ".write";
19319
- fs52.writeFileSync(tmpPath, content, "utf-8");
19328
+ fs53.writeFileSync(tmpPath, content, "utf-8");
19320
19329
  try {
19321
- fs52.renameSync(tmpPath, filePath);
19330
+ fs53.renameSync(tmpPath, filePath);
19322
19331
  } catch (e) {
19323
19332
  try {
19324
- fs52.unlinkSync(tmpPath);
19333
+ fs53.unlinkSync(tmpPath);
19325
19334
  } catch {
19326
19335
  }
19327
- fs52.writeFileSync(filePath, content, "utf-8");
19336
+ fs53.writeFileSync(filePath, content, "utf-8");
19328
19337
  }
19329
19338
  }
19330
19339
  function simpleDiff(oldContent, newContent) {
@@ -19418,15 +19427,15 @@ Usage:
19418
19427
  }
19419
19428
  const rawContent = args.content;
19420
19429
  const content = rawContent.replaceAll("\r\n", "\n");
19421
- if (fs52.existsSync(filePath) && fs52.statSync(filePath).isDirectory()) {
19430
+ if (fs53.existsSync(filePath) && fs53.statSync(filePath).isDirectory()) {
19422
19431
  return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
19423
19432
  }
19424
19433
  let oldContent = null;
19425
19434
  let isCreate = true;
19426
- if (fs52.existsSync(filePath)) {
19435
+ if (fs53.existsSync(filePath)) {
19427
19436
  isCreate = false;
19428
19437
  try {
19429
- oldContent = fs52.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
19438
+ oldContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
19430
19439
  } catch {
19431
19440
  isCreate = true;
19432
19441
  }
@@ -19442,10 +19451,10 @@ Usage:
19442
19451
  isError: true
19443
19452
  };
19444
19453
  }
19445
- const currentStat = fs52.statSync(filePath);
19454
+ const currentStat = fs53.statSync(filePath);
19446
19455
  const lastWriteTime = Math.floor(currentStat.mtimeMs);
19447
19456
  if (lastWriteTime > readState.timestamp) {
19448
- const currentContent = fs52.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
19457
+ const currentContent = fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
19449
19458
  if (currentContent !== oldContent) {
19450
19459
  return {
19451
19460
  content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u5199\u5165: ${filePath}`,
@@ -19454,9 +19463,9 @@ Usage:
19454
19463
  }
19455
19464
  }
19456
19465
  }
19457
- const dir = path51.dirname(filePath);
19466
+ const dir = path52.dirname(filePath);
19458
19467
  try {
19459
- fs52.mkdirSync(dir, { recursive: true });
19468
+ fs53.mkdirSync(dir, { recursive: true });
19460
19469
  } catch (e) {
19461
19470
  return { content: `\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55: ${dir} \u2014 ${e.message}`, isError: true };
19462
19471
  }
@@ -19465,11 +19474,11 @@ Usage:
19465
19474
  } catch (e) {
19466
19475
  return { content: `\u5199\u5165\u5931\u8D25: ${e.message}`, isError: true };
19467
19476
  }
19468
- readFileState.set(filePath, { timestamp: fs52.statSync(filePath).mtimeMs });
19477
+ readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
19469
19478
  const action = isCreate ? "\u521B\u5EFA" : "\u66F4\u65B0";
19470
19479
  const lines = content.split("\n").length;
19471
19480
  const chars = content.length;
19472
- const stat4 = fs52.statSync(filePath);
19481
+ const stat4 = fs53.statSync(filePath);
19473
19482
  let diff = "";
19474
19483
  if (!isCreate && oldContent !== null) {
19475
19484
  diff = `
@@ -19489,8 +19498,8 @@ ${simpleDiff(oldContent, content)}`;
19489
19498
 
19490
19499
  // src/tools/edit.ts
19491
19500
  var edit_exports = {};
19492
- import * as fs53 from "node:fs";
19493
- import * as path52 from "node:path";
19501
+ import * as fs54 from "node:fs";
19502
+ import * as path53 from "node:path";
19494
19503
  function normalizeQuotes(str) {
19495
19504
  return str.replaceAll(LEFT_SINGLE_CURLY, "'").replaceAll(RIGHT_SINGLE_CURLY, "'").replaceAll(LEFT_DOUBLE_CURLY, '"').replaceAll(RIGHT_DOUBLE_CURLY, '"');
19496
19505
  }
@@ -19643,7 +19652,7 @@ Usage:
19643
19652
  }
19644
19653
  let fileContent = null;
19645
19654
  try {
19646
- const stat4 = fs53.statSync(filePath);
19655
+ const stat4 = fs54.statSync(filePath);
19647
19656
  if (stat4.isDirectory()) {
19648
19657
  return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
19649
19658
  }
@@ -19653,21 +19662,21 @@ Usage:
19653
19662
  } catch (e) {
19654
19663
  if (e.code === "ENOENT") {
19655
19664
  if (oldString === "") {
19656
- const dir = path52.dirname(filePath);
19657
- fs53.mkdirSync(dir, { recursive: true });
19658
- fs53.writeFileSync(filePath, newString, "utf-8");
19659
- readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
19665
+ const dir = path53.dirname(filePath);
19666
+ fs54.mkdirSync(dir, { recursive: true });
19667
+ fs54.writeFileSync(filePath, newString, "utf-8");
19668
+ readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
19660
19669
  return { content: `\u521B\u5EFA\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
19661
19670
  }
19662
19671
  return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
19663
19672
  }
19664
19673
  throw e;
19665
19674
  }
19666
- const rawContent = fs53.readFileSync(filePath, "utf-8");
19675
+ const rawContent = fs54.readFileSync(filePath, "utf-8");
19667
19676
  fileContent = rawContent.replaceAll("\r\n", "\n");
19668
19677
  if (oldString === "" && fileContent.trim() === "") {
19669
- fs53.writeFileSync(filePath, newString, "utf-8");
19670
- readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
19678
+ fs54.writeFileSync(filePath, newString, "utf-8");
19679
+ readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
19671
19680
  return { content: `\u5199\u5165\u7A7A\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
19672
19681
  }
19673
19682
  const readState = readFileState.get(filePath);
@@ -19677,11 +19686,11 @@ Usage:
19677
19686
  isError: true
19678
19687
  };
19679
19688
  }
19680
- const currentStat = fs53.statSync(filePath);
19689
+ const currentStat = fs54.statSync(filePath);
19681
19690
  const lastWriteTime = Math.floor(currentStat.mtimeMs);
19682
19691
  if (lastWriteTime > readState.timestamp) {
19683
19692
  if (fileContent !== rawContent.replaceAll("\r\n", "\n")) {
19684
- if (fileContent !== fs53.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
19693
+ if (fileContent !== fs54.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
19685
19694
  return {
19686
19695
  content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u7F16\u8F91: ${filePath}`,
19687
19696
  isError: true
@@ -19713,8 +19722,8 @@ ${preview}
19713
19722
  const actualNewString = preserveQuoteStyle(oldString, actualOldString, newString);
19714
19723
  const diffView = generateEditDiff(fileContent, actualOldString, actualNewString);
19715
19724
  const newContent = applyEditToFile(fileContent, actualOldString, actualNewString, replaceAll);
19716
- fs53.writeFileSync(filePath, newContent, "utf-8");
19717
- readFileState.set(filePath, { timestamp: fs53.statSync(filePath).mtimeMs });
19725
+ fs54.writeFileSync(filePath, newContent, "utf-8");
19726
+ readFileState.set(filePath, { timestamp: fs54.statSync(filePath).mtimeMs });
19718
19727
  const strategy = actualOldString === oldString ? "\u7CBE\u786E\u5339\u914D" : "\u5F15\u53F7\u89C4\u8303\u5316\u5339\u914D";
19719
19728
  const count = replaceAll ? matchCount : 1;
19720
19729
  const diff = `${oldString.length}\u2192${newString.length}\u5B57\u7B26`;
@@ -19732,8 +19741,8 @@ ${diffView}`
19732
19741
 
19733
19742
  // src/tools/glob.ts
19734
19743
  var glob_exports = {};
19735
- import * as fs54 from "node:fs";
19736
- import * as path53 from "node:path";
19744
+ import * as fs55 from "node:fs";
19745
+ import * as path54 from "node:path";
19737
19746
  function globMatch(pattern, filename) {
19738
19747
  const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
19739
19748
  try {
@@ -19753,18 +19762,18 @@ function findFiles(dir, pattern, limit, baseDir) {
19753
19762
  }
19754
19763
  let entries2;
19755
19764
  try {
19756
- entries2 = fs54.readdirSync(currentDir, { withFileTypes: true });
19765
+ entries2 = fs55.readdirSync(currentDir, { withFileTypes: true });
19757
19766
  } catch {
19758
19767
  return;
19759
19768
  }
19760
19769
  for (const entry of entries2) {
19761
19770
  if (truncated) return;
19762
- const fullPath = path53.join(currentDir, entry.name);
19771
+ const fullPath = path54.join(currentDir, entry.name);
19763
19772
  if (entry.isDirectory()) {
19764
19773
  if (VCS_DIRS.has(entry.name)) continue;
19765
19774
  walk(fullPath);
19766
19775
  } else if (entry.isFile()) {
19767
- const relativePath = path53.relative(baseDir, fullPath).replace(/\\/g, "/");
19776
+ const relativePath = path54.relative(baseDir, fullPath).replace(/\\/g, "/");
19768
19777
  const patternsToTry = [pattern];
19769
19778
  if (pattern.startsWith("**/")) {
19770
19779
  patternsToTry.push(pattern.slice(3));
@@ -19774,7 +19783,7 @@ function findFiles(dir, pattern, limit, baseDir) {
19774
19783
  );
19775
19784
  if (matched) {
19776
19785
  try {
19777
- const stat4 = fs54.statSync(fullPath);
19786
+ const stat4 = fs55.statSync(fullPath);
19778
19787
  results.push({ path: fullPath, mtimeMs: stat4.mtimeMs });
19779
19788
  } catch {
19780
19789
  }
@@ -19791,7 +19800,7 @@ function findFiles(dir, pattern, limit, baseDir) {
19791
19800
  };
19792
19801
  }
19793
19802
  function toRelativePath(absolutePath, cwd) {
19794
- if (absolutePath.startsWith(cwd + path53.sep)) {
19803
+ if (absolutePath.startsWith(cwd + path54.sep)) {
19795
19804
  return absolutePath.slice(cwd.length + 1);
19796
19805
  }
19797
19806
  return absolutePath;
@@ -19824,10 +19833,10 @@ var init_glob = __esm({
19824
19833
  const searchPath = args.path ? resolvePath(args.path, ctx.workspace) : ctx.workspace;
19825
19834
  const pattern = args.pattern;
19826
19835
  const limit = args.limit || DEFAULT_LIMIT;
19827
- if (!fs54.existsSync(searchPath)) {
19836
+ if (!fs55.existsSync(searchPath)) {
19828
19837
  return { content: `\u76EE\u5F55\u4E0D\u5B58\u5728: ${searchPath}`, isError: true };
19829
19838
  }
19830
- if (!fs54.statSync(searchPath).isDirectory()) {
19839
+ if (!fs55.statSync(searchPath).isDirectory()) {
19831
19840
  return { content: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${searchPath}`, isError: true };
19832
19841
  }
19833
19842
  const start = Date.now();
@@ -19852,7 +19861,7 @@ ${filenames.join("\n")}${truncatedNote}`
19852
19861
  // src/tools/grep.ts
19853
19862
  var grep_exports = {};
19854
19863
  import { execFile as execFile4 } from "node:child_process";
19855
- import * as path54 from "node:path";
19864
+ import * as path55 from "node:path";
19856
19865
  function ripGrep(args, searchPath, signal) {
19857
19866
  return new Promise((resolve12) => {
19858
19867
  const fullArgs = [...args, searchPath];
@@ -19884,7 +19893,7 @@ function applyHeadLimit(items, limit, offset = 0) {
19884
19893
  };
19885
19894
  }
19886
19895
  function toRelativePath2(absolutePath, cwd) {
19887
- if (absolutePath.startsWith(cwd + path54.sep)) {
19896
+ if (absolutePath.startsWith(cwd + path55.sep)) {
19888
19897
  return absolutePath.slice(cwd.length + 1);
19889
19898
  }
19890
19899
  if (absolutePath.startsWith(cwd)) {
@@ -21693,7 +21702,7 @@ var init_TeamDeleteTool = __esm({
21693
21702
  },
21694
21703
  isEnabled: () => isAgentSwarmsEnabled(),
21695
21704
  handler: async (_args, ctx) => {
21696
- const teamName = ctx.teamName;
21705
+ const teamName = ctx.teamName || getLeaderTeamName() || void 0;
21697
21706
  if (teamName) {
21698
21707
  const teamFile = readTeamFile(teamName);
21699
21708
  if (teamFile) {
@@ -21777,7 +21786,7 @@ function getAgentName(ctx) {
21777
21786
  return ctx.agentName || TEAM_LEAD_NAME;
21778
21787
  }
21779
21788
  function getTeamName(ctx) {
21780
- return ctx.teamName;
21789
+ return ctx.teamName || getLeaderTeamName() || void 0;
21781
21790
  }
21782
21791
  async function handleMessage(recipientName, content, summary, ctx) {
21783
21792
  const teamName = getTeamName(ctx);
@@ -22406,8 +22415,8 @@ __export(manager_exports, {
22406
22415
  McpManager: () => McpManager,
22407
22416
  convertInputSchema: () => convertInputSchema
22408
22417
  });
22409
- import * as fs57 from "node:fs";
22410
- import * as path57 from "node:path";
22418
+ import * as fs58 from "node:fs";
22419
+ import * as path58 from "node:path";
22411
22420
  import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
22412
22421
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
22413
22422
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -22448,12 +22457,12 @@ function convertInputSchema(inputSchema) {
22448
22457
  }
22449
22458
  function persistBinary(base64Data, mimeType, persistId) {
22450
22459
  const ext = mimeType?.split("/")[1] || "bin";
22451
- const dir = path57.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
22452
- fs57.mkdirSync(dir, { recursive: true });
22453
- const filepath = path57.join(dir, `${persistId}.${ext}`);
22460
+ const dir = path58.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
22461
+ fs58.mkdirSync(dir, { recursive: true });
22462
+ const filepath = path58.join(dir, `${persistId}.${ext}`);
22454
22463
  try {
22455
22464
  const buf = Buffer.from(base64Data, "base64");
22456
- fs57.writeFileSync(filepath, buf);
22465
+ fs58.writeFileSync(filepath, buf);
22457
22466
  return { filepath, size: buf.length };
22458
22467
  } catch (err) {
22459
22468
  return { error: err.message };
@@ -22787,7 +22796,7 @@ __export(resources_exports, {
22787
22796
  registerMcpResourceTools: () => registerMcpResourceTools,
22788
22797
  unregisterMcpResourceTools: () => unregisterMcpResourceTools
22789
22798
  });
22790
- import * as path58 from "node:path";
22799
+ import * as path59 from "node:path";
22791
22800
  function registerMcpResourceTools(manager) {
22792
22801
  mcpManagerRef = manager;
22793
22802
  registry.register(listResourcesTool);
@@ -22805,7 +22814,7 @@ var init_resources = __esm({
22805
22814
  "use strict";
22806
22815
  init_registry();
22807
22816
  MAX_RESULT_CHARS2 = 1e5;
22808
- MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path58.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
22817
+ MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path59.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
22809
22818
  MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
22810
22819
  MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
22811
22820
  mcpManagerRef = null;
@@ -22893,7 +22902,7 @@ ${lines.join("\n\n")}`;
22893
22902
  // src/engine-startup.ts
22894
22903
  init_query();
22895
22904
  init_live();
22896
- import * as path59 from "node:path";
22905
+ import * as path60 from "node:path";
22897
22906
 
22898
22907
  // src/startup/providers.ts
22899
22908
  init_provider_factory();
@@ -23138,16 +23147,32 @@ import * as path10 from "node:path";
23138
23147
  import { fileURLToPath } from "node:url";
23139
23148
 
23140
23149
  // src/config/secrets-env.ts
23141
- import { basename, dirname, join as join9 } from "node:path";
23150
+ import { createHash } from "node:crypto";
23151
+ import { basename, dirname, join as join9, normalize as normalize3 } from "node:path";
23142
23152
  function secretsHome() {
23143
23153
  return process.env.HOME || process.env.USERPROFILE || ".";
23144
23154
  }
23155
+ function normalizeStateDir(stateDir) {
23156
+ return normalize3(stateDir).split("\\").join("/").replace(/\/+$/, "").toLowerCase();
23157
+ }
23158
+ function stateKey(stateDir) {
23159
+ return createHash("sha256").update(normalizeStateDir(stateDir)).digest("hex").slice(0, 8);
23160
+ }
23161
+ function isDefaultStateDir(stateDir) {
23162
+ return normalizeStateDir(stateDir) === normalizeStateDir(join9(secretsHome(), ".engine7"));
23163
+ }
23145
23164
  function secretsCandidates(stateDir, configName) {
23146
- return [
23147
- join9(secretsHome(), ".engine7-secrets", `${configName}.env`),
23165
+ const shared = join9(secretsHome(), ".engine7-secrets", `${configName}.env`);
23166
+ const inState = [
23148
23167
  join9(stateDir, "configs", `.env.${configName}`),
23149
23168
  join9(stateDir, "configs", ".env")
23150
23169
  ];
23170
+ if (isDefaultStateDir(stateDir)) return [shared, ...inState];
23171
+ return [
23172
+ join9(secretsHome(), ".engine7-secrets", `${configName}-${stateKey(stateDir)}.env`),
23173
+ shared,
23174
+ ...inState
23175
+ ];
23151
23176
  }
23152
23177
 
23153
23178
  // src/reload/orchestrator.ts
@@ -24259,7 +24284,7 @@ ${formatted}` };
24259
24284
  };
24260
24285
  }
24261
24286
  function createEverosGetTool() {
24262
- const fs58 = __require("node:fs/promises");
24287
+ const fs59 = __require("node:fs/promises");
24263
24288
  return {
24264
24289
  name: "memory_get",
24265
24290
  description: "Read a memory file by path.",
@@ -24275,7 +24300,7 @@ function createEverosGetTool() {
24275
24300
  handler: async (args) => {
24276
24301
  try {
24277
24302
  const filePath = args.path;
24278
- const content = await fs58.readFile(filePath, "utf-8");
24303
+ const content = await fs59.readFile(filePath, "utf-8");
24279
24304
  const lines = content.split("\n");
24280
24305
  const fromLine = args.from ?? 1;
24281
24306
  const numLines = args.lines ?? lines.length;
@@ -24327,11 +24352,11 @@ var ALL_PAID_FEATURES = [
24327
24352
  "report-gen",
24328
24353
  "custom-soul"
24329
24354
  ];
24330
- function loadLicense(stateDir, devMode) {
24355
+ function loadLicense(stateDir, devMode = true) {
24331
24356
  if (_licenseChecked) return _cachedLicense;
24332
24357
  _licenseChecked = true;
24333
24358
  if (devMode) {
24334
- console.log("[license] devMode enabled \u2014 all features active (internal development)");
24359
+ console.log("[license] \u5F00\u653E\u6A21\u5F0F \u2014 \u5168\u90E8 feature \u6FC0\u6D3B\uFF08\u8981\u5206\u6863\uFF1Aagents.defaults.devMode = false\uFF09");
24335
24360
  const allActive = {};
24336
24361
  for (const id of ALL_PAID_FEATURES) {
24337
24362
  allActive[id] = { active: true };
@@ -24341,7 +24366,7 @@ function loadLicense(stateDir, devMode) {
24341
24366
  }
24342
24367
  const licensePath = path15.join(stateDir, "license.json");
24343
24368
  if (!fs16.existsSync(licensePath)) {
24344
- console.log("[license] No license.json found, running basic engine only");
24369
+ console.log("[license] devMode=false \u4E14\u65E0 license.json \u2014\u2014 \u57FA\u7840\u5F15\u64CE\u6A21\u5F0F");
24345
24370
  return null;
24346
24371
  }
24347
24372
  try {
@@ -26163,18 +26188,18 @@ function resetSessionStartInjection(sessionId) {
26163
26188
  }
26164
26189
  var externalChanRulesCache = null;
26165
26190
  function loadExternalChanRules(workspace) {
26166
- const path60 = join36(workspace, "prompts", "external-chan-rules.md");
26167
- if (externalChanRulesCache && externalChanRulesCache.path === path60) return externalChanRulesCache;
26191
+ const path61 = join36(workspace, "prompts", "external-chan-rules.md");
26192
+ if (externalChanRulesCache && externalChanRulesCache.path === path61) return externalChanRulesCache;
26168
26193
  let content = "";
26169
- if (existsSync19(path60)) {
26194
+ if (existsSync19(path61)) {
26170
26195
  try {
26171
- content = readFileSync26(path60, "utf-8").trim();
26196
+ content = readFileSync26(path61, "utf-8").trim();
26172
26197
  } catch (e) {
26173
26198
  console.warn(`[external-chan-rules] Failed to load: ${e}`);
26174
26199
  }
26175
26200
  }
26176
- externalChanRulesCache = { path: path60, content };
26177
- console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path60}`);
26201
+ externalChanRulesCache = { path: path61, content };
26202
+ console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path61}`);
26178
26203
  return externalChanRulesCache;
26179
26204
  }
26180
26205
  function getExternalChanRulesBlock(inboundMeta, workspace) {
@@ -27061,15 +27086,15 @@ function registerCompactHooks(config) {
27061
27086
  const dailyDir = path22.join(workspace, "memory", "daily");
27062
27087
  const dailyPath = path22.join(dailyDir, `${dateStr}.md`);
27063
27088
  try {
27064
- const fs58 = await import("node:fs");
27065
- if (!fs58.existsSync(dailyDir)) {
27066
- fs58.mkdirSync(dailyDir, { recursive: true });
27089
+ const fs59 = await import("node:fs");
27090
+ if (!fs59.existsSync(dailyDir)) {
27091
+ fs59.mkdirSync(dailyDir, { recursive: true });
27067
27092
  }
27068
27093
  const sessionsDir = path22.join(config.stateDir, "agents", "main", "sessions");
27069
27094
  const sessionFile = path22.join(sessionsDir, `${sessionId}.jsonl`);
27070
27095
  const recentLines = [];
27071
- if (fs58.existsSync(sessionFile)) {
27072
- const content = fs58.readFileSync(sessionFile, "utf-8");
27096
+ if (fs59.existsSync(sessionFile)) {
27097
+ const content = fs59.readFileSync(sessionFile, "utf-8");
27073
27098
  const lines = content.trim().split("\n").filter(Boolean);
27074
27099
  const userLines = lines.filter((l) => {
27075
27100
  try {
@@ -27099,10 +27124,10 @@ function registerCompactHooks(config) {
27099
27124
  const entry = `${header}
27100
27125
  ${body}
27101
27126
  `;
27102
- if (fs58.existsSync(dailyPath)) {
27103
- fs58.appendFileSync(dailyPath, entry);
27127
+ if (fs59.existsSync(dailyPath)) {
27128
+ fs59.appendFileSync(dailyPath, entry);
27104
27129
  } else {
27105
- fs58.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27130
+ fs59.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27106
27131
  ${entry}`);
27107
27132
  }
27108
27133
  console.log(`[hooks] PreCompact: saved ${recentLines.length} lines to ${dailyPath}`);
@@ -27118,16 +27143,16 @@ ${entry}`);
27118
27143
  const workspace = input.cwd || input.workspace || "";
27119
27144
  if (!workspace) return { continue: true };
27120
27145
  try {
27121
- const fs58 = await import("node:fs");
27146
+ const fs59 = await import("node:fs");
27122
27147
  const bufferPath = path22.join(workspace, "memory", "working-buffer.md");
27123
- if (fs58.existsSync(bufferPath)) {
27124
- const stat4 = fs58.statSync(bufferPath);
27148
+ if (fs59.existsSync(bufferPath)) {
27149
+ const stat4 = fs59.statSync(bufferPath);
27125
27150
  const ageMs = Date.now() - stat4.mtimeMs;
27126
27151
  const ageMin = Math.round(ageMs / 6e4);
27127
27152
  if (ageMin > 10) {
27128
27153
  console.warn(`[hooks] PostCompact: \u26A0\uFE0F working-buffer.md is ${ageMin}min old (last modified ${stat4.mtime.toISOString()}) \u2014 content may be stale!`);
27129
27154
  }
27130
- const content = fs58.readFileSync(bufferPath, "utf-8");
27155
+ const content = fs59.readFileSync(bufferPath, "utf-8");
27131
27156
  if (content.trim()) {
27132
27157
  console.log(`[hooks] PostCompact: injecting working-buffer (${content.length} chars, ${ageMin}min old)`);
27133
27158
  return {
@@ -33582,10 +33607,10 @@ function writeReportState(stateDir, s) {
33582
33607
  }
33583
33608
 
33584
33609
  // src/optional/reporting/payload.ts
33585
- import { createHash as createHash2 } from "node:crypto";
33610
+ import { createHash as createHash3 } from "node:crypto";
33586
33611
  import * as os3 from "node:os";
33587
33612
  function installKeyOf(installId, installSecret) {
33588
- return createHash2("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
33613
+ return createHash3("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
33589
33614
  }
33590
33615
  function buildReportPayload(opts) {
33591
33616
  const p = {
@@ -33962,13 +33987,13 @@ var DiscordAdapter = class _DiscordAdapter {
33962
33987
  }
33963
33988
  /** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
33964
33989
  async sendFile(target, message, attachment) {
33965
- const fs58 = await import("node:fs");
33966
- const path60 = await import("node:path");
33967
- if (!fs58.existsSync(attachment.path)) {
33990
+ const fs59 = await import("node:fs");
33991
+ const path61 = await import("node:path");
33992
+ if (!fs59.existsSync(attachment.path)) {
33968
33993
  throw new Error(`File not found: ${attachment.path}`);
33969
33994
  }
33970
- const filename = attachment.filename || path60.basename(attachment.path);
33971
- const fileBuffer = fs58.readFileSync(attachment.path);
33995
+ const filename = attachment.filename || path61.basename(attachment.path);
33996
+ const fileBuffer = fs59.readFileSync(attachment.path);
33972
33997
  const filePayload = {
33973
33998
  attachment: fileBuffer,
33974
33999
  name: filename
@@ -34417,13 +34442,13 @@ var FeishuAdapter = class _FeishuAdapter {
34417
34442
  }
34418
34443
  /** 发送媒体附件(图片/文件) */
34419
34444
  async sendFile(target, message, attachment) {
34420
- const fs58 = await import("node:fs");
34421
- const path60 = await import("node:path");
34422
- if (!fs58.existsSync(attachment.path)) {
34445
+ const fs59 = await import("node:fs");
34446
+ const path61 = await import("node:path");
34447
+ if (!fs59.existsSync(attachment.path)) {
34423
34448
  throw new Error(`File not found: ${attachment.path}`);
34424
34449
  }
34425
- const filename = attachment.filename || path60.basename(attachment.path);
34426
- const fileBuffer = fs58.readFileSync(attachment.path);
34450
+ const filename = attachment.filename || path61.basename(attachment.path);
34451
+ const fileBuffer = fs59.readFileSync(attachment.path);
34427
34452
  const safeTarget = target || this.config.defaultTarget || "";
34428
34453
  if (!safeTarget) throw new Error("Feishu sendFile: no target (\u4E3B\u52A8\u53D1\u9001\u9700\u914D\u7F6E defaultTarget \u6216\u4F20\u5165 target)");
34429
34454
  const receiveIdType = safeTarget.startsWith("ou_") ? "open_id" : "chat_id";
@@ -36245,22 +36270,22 @@ var DeskBuddyAdapter = class {
36245
36270
  try {
36246
36271
  if (!this.historyFile) {
36247
36272
  const os7 = __require("os");
36248
- const fs59 = __require("fs");
36249
- const path60 = __require("path");
36250
- const dir = path60.join(os7.homedir(), "xiaowen", "workspace", ".desktopBuddy");
36251
- fs59.mkdirSync(dir, { recursive: true });
36252
- this.historyFile = path60.join(dir, "history.json");
36253
- this._fs = fs59;
36254
- }
36255
- const fs58 = this._fs;
36273
+ const fs60 = __require("fs");
36274
+ const path61 = __require("path");
36275
+ const dir = path61.join(os7.homedir(), "xiaowen", "workspace", ".desktopBuddy");
36276
+ fs60.mkdirSync(dir, { recursive: true });
36277
+ this.historyFile = path61.join(dir, "history.json");
36278
+ this._fs = fs60;
36279
+ }
36280
+ const fs59 = this._fs;
36256
36281
  let arr = [];
36257
36282
  try {
36258
- arr = JSON.parse(fs58.readFileSync(this.historyFile, "utf-8")) || [];
36283
+ arr = JSON.parse(fs59.readFileSync(this.historyFile, "utf-8")) || [];
36259
36284
  } catch {
36260
36285
  }
36261
36286
  arr.push({ ...entry, ts: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN", { hour12: false }) });
36262
36287
  if (arr.length > 500) arr = arr.slice(-500);
36263
- fs58.writeFileSync(this.historyFile, JSON.stringify(arr, null, 1));
36288
+ fs59.writeFileSync(this.historyFile, JSON.stringify(arr, null, 1));
36264
36289
  } catch (e) {
36265
36290
  console.warn("[deskBuddy] history write fail", e);
36266
36291
  }
@@ -36539,8 +36564,365 @@ var DeskBuddyAdapter = class {
36539
36564
  }
36540
36565
  };
36541
36566
 
36567
+ // src/channels/cc-bridge.ts
36568
+ init_live();
36569
+ import fs41 from "node:fs";
36570
+ import path41 from "node:path";
36571
+ import { WebSocketServer as WebSocketServer2, WebSocket as WebSocket2 } from "ws";
36572
+ var DEFAULT_PORT = 16996;
36573
+ var CcBridgeAdapter = class {
36574
+ name = "ccBridge";
36575
+ config;
36576
+ wss = null;
36577
+ clients = /* @__PURE__ */ new Set();
36578
+ messageHandler = null;
36579
+ seq = 0;
36580
+ constructor(config) {
36581
+ this.config = config;
36582
+ }
36583
+ /** 工具流水默认不推(见 CcBridgeConfig.showToolDisplay) */
36584
+ get suppressToolDisplay() {
36585
+ return this.config.showToolDisplay !== true;
36586
+ }
36587
+ /** 实际监听的端口。配 `port: 0` 时由内核分配 —— 测试用它拿真端口,不用赌某个数字没被占 */
36588
+ get boundPort() {
36589
+ const a = this.wss?.address();
36590
+ return a && typeof a === "object" ? a.port : null;
36591
+ }
36592
+ onMessage(handler2) {
36593
+ this.messageHandler = handler2;
36594
+ }
36595
+ // ── outbox(持久队列)──
36596
+ //
36597
+ // 每次现读 stateDir,不缓存 —— stateDir 的 effect 是 restart,缓存下来的
36598
+ // 只会在热加载后变成指向旧目录的幽灵路径(配置访问铁律)。
36599
+ outboxFile() {
36600
+ const sd = liveConfig.get("stateDir") || process.cwd();
36601
+ return path41.join(sd, "state", "cc-bridge", "outbox.jsonl");
36602
+ }
36603
+ /**
36604
+ * 读队列。**只有 ENOENT 才等于"空队列"。**
36605
+ *
36606
+ * ⚠️ 这里原来写的是 `catch { return [] }` —— 那是**会丢数据的**:
36607
+ * 读失败(Windows 上 AV/同步盘/搜索索引的瞬时共享锁 EBUSY,或 EPERM/EACCES)
36608
+ * 返回空数组,紧接着 `enqueue` 拿这份空数组**整份覆写**,待投递的消息全没,
36609
+ * 而且一句警告都没有。而"读不了"和"不存在"是完全不同的两件事 ——
36610
+ * 这个区分正是"宁可重复,不可丢失"能不能成立的支点。
36611
+ */
36612
+ readOutbox() {
36613
+ let raw;
36614
+ try {
36615
+ raw = fs41.readFileSync(this.outboxFile(), "utf8");
36616
+ } catch (e) {
36617
+ if (e?.code === "ENOENT") return [];
36618
+ throw e;
36619
+ }
36620
+ const out = [];
36621
+ for (const line of raw.split("\n")) {
36622
+ if (!line.trim()) continue;
36623
+ try {
36624
+ out.push(JSON.parse(line));
36625
+ } catch {
36626
+ }
36627
+ }
36628
+ return out;
36629
+ }
36630
+ /**
36631
+ * 写队列:**先写 .tmp 再 rename**。
36632
+ *
36633
+ * `writeFileSync(file, ...)` 是原地截断(`w` 模式先清零再写)——
36634
+ * 在这中间被 taskkill / OOM / 断电,文件就是空的或半截,
36635
+ * 下次读回来是 [] 且无从分辨,最多 maxQueue 条待投递消息整体消失。
36636
+ * rename 到已存在文件走的是替换语义,落盘要么全旧要么全新。
36637
+ *
36638
+ * **失败要抛**:调用方(cron/heartbeat 经 `manager.send`)必须知道没存住 ——
36639
+ * 吞成 warning 的话,磁盘满/只读时客户端在线还能收到实时那条,
36640
+ * 客户端离线就是真丢,而这恰恰是这个通道要保的场景。
36641
+ */
36642
+ writeOutbox(items) {
36643
+ const file2 = this.outboxFile();
36644
+ const tmp = `${file2}.tmp`;
36645
+ fs41.mkdirSync(path41.dirname(file2), { recursive: true });
36646
+ fs41.writeFileSync(tmp, items.map((i) => JSON.stringify(i)).join("\n") + (items.length ? "\n" : ""), "utf8");
36647
+ fs41.renameSync(tmp, file2);
36648
+ }
36649
+ /** 入队(dedupe 不做 —— id 是新的,重复投递由 ack 收敛)。读/写失败都往外抛,不吞。 */
36650
+ enqueue(item) {
36651
+ const items = this.readOutbox();
36652
+ items.push(item);
36653
+ const cap = this.config.maxQueue ?? 500;
36654
+ if (items.length > cap) {
36655
+ const dropped = items.length - cap;
36656
+ items.splice(0, dropped);
36657
+ console.warn(`[ccBridge] outbox \u8D85\u8FC7 ${cap} \u6761\uFF0C\u4E22\u4E86\u6700\u8001\u7684 ${dropped} \u6761 \u2014\u2014 CC \u592A\u4E45\u6CA1\u6765\u6536`);
36658
+ }
36659
+ this.writeOutbox(items);
36660
+ }
36661
+ /**
36662
+ * ack:从队列删掉客户端确认过的。
36663
+ *
36664
+ * 读失败时**返回 0 而不是抛** —— 这里宁可"删不掉"也不能"删错":
36665
+ * ack 的语义是收敛,失败留给下一轮再收敛,代价只是重复投递。
36666
+ */
36667
+ ack(ids) {
36668
+ if (!ids.length) return 0;
36669
+ const set = new Set(ids);
36670
+ let items;
36671
+ try {
36672
+ items = this.readOutbox();
36673
+ } catch (e) {
36674
+ console.warn(`[ccBridge] ack \u8BFB\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u4E0D\u5220\uFF08\u6D88\u606F\u4F1A\u91CD\u590D\u6295\u9012\uFF09\uFF1A${e.message}`);
36675
+ return 0;
36676
+ }
36677
+ const kept = items.filter((i) => !set.has(i.id));
36678
+ const removed = items.length - kept.length;
36679
+ if (removed) {
36680
+ try {
36681
+ this.writeOutbox(kept);
36682
+ } catch (e) {
36683
+ console.warn(`[ccBridge] ack \u5199\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u4E0D\u5220\uFF1A${e.message}`);
36684
+ return 0;
36685
+ }
36686
+ }
36687
+ return removed;
36688
+ }
36689
+ /** 队列条数(读不到就算 0 —— 只用于日志/握手,不能用来做删除决策) */
36690
+ queuedCount() {
36691
+ try {
36692
+ return this.readOutbox().length;
36693
+ } catch {
36694
+ return 0;
36695
+ }
36696
+ }
36697
+ /** 读队列但**不因读失败而中断**(握手补发、日志这类"读不到也能继续"的场合) */
36698
+ safeReadOutbox(where) {
36699
+ try {
36700
+ return this.readOutbox();
36701
+ } catch (e) {
36702
+ console.warn(`[ccBridge] ${where}\uFF1A\u8BFB\u961F\u5217\u5931\u8D25\uFF0C\u672C\u8F6E\u5F53\u4F5C\u7A7A\uFF08**\u672A\u5220\u9664\u4EFB\u4F55\u4E1C\u897F**\uFF09\uFF1A${e.message}`);
36703
+ return [];
36704
+ }
36705
+ }
36706
+ /**
36707
+ * 升级握手校验。见 `connect()` 里那段注释 —— 绑回环挡不住浏览器。
36708
+ * 返回 false 时 `ws` 会直接拒绝升级(HTTP 401),连接根本建立不起来。
36709
+ */
36710
+ verifyUpgrade(info) {
36711
+ const h = info?.req?.headers ?? {};
36712
+ if (h.origin) {
36713
+ console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u5E26 Origin \u7684\u8FDE\u63A5\uFF1A${h.origin}\uFF08\u7591\u4F3C\u7F51\u9875\u53D1\u8D77\uFF09`);
36714
+ return false;
36715
+ }
36716
+ const host = String(h.host || "").replace(/:\d+$/, "").toLowerCase();
36717
+ const ok = host === "127.0.0.1" || host === "localhost" || host === "[::1]" || host === "::1" || host === "";
36718
+ if (!ok) console.warn(`[ccBridge] \u26D4 \u62D2\u7EDD\u975E\u56DE\u73AF Host\uFF1A${h.host}\uFF08\u7591\u4F3C DNS rebinding\uFF09`);
36719
+ return ok;
36720
+ }
36721
+ // ── 连接 ──
36722
+ async connect() {
36723
+ const port = this.config.port ?? DEFAULT_PORT;
36724
+ try {
36725
+ fs41.mkdirSync(path41.dirname(this.outboxFile()), { recursive: true });
36726
+ } catch {
36727
+ }
36728
+ this.wss = new WebSocketServer2({
36729
+ port,
36730
+ host: "127.0.0.1",
36731
+ maxPayload: 4 * 1024 * 1024,
36732
+ // ⛔ **绑 127.0.0.1 不是安全边界。** WebSocket 不受同源策略约束,
36733
+ // 用户浏览器里**任意** http 页面都能 `new WebSocket('ws://127.0.0.1:16996')`
36734
+ // 连上来 —— 而这里连上就能以任意 `from` 往 agent 主会话注入消息
36735
+ // (实测过:伪造 Origin/Host 能成功注入一条 `from: 翀哥` 的消息)。
36736
+ // 所以必须自己校验:
36737
+ // · 带 `Origin` 头 = 网页发起的 → 直接拒(`scripts/cc-inbox.mjs` 是 Node
36738
+ // 客户端,本来就不发这个头,不受影响)
36739
+ // · `Host` 必须是本机回环 → 挡 DNS rebinding(域名解析到 127.0.0.1 时
36740
+ // Host 会是那个域名,而不是 127.0.0.1)
36741
+ verifyClient: (info) => this.verifyUpgrade(info)
36742
+ });
36743
+ this.wss.on("connection", (ws) => {
36744
+ this.clients.add(ws);
36745
+ console.log(`[ccBridge] CC connected (${this.clients.size} online)`);
36746
+ const pending2 = this.safeReadOutbox("\u63E1\u624B\u8865\u53D1");
36747
+ this.sendTo(ws, { type: "hello", channel: this.name, queued: pending2.length });
36748
+ if (pending2.length) {
36749
+ console.log(`[ccBridge] \u8865\u53D1 ${pending2.length} \u6761\u79EF\u538B`);
36750
+ this.sendTo(ws, { type: "backlog", messages: pending2 });
36751
+ }
36752
+ ws.on("message", (data) => this.handleFrame(ws, data));
36753
+ ws.on("close", () => {
36754
+ this.clients.delete(ws);
36755
+ console.log(`[ccBridge] CC disconnected (${this.clients.size} online)`);
36756
+ });
36757
+ ws.on("error", () => this.clients.delete(ws));
36758
+ });
36759
+ this.wss.on("error", (e) => {
36760
+ console.error(`[ccBridge] server error on 127.0.0.1:${port}: ${e.message}`);
36761
+ });
36762
+ await new Promise((resolve12, reject) => {
36763
+ const wss = this.wss;
36764
+ const ok = () => {
36765
+ wss.off("error", bad);
36766
+ resolve12();
36767
+ };
36768
+ const bad = (e) => {
36769
+ wss.off("listening", ok);
36770
+ reject(e);
36771
+ };
36772
+ wss.once("listening", ok);
36773
+ wss.once("error", bad);
36774
+ });
36775
+ console.log(`[ccBridge] listening on 127.0.0.1:${this.boundPort} (${this.queuedCount()} queued)`);
36776
+ }
36777
+ handleFrame(ws, data) {
36778
+ let msg2;
36779
+ try {
36780
+ msg2 = JSON.parse(String(data));
36781
+ } catch {
36782
+ this.sendTo(ws, { type: "error", error: "bad json" });
36783
+ return;
36784
+ }
36785
+ if (msg2.type === "ping") {
36786
+ this.sendTo(ws, { type: "pong" });
36787
+ return;
36788
+ }
36789
+ if (msg2.type === "ack") {
36790
+ const ids = Array.isArray(msg2.ids) ? msg2.ids.map(String) : [];
36791
+ const n = this.ack(ids);
36792
+ this.sendTo(ws, { type: "ack-ok", removed: n, queued: this.queuedCount() });
36793
+ return;
36794
+ }
36795
+ if (msg2.type === "chat") {
36796
+ const text = String(msg2.text ?? "").trim();
36797
+ if (!text) {
36798
+ this.sendTo(ws, { type: "error", error: "empty text" });
36799
+ return;
36800
+ }
36801
+ const from = String(msg2.from || this.config.userId || "cc");
36802
+ const inbound = {
36803
+ content: text,
36804
+ from,
36805
+ fromName: String(msg2.fromName || from),
36806
+ channel_id: this.name,
36807
+ channel: this.name,
36808
+ channelType: "dm",
36809
+ isBot: false,
36810
+ messageId: `ccb-${Date.now()}-${++this.seq}`,
36811
+ recvAt: Date.now()
36812
+ };
36813
+ console.log(`[ccBridge] \u2190 ${inbound.fromName}: ${text.slice(0, 80)}`);
36814
+ this.messageHandler?.(inbound);
36815
+ return;
36816
+ }
36817
+ this.sendTo(ws, { type: "error", error: `unknown type: ${msg2.type}` });
36818
+ }
36819
+ sendTo(ws, payload) {
36820
+ if (ws.readyState !== WebSocket2.OPEN) return;
36821
+ try {
36822
+ ws.send(JSON.stringify(payload));
36823
+ } catch {
36824
+ }
36825
+ }
36826
+ /** 广播给出站消息之外的控制帧(握手/pong 走 sendTo 单播) */
36827
+ broadcast(payload) {
36828
+ const s = JSON.stringify(payload);
36829
+ let n = 0;
36830
+ for (const ws of [...this.clients]) {
36831
+ if (ws.readyState !== WebSocket2.OPEN) {
36832
+ this.clients.delete(ws);
36833
+ continue;
36834
+ }
36835
+ try {
36836
+ ws.send(s);
36837
+ n++;
36838
+ } catch {
36839
+ this.clients.delete(ws);
36840
+ }
36841
+ }
36842
+ return n;
36843
+ }
36844
+ async disconnect() {
36845
+ for (const ws of [...this.clients]) {
36846
+ try {
36847
+ ws.close();
36848
+ } catch {
36849
+ }
36850
+ }
36851
+ this.clients.clear();
36852
+ this.wss?.close();
36853
+ this.wss = null;
36854
+ }
36855
+ // ── 出站 ──
36856
+ /**
36857
+ * 发消息给 CC。
36858
+ *
36859
+ * **先落 outbox 再推实时** —— 顺序反过来的话,推送成功但 CC 恰好这时断开,
36860
+ * 这条消息就既不在队列里也没送达,静默消失。宁可重复,不可丢失:
36861
+ * 重复只是多读一遍,丢失是 agent 主动说了话而没人知道。
36862
+ */
36863
+ async send(_target, message, _options) {
36864
+ if (!message.trim()) return;
36865
+ const item = {
36866
+ id: `m${Date.now()}-${++this.seq}`,
36867
+ at: (/* @__PURE__ */ new Date()).toISOString(),
36868
+ text: message,
36869
+ agent: this.config.agentName || "agent"
36870
+ };
36871
+ this.enqueue(item);
36872
+ const n = this.broadcast({ type: "message", message: item });
36873
+ if (n === 0) console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u5DF2\u5165\u961F\uFF08${this.queuedCount()} \u6761\u5F85\u6536\uFF09`);
36874
+ }
36875
+ /**
36876
+ * 发文件。
36877
+ *
36878
+ * **只送路径,不送内容**(`mode: 'path-ref'`)。这个通道两端都在 127.0.0.1 上
36879
+ * (引擎与 CC 同机),把文件 base64 塞进 WS 帧纯属浪费 —— 大文件还会把 outbox 撑爆。
36880
+ *
36881
+ * ⚠️ **但"只送路径"是把解析推迟到接收端,推迟不等于可以不做。**
36882
+ * 它有一个很难看的退化形态:发送端返回「已发送」(路径形式上没问题),
36883
+ * 接收端拿着路径在**它自己的机器上**找不到 —— **失败发生在接收端,
36884
+ * 而发送端日志一切正常**。(张小T 0928 指出的,她是对的。)
36885
+ *
36886
+ * 所以这里补两道:
36887
+ * 1. **入队前 `statSync`** —— 路径打错/文件被挪走,**在本机就报错**,
36888
+ * 而不是等接收端去找一个不存在的路径
36889
+ * 2. 帧里带 `size`/`mtimeMs` + `mode: 'path-ref'` —— 接收端知道这是**引用不是内容**,
36890
+ * 且能对一下"拿到的是不是你说的那份"
36891
+ *
36892
+ * 客户端那侧对应地**读不到就不 ack**(见 `scripts/cc-inbox.mjs`)——
36893
+ * ack 的语义是"我拿到了",拿到一半不能算。
36894
+ */
36895
+ async sendFile(_target, message, attachment) {
36896
+ let st;
36897
+ try {
36898
+ st = fs41.statSync(attachment.path);
36899
+ } catch (e) {
36900
+ throw new Error(`\u9644\u4EF6\u4E0D\u5B58\u5728\uFF0C\u672A\u53D1\u9001\uFF1A${attachment.path}\uFF08${e?.code || e?.message}\uFF09`);
36901
+ }
36902
+ const file2 = {
36903
+ mode: "path-ref",
36904
+ path: attachment.path,
36905
+ filename: attachment.filename || path41.basename(attachment.path),
36906
+ mimeType: attachment.mimeType || "application/octet-stream",
36907
+ size: st.size,
36908
+ mtimeMs: Math.round(st.mtimeMs)
36909
+ };
36910
+ const item = {
36911
+ id: `f${Date.now()}-${++this.seq}`,
36912
+ at: (/* @__PURE__ */ new Date()).toISOString(),
36913
+ text: message || "",
36914
+ agent: this.config.agentName || "agent",
36915
+ file: file2
36916
+ };
36917
+ this.enqueue(item);
36918
+ if (this.broadcast({ type: "message", message: item }) === 0) {
36919
+ console.log(`[ccBridge] CC \u4E0D\u5728\u7EBF\uFF0C\u9644\u4EF6\u5DF2\u5165\u961F\uFF1A${file2.filename}`);
36920
+ }
36921
+ }
36922
+ };
36923
+
36542
36924
  // src/channels/manager.ts
36543
- var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy"];
36925
+ var MANAGED_CHANNEL_NAMES = ["discord", "feishu", "wechat", "deskBuddy", "ccBridge"];
36544
36926
  var defaultFactories = {
36545
36927
  discord: (block) => {
36546
36928
  const accounts = block.accounts || {};
@@ -36602,6 +36984,9 @@ var defaultFactories = {
36602
36984
  },
36603
36985
  deskBuddy: (block) => {
36604
36986
  return new DeskBuddyAdapter(block);
36987
+ },
36988
+ ccBridge: (block) => {
36989
+ return new CcBridgeAdapter(block);
36605
36990
  }
36606
36991
  };
36607
36992
  var ChannelManager = class {
@@ -36873,20 +37258,20 @@ var ChannelManager = class {
36873
37258
  // src/session/session-manager.ts
36874
37259
  init_types();
36875
37260
  init_types();
36876
- import * as path42 from "node:path";
36877
- import * as fs42 from "node:fs";
37261
+ import * as path43 from "node:path";
37262
+ import * as fs43 from "node:fs";
36878
37263
  import * as crypto6 from "node:crypto";
36879
37264
 
36880
37265
  // src/session/reader.ts
36881
37266
  init_attachments();
36882
- import * as fs41 from "node:fs";
36883
- import * as path41 from "node:path";
37267
+ import * as fs42 from "node:fs";
37268
+ import * as path42 from "node:path";
36884
37269
  import * as readline2 from "node:readline";
36885
37270
  var SKIP_PRECOMPACT_THRESHOLD = 5 * 1024 * 1024;
36886
37271
  var TRANSCRIPT_READ_CHUNK_SIZE = 1024 * 1024;
36887
37272
  var MAX_TRANSCRIPT_READ_BYTES = 50 * 1024 * 1024;
36888
37273
  async function readSessionHeader(filePath) {
36889
- const stream = fs41.createReadStream(filePath, { encoding: "utf-8" });
37274
+ const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
36890
37275
  const rl = readline2.createInterface({ input: stream });
36891
37276
  return new Promise((resolve12) => {
36892
37277
  let resolved = false;
@@ -36917,7 +37302,7 @@ async function readSessionHeader(filePath) {
36917
37302
  });
36918
37303
  }
36919
37304
  async function readLargeFilePostBoundary(filePath) {
36920
- const stat4 = fs41.statSync(filePath);
37305
+ const stat4 = fs42.statSync(filePath);
36921
37306
  const fileSize = stat4.size;
36922
37307
  const boundaryMarker = '"compact_boundary"';
36923
37308
  const CHUNK_SIZE = TRANSCRIPT_READ_CHUNK_SIZE;
@@ -36925,13 +37310,13 @@ async function readLargeFilePostBoundary(filePath) {
36925
37310
  let outLen = 0;
36926
37311
  let boundaryStartOffset = 0;
36927
37312
  const chunk = Buffer.allocUnsafe(CHUNK_SIZE);
36928
- const fd = fs41.openSync(filePath, "r");
37313
+ const fd = fs42.openSync(filePath, "r");
36929
37314
  try {
36930
37315
  let filePos = 0;
36931
37316
  let carryBuf;
36932
37317
  let carryLen = 0;
36933
37318
  while (filePos < fileSize) {
36934
- const bytesRead = fs41.readSync(fd, chunk, 0, Math.min(CHUNK_SIZE, fileSize - filePos), filePos);
37319
+ const bytesRead = fs42.readSync(fd, chunk, 0, Math.min(CHUNK_SIZE, fileSize - filePos), filePos);
36935
37320
  if (bytesRead === 0) break;
36936
37321
  filePos += bytesRead;
36937
37322
  let scanBuf;
@@ -36983,7 +37368,7 @@ async function readLargeFilePostBoundary(filePath) {
36983
37368
  }
36984
37369
  }
36985
37370
  } finally {
36986
- fs41.closeSync(fd);
37371
+ fs42.closeSync(fd);
36987
37372
  }
36988
37373
  const postBoundaryText = outBuf.subarray(0, outLen).toString("utf-8");
36989
37374
  return postBoundaryText.split("\n").filter((l) => l.trim().length > 0);
@@ -37107,7 +37492,7 @@ function recoverOrphanedParallelToolResults(allEntries, chain, byUuid, seen) {
37107
37492
  return result;
37108
37493
  }
37109
37494
  async function readSessionHistory(filePath) {
37110
- const stat4 = fs41.statSync(filePath);
37495
+ const stat4 = fs42.statSync(filePath);
37111
37496
  const fileSize = stat4.size;
37112
37497
  if (fileSize > MAX_TRANSCRIPT_READ_BYTES) {
37113
37498
  console.warn(`[reader] Session file exceeds 50MB limit (${(fileSize / 1024 / 1024).toFixed(1)}MB), using chunked fd read with boundary skip`);
@@ -37146,7 +37531,7 @@ async function readSessionHistory(filePath) {
37146
37531
  return [...sessionStarts, ...filtered, ...restAttachments];
37147
37532
  }
37148
37533
  async function readAllLines(filePath) {
37149
- const stream = fs41.createReadStream(filePath, { encoding: "utf-8" });
37534
+ const stream = fs42.createReadStream(filePath, { encoding: "utf-8" });
37150
37535
  const rl = readline2.createInterface({ input: stream });
37151
37536
  const lines = [];
37152
37537
  return new Promise((resolve12, reject) => {
@@ -37300,12 +37685,12 @@ function extractText4(content) {
37300
37685
  return content.filter((b) => b.type === "text").map((b) => b.text || "").join("") || null;
37301
37686
  }
37302
37687
  function listSessions(sessionsDir) {
37303
- if (!fs41.existsSync(sessionsDir)) return [];
37304
- const files = fs41.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") || f.includes(".jsonl.")).map((f) => ({
37305
- file: path41.join(sessionsDir, f),
37688
+ if (!fs42.existsSync(sessionsDir)) return [];
37689
+ const files = fs42.readdirSync(sessionsDir).filter((f) => f.endsWith(".jsonl") || f.includes(".jsonl.")).map((f) => ({
37690
+ file: path42.join(sessionsDir, f),
37306
37691
  id: f.replace(/\.jsonl.*$/, ""),
37307
37692
  // strip .jsonl and everything after (archived suffix)
37308
- modified: new Date(fs41.statSync(path41.join(sessionsDir, f)).mtimeMs)
37693
+ modified: new Date(fs42.statSync(path42.join(sessionsDir, f)).mtimeMs)
37309
37694
  })).sort((a, b) => b.modified.getTime() - a.modified.getTime());
37310
37695
  return files;
37311
37696
  }
@@ -37340,24 +37725,24 @@ var SessionManager = class {
37340
37725
  constructor(config) {
37341
37726
  this.config = config;
37342
37727
  this.scopeConfig = config.scopeConfig;
37343
- this.sessionsDir = path42.join(config.stateDir, "agents", "main", "sessions");
37344
- this.indexPath = path42.join(this.sessionsDir, "session-index.json");
37345
- this.platformMapPath = path42.join(this.sessionsDir, "platform-map.json");
37346
- fs42.mkdirSync(this.sessionsDir, { recursive: true });
37728
+ this.sessionsDir = path43.join(config.stateDir, "agents", "main", "sessions");
37729
+ this.indexPath = path43.join(this.sessionsDir, "session-index.json");
37730
+ this.platformMapPath = path43.join(this.sessionsDir, "platform-map.json");
37731
+ fs43.mkdirSync(this.sessionsDir, { recursive: true });
37347
37732
  }
37348
37733
  // === 索引操作 ===
37349
37734
  loadIndex() {
37350
37735
  try {
37351
- if (fs42.existsSync(this.indexPath)) {
37352
- const raw = fs42.readFileSync(this.indexPath, "utf-8");
37736
+ if (fs43.existsSync(this.indexPath)) {
37737
+ const raw = fs43.readFileSync(this.indexPath, "utf-8");
37353
37738
  const obj = JSON.parse(raw);
37354
37739
  this.index = new Map(Object.entries(obj));
37355
37740
  let fixed = 0;
37356
37741
  for (const [key, entry] of this.index) {
37357
- if (fs42.existsSync(entry.file)) continue;
37358
- const basename10 = path42.basename(entry.file);
37359
- const localPath = path42.join(this.sessionsDir, basename10);
37360
- if (fs42.existsSync(localPath)) {
37742
+ if (fs43.existsSync(entry.file)) continue;
37743
+ const basename10 = path43.basename(entry.file);
37744
+ const localPath = path43.join(this.sessionsDir, basename10);
37745
+ if (fs43.existsSync(localPath)) {
37361
37746
  entry.file = localPath;
37362
37747
  fixed++;
37363
37748
  }
@@ -37376,7 +37761,7 @@ var SessionManager = class {
37376
37761
  try {
37377
37762
  const obj = {};
37378
37763
  for (const [k, v] of this.index) obj[k] = v;
37379
- fs42.writeFileSync(this.indexPath, JSON.stringify(obj, null, 2));
37764
+ fs43.writeFileSync(this.indexPath, JSON.stringify(obj, null, 2));
37380
37765
  } catch (err) {
37381
37766
  console.error(`[session] Failed to save index: ${err.message}`);
37382
37767
  }
@@ -37384,8 +37769,8 @@ var SessionManager = class {
37384
37769
  // === 平台映射 ===
37385
37770
  loadPlatformMap() {
37386
37771
  try {
37387
- if (fs42.existsSync(this.platformMapPath)) {
37388
- const raw = fs42.readFileSync(this.platformMapPath, "utf-8");
37772
+ if (fs43.existsSync(this.platformMapPath)) {
37773
+ const raw = fs43.readFileSync(this.platformMapPath, "utf-8");
37389
37774
  const obj = JSON.parse(raw);
37390
37775
  this.platformMap = new Map(Object.entries(obj));
37391
37776
  console.log(`[session] Platform map loaded: ${this.platformMap.size} entries`);
@@ -37397,7 +37782,7 @@ var SessionManager = class {
37397
37782
  try {
37398
37783
  const obj = {};
37399
37784
  for (const [k, v] of this.platformMap) obj[k] = v;
37400
- fs42.writeFileSync(this.platformMapPath, JSON.stringify(obj, null, 2));
37785
+ fs43.writeFileSync(this.platformMapPath, JSON.stringify(obj, null, 2));
37401
37786
  } catch (err) {
37402
37787
  console.error(`[session] Failed to save platform map: ${err.message}`);
37403
37788
  }
@@ -37436,17 +37821,17 @@ var SessionManager = class {
37436
37821
  */
37437
37822
  getSessionFilePath(sessionId) {
37438
37823
  const entry = this.index.get(sessionId);
37439
- if (entry && fs42.existsSync(entry.file)) return entry.file;
37440
- const direct = path42.join(this.sessionsDir, `${sessionId}.jsonl`);
37441
- return fs42.existsSync(direct) ? direct : null;
37824
+ if (entry && fs43.existsSync(entry.file)) return entry.file;
37825
+ const direct = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
37826
+ return fs43.existsSync(direct) ? direct : null;
37442
37827
  }
37443
37828
  // === Writer 管理 ===
37444
37829
  getWriter(sessionId) {
37445
37830
  let writer = this.writers.get(sessionId);
37446
37831
  if (!writer) {
37447
37832
  const existing = this.index.get(sessionId);
37448
- if (existing && fs42.existsSync(existing.file)) {
37449
- const existingFileId = path42.basename(existing.file, ".jsonl");
37833
+ if (existing && fs43.existsSync(existing.file)) {
37834
+ const existingFileId = path43.basename(existing.file, ".jsonl");
37450
37835
  writer = new SessionWriter(this.sessionsDir, existingFileId);
37451
37836
  writer.open(this.config.workspace, sessionId);
37452
37837
  } else {
@@ -37458,7 +37843,7 @@ var SessionManager = class {
37458
37843
  this.saveIndex();
37459
37844
  this.writers.set(sessionId, writer);
37460
37845
  this.onWriterCreated?.(writer, sessionId);
37461
- console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${path42.basename(writer.path)}`);
37846
+ console.log(`[session] ${existing ? "Resumed" : "New"}: ${sessionId} \u2192 ${path43.basename(writer.path)}`);
37462
37847
  }
37463
37848
  return writer;
37464
37849
  }
@@ -37477,7 +37862,7 @@ var SessionManager = class {
37477
37862
  if (!writer) return null;
37478
37863
  try {
37479
37864
  this.lastArchiveCheck = Date.now();
37480
- const stat4 = fs42.statSync(writer.path);
37865
+ const stat4 = fs43.statSync(writer.path);
37481
37866
  if (stat4.size < threshold) return null;
37482
37867
  const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
37483
37868
  const thresholdMB = (threshold / 1024 / 1024).toFixed(1);
@@ -37486,10 +37871,10 @@ var SessionManager = class {
37486
37871
  if (archivedPath) {
37487
37872
  this.index.set(sessionId, { file: writer.path, updatedAt: Date.now() });
37488
37873
  this.saveIndex();
37489
- const newSize = fs42.statSync(writer.path).size;
37874
+ const newSize = fs43.statSync(writer.path).size;
37490
37875
  const oldSizeMB = (stat4.size / 1024 / 1024).toFixed(1);
37491
- console.log(`[session] Archive complete: ${path42.basename(archivedPath)} (${oldSizeMB}MB) \u2192 new JSONL created (${newSize} bytes)`);
37492
- const fileId = path42.basename(writer.path).replace(/\.jsonl$/, "");
37876
+ console.log(`[session] Archive complete: ${path43.basename(archivedPath)} (${oldSizeMB}MB) \u2192 new JSONL created (${newSize} bytes)`);
37877
+ const fileId = path43.basename(writer.path).replace(/\.jsonl$/, "");
37493
37878
  this.purgeOldArchives(fileId, 3);
37494
37879
  return archivedPath;
37495
37880
  }
@@ -37505,15 +37890,15 @@ var SessionManager = class {
37505
37890
  purgeOldArchives(sessionId, keepCount = 3) {
37506
37891
  try {
37507
37892
  const dir = this.sessionsDir;
37508
- if (!fs42.existsSync(dir)) return;
37509
- const allFiles = fs42.readdirSync(dir);
37893
+ if (!fs43.existsSync(dir)) return;
37894
+ const allFiles = fs43.readdirSync(dir);
37510
37895
  const archiveFiles = allFiles.filter((f) => {
37511
37896
  if (!f.startsWith(sessionId)) return false;
37512
37897
  return f.includes(".archived.") || f.includes(".compaction.");
37513
37898
  }).map((f) => ({
37514
37899
  name: f,
37515
- path: path42.join(dir, f),
37516
- mtime: fs42.statSync(path42.join(dir, f)).mtimeMs
37900
+ path: path43.join(dir, f),
37901
+ mtime: fs43.statSync(path43.join(dir, f)).mtimeMs
37517
37902
  })).sort((a, b) => b.mtime - a.mtime);
37518
37903
  console.log(`[session] purgeOldArchives: sessionId=${sessionId.substring(0, 8)}... found=${archiveFiles.length} keep=${keepCount}`);
37519
37904
  if (archiveFiles.length <= keepCount) return;
@@ -37523,7 +37908,7 @@ var SessionManager = class {
37523
37908
  console.log(`[session] Skipping archived (kept for history): ${f.name}`);
37524
37909
  continue;
37525
37910
  }
37526
- fs42.unlinkSync(f.path);
37911
+ fs43.unlinkSync(f.path);
37527
37912
  console.log(`[session] Purged old archive: ${f.name}`);
37528
37913
  }
37529
37914
  if (toDelete.length > 0) {
@@ -37572,29 +37957,29 @@ var SessionManager = class {
37572
37957
  /** 列出 session 的所有文件(当前 + 归档),按修改时间排序 */
37573
37958
  findAllSessionFiles(sessionId) {
37574
37959
  const results = [];
37575
- const mainFile = path42.join(this.sessionsDir, `${sessionId}.jsonl`);
37576
- if (fs42.existsSync(mainFile)) {
37960
+ const mainFile = path43.join(this.sessionsDir, `${sessionId}.jsonl`);
37961
+ if (fs43.existsSync(mainFile)) {
37577
37962
  results.push(mainFile);
37578
37963
  }
37579
- if (fs42.existsSync(this.sessionsDir)) {
37580
- const compactionFiles = fs42.readdirSync(this.sessionsDir).filter((f) => f.startsWith(`${sessionId}.jsonl.compaction.`)).map((f) => ({
37964
+ if (fs43.existsSync(this.sessionsDir)) {
37965
+ const compactionFiles = fs43.readdirSync(this.sessionsDir).filter((f) => f.startsWith(`${sessionId}.jsonl.compaction.`)).map((f) => ({
37581
37966
  name: f,
37582
- path: path42.join(this.sessionsDir, f),
37583
- mtime: fs42.statSync(path42.join(this.sessionsDir, f)).mtimeMs
37967
+ path: path43.join(this.sessionsDir, f),
37968
+ mtime: fs43.statSync(path43.join(this.sessionsDir, f)).mtimeMs
37584
37969
  })).sort((a, b) => b.mtime - a.mtime);
37585
37970
  if (compactionFiles.length > 0) {
37586
37971
  results.push(compactionFiles[0].path);
37587
37972
  }
37588
37973
  }
37589
37974
  results.sort((a, b) => {
37590
- const ta = fs42.statSync(a).mtimeMs;
37591
- const tb = fs42.statSync(b).mtimeMs;
37975
+ const ta = fs43.statSync(a).mtimeMs;
37976
+ const tb = fs43.statSync(b).mtimeMs;
37592
37977
  return ta - tb;
37593
37978
  });
37594
37979
  return results;
37595
37980
  }
37596
37981
  async findSessionFiles(sessionId) {
37597
- if (!fs42.existsSync(this.sessionsDir)) {
37982
+ if (!fs43.existsSync(this.sessionsDir)) {
37598
37983
  console.log(`[session:find] SESSIONS_DIR not found: ${this.sessionsDir}`);
37599
37984
  return [];
37600
37985
  }
@@ -37606,15 +37991,15 @@ var SessionManager = class {
37606
37991
  return mappedFiles;
37607
37992
  }
37608
37993
  const indexedEntry = this.index.get(mappedId);
37609
- if (indexedEntry && fs42.existsSync(indexedEntry.file)) {
37610
- const fileUuid = path42.basename(indexedEntry.file, ".jsonl");
37994
+ if (indexedEntry && fs43.existsSync(indexedEntry.file)) {
37995
+ const fileUuid = path43.basename(indexedEntry.file, ".jsonl");
37611
37996
  return this.findAllSessionFiles(fileUuid);
37612
37997
  }
37613
37998
  }
37614
37999
  const allFiles = this.findAllSessionFiles(sessionId);
37615
38000
  if (allFiles.length > 0) {
37616
38001
  const labels = allFiles.map((f) => {
37617
- const base = path42.basename(f);
38002
+ const base = path43.basename(f);
37618
38003
  return base.endsWith(".jsonl") ? base : `${base} (archived)`;
37619
38004
  });
37620
38005
  console.log(`[session:find] Found ${allFiles.length} file(s) for ${sessionId}: ${labels.join(", ")}`);
@@ -37622,9 +38007,9 @@ var SessionManager = class {
37622
38007
  }
37623
38008
  const indexed = this.index.get(sessionId);
37624
38009
  if (indexed) {
37625
- console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${fs42.existsSync(indexed.file)})`);
37626
- if (fs42.existsSync(indexed.file)) {
37627
- return this.findAllSessionFiles(path42.basename(indexed.file, ".jsonl"));
38010
+ console.log(`[session:find] Index hit: ${sessionId} \u2192 ${indexed.file} (exists=${fs43.existsSync(indexed.file)})`);
38011
+ if (fs43.existsSync(indexed.file)) {
38012
+ return this.findAllSessionFiles(path43.basename(indexed.file, ".jsonl"));
37628
38013
  }
37629
38014
  } else {
37630
38015
  console.log(`[session:find] Index miss: ${sessionId} (index has ${this.index.size} entries)`);
@@ -37653,8 +38038,8 @@ var SessionManager = class {
37653
38038
  return [];
37654
38039
  }
37655
38040
  files.sort((a, b) => {
37656
- const ta = fs42.statSync(a).mtimeMs;
37657
- const tb = fs42.statSync(b).mtimeMs;
38041
+ const ta = fs43.statSync(a).mtimeMs;
38042
+ const tb = fs43.statSync(b).mtimeMs;
37658
38043
  return ta - tb;
37659
38044
  });
37660
38045
  const latestFile = files[files.length - 1];
@@ -37662,7 +38047,7 @@ var SessionManager = class {
37662
38047
  if (!currentEntry || currentEntry.file !== latestFile) {
37663
38048
  this.index.set(sessionId, { file: latestFile, updatedAt: Date.now() });
37664
38049
  this.saveIndex();
37665
- console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${path42.basename(latestFile)}`);
38050
+ console.log(`[session:restore] Synced index: ${sessionId} \u2192 ${path43.basename(latestFile)}`);
37666
38051
  }
37667
38052
  const allMessages = [];
37668
38053
  for (const file2 of files) {
@@ -37701,7 +38086,7 @@ var SessionManager = class {
37701
38086
  if (isAttachmentMessage(m) && m.attachment.type === "relevant_memories") {
37702
38087
  for (const mem of m.attachment.memories) {
37703
38088
  rawCount++;
37704
- restoredPaths.add(path42.resolve(mem.path));
38089
+ restoredPaths.add(path43.resolve(mem.path));
37705
38090
  }
37706
38091
  }
37707
38092
  }
@@ -37733,15 +38118,15 @@ var SessionManager = class {
37733
38118
  */
37734
38119
  rotateMainSession() {
37735
38120
  const entry = this.index.get("main");
37736
- if (!entry || !fs42.existsSync(entry.file)) return;
37737
- const oldFileId = path42.basename(entry.file, ".jsonl");
37738
- const stat4 = fs42.statSync(entry.file);
38121
+ if (!entry || !fs43.existsSync(entry.file)) return;
38122
+ const oldFileId = path43.basename(entry.file, ".jsonl");
38123
+ const stat4 = fs43.statSync(entry.file);
37739
38124
  if (stat4.size < 1024) return;
37740
38125
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/:/g, "-");
37741
38126
  const archivedPath = `${entry.file}.archived.${ts}`;
37742
- fs42.renameSync(entry.file, archivedPath);
38127
+ fs43.renameSync(entry.file, archivedPath);
37743
38128
  const sizeMB = (stat4.size / 1024 / 1024).toFixed(1);
37744
- console.log(`[session] Archived: ${path42.basename(entry.file)} \u2192 ${path42.basename(archivedPath)} (${sizeMB}MB)`);
38129
+ console.log(`[session] Archived: ${path43.basename(entry.file)} \u2192 ${path43.basename(archivedPath)} (${sizeMB}MB)`);
37745
38130
  const newWriter = new SessionWriter(this.sessionsDir);
37746
38131
  newWriter.open(this.config.workspace, "main");
37747
38132
  newWriter.writeModelChange(this.config.providerId, this.config.model);
@@ -37750,7 +38135,7 @@ var SessionManager = class {
37750
38135
  this.index.set("main", { file: newPath, updatedAt: Date.now() });
37751
38136
  this.saveIndex();
37752
38137
  this.purgeOldArchives(oldFileId, 3);
37753
- console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${path42.basename(newPath)}`);
38138
+ console.log(`[session] Rotated main: ${oldFileId}.jsonl \u2192 ${path43.basename(newPath)}`);
37754
38139
  }
37755
38140
  // === 迁移 ===
37756
38141
  /** 迁移旧索引:session-index.json 里有 "discord:xxx" 格式的 key 时,生成 UUID 替换 */
@@ -38553,7 +38938,7 @@ Examples:
38553
38938
  properties: {
38554
38939
  to: { type: "string", description: "\u63A5\u6536\u8005ID\u3002\u586B channel_id \u65F6\u81EA\u52A8 @\u5BF9\u65B9\uFF1B\u4E0D\u586B channel_id \u65F6\u53D1 DM\u3002\u591A\u4E2A\u6536\u4EF6\u4EBA\u7528\u9017\u53F7\u5206\u9694\u3002" },
38555
38940
  channel_id: { type: "string", description: "\u9891\u9053 ID\uFF08\u53EF\u9009\uFF09\u3002\u4E0D\u586B\u5219\u53D1 DM \u7ED9 to\u3002\u90FD\u4E0D\u586B\u65F6\u56DE\u590D\u6765\u6E90\u9891\u9053\u3002" },
38556
- source: { type: "string", description: "\u6765\u6E90\u901A\u9053\uFF1Adiscord / feishu / wechat / deskBuddy\uFF08\u684C\u9762\u5C0F\u96EF\u8BED\u97F3\uFF09\u3002\u4E0D\u586B\u65F6\u4F7F\u7528\u5F53\u524D\u6765\u6E90\u901A\u9053\u3002", enum: ["discord", "feishu", "wechat", "deskBuddy"] },
38941
+ source: { type: "string", description: "\u6765\u6E90\u901A\u9053\uFF1Adiscord / feishu / wechat / deskBuddy\uFF08\u684C\u9762\u5C0F\u96EF\u8BED\u97F3\uFF09/ ccBridge\uFF08CC\uFF0C\u672C\u673A\u5F00\u53D1\u901A\u9053\uFF09\u3002\u4E0D\u586B\u65F6\u4F7F\u7528\u5F53\u524D\u6765\u6E90\u901A\u9053\u3002", enum: ["discord", "feishu", "wechat", "deskBuddy", "ccBridge"] },
38557
38942
  content: { type: "string", description: "\u6D88\u606F\u6B63\u6587" }
38558
38943
  },
38559
38944
  required: ["content", "to"]
@@ -38642,12 +39027,12 @@ Examples:
38642
39027
  // src/tools/msg-husband.ts
38643
39028
  init_registry();
38644
39029
  init_live();
38645
- import fs43 from "node:fs";
38646
- import path43 from "node:path";
39030
+ import fs44 from "node:fs";
39031
+ import path44 from "node:path";
38647
39032
  function getHusbandFeishuId(workspace) {
38648
- const contactsPath = path43.join(workspace, "prompts", "contacts.md");
39033
+ const contactsPath = path44.join(workspace, "prompts", "contacts.md");
38649
39034
  try {
38650
- const text = fs43.readFileSync(contactsPath, "utf-8");
39035
+ const text = fs44.readFileSync(contactsPath, "utf-8");
38651
39036
  const m = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
38652
39037
  return m ? m[1] : null;
38653
39038
  } catch {
@@ -38789,8 +39174,8 @@ Examples:
38789
39174
  if (!to && !resolvedChannelId) {
38790
39175
  return { content: "to \u548C channel_id \u4E0D\u80FD\u540C\u65F6\u4E3A\u7A7A\uFF0C\u4E14\u6CA1\u6709\u53EF\u7528\u7684\u6765\u6E90\u9891\u9053", isError: true };
38791
39176
  }
38792
- const fs58 = await import("node:fs");
38793
- if (!fs58.existsSync(filePath)) {
39177
+ const fs59 = await import("node:fs");
39178
+ if (!fs59.existsSync(filePath)) {
38794
39179
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
38795
39180
  }
38796
39181
  const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
@@ -38818,7 +39203,7 @@ Examples:
38818
39203
  md: "text/markdown"
38819
39204
  };
38820
39205
  const mimeType = mimeTypeMap[ext] || "application/octet-stream";
38821
- const stat4 = fs58.statSync(filePath);
39206
+ const stat4 = fs59.statSync(filePath);
38822
39207
  const sizeMB = stat4.size / 1024 / 1024;
38823
39208
  if (sizeMB > 25) {
38824
39209
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
@@ -38848,8 +39233,8 @@ Examples:
38848
39233
  init_live();
38849
39234
  init_registry();
38850
39235
  init_well_formed();
38851
- import * as fs44 from "node:fs";
38852
- import * as path44 from "node:path";
39236
+ import * as fs45 from "node:fs";
39237
+ import * as path45 from "node:path";
38853
39238
  var MIME_MAP = {
38854
39239
  ".jpg": "jpeg",
38855
39240
  ".jpeg": "jpeg",
@@ -38861,9 +39246,9 @@ var MIME_MAP = {
38861
39246
  var VIDEO_EXT = /\.(mp4|mov)$/i;
38862
39247
  var VIDEO_MAX_BYTES = 40 * 1024 * 1024;
38863
39248
  function resolveLatestImage(specifiedPath, mediaDir) {
38864
- if (specifiedPath && fs44.existsSync(specifiedPath)) return specifiedPath;
38865
- if (!fs44.existsSync(mediaDir)) return null;
38866
- const files = fs44.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path44.join(mediaDir, f), mtime: fs44.statSync(path44.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
39249
+ if (specifiedPath && fs45.existsSync(specifiedPath)) return specifiedPath;
39250
+ if (!fs45.existsSync(mediaDir)) return null;
39251
+ const files = fs45.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs45.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
38867
39252
  return files[0]?.p || null;
38868
39253
  }
38869
39254
  registry.register({
@@ -38894,21 +39279,21 @@ registry.register({
38894
39279
  if (!provider?.streamChat) {
38895
39280
  return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
38896
39281
  }
38897
- const mediaDir = path44.join(ctx.stateDir, "media", "inbound");
39282
+ const mediaDir = path45.join(ctx.stateDir, "media", "inbound");
38898
39283
  const imagePath = resolveLatestImage(args.image_path, mediaDir);
38899
39284
  if (!imagePath) {
38900
39285
  return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
38901
39286
  }
38902
39287
  const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
38903
- const ext = path44.extname(imagePath).toLowerCase();
39288
+ const ext = path45.extname(imagePath).toLowerCase();
38904
39289
  if (VIDEO_EXT.test(imagePath)) {
38905
- const stat4 = fs44.statSync(imagePath);
39290
+ const stat4 = fs45.statSync(imagePath);
38906
39291
  if (stat4.size > VIDEO_MAX_BYTES) {
38907
39292
  return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
38908
39293
  }
38909
39294
  const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
38910
39295
  const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
38911
- const vidB64 = fs44.readFileSync(imagePath).toString("base64");
39296
+ const vidB64 = fs45.readFileSync(imagePath).toString("base64");
38912
39297
  try {
38913
39298
  const res = await fetch(`${baseUrl}/chat/completions`, {
38914
39299
  method: "POST",
@@ -38938,7 +39323,7 @@ registry.register({
38938
39323
  }
38939
39324
  }
38940
39325
  const mime = MIME_MAP[ext] || "jpeg";
38941
- const imgB64 = fs44.readFileSync(imagePath).toString("base64");
39326
+ const imgB64 = fs45.readFileSync(imagePath).toString("base64");
38942
39327
  const userMsg = {
38943
39328
  role: "user",
38944
39329
  content: [
@@ -39016,7 +39401,7 @@ var TTS_PROVIDERS = [
39016
39401
  init_registry();
39017
39402
 
39018
39403
  // src/tools/opus-encoder.ts
39019
- import * as fs45 from "node:fs";
39404
+ import * as fs46 from "node:fs";
39020
39405
  var FRAME_MS = 20;
39021
39406
  var PRESKIP_48K = 312;
39022
39407
  var OPUS_RATE = 48e3;
@@ -39133,7 +39518,7 @@ async function wavToOggOpusNative(wavPath) {
39133
39518
  const mod = await import("@discordjs/opus");
39134
39519
  const OpusEncoder = mod.OpusEncoder ?? mod.default?.OpusEncoder;
39135
39520
  if (!OpusEncoder) throw new Error("@discordjs/opus export shape unexpected");
39136
- const wav = parseWav(fs45.readFileSync(wavPath));
39521
+ const wav = parseWav(fs46.readFileSync(wavPath));
39137
39522
  if (wav.channels !== 1) throw new Error(`unsupported ${wav.channels}ch (need mono)`);
39138
39523
  if (!NATIVE_RATES.includes(wav.sampleRate)) throw new Error(`unsupported rate ${wav.sampleRate}`);
39139
39524
  if (wav.pcm.length === 0) throw new Error("empty pcm data");
@@ -39169,7 +39554,7 @@ async function wavToOggOpusNative(wavPath) {
39169
39554
  }
39170
39555
  ogg.page(cur, pageGranule, false, true);
39171
39556
  const oggPath = wavPath.replace(/\.wav$/, ".ogg");
39172
- fs45.writeFileSync(oggPath, ogg.finish());
39557
+ fs46.writeFileSync(oggPath, ogg.finish());
39173
39558
  return oggPath;
39174
39559
  }
39175
39560
 
@@ -39198,10 +39583,10 @@ function fallbackChainOf(raw) {
39198
39583
  init_live();
39199
39584
 
39200
39585
  // src/tools/tts/relay.ts
39201
- import * as fs46 from "node:fs";
39586
+ import * as fs47 from "node:fs";
39202
39587
  import * as os6 from "node:os";
39203
- import * as path45 from "node:path";
39204
- var VOICE_DIR2 = path45.join(os6.tmpdir(), "engine-voice");
39588
+ import * as path46 from "node:path";
39589
+ var VOICE_DIR2 = path46.join(os6.tmpdir(), "engine-voice");
39205
39590
  var fetchFn = fetch;
39206
39591
  async function synthRelay(text, cfg) {
39207
39592
  const res = await fetchFn(`${cfg.baseUrl}/audio/speech`, {
@@ -39220,9 +39605,9 @@ async function synthRelay(text, cfg) {
39220
39605
  }
39221
39606
  const buf = Buffer.from(await res.arrayBuffer());
39222
39607
  if (buf.length < 20) throw new Error(`relay tts: \u54CD\u5E94\u4F53\u53EA\u6709 ${buf.length} \u5B57\u8282\uFF0C\u4E0D\u662F\u6709\u6548\u97F3\u9891`);
39223
- fs46.mkdirSync(VOICE_DIR2, { recursive: true });
39224
- const out = path45.join(VOICE_DIR2, `tts_relay_${Date.now()}.mp3`);
39225
- fs46.writeFileSync(out, buf);
39608
+ fs47.mkdirSync(VOICE_DIR2, { recursive: true });
39609
+ const out = path46.join(VOICE_DIR2, `tts_relay_${Date.now()}.mp3`);
39610
+ fs47.writeFileSync(out, buf);
39226
39611
  return {
39227
39612
  path: out,
39228
39613
  format: "mp3",
@@ -39341,8 +39726,8 @@ function synthesize(id, text, sink) {
39341
39726
  // src/tools/my-voice.ts
39342
39727
  import { execFile as execFile3 } from "node:child_process";
39343
39728
  import { promisify as promisify3 } from "node:util";
39344
- import * as fs47 from "node:fs";
39345
- import * as path46 from "node:path";
39729
+ import * as fs48 from "node:fs";
39730
+ import * as path47 from "node:path";
39346
39731
  var execFileAsync3 = promisify3(execFile3);
39347
39732
  async function toWav24kWithDuration(audioPath) {
39348
39733
  let wavPath = audioPath;
@@ -39360,15 +39745,15 @@ async function toWav24kWithDuration(audioPath) {
39360
39745
  "pcm_s16le",
39361
39746
  wavPath
39362
39747
  ], { timeout: 3e4 });
39363
- if (!fs47.existsSync(wavPath) || fs47.statSync(wavPath).size < 44) {
39748
+ if (!fs48.existsSync(wavPath) || fs48.statSync(wavPath).size < 44) {
39364
39749
  throw new Error("toWav24k produced empty output");
39365
39750
  }
39366
39751
  try {
39367
- fs47.unlinkSync(audioPath);
39752
+ fs48.unlinkSync(audioPath);
39368
39753
  } catch {
39369
39754
  }
39370
39755
  }
39371
- const durationSec = wavDurationSec(fs47.readFileSync(wavPath));
39756
+ const durationSec = wavDurationSec(fs48.readFileSync(wavPath));
39372
39757
  return { wavPath, durationSec };
39373
39758
  }
39374
39759
  async function compressWav(wavPath) {
@@ -39386,7 +39771,7 @@ async function compressWav(wavPath) {
39386
39771
  "+faststart",
39387
39772
  m4aPath
39388
39773
  ], { timeout: 3e4 });
39389
- fs47.unlinkSync(wavPath);
39774
+ fs48.unlinkSync(wavPath);
39390
39775
  return m4aPath;
39391
39776
  } catch {
39392
39777
  return wavPath;
@@ -39413,10 +39798,10 @@ async function wavToOggOpus(wavPath) {
39413
39798
  oggPath
39414
39799
  ], { timeout: 3e4 });
39415
39800
  }
39416
- if (!fs47.existsSync(oggPath) || fs47.statSync(oggPath).size < 20) {
39801
+ if (!fs48.existsSync(oggPath) || fs48.statSync(oggPath).size < 20) {
39417
39802
  throw new Error("ogg/opus encode produced empty output");
39418
39803
  }
39419
- fs47.unlinkSync(wavPath);
39804
+ fs48.unlinkSync(wavPath);
39420
39805
  return oggPath;
39421
39806
  }
39422
39807
  async function sendVoiceToMulticaIssue(text, issueId, opts) {
@@ -39431,7 +39816,7 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
39431
39816
  } catch (e) {
39432
39817
  return { content: `\u8BED\u97F3\u751F\u6210\u5931\u8D25\uFF08headless\u2192multaca\uFF09\uFF1A${e.message}\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002`, isError: true };
39433
39818
  }
39434
- if (!audioPath || !fs47.existsSync(audioPath)) {
39819
+ if (!audioPath || !fs48.existsSync(audioPath)) {
39435
39820
  return { content: "\u8BED\u97F3\u751F\u6210\u5931\u8D25\uFF08headless\u2192multaca\uFF09\uFF1ATTS \u672A\u4EA7\u51FA\u6587\u4EF6\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002", isError: true };
39436
39821
  }
39437
39822
  if (audioPath.endsWith(".wav")) {
@@ -39441,21 +39826,21 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
39441
39826
  console.warn(`[my-voice:multaca] opus encode failed (${e.message}), attaching wav`);
39442
39827
  }
39443
39828
  }
39444
- const ext = path46.extname(audioPath).toLowerCase();
39445
- const sizeKB = fs47.statSync(audioPath).size / 1024;
39829
+ const ext = path47.extname(audioPath).toLowerCase();
39830
+ const sizeKB = fs48.statSync(audioPath).size / 1024;
39446
39831
  const voiceFile = `voice_${Date.now()}${ext}`;
39447
- const cwdCopy = path46.join(process.cwd(), voiceFile);
39832
+ const cwdCopy = path47.join(process.cwd(), voiceFile);
39448
39833
  try {
39449
- fs47.copyFileSync(audioPath, cwdCopy);
39834
+ fs48.copyFileSync(audioPath, cwdCopy);
39450
39835
  } catch (e) {
39451
39836
  try {
39452
- fs47.unlinkSync(audioPath);
39837
+ fs48.unlinkSync(audioPath);
39453
39838
  } catch {
39454
39839
  }
39455
39840
  return { content: `\u8BED\u97F3\u6302\u8F7D\u5931\u8D25\uFF08\u62F7\u8D1D\u5230\u5DE5\u4F5C\u76EE\u5F55\uFF09\uFF1A${e.message}`, isError: true };
39456
39841
  }
39457
39842
  try {
39458
- fs47.unlinkSync(audioPath);
39843
+ fs48.unlinkSync(audioPath);
39459
39844
  } catch {
39460
39845
  }
39461
39846
  const content = opts.caption || `\u{1F399}\uFE0F \u8BED\u97F3\u6D88\u606F\uFF08${text.length} \u5B57\uFF09`;
@@ -39484,13 +39869,13 @@ async function sendVoiceToMulticaIssue(text, issueId, opts) {
39484
39869
  }
39485
39870
  if (!ok) throw new Error(`unexpected CLI output: ${String(stdout).slice(0, 200)}`);
39486
39871
  try {
39487
- fs47.unlinkSync(cwdCopy);
39872
+ fs48.unlinkSync(cwdCopy);
39488
39873
  } catch {
39489
39874
  }
39490
39875
  return { content: `Voice sent to multaca issue ${issueId}! (${actualEngine}, ${sizeKB.toFixed(0)}KB, \u9644\u4EF6\u5DF2\u6302\u5230\u8BC4\u8BBA\u2014\u2014\u5728 multaca \u5BA2\u6237\u7AEF\u64AD\u653E)` };
39491
39876
  } catch (e) {
39492
39877
  try {
39493
- fs47.unlinkSync(cwdCopy);
39878
+ fs48.unlinkSync(cwdCopy);
39494
39879
  } catch {
39495
39880
  }
39496
39881
  return { content: `\u8BED\u97F3\u6302\u5230 multaca issue \u5931\u8D25\uFF1A${e.message}\u3002\u8BF7\u7528\u6587\u5B57\u56DE\u590D\u4EE3\u66FF\u3002`, isError: true };
@@ -39607,10 +39992,10 @@ registry.register({
39607
39992
  audioPath = await compressWav(audioPath);
39608
39993
  }
39609
39994
  }
39610
- const ext = path46.extname(audioPath).toLowerCase();
39995
+ const ext = path47.extname(audioPath).toLowerCase();
39611
39996
  const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg", ".silk": "audio/silk" };
39612
39997
  const mimeType = mimeMap[ext] || "audio/mpeg";
39613
- const sizeKB = fs47.statSync(audioPath).size / 1024;
39998
+ const sizeKB = fs48.statSync(audioPath).size / 1024;
39614
39999
  try {
39615
40000
  await mgr.sendFile(resolvedChannel, target, caption, {
39616
40001
  path: audioPath,
@@ -39619,7 +40004,7 @@ registry.register({
39619
40004
  ...voiceDurationSec !== void 0 ? { voiceDurationSec } : {}
39620
40005
  });
39621
40006
  try {
39622
- fs47.unlinkSync(audioPath);
40007
+ fs48.unlinkSync(audioPath);
39623
40008
  } catch {
39624
40009
  }
39625
40010
  return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
@@ -39637,8 +40022,8 @@ registry.register({
39637
40022
  init_live();
39638
40023
  init_registry();
39639
40024
  init_well_formed();
39640
- import * as fs48 from "node:fs";
39641
- import * as path47 from "node:path";
40025
+ import * as fs49 from "node:fs";
40026
+ import * as path48 from "node:path";
39642
40027
  function getProxyDispatcher2() {
39643
40028
  const cfg = liveConfig.all();
39644
40029
  const proxy = cfg.providers?.xai?.proxy;
@@ -39652,7 +40037,7 @@ function getProxyDispatcher2() {
39652
40037
  }
39653
40038
  function dimsOf(p) {
39654
40039
  try {
39655
- const b = fs48.readFileSync(p);
40040
+ const b = fs49.readFileSync(p);
39656
40041
  let w = 0, h = 0;
39657
40042
  if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
39658
40043
  w = b.readUInt32BE(16);
@@ -39775,9 +40160,9 @@ function registerPortrait(relPath, name) {
39775
40160
  const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
39776
40161
  const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
39777
40162
  next.unshift({ name, path: relPath });
39778
- const path60 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
39779
- void liveConfig.set(path60, next);
39780
- console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path60}[0] = ${relPath}`);
40163
+ const path61 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
40164
+ void liveConfig.set(path61, next);
40165
+ console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path61}[0] = ${relPath}`);
39781
40166
  } catch (e) {
39782
40167
  console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
39783
40168
  }
@@ -40058,10 +40443,10 @@ registry.register({
40058
40443
  const REFERENCES = getReferences(ctx);
40059
40444
  const refName = args.reference || "default";
40060
40445
  const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
40061
- let refPath = path47.join(ctx.workspace, refEntry.p);
40446
+ let refPath = path48.join(ctx.workspace, refEntry.p);
40062
40447
  const adoptImg = args.referenceImage?.trim();
40063
40448
  if (adoptImg) {
40064
- if (!fs48.existsSync(adoptImg)) {
40449
+ if (!fs49.existsSync(adoptImg)) {
40065
40450
  return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
40066
40451
  }
40067
40452
  const acheck = dimsOf(adoptImg);
@@ -40072,16 +40457,16 @@ registry.register({
40072
40457
  isError: true
40073
40458
  };
40074
40459
  }
40075
- const adir = path47.join(ctx.workspace, "images");
40076
- if (!fs48.existsSync(adir)) fs48.mkdirSync(adir, { recursive: true });
40077
- const aext = (path47.extname(adoptImg) || ".png").toLowerCase();
40460
+ const adir = path48.join(ctx.workspace, "images");
40461
+ if (!fs49.existsSync(adir)) fs49.mkdirSync(adir, { recursive: true });
40462
+ const aext = (path48.extname(adoptImg) || ".png").toLowerCase();
40078
40463
  const afname = `portrait_${Date.now()}${aext}`;
40079
- fs48.copyFileSync(adoptImg, path47.join(adir, afname));
40464
+ fs49.copyFileSync(adoptImg, path48.join(adir, afname));
40080
40465
  registerPortrait(`images/${afname}`, refName);
40081
- refPath = path47.join(adir, afname);
40466
+ refPath = path48.join(adir, afname);
40082
40467
  console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
40083
40468
  }
40084
- if (provider !== "autodl" && !fs48.existsSync(refPath)) {
40469
+ if (provider !== "autodl" && !fs49.existsSync(refPath)) {
40085
40470
  const cfgAll = liveConfig.all();
40086
40471
  const portraitDesc = resolvePortraitDesc(
40087
40472
  args.portrait,
@@ -40106,16 +40491,16 @@ registry.register({
40106
40491
  } catch (e) {
40107
40492
  return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
40108
40493
  }
40109
- const pdir = path47.join(ctx.workspace, "images");
40110
- if (!fs48.existsSync(pdir)) fs48.mkdirSync(pdir, { recursive: true });
40494
+ const pdir = path48.join(ctx.workspace, "images");
40495
+ if (!fs49.existsSync(pdir)) fs49.mkdirSync(pdir, { recursive: true });
40111
40496
  const pfname = `portrait_${Date.now()}.png`;
40112
- fs48.writeFileSync(path47.join(pdir, pfname), portraitBuf);
40497
+ fs49.writeFileSync(path48.join(pdir, pfname), portraitBuf);
40113
40498
  registerPortrait(`images/${pfname}`, refName);
40114
- refPath = path47.join(pdir, pfname);
40499
+ refPath = path48.join(pdir, pfname);
40115
40500
  console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
40116
40501
  }
40117
40502
  const resolution = args.resolution || DEFAULT_RESOLUTION;
40118
- const refB64 = fs48.existsSync(refPath) ? fs48.readFileSync(refPath).toString("base64") : "";
40503
+ const refB64 = fs49.existsSync(refPath) ? fs49.readFileSync(refPath).toString("base64") : "";
40119
40504
  let imageBuffer;
40120
40505
  try {
40121
40506
  if (provider === "autodl") {
@@ -40131,7 +40516,7 @@ registry.register({
40131
40516
  return { content: `Error: relay provider \u672A\u914D\u7F6E\u3002config \u91CC\u8981\u6709 models.providers.${cfgAll?.my_selfie?.relayProviderId || "7yi"} \u7684 baseUrl \u548C apiKey\u3002`, isError: true };
40132
40517
  }
40133
40518
  const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
40134
- const ext = path47.extname(refPath).toLowerCase();
40519
+ const ext = path48.extname(refPath).toLowerCase();
40135
40520
  const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
40136
40521
  imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
40137
40522
  } else {
@@ -40140,11 +40525,11 @@ registry.register({
40140
40525
  } catch (err) {
40141
40526
  return { content: `Selfie generation failed: ${err.message}`, isError: true };
40142
40527
  }
40143
- const imagesDir = path47.join(ctx.workspace, "images");
40144
- if (!fs48.existsSync(imagesDir)) fs48.mkdirSync(imagesDir, { recursive: true });
40528
+ const imagesDir = path48.join(ctx.workspace, "images");
40529
+ if (!fs49.existsSync(imagesDir)) fs49.mkdirSync(imagesDir, { recursive: true });
40145
40530
  const filename = `selfie_${Date.now()}.jpg`;
40146
- const outputPath = path47.join(imagesDir, filename);
40147
- fs48.writeFileSync(outputPath, imageBuffer);
40531
+ const outputPath = path48.join(imagesDir, filename);
40532
+ fs49.writeFileSync(outputPath, imageBuffer);
40148
40533
  const mgr = ctx.channelManager;
40149
40534
  if (mgr) {
40150
40535
  const resolvedChannel = ctx.channel || "feishu";
@@ -40155,11 +40540,11 @@ registry.register({
40155
40540
  mimeType: "image/jpeg"
40156
40541
  });
40157
40542
  } catch (err) {
40158
- return { content: `Selfie generated but send failed: ${err.message}. Image: ${path47.resolve(outputPath)}`, isError: false };
40543
+ return { content: `Selfie generated but send failed: ${err.message}. Image: ${path48.resolve(outputPath)}`, isError: false };
40159
40544
  }
40160
40545
  return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
40161
40546
  }
40162
- return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path47.resolve(outputPath)}` };
40547
+ return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path48.resolve(outputPath)}` };
40163
40548
  },
40164
40549
  isConcurrencySafe: () => false,
40165
40550
  interruptBehavior: () => "block",
@@ -40711,8 +41096,8 @@ ${lines.join("\n")}` };
40711
41096
  init_registry();
40712
41097
  init_live();
40713
41098
  init_clock();
40714
- import * as fs49 from "node:fs";
40715
- import * as path48 from "node:path";
41099
+ import * as fs50 from "node:fs";
41100
+ import * as path49 from "node:path";
40716
41101
  import { exec as exec2, spawn as spawn6 } from "node:child_process";
40717
41102
  import { promisify as promisify4 } from "node:util";
40718
41103
  var execAsync = promisify4(exec2);
@@ -40721,9 +41106,9 @@ function getServices() {
40721
41106
  }
40722
41107
  function serviceLogPath(fileName) {
40723
41108
  const stateDir = String(liveConfig.get("stateDir") ?? "");
40724
- const logDir = path48.join(stateDir, "logs");
40725
- if (!fs49.existsSync(logDir)) fs49.mkdirSync(logDir, { recursive: true });
40726
- return path48.join(logDir, fileName);
41109
+ const logDir = path49.join(stateDir, "logs");
41110
+ if (!fs50.existsSync(logDir)) fs50.mkdirSync(logDir, { recursive: true });
41111
+ return path49.join(logDir, fileName);
40727
41112
  }
40728
41113
  async function runCmd(cmd, timeoutMs = 15e3) {
40729
41114
  try {
@@ -41042,16 +41427,16 @@ var EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
41042
41427
  init_planModeState();
41043
41428
 
41044
41429
  // src/utils/plans.ts
41045
- import * as fs50 from "node:fs";
41046
- import * as path49 from "node:path";
41430
+ import * as fs51 from "node:fs";
41431
+ import * as path50 from "node:path";
41047
41432
  import * as crypto7 from "node:crypto";
41048
41433
  var MAX_SLUG_RETRIES = 10;
41049
41434
  function generateSlug() {
41050
41435
  return crypto7.randomBytes(4).toString("hex");
41051
41436
  }
41052
41437
  function getPlansDirectory(stateDir) {
41053
- const plansDir = path49.join(stateDir, "plans");
41054
- fs50.mkdirSync(plansDir, { recursive: true });
41438
+ const plansDir = path50.join(stateDir, "plans");
41439
+ fs51.mkdirSync(plansDir, { recursive: true });
41055
41440
  return plansDir;
41056
41441
  }
41057
41442
  var planSlugCache = /* @__PURE__ */ new Map();
@@ -41061,8 +41446,8 @@ function getPlanSlug(sessionId, stateDir) {
41061
41446
  const plansDir = getPlansDirectory(stateDir);
41062
41447
  for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
41063
41448
  slug = generateSlug();
41064
- const filePath = path49.join(plansDir, `${slug}.md`);
41065
- if (!fs50.existsSync(filePath)) {
41449
+ const filePath = path50.join(plansDir, `${slug}.md`);
41450
+ if (!fs51.existsSync(filePath)) {
41066
41451
  break;
41067
41452
  }
41068
41453
  }
@@ -41073,21 +41458,21 @@ function getPlanSlug(sessionId, stateDir) {
41073
41458
  function getPlanFilePath(sessionId, stateDir, agentId) {
41074
41459
  const slug = getPlanSlug(sessionId, stateDir);
41075
41460
  if (!agentId) {
41076
- return path49.join(getPlansDirectory(stateDir), `${slug}.md`);
41461
+ return path50.join(getPlansDirectory(stateDir), `${slug}.md`);
41077
41462
  }
41078
- return path49.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
41463
+ return path50.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
41079
41464
  }
41080
41465
  function getPlan(sessionId, stateDir, agentId) {
41081
41466
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
41082
41467
  try {
41083
- return fs50.readFileSync(filePath, "utf-8");
41468
+ return fs51.readFileSync(filePath, "utf-8");
41084
41469
  } catch {
41085
41470
  return null;
41086
41471
  }
41087
41472
  }
41088
41473
  function writePlan(sessionId, stateDir, content, agentId) {
41089
41474
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
41090
- fs50.writeFileSync(filePath, content, "utf-8");
41475
+ fs51.writeFileSync(filePath, content, "utf-8");
41091
41476
  return filePath;
41092
41477
  }
41093
41478
 
@@ -41905,8 +42290,8 @@ async function setupFeatures(features, licensedFeatures) {
41905
42290
  init_agent_registry();
41906
42291
 
41907
42292
  // src/commands/work.ts
41908
- import * as fs55 from "node:fs";
41909
- import * as path55 from "node:path";
42293
+ import * as fs56 from "node:fs";
42294
+ import * as path56 from "node:path";
41910
42295
  registerCommand({
41911
42296
  name: "work",
41912
42297
  description: "\u5DE5\u4F5C\u6A21\u5F0F\uFF1A\u65E0\u53C2\u770B\u72B6\u6001\uFF0Con=\u5F3A\u5236work\uFF0Coff=\u5F3A\u5236emotion\uFF0Cauto=\u81EA\u52A8\u5224\u5B9A",
@@ -41919,20 +42304,20 @@ registerCommand({
41919
42304
  ],
41920
42305
  handler: async (ctx, deps) => {
41921
42306
  const mode = (ctx.args.mode || "").trim().toLowerCase();
41922
- const modeFile = path55.join(deps.config.workspace, ".work-mode");
42307
+ const modeFile = path56.join(deps.config.workspace, ".work-mode");
41923
42308
  if (mode === "on" || mode === "off") {
41924
- fs55.writeFileSync(modeFile, mode, "utf8");
42309
+ fs56.writeFileSync(modeFile, mode, "utf8");
41925
42310
  await ctx.reply(mode === "on" ? "\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u5F3A\u5236 **work**\uFF08\u6240\u6709\u6D88\u606F\u5168\u91CF\u4E0A\u4E0B\u6587\uFF0C\u4E0B\u4E00\u6761\u751F\u6548\uFF09" : "\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u5F3A\u5236 **emotion**\uFF08\u6240\u6709\u6D88\u606F\u8F7B\u91CF\u95F2\u804A\uFF0C\u4E0B\u4E00\u6761\u751F\u6548\uFF09");
41926
42311
  } else if (mode === "auto") {
41927
42312
  try {
41928
- fs55.unlinkSync(modeFile);
42313
+ fs56.unlinkSync(modeFile);
41929
42314
  } catch {
41930
42315
  }
41931
42316
  await ctx.reply("\u5DE5\u4F5C\u6A21\u5F0F\u5DF2\u6062\u590D **auto**\uFF08emotion_state_machine \u9010\u6761\u81EA\u52A8\u5224\u5B9A\uFF09");
41932
42317
  } else {
41933
42318
  let cur = "auto";
41934
42319
  try {
41935
- cur = fs55.readFileSync(modeFile, "utf8").trim() || "auto";
42320
+ cur = fs56.readFileSync(modeFile, "utf8").trim() || "auto";
41936
42321
  } catch {
41937
42322
  }
41938
42323
  const hint = cur === "on" ? "\uFF08\u5F3A\u5236 work\uFF1A\u5168\u91CF\u4E0A\u4E0B\u6587\uFF09" : cur === "off" ? "\uFF08\u5F3A\u5236 emotion\uFF1A\u8F7B\u91CF\u4E0A\u4E0B\u6587\uFF09" : "\uFF08\u6BCF\u6761\u6D88\u606F\u7531 emotion_state_machine \u81EA\u52A8\u5224\u5B9A\uFF09";
@@ -42402,8 +42787,8 @@ ${err.stack}`);
42402
42787
  });
42403
42788
 
42404
42789
  // src/commands/model.ts
42405
- import * as fs56 from "node:fs";
42406
- import * as path56 from "node:path";
42790
+ import * as fs57 from "node:fs";
42791
+ import * as path57 from "node:path";
42407
42792
  import { fileURLToPath as fileURLToPath8 } from "node:url";
42408
42793
  function collectModels(config) {
42409
42794
  const aliases = config.modelAliases || {};
@@ -42435,11 +42820,11 @@ function resolveConfigWritePath(config, label) {
42435
42820
  const configPath = config._configFilePath;
42436
42821
  if (!configPath) return void 0;
42437
42822
  let writePath = configPath;
42438
- if (configPath && !fs56.existsSync(configPath)) {
42823
+ if (configPath && !fs57.existsSync(configPath)) {
42439
42824
  const __pFile = fileURLToPath8(import.meta.url);
42440
- const __pDir = path56.dirname(__pFile);
42441
- const altPath = path56.join(path56.resolve(__pDir, "../configs"), path56.basename(configPath));
42442
- if (fs56.existsSync(altPath)) {
42825
+ const __pDir = path57.dirname(__pFile);
42826
+ const altPath = path57.join(path57.resolve(__pDir, "../configs"), path57.basename(configPath));
42827
+ if (fs57.existsSync(altPath)) {
42443
42828
  console.warn(`[${label}] Config not found at ${configPath}, falling back to ${altPath}`);
42444
42829
  writePath = altPath;
42445
42830
  }
@@ -42594,14 +42979,14 @@ Use full ref like \`/primary ${candidates[0].ref}\``);
42594
42979
  return;
42595
42980
  }
42596
42981
  try {
42597
- const raw = await fs56.promises.readFile(writePath, "utf-8");
42982
+ const raw = await fs57.promises.readFile(writePath, "utf-8");
42598
42983
  const cfg = JSON.parse(raw);
42599
42984
  if (!cfg.agents?.defaults?.model) {
42600
42985
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
42601
42986
  return;
42602
42987
  }
42603
42988
  cfg.agents.defaults.model.primary = target;
42604
- await fs56.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
42989
+ await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
42605
42990
  console.log(`[primary] Persisted primary=${target} to ${writePath}`);
42606
42991
  await ctx.reply(`\u2705 Primary model set to **${target}** (${candidates[0].name})
42607
42992
  Written to config, hot-reloading provider chain now.`);
@@ -42648,7 +43033,7 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
42648
43033
  return;
42649
43034
  }
42650
43035
  try {
42651
- const raw = await fs56.promises.readFile(writePath, "utf-8");
43036
+ const raw = await fs57.promises.readFile(writePath, "utf-8");
42652
43037
  const cfg = JSON.parse(raw);
42653
43038
  if (!cfg.agents?.defaults?.model) {
42654
43039
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
@@ -42656,12 +43041,12 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
42656
43041
  }
42657
43042
  if (input === "auto" || input === "reset") {
42658
43043
  delete cfg.agents.defaults.model.vision;
42659
- await fs56.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
43044
+ await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
42660
43045
  console.log(`[vision-primary] Cleared vision primary in ${writePath}`);
42661
43046
  await ctx.reply(`\u2705 Vision primary cleared (auto). Written to config. Hot-reload will apply.`);
42662
43047
  } else {
42663
43048
  cfg.agents.defaults.model.vision = target;
42664
- await fs56.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
43049
+ await fs57.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
42665
43050
  console.log(`[vision-primary] Persisted vision=${target} to ${writePath}`);
42666
43051
  await ctx.reply(`\u2705 Vision primary set to **${target}** (${candidates[0].name})
42667
43052
  Written to config. Hot-reload will apply.`);
@@ -43069,7 +43454,7 @@ async function startEngine(config, opts) {
43069
43454
  const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
43070
43455
  initSessionMemory2({
43071
43456
  workspace: config.workspace,
43072
- stateDir: path59.join(config.stateDir, "session-memory"),
43457
+ stateDir: path60.join(config.stateDir, "session-memory"),
43073
43458
  provider,
43074
43459
  model: config.provider.modelId || config.model || "deepseek-v4-flash",
43075
43460
  features: config.profile.features