engine7 7.1.34 → 7.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.mjs CHANGED
@@ -26,6 +26,119 @@ var __copyProps = (to, from, except, desc) => {
26
26
  };
27
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
28
 
29
+ // src/config/live.ts
30
+ var live_exports = {};
31
+ __export(live_exports, {
32
+ liveConfig: () => liveConfig
33
+ });
34
+ var LiveConfigClass, liveConfig;
35
+ var init_live = __esm({
36
+ "src/config/live.ts"() {
37
+ "use strict";
38
+ LiveConfigClass = class {
39
+ current = null;
40
+ /** 启动时注入(替代 registry.config = config) */
41
+ init(config2) {
42
+ this.current = config2;
43
+ }
44
+ /** 取整个 config 对象(只读引用,不要缓存) */
45
+ all() {
46
+ if (!this.current) {
47
+ throw new Error("[liveConfig] not initialized \u2014 call liveConfig.init() first");
48
+ }
49
+ return this.current;
50
+ }
51
+ /** 安全取子段('services.voice-chat.start' → current.services.voice-chat.start) */
52
+ get(dotPath) {
53
+ if (!this.current) return void 0;
54
+ return dotPath.split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], this.current);
55
+ }
56
+ /** reload 时原地更新(Object.assign 保持引用不变) */
57
+ assign(newConfig) {
58
+ if (!this.current) {
59
+ this.current = newConfig;
60
+ return;
61
+ }
62
+ Object.assign(this.current, newConfig);
63
+ }
64
+ /** 改活树上的值;文件承载路径同步持久化(read-modify-write 回 config 文件) */
65
+ async set(dotPath, val) {
66
+ if (!this.current) throw new Error("[liveConfig] not initialized \u2014 call liveConfig.init() first");
67
+ const keys = dotPath.split(".");
68
+ let obj = this.current;
69
+ for (let i = 0; i < keys.length - 1; i++) {
70
+ if (obj[keys[i]] == null) obj[keys[i]] = {};
71
+ obj = obj[keys[i]];
72
+ }
73
+ obj[keys[keys.length - 1]] = val;
74
+ await this.persistToFile(dotPath, val);
75
+ }
76
+ /** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
77
+ async persistToFile(dotPath, val) {
78
+ const fs43 = await import("node:fs");
79
+ const cfgPath = this.current?._configFilePath;
80
+ if (!cfgPath || !fs43.existsSync(cfgPath)) {
81
+ console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
82
+ return;
83
+ }
84
+ try {
85
+ const raw = JSON.parse(fs43.readFileSync(cfgPath, "utf-8"));
86
+ const keys = dotPath.split(".");
87
+ let o = raw;
88
+ for (let i = 0; i < keys.length - 1; i++) {
89
+ if (o[keys[i]] == null) o[keys[i]] = {};
90
+ o = o[keys[i]];
91
+ }
92
+ o[keys[keys.length - 1]] = val;
93
+ fs43.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
94
+ console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
95
+ } catch (e) {
96
+ console.warn(`[liveConfig] persist failed (${dotPath}): ${e.message}`);
97
+ }
98
+ }
99
+ /** 是否已初始化 */
100
+ isReady() {
101
+ return this.current !== null;
102
+ }
103
+ };
104
+ liveConfig = new LiveConfigClass();
105
+ }
106
+ });
107
+
108
+ // src/config/features.ts
109
+ function getFeature(key) {
110
+ const v = liveConfig.get(`agents.defaults.features.${key}`);
111
+ return v === void 0 ? FEATURE_DEFAULTS[key] : v;
112
+ }
113
+ var FEATURE_DEFAULTS;
114
+ var init_features = __esm({
115
+ "src/config/features.ts"() {
116
+ "use strict";
117
+ init_live();
118
+ FEATURE_DEFAULTS = {
119
+ filesystem: true,
120
+ shell: true,
121
+ memory: true,
122
+ "topic-extract": true,
123
+ "topic-recall": true,
124
+ "session-memory": true,
125
+ todo: true,
126
+ cron: false,
127
+ voice: false,
128
+ selfie: false,
129
+ eyes: false,
130
+ calendar: false,
131
+ webSearch: true,
132
+ webFetch: true,
133
+ agentTeams: true,
134
+ autoDream: true,
135
+ processOutput: "verbose",
136
+ interrupt: "command",
137
+ debounceMs: 5e3
138
+ };
139
+ }
140
+ });
141
+
29
142
  // src/tools/ToolSearchTool/constants.ts
30
143
  var TOOL_SEARCH_TOOL_NAME;
31
144
  var init_constants = __esm({
@@ -174,17 +287,19 @@ var init_types = __esm({
174
287
  "src/messages/types.ts"() {
175
288
  "use strict";
176
289
  msg = {
177
- system: (content) => ({ role: "system", content }),
178
- user: (content) => ({ role: "user", content }),
290
+ system: (content) => ({ role: "system", content, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
291
+ user: (content) => ({ role: "user", content, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
179
292
  assistant: (content, tool_calls) => ({
180
293
  role: "assistant",
181
294
  content,
295
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
182
296
  ...tool_calls ? { tool_calls } : {}
183
297
  }),
184
298
  tool: (tool_call_id, content, isError) => ({
185
299
  role: "tool",
186
300
  tool_call_id,
187
301
  content,
302
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
188
303
  ...isError ? { is_error: true } : {}
189
304
  })
190
305
  };
@@ -2139,9 +2254,9 @@ function isAutoMemPath(absolutePath, workspace) {
2139
2254
  return normalizedPath.startsWith(getAutoMemPath(workspace));
2140
2255
  }
2141
2256
  async function ensureMemoryDirExists(memoryDir) {
2142
- const fs42 = await import("node:fs");
2257
+ const fs43 = await import("node:fs");
2143
2258
  try {
2144
- await fs42.promises.mkdir(memoryDir, { recursive: true });
2259
+ await fs43.promises.mkdir(memoryDir, { recursive: true });
2145
2260
  } catch (e) {
2146
2261
  const code = e?.code;
2147
2262
  if (code !== "EEXIST") {
@@ -3308,50 +3423,6 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
3308
3423
  }
3309
3424
  });
3310
3425
 
3311
- // src/config/live.ts
3312
- var live_exports = {};
3313
- __export(live_exports, {
3314
- liveConfig: () => liveConfig
3315
- });
3316
- var LiveConfigClass, liveConfig;
3317
- var init_live = __esm({
3318
- "src/config/live.ts"() {
3319
- "use strict";
3320
- LiveConfigClass = class {
3321
- current = null;
3322
- /** 启动时注入(替代 registry.config = config) */
3323
- init(config2) {
3324
- this.current = config2;
3325
- }
3326
- /** 取整个 config 对象(只读引用,不要缓存) */
3327
- all() {
3328
- if (!this.current) {
3329
- throw new Error("[liveConfig] not initialized \u2014 call liveConfig.init() first");
3330
- }
3331
- return this.current;
3332
- }
3333
- /** 安全取子段('services.voice-chat.start' → current.services.voice-chat.start) */
3334
- get(dotPath) {
3335
- if (!this.current) return void 0;
3336
- return dotPath.split(".").reduce((acc, key) => acc == null ? void 0 : acc[key], this.current);
3337
- }
3338
- /** reload 时原地更新(Object.assign 保持引用不变) */
3339
- assign(newConfig) {
3340
- if (!this.current) {
3341
- this.current = newConfig;
3342
- return;
3343
- }
3344
- Object.assign(this.current, newConfig);
3345
- }
3346
- /** 是否已初始化 */
3347
- isReady() {
3348
- return this.current !== null;
3349
- }
3350
- };
3351
- liveConfig = new LiveConfigClass();
3352
- }
3353
- });
3354
-
3355
3426
  // src/utils/path.ts
3356
3427
  var path_exports = {};
3357
3428
  __export(path_exports, {
@@ -4400,7 +4471,7 @@ async function acquireLock(inboxPath) {
4400
4471
  for (let i = 0; i < LOCK_RETRIES; i++) {
4401
4472
  if (await isLockStale(lockPath2)) {
4402
4473
  try {
4403
- await import("node:fs/promises").then((fs42) => fs42.rm(lockPath2, { force: true }));
4474
+ await import("node:fs/promises").then((fs43) => fs43.rm(lockPath2, { force: true }));
4404
4475
  } catch {
4405
4476
  }
4406
4477
  }
@@ -4408,7 +4479,7 @@ async function acquireLock(inboxPath) {
4408
4479
  await writeFile2(lockPath2, `${process.pid}-${Date.now()}`, { encoding: "utf-8", flag: "wx" });
4409
4480
  return async () => {
4410
4481
  try {
4411
- await import("node:fs/promises").then((fs42) => fs42.rm(lockPath2, { force: true }));
4482
+ await import("node:fs/promises").then((fs43) => fs43.rm(lockPath2, { force: true }));
4412
4483
  } catch {
4413
4484
  }
4414
4485
  };
@@ -4827,76 +4898,6 @@ var init_extractPrompts = __esm({
4827
4898
  }
4828
4899
  });
4829
4900
 
4830
- // src/memory/everos/ingest.ts
4831
- function parseMeta(text) {
4832
- const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
4833
- if (!m) return null;
4834
- return { senderName: m[1].trim() };
4835
- }
4836
- async function readConfig() {
4837
- const defaults = {
4838
- url: "http://127.0.0.1:8100",
4839
- appId: "default",
4840
- agentName: "assistant",
4841
- enabled: false
4842
- };
4843
- try {
4844
- const { liveConfig: liveConfig2 } = await Promise.resolve().then(() => (init_live(), live_exports));
4845
- const cfg = liveConfig2.all()?.everos;
4846
- if (!cfg) return defaults;
4847
- return {
4848
- url: cfg.everosUrl || defaults.url,
4849
- appId: cfg.userId || defaults.appId,
4850
- agentName: cfg.agentName || cfg.userId || defaults.agentName,
4851
- enabled: cfg.enabled === true
4852
- };
4853
- } catch {
4854
- return defaults;
4855
- }
4856
- }
4857
- async function pushConversation(messages, sessionId) {
4858
- const cfg = await readConfig();
4859
- if (!cfg.enabled) return;
4860
- if (!messages.length) return;
4861
- const payload = {
4862
- session_id: `extract-${sessionId}`,
4863
- app_id: cfg.appId,
4864
- project_id: "default",
4865
- messages: messages.map((m) => {
4866
- const text = typeof m.content === "string" ? m.content : "[content blocks]";
4867
- const meta = m.role === "user" ? parseMeta(text) : null;
4868
- const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
4869
- return {
4870
- sender_id: cfg.appId,
4871
- sender_name: senderName,
4872
- role: m.role === "toolResult" ? "tool" : m.role,
4873
- timestamp: Date.now(),
4874
- content: text
4875
- };
4876
- })
4877
- };
4878
- console.log(`[everos-ingest] pushing ${payload.messages.length} messages to ${cfg.url} (appId=${cfg.appId})`);
4879
- try {
4880
- const resp = await fetch(`${cfg.url}/api/v1/memory/add`, {
4881
- method: "POST",
4882
- headers: { "Content-Type": "application/json" },
4883
- body: JSON.stringify(payload),
4884
- signal: AbortSignal.timeout(1e4)
4885
- });
4886
- if (resp.ok) {
4887
- console.log(`[everos-ingest] \u2705 pushed ${payload.messages.length} messages`);
4888
- } else {
4889
- console.warn(`[everos-ingest] memory/add ${resp.status}`);
4890
- }
4891
- } catch {
4892
- }
4893
- }
4894
- var init_ingest = __esm({
4895
- "src/memory/everos/ingest.ts"() {
4896
- "use strict";
4897
- }
4898
- });
4899
-
4900
4901
  // src/memory/memdir/extractMemories.ts
4901
4902
  var extractMemories_exports = {};
4902
4903
  __export(extractMemories_exports, {
@@ -4907,17 +4908,18 @@ import * as path12 from "path";
4907
4908
  function isModelVisibleMessage(message) {
4908
4909
  return message.role === "user" || message.role === "assistant";
4909
4910
  }
4910
- function countModelVisibleMessagesSince(messages, sinceIndex) {
4911
- if (sinceIndex === void 0) {
4912
- return messages.filter(isModelVisibleMessage).length;
4913
- }
4914
- let n = 0;
4915
- for (let i = sinceIndex + 1; i < messages.length; i++) {
4916
- if (isModelVisibleMessage(messages[i])) {
4917
- n++;
4918
- }
4911
+ function filterNewMessagesByTs(messages, lastTs) {
4912
+ const newMessages = [];
4913
+ let maxNewTs = lastTs ?? 0;
4914
+ for (const m of messages) {
4915
+ if (!isModelVisibleMessage(m)) continue;
4916
+ const ts = m.timestamp;
4917
+ const tsMs = ts ? new Date(ts).getTime() : NaN;
4918
+ if (lastTs !== void 0 && !Number.isNaN(tsMs) && tsMs <= lastTs) continue;
4919
+ if (!Number.isNaN(tsMs)) maxNewTs = Math.max(maxNewTs, tsMs);
4920
+ newMessages.push(m);
4919
4921
  }
4920
- return n;
4922
+ return { newMessages, maxNewTs };
4921
4923
  }
4922
4924
  function getMemoryTools() {
4923
4925
  const MEMORY_TOOL_NAMES = ["write", "read", "edit", "glob", "grep"];
@@ -4932,13 +4934,13 @@ function loadPersistedState(workspace) {
4932
4934
  const p = getStatePath(workspace);
4933
4935
  if (fs12.existsSync(p)) {
4934
4936
  const data = JSON.parse(fs12.readFileSync(p, "utf-8"));
4935
- for (const [sid, idx] of Object.entries(data.indices ?? {})) {
4936
- lastIndexMap.set(sid, idx);
4937
+ for (const [sid, ts] of Object.entries(data.lastTs ?? {})) {
4938
+ lastTsMap.set(sid, ts);
4937
4939
  }
4938
4940
  for (const [sid, ts] of Object.entries(data.timestamps ?? {})) {
4939
4941
  lastExtractTimeMap.set(sid, ts);
4940
4942
  }
4941
- console.log(`[memory] loaded extract state for ${Object.keys(data.indices ?? {}).length} session(s) from ${STATE_FILE}`);
4943
+ console.log(`[memory] loaded extract state for ${Object.keys(data.lastTs ?? {}).length} session(s) from ${STATE_FILE}`);
4942
4944
  }
4943
4945
  } catch (e) {
4944
4946
  console.warn(`[memory] failed to load extract state: ${e?.message ?? e}`);
@@ -4946,11 +4948,11 @@ function loadPersistedState(workspace) {
4946
4948
  }
4947
4949
  function persistState(workspace) {
4948
4950
  try {
4949
- const indices = {};
4951
+ const lastTs = {};
4950
4952
  const timestamps = {};
4951
- for (const [sid, idx] of lastIndexMap.entries()) indices[sid] = idx;
4953
+ for (const [sid, ts] of lastTsMap.entries()) lastTs[sid] = ts;
4952
4954
  for (const [sid, ts] of lastExtractTimeMap.entries()) timestamps[sid] = ts;
4953
- const data = JSON.stringify({ indices, timestamps }, null, 2);
4955
+ const data = JSON.stringify({ lastTs, timestamps }, null, 2);
4954
4956
  fs12.writeFileSync(getStatePath(workspace), data, "utf-8");
4955
4957
  } catch (e) {
4956
4958
  console.warn(`[memory] failed to persist extract state: ${e?.message ?? e}`);
@@ -4961,7 +4963,7 @@ function createMemoryExtractor(workspace, enabled) {
4961
4963
  loadPersistedState(workspace);
4962
4964
  return {
4963
4965
  reset(sessionId) {
4964
- lastIndexMap.delete(sessionId);
4966
+ lastTsMap.delete(sessionId);
4965
4967
  lastExtractTimeMap.delete(sessionId);
4966
4968
  persistState(workspace);
4967
4969
  inProgress = false;
@@ -4977,13 +4979,12 @@ function createMemoryExtractor(workspace, enabled) {
4977
4979
  return;
4978
4980
  }
4979
4981
  }
4980
- const lastProcessedIndex = lastIndexMap.get(sessionId);
4981
- const memoryDir = getAutoMemPath(workspace);
4982
- const newMessageCount = countModelVisibleMessagesSince(
4983
- messages,
4984
- lastProcessedIndex
4985
- );
4982
+ const lastTs = lastTsMap.get(sessionId);
4983
+ const { newMessages, maxNewTs } = filterNewMessagesByTs(messages, lastTs);
4984
+ const newMessageCount = newMessages.length;
4985
+ console.log(`[memory] extract ts-watermark: session=${sessionId.slice(0, 8)} lastTs=${lastTs ? new Date(lastTs).toISOString().slice(11, 19) : "\u2205(\u9996\u6B21)"} new=${newMessageCount}/${messages.length}`);
4986
4986
  if (newMessageCount === 0) return;
4987
+ const memoryDir = getAutoMemPath(workspace);
4987
4988
  inProgress = true;
4988
4989
  const startTime = Date.now();
4989
4990
  try {
@@ -5022,8 +5023,7 @@ Check this list before writing \u2014 update an existing file rather than creati
5022
5023
  false
5023
5024
  );
5024
5025
  }
5025
- const visibleMessages = messages.filter(isModelVisibleMessage);
5026
- const recentMessages2 = visibleMessages.slice(-newMessageCount);
5026
+ const recentMessages2 = newMessages;
5027
5027
  const conversationText = recentMessages2.map((m) => `[${m.role}]: ${(typeof m.content === "string" ? m.content : "[content blocks]").slice(0, 500)}`).join("\n\n");
5028
5028
  const extractMessages = [
5029
5029
  {
@@ -5060,11 +5060,9 @@ ${conversationText}`
5060
5060
  toolCount++;
5061
5061
  }
5062
5062
  }
5063
- lastIndexMap.set(sessionId, messages.length - 1);
5063
+ lastTsMap.set(sessionId, Math.max(maxNewTs, Date.now()));
5064
5064
  lastExtractTimeMap.set(sessionId, Date.now());
5065
5065
  persistState(workspace);
5066
- pushConversation(recentMessages2, sessionId).catch(() => {
5067
- });
5068
5066
  const duration = Date.now() - startTime;
5069
5067
  console.log(
5070
5068
  `[memory] extractMemories finished in ${duration}ms \u2014 ${toolCount} tools used, ${result.length} chars`
@@ -5079,7 +5077,7 @@ ${conversationText}`
5079
5077
  }
5080
5078
  };
5081
5079
  }
5082
- var lastIndexMap, lastExtractTimeMap, STATE_FILE;
5080
+ var lastTsMap, lastExtractTimeMap, STATE_FILE;
5083
5081
  var init_extractMemories = __esm({
5084
5082
  "src/memory/memdir/extractMemories.ts"() {
5085
5083
  "use strict";
@@ -5088,13 +5086,207 @@ var init_extractMemories = __esm({
5088
5086
  init_paths();
5089
5087
  init_memoryScan();
5090
5088
  init_extractPrompts();
5091
- init_ingest();
5092
- lastIndexMap = /* @__PURE__ */ new Map();
5089
+ lastTsMap = /* @__PURE__ */ new Map();
5093
5090
  lastExtractTimeMap = /* @__PURE__ */ new Map();
5094
5091
  STATE_FILE = ".extract-state.json";
5095
5092
  }
5096
5093
  });
5097
5094
 
5095
+ // src/memory/everos/ingest.ts
5096
+ var ingest_exports = {};
5097
+ __export(ingest_exports, {
5098
+ pushConversation: () => pushConversation
5099
+ });
5100
+ import fs13 from "node:fs";
5101
+ import path13 from "node:path";
5102
+ function parseMeta(text) {
5103
+ const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
5104
+ if (!m) return null;
5105
+ return { senderName: m[1].trim() };
5106
+ }
5107
+ async function readConfig() {
5108
+ const defaults = {
5109
+ url: "http://127.0.0.1:8100",
5110
+ appId: "default",
5111
+ agentName: "assistant",
5112
+ enabled: false,
5113
+ ingestTimeoutMs: 3e4,
5114
+ syncIntervalMs: 9e5
5115
+ };
5116
+ try {
5117
+ const { liveConfig: liveConfig2 } = await Promise.resolve().then(() => (init_live(), live_exports));
5118
+ const cfg = liveConfig2.all()?.everos;
5119
+ if (!cfg) return defaults;
5120
+ return {
5121
+ url: cfg.everosUrl || defaults.url,
5122
+ appId: cfg.userId || defaults.appId,
5123
+ agentName: cfg.agentName || cfg.userId || defaults.agentName,
5124
+ enabled: cfg.enabled === true,
5125
+ ingestTimeoutMs: typeof cfg.ingestTimeoutMs === "number" ? cfg.ingestTimeoutMs : defaults.ingestTimeoutMs,
5126
+ syncIntervalMs: typeof cfg.syncIntervalMs === "number" ? cfg.syncIntervalMs : defaults.syncIntervalMs
5127
+ };
5128
+ } catch {
5129
+ return defaults;
5130
+ }
5131
+ }
5132
+ function loadPushTs(workspace, sessionId) {
5133
+ try {
5134
+ const p = path13.join(workspace, PUSH_STATE_FILE);
5135
+ if (fs13.existsSync(p)) {
5136
+ const data = JSON.parse(fs13.readFileSync(p, "utf-8"));
5137
+ return typeof data[sessionId] === "number" ? data[sessionId] : 0;
5138
+ }
5139
+ } catch (e) {
5140
+ console.warn(`[everos-ingest] load pushTs failed: ${e?.message ?? e}`);
5141
+ }
5142
+ return 0;
5143
+ }
5144
+ function savePushTs(workspace, sessionId, ts) {
5145
+ try {
5146
+ const p = path13.join(workspace, PUSH_STATE_FILE);
5147
+ let data = {};
5148
+ try {
5149
+ if (fs13.existsSync(p)) data = JSON.parse(fs13.readFileSync(p, "utf-8"));
5150
+ } catch {
5151
+ }
5152
+ data[sessionId] = ts;
5153
+ fs13.writeFileSync(p, JSON.stringify(data, null, 2), "utf-8");
5154
+ } catch (e) {
5155
+ console.warn(`[everos-ingest] save pushTs failed: ${e?.message ?? e}`);
5156
+ }
5157
+ }
5158
+ function buildPayload(messages, cfg, sessionId) {
5159
+ return {
5160
+ session_id: `extract-${sessionId}`,
5161
+ app_id: cfg.appId,
5162
+ project_id: "default",
5163
+ messages: messages.map((m) => {
5164
+ const text = typeof m.content === "string" ? m.content : "[content blocks]";
5165
+ const meta = m.role === "user" ? parseMeta(text) : null;
5166
+ const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
5167
+ const rawRole = m.role === "toolResult" ? "tool" : m.role;
5168
+ const validRoles = ["user", "assistant", "tool"];
5169
+ return {
5170
+ sender_id: cfg.appId,
5171
+ sender_name: senderName,
5172
+ role: validRoles.includes(rawRole) ? rawRole : "user",
5173
+ timestamp: Date.now(),
5174
+ content: text
5175
+ };
5176
+ })
5177
+ };
5178
+ }
5179
+ async function fetchWithTimeout(url, options, timeoutMs) {
5180
+ const controller = new AbortController();
5181
+ let timer;
5182
+ const timeoutPromise = new Promise((_, reject) => {
5183
+ timer = setTimeout(() => {
5184
+ try {
5185
+ controller.abort();
5186
+ } catch {
5187
+ }
5188
+ reject(new Error(`fetch timeout ${timeoutMs}ms`));
5189
+ }, timeoutMs);
5190
+ });
5191
+ try {
5192
+ return await Promise.race([
5193
+ fetch(url, { ...options, signal: controller.signal }),
5194
+ timeoutPromise
5195
+ ]);
5196
+ } finally {
5197
+ if (timer) clearTimeout(timer);
5198
+ }
5199
+ }
5200
+ async function pushConversation(messages, sessionId, workspace) {
5201
+ const cfg = await readConfig();
5202
+ if (!cfg.enabled) return;
5203
+ if (!messages.length) return;
5204
+ if (Date.now() - lastPushAt < cfg.syncIntervalMs) return;
5205
+ if (pushInProgress) {
5206
+ console.log("[everos-ingest] previous push still running, skip (pushTs \u4F1A\u7EED\u63A8\uFF0C\u4E0D\u4E22)");
5207
+ return;
5208
+ }
5209
+ lastPushAt = Date.now();
5210
+ pushInProgress = true;
5211
+ try {
5212
+ if (!workspace) {
5213
+ const payload = buildPayload(messages, cfg, sessionId);
5214
+ console.log(`[everos-ingest] pushing ${payload.messages.length} messages to ${cfg.url} (no pushTs, one-shot, timeout=${cfg.ingestTimeoutMs}ms)`);
5215
+ try {
5216
+ const resp = await fetchWithTimeout(`${cfg.url}/api/v1/memory/add`, {
5217
+ method: "POST",
5218
+ headers: { "Content-Type": "application/json" },
5219
+ body: JSON.stringify(payload)
5220
+ }, cfg.ingestTimeoutMs);
5221
+ if (resp.ok) console.log(`[everos-ingest] \u2705 pushed ${payload.messages.length} messages`);
5222
+ else console.warn(`[everos-ingest] memory/add ${resp.status}`);
5223
+ } catch (e) {
5224
+ console.warn(`[everos-ingest] push failed (${e?.name || "error"}: ${e?.message || "unknown"})`);
5225
+ }
5226
+ return;
5227
+ }
5228
+ const pushTs = loadPushTs(workspace, sessionId);
5229
+ const toPush = [];
5230
+ let maxTs = pushTs;
5231
+ for (const m of messages) {
5232
+ const ms = m.timestamp ? new Date(m.timestamp).getTime() : NaN;
5233
+ if (Number.isNaN(ms)) {
5234
+ toPush.push(m);
5235
+ continue;
5236
+ }
5237
+ if (ms <= pushTs) continue;
5238
+ toPush.push(m);
5239
+ if (ms > maxTs) maxTs = ms;
5240
+ }
5241
+ if (toPush.length === 0) return;
5242
+ const total = toPush.length;
5243
+ const chunkTotal = Math.ceil(total / PUSH_CHUNK);
5244
+ let pushed = 0;
5245
+ for (let i = 0; i < total; i += PUSH_CHUNK) {
5246
+ const chunk = toPush.slice(i, i + PUSH_CHUNK);
5247
+ const chunkMaxTs = chunk.reduce((mx, m) => {
5248
+ const ms = m.timestamp ? new Date(m.timestamp).getTime() : 0;
5249
+ return Number.isNaN(ms) || ms < mx ? mx : ms;
5250
+ }, pushTs);
5251
+ const chunkNo = Math.floor(i / PUSH_CHUNK) + 1;
5252
+ const payload = buildPayload(chunk, cfg, sessionId);
5253
+ console.log(`[everos-ingest] pushing chunk ${chunkNo}/${chunkTotal} (${chunk.length} msgs) to ${cfg.url} (timeout=${cfg.ingestTimeoutMs}ms)`);
5254
+ try {
5255
+ const resp = await fetchWithTimeout(`${cfg.url}/api/v1/memory/add`, {
5256
+ method: "POST",
5257
+ headers: { "Content-Type": "application/json" },
5258
+ body: JSON.stringify(payload)
5259
+ }, cfg.ingestTimeoutMs);
5260
+ if (resp.ok) {
5261
+ savePushTs(workspace, sessionId, chunkMaxTs);
5262
+ pushed += chunk.length;
5263
+ console.log(`[everos-ingest] \u2705 pushed chunk ${chunkNo}/${chunkTotal} (${chunk.length} msgs, pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)})`);
5264
+ } else {
5265
+ savePushTs(workspace, sessionId, chunkMaxTs);
5266
+ const errBody = await resp.text().catch(() => "?");
5267
+ console.warn(`[everos-ingest] memory/add ${resp.status} on chunk ${chunkNo}, body=${errBody.slice(0, 300)}, skip & advance pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)}`);
5268
+ }
5269
+ } catch (e) {
5270
+ savePushTs(workspace, sessionId, chunkMaxTs);
5271
+ console.warn(`[everos-ingest] chunk ${chunkNo} push failed (${e?.name || "error"}: ${e?.message || "unknown"}), skip & advance pushTs\u2192${new Date(chunkMaxTs).toISOString().slice(11, 19)}`);
5272
+ }
5273
+ }
5274
+ console.log(`[everos-ingest] done: ${pushed}/${total} msgs pushed`);
5275
+ } finally {
5276
+ pushInProgress = false;
5277
+ }
5278
+ }
5279
+ var PUSH_CHUNK, PUSH_STATE_FILE, pushInProgress, lastPushAt;
5280
+ var init_ingest = __esm({
5281
+ "src/memory/everos/ingest.ts"() {
5282
+ "use strict";
5283
+ PUSH_CHUNK = 10;
5284
+ PUSH_STATE_FILE = ".everos-push-state.json";
5285
+ pushInProgress = false;
5286
+ lastPushAt = 0;
5287
+ }
5288
+ });
5289
+
5098
5290
  // src/memory/sessionMemory/sessionMemoryUtils.ts
5099
5291
  import { join as join17 } from "node:path";
5100
5292
  import { mkdirSync as mkdirSync6, readFileSync as readFileSync13 } from "node:fs";
@@ -5452,7 +5644,7 @@ async function extractSessionMemory(messages, overrideProvider, overrideModel) {
5452
5644
  if (!_deps) return { fired: false, reason: "not initialized" };
5453
5645
  if (isExtractionInProgress()) return { fired: false, reason: "extraction already in progress" };
5454
5646
  const provider = overrideProvider || _deps.provider;
5455
- const model = overrideModel || _deps.model;
5647
+ const model = overrideModel || liveConfig.get("model") || "";
5456
5648
  markExtractionStarted();
5457
5649
  try {
5458
5650
  const { memoryPath, currentMemory } = await setupSessionMemoryFile();
@@ -5525,7 +5717,7 @@ function getSessionMemoryForCompaction() {
5525
5717
  }
5526
5718
  function isSessionMemoryEnabled() {
5527
5719
  if (!_deps) return false;
5528
- if (_deps.features && _deps.features["session-memory"] === false) return false;
5720
+ if (getFeature("session-memory") === false) return false;
5529
5721
  return true;
5530
5722
  }
5531
5723
  var _deps, lastMemoryMessageIndex;
@@ -5536,6 +5728,8 @@ var init_sessionMemory = __esm({
5536
5728
  init_prompts();
5537
5729
  init_query();
5538
5730
  init_registry();
5731
+ init_features();
5732
+ init_live();
5539
5733
  _deps = null;
5540
5734
  }
5541
5735
  });
@@ -5551,23 +5745,11 @@ __export(config_exports, {
5551
5745
  isAutoDreamEnabled: () => isAutoDreamEnabled,
5552
5746
  setAutoDreamConfig: () => setAutoDreamConfig
5553
5747
  });
5554
- function dlog(msg2) {
5555
- if (process.env.AUTODREAM_DEBUG) console.log(`[autoDream] ${msg2}`);
5556
- }
5557
5748
  function setAutoDreamConfig(config2) {
5558
5749
  _config = config2;
5559
- const c = config2;
5560
- dlog(`setAutoDreamConfig: keys=${Object.keys(c).join(",")} | config.features.autoDream=${c.features?.autoDream} | config.agents.defaults.features.autoDream=${c.agents?.defaults?.features?.autoDream} | config.topics.autoDream=${c.topics?.autoDream ? JSON.stringify(c.topics.autoDream) : "(none)"}`);
5561
5750
  }
5562
5751
  function isAutoDreamEnabled() {
5563
- if (!_config) {
5564
- dlog("isAutoDreamEnabled: _config null");
5565
- return false;
5566
- }
5567
- const features = _config.profile?.features ?? _config.features ?? _config.agents?.defaults?.features;
5568
- const result = features?.autoDream === true;
5569
- dlog(`isAutoDreamEnabled: config.features.autoDream=${_config.features?.autoDream} | agents.defaults.features.autoDream=${_config.agents?.defaults?.features?.autoDream} | resolved=${features?.autoDream} | result=${result}`);
5570
- return result;
5752
+ return getFeature("autoDream") === true;
5571
5753
  }
5572
5754
  function getAutoDreamConfig() {
5573
5755
  const raw = _config?.topics?.autoDream ?? _config?.autoDream;
@@ -5593,6 +5775,7 @@ var _config, DEFAULTS;
5593
5775
  var init_config2 = __esm({
5594
5776
  "src/memory/autoDream/config.ts"() {
5595
5777
  "use strict";
5778
+ init_features();
5596
5779
  _config = null;
5597
5780
  DEFAULTS = {
5598
5781
  minHours: 24,
@@ -5616,11 +5799,11 @@ async function readLastConsolidatedAt(memoryDir) {
5616
5799
  }
5617
5800
  }
5618
5801
  async function tryAcquireConsolidationLock(memoryDir) {
5619
- const path45 = lockPath(memoryDir);
5802
+ const path46 = lockPath(memoryDir);
5620
5803
  let mtimeMs;
5621
5804
  let holderPid;
5622
5805
  try {
5623
- const [s, raw] = await Promise.all([stat3(path45), readFile5(path45, "utf8")]);
5806
+ const [s, raw] = await Promise.all([stat3(path46), readFile5(path46, "utf8")]);
5624
5807
  mtimeMs = s.mtimeMs;
5625
5808
  const parsed = parseInt(raw.trim(), 10);
5626
5809
  holderPid = Number.isFinite(parsed) ? parsed : void 0;
@@ -5633,10 +5816,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
5633
5816
  }
5634
5817
  }
5635
5818
  await mkdir3(memoryDir, { recursive: true });
5636
- await writeFile4(path45, String(process.pid));
5819
+ await writeFile4(path46, String(process.pid));
5637
5820
  let verify2;
5638
5821
  try {
5639
- verify2 = await readFile5(path45, "utf8");
5822
+ verify2 = await readFile5(path46, "utf8");
5640
5823
  } catch {
5641
5824
  return null;
5642
5825
  }
@@ -5644,15 +5827,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
5644
5827
  return mtimeMs ?? 0;
5645
5828
  }
5646
5829
  async function rollbackConsolidationLock(memoryDir, priorMtime) {
5647
- const path45 = lockPath(memoryDir);
5830
+ const path46 = lockPath(memoryDir);
5648
5831
  try {
5649
5832
  if (priorMtime === 0) {
5650
- await unlink(path45);
5833
+ await unlink(path46);
5651
5834
  return;
5652
5835
  }
5653
- await writeFile4(path45, "");
5836
+ await writeFile4(path46, "");
5654
5837
  const t = priorMtime / 1e3;
5655
- await utimes(path45, t, t);
5838
+ await utimes(path46, t, t);
5656
5839
  } catch (e) {
5657
5840
  console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
5658
5841
  }
@@ -5783,48 +5966,48 @@ var init_consolidationPrompt = __esm({
5783
5966
  // src/memory/autoDream/autoDream.ts
5784
5967
  var autoDream_exports = {};
5785
5968
  __export(autoDream_exports, {
5786
- dlog: () => dlog2,
5969
+ dlog: () => dlog,
5787
5970
  executeAutoDream: () => executeAutoDream,
5788
5971
  initAutoDream: () => initAutoDream
5789
5972
  });
5790
- function dlog2(msg2) {
5973
+ function dlog(msg2) {
5791
5974
  if (process.env.AUTODREAM_DEBUG) console.log(`[autoDream] ${msg2}`);
5792
5975
  }
5793
5976
  function initAutoDream(deps) {
5794
5977
  _deps2 = deps;
5795
5978
  lastSessionScanAt = 0;
5796
- dlog2(`initAutoDream: workspace=${deps.workspace} sessionsDir=${deps.sessionsDir} model=${deps.model} provider=${deps.provider?.constructor?.name}`);
5979
+ dlog(`initAutoDream: workspace=${deps.workspace} sessionsDir=${deps.sessionsDir} model=${deps.model} provider=${deps.provider?.constructor?.name}`);
5797
5980
  }
5798
5981
  async function executeAutoDream() {
5799
- dlog2(`=== executeAutoDream START === _deps=${_deps2 ? "set" : "null"} isAutoDreamEnabled=${isAutoDreamEnabled()}`);
5982
+ dlog(`=== executeAutoDream START === _deps=${_deps2 ? "set" : "null"} isAutoDreamEnabled=${isAutoDreamEnabled()}`);
5800
5983
  if (!_deps2) {
5801
- dlog2("EXIT: not initialized");
5984
+ dlog("EXIT: not initialized");
5802
5985
  return { fired: false, reason: "not initialized" };
5803
5986
  }
5804
5987
  if (!isAutoDreamEnabled()) {
5805
- dlog2("EXIT: disabled (features.autoDream not true)");
5988
+ dlog("EXIT: disabled (features.autoDream not true)");
5806
5989
  return { fired: false, reason: "disabled" };
5807
5990
  }
5808
5991
  const cfg = getAutoDreamConfig();
5809
5992
  const { workspace, sessionsDir, provider, model, toolOverride, disableThinking } = _deps2;
5810
5993
  const memoryDir = getAutoMemPath(workspace);
5811
- dlog2(`cfg: minHours=${cfg.minHours} minSessions=${cfg.minSessions} | memoryDir=${memoryDir} | distillOutput=${getDistillOutput() ?? "(none)"} dailyLogDir=${getDailyLogDir() ?? "(none)"} | sessionsDir=${sessionsDir}`);
5994
+ dlog(`cfg: minHours=${cfg.minHours} minSessions=${cfg.minSessions} | memoryDir=${memoryDir} | distillOutput=${getDistillOutput() ?? "(none)"} dailyLogDir=${getDailyLogDir() ?? "(none)"} | sessionsDir=${sessionsDir}`);
5812
5995
  let lastAt;
5813
5996
  try {
5814
5997
  lastAt = await readLastConsolidatedAt(memoryDir);
5815
5998
  } catch (e) {
5816
- dlog2(`EXIT: readLastConsolidatedAt failed: ${e.message}`);
5999
+ dlog(`EXIT: readLastConsolidatedAt failed: ${e.message}`);
5817
6000
  return { fired: false, reason: `readLastConsolidatedAt failed: ${e.message}` };
5818
6001
  }
5819
6002
  const hoursSince = (Date.now() - lastAt) / 36e5;
5820
- dlog2(`time gate: lastAt=${lastAt}(${lastAt === 0 ? "no lock \u2192 \u6C38\u8FDC\u6EE1\u8DB3" : new Date(lastAt).toISOString()}) hoursSince=${hoursSince.toFixed(1)} need>=${cfg.minHours}`);
6003
+ dlog(`time gate: lastAt=${lastAt}(${lastAt === 0 ? "no lock \u2192 \u6C38\u8FDC\u6EE1\u8DB3" : new Date(lastAt).toISOString()}) hoursSince=${hoursSince.toFixed(1)} need>=${cfg.minHours}`);
5821
6004
  if (hoursSince < cfg.minHours) {
5822
- dlog2("EXIT: time gate not met");
6005
+ dlog("EXIT: time gate not met");
5823
6006
  return { fired: false, reason: `time gate: ${hoursSince.toFixed(1)}h < ${cfg.minHours}h` };
5824
6007
  }
5825
6008
  const sinceScanMs = Date.now() - lastSessionScanAt;
5826
6009
  if (sinceScanMs < SESSION_SCAN_INTERVAL_MS) {
5827
- dlog2(`EXIT: scan throttle ${Math.round(sinceScanMs / 1e3)}s ago < 10min`);
6010
+ dlog(`EXIT: scan throttle ${Math.round(sinceScanMs / 1e3)}s ago < 10min`);
5828
6011
  return { fired: false, reason: `scan throttle: last scan ${Math.round(sinceScanMs / 1e3)}s ago` };
5829
6012
  }
5830
6013
  lastSessionScanAt = Date.now();
@@ -5832,27 +6015,27 @@ async function executeAutoDream() {
5832
6015
  try {
5833
6016
  sessionIds = await listSessionsTouchedSince(sessionsDir, lastAt);
5834
6017
  } catch (e) {
5835
- dlog2(`EXIT: listSessionsTouchedSince failed: ${e.message} | sessionsDir=${sessionsDir}`);
6018
+ dlog(`EXIT: listSessionsTouchedSince failed: ${e.message} | sessionsDir=${sessionsDir}`);
5836
6019
  return { fired: false, reason: `listSessionsTouchedSince failed: ${e.message}` };
5837
6020
  }
5838
- dlog2(`session gate: ${sessionIds.length} sessions touched since lastAt, need>=${cfg.minSessions} | sessionsDir=${sessionsDir}`);
6021
+ dlog(`session gate: ${sessionIds.length} sessions touched since lastAt, need>=${cfg.minSessions} | sessionsDir=${sessionsDir}`);
5839
6022
  if (sessionIds.length < cfg.minSessions) {
5840
- dlog2("EXIT: session gate not met");
6023
+ dlog("EXIT: session gate not met");
5841
6024
  return { fired: false, reason: `session gate: ${sessionIds.length} < ${cfg.minSessions}` };
5842
6025
  }
5843
6026
  let priorMtime;
5844
6027
  try {
5845
6028
  priorMtime = await tryAcquireConsolidationLock(memoryDir);
5846
6029
  } catch (e) {
5847
- dlog2(`EXIT: lock acquire failed: ${e.message}`);
6030
+ dlog(`EXIT: lock acquire failed: ${e.message}`);
5848
6031
  return { fired: false, reason: `lock acquire failed: ${e.message}` };
5849
6032
  }
5850
6033
  if (priorMtime === null) {
5851
- dlog2("EXIT: lock held by another process");
6034
+ dlog("EXIT: lock held by another process");
5852
6035
  return { fired: false, reason: "lock held by another process" };
5853
6036
  }
5854
- dlog2(`lock acquired: priorMtime=${priorMtime}`);
5855
- dlog2(`FIRING \u2014 ${hoursSince.toFixed(1)}h since last, ${sessionIds.length} sessions to review`);
6037
+ dlog(`lock acquired: priorMtime=${priorMtime}`);
6038
+ dlog(`FIRING \u2014 ${hoursSince.toFixed(1)}h since last, ${sessionIds.length} sessions to review`);
5856
6039
  console.log(`[autoDream] firing \u2014 ${hoursSince.toFixed(1)}h since last, ${sessionIds.length} sessions to review`);
5857
6040
  try {
5858
6041
  const extra = `
@@ -5860,14 +6043,14 @@ async function executeAutoDream() {
5860
6043
  Sessions since last consolidation (${sessionIds.length}):
5861
6044
  ${sessionIds.map((id) => `- ${id}`).join("\n")}`;
5862
6045
  const prompt = buildConsolidationPrompt(memoryDir, sessionsDir, extra, getDailyLogDir(), getDistillOutput(), getMaxEntrypointLines());
5863
- dlog2(`prompt built (${prompt.length} chars)`);
6046
+ dlog(`prompt built (${prompt.length} chars)`);
5864
6047
  const { QueryEngine: QueryEngine2 } = await Promise.resolve().then(() => (init_query(), query_exports));
5865
- dlog2("QueryEngine imported");
6048
+ dlog("QueryEngine imported");
5866
6049
  const { registry: registry2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
5867
6050
  const SAFE_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "write", "edit", "grep", "glob"]);
5868
6051
  const safeTools = registry2.definitions().filter((d) => SAFE_TOOL_NAMES.has(d.function.name));
5869
6052
  const memoryTools = toolOverride && toolOverride.length > 0 ? toolOverride : safeTools;
5870
- dlog2(`tools: memoryTools=${memoryTools.length} (registry total=${registry2.list().length})`);
6053
+ dlog(`tools: memoryTools=${memoryTools.length} (registry total=${registry2.list().length})`);
5871
6054
  const dreamEngine = new QueryEngine2(provider, {
5872
6055
  model,
5873
6056
  systemPrompt: prompt,
@@ -5876,7 +6059,7 @@ ${sessionIds.map((id) => `- ${id}`).join("\n")}`;
5876
6059
  disableThinking: disableThinking ?? true,
5877
6060
  agentLabel: "auto-dream"
5878
6061
  });
5879
- dlog2("dreamEngine created");
6062
+ dlog("dreamEngine created");
5880
6063
  const messages = [{ role: "user", content: prompt }];
5881
6064
  const toolContext = {
5882
6065
  sessionId: "auto-dream",
@@ -5886,24 +6069,24 @@ ${sessionIds.map((id) => `- ${id}`).join("\n")}`;
5886
6069
  };
5887
6070
  let result = "";
5888
6071
  let turnCount = 0;
5889
- dlog2("dream query START");
6072
+ dlog("dream query START");
5890
6073
  for await (const chunk of dreamEngine.query(messages, void 0, toolContext)) {
5891
6074
  if (chunk.type === "text") {
5892
6075
  result += chunk.text || "";
5893
6076
  }
5894
6077
  if (chunk.type === "tool_call") {
5895
6078
  turnCount++;
5896
- dlog2(`dream turn ${turnCount}: tool_call`);
6079
+ dlog(`dream turn ${turnCount}: tool_call`);
5897
6080
  }
5898
6081
  }
5899
- dlog2(`dream query DONE \u2014 ${turnCount} tool_calls, result=${result.length} chars`);
6082
+ dlog(`dream query DONE \u2014 ${turnCount} tool_calls, result=${result.length} chars`);
5900
6083
  await recordConsolidation(memoryDir);
5901
- dlog2(`recordConsolidation OK \u2014 wrote .consolidate-lock`);
6084
+ dlog(`recordConsolidation OK \u2014 wrote .consolidate-lock`);
5902
6085
  console.log(`[autoDream] completed \u2014 reviewed ${sessionIds.length} sessions`);
5903
6086
  return { fired: true, summary: result.slice(0, 500) };
5904
6087
  } catch (e) {
5905
6088
  const err = e;
5906
- dlog2(`CATCH failed: ${err.message}
6089
+ dlog(`CATCH failed: ${err.message}
5907
6090
  stack: ${err.stack ?? "(no stack)"}`);
5908
6091
  console.log(`[autoDream] failed: ${err.message}`);
5909
6092
  await rollbackConsolidationLock(memoryDir, priorMtime);
@@ -5984,30 +6167,30 @@ __export(TodoWriteTool_exports, {
5984
6167
  initTodoStore: () => initTodoStore,
5985
6168
  loadTodos: () => loadTodos
5986
6169
  });
5987
- import fs31 from "node:fs";
5988
- import path32 from "node:path";
6170
+ import fs32 from "node:fs";
6171
+ import path33 from "node:path";
5989
6172
  function initTodoStore(stateDir) {
5990
- todosDir = path32.join(stateDir, "todos");
5991
- if (!fs31.existsSync(todosDir)) {
5992
- fs31.mkdirSync(todosDir, { recursive: true });
6173
+ todosDir = path33.join(stateDir, "todos");
6174
+ if (!fs32.existsSync(todosDir)) {
6175
+ fs32.mkdirSync(todosDir, { recursive: true });
5993
6176
  }
5994
6177
  }
5995
6178
  function todoFilePath(sessionId) {
5996
- return path32.join(todosDir, `${sessionId}.json`);
6179
+ return path33.join(todosDir, `${sessionId}.json`);
5997
6180
  }
5998
6181
  function loadTodos(sessionId) {
5999
6182
  if (!todosDir) return [];
6000
6183
  try {
6001
6184
  const filePath = todoFilePath(sessionId);
6002
- if (!fs31.existsSync(filePath)) return [];
6003
- return JSON.parse(fs31.readFileSync(filePath, "utf-8"));
6185
+ if (!fs32.existsSync(filePath)) return [];
6186
+ return JSON.parse(fs32.readFileSync(filePath, "utf-8"));
6004
6187
  } catch {
6005
6188
  return [];
6006
6189
  }
6007
6190
  }
6008
6191
  function saveTodos(sessionId, todos) {
6009
6192
  if (!todosDir) return;
6010
- fs31.writeFileSync(todoFilePath(sessionId), JSON.stringify(todos, null, 2), "utf-8");
6193
+ fs32.writeFileSync(todoFilePath(sessionId), JSON.stringify(todos, null, 2), "utf-8");
6011
6194
  }
6012
6195
  var todosDir;
6013
6196
  var init_TodoWriteTool = __esm({
@@ -6088,28 +6271,28 @@ __export(tasks_exports, {
6088
6271
  unassignTeammateTasks: () => unassignTeammateTasks,
6089
6272
  updateTask: () => updateTask
6090
6273
  });
6091
- import * as fs33 from "node:fs";
6092
- import * as path34 from "node:path";
6274
+ import * as fs34 from "node:fs";
6275
+ import * as path35 from "node:path";
6093
6276
  function sanitizePathComponent2(input) {
6094
6277
  return input.replace(/[^a-zA-Z0-9_-]/g, "-");
6095
6278
  }
6096
6279
  function getTasksDir2(stateDir, listId) {
6097
- return path34.join(stateDir, "tasks", sanitizePathComponent2(listId));
6280
+ return path35.join(stateDir, "tasks", sanitizePathComponent2(listId));
6098
6281
  }
6099
6282
  function getTaskPath(stateDir, listId, taskId) {
6100
- return path34.join(getTasksDir2(stateDir, listId), `${sanitizePathComponent2(taskId)}.json`);
6283
+ return path35.join(getTasksDir2(stateDir, listId), `${sanitizePathComponent2(taskId)}.json`);
6101
6284
  }
6102
6285
  function ensureTasksDir2(stateDir, listId) {
6103
6286
  const dir = getTasksDir2(stateDir, listId);
6104
- fs33.mkdirSync(dir, { recursive: true });
6287
+ fs34.mkdirSync(dir, { recursive: true });
6105
6288
  return dir;
6106
6289
  }
6107
6290
  function getHighWaterMarkPath(stateDir, listId) {
6108
- return path34.join(getTasksDir2(stateDir, listId), HIGH_WATER_MARK_FILE);
6291
+ return path35.join(getTasksDir2(stateDir, listId), HIGH_WATER_MARK_FILE);
6109
6292
  }
6110
6293
  function readHighWaterMark(stateDir, listId) {
6111
6294
  try {
6112
- const content = fs33.readFileSync(getHighWaterMarkPath(stateDir, listId), "utf-8").trim();
6295
+ const content = fs34.readFileSync(getHighWaterMarkPath(stateDir, listId), "utf-8").trim();
6113
6296
  const value = parseInt(content, 10);
6114
6297
  return isNaN(value) ? 0 : value;
6115
6298
  } catch {
@@ -6117,13 +6300,13 @@ function readHighWaterMark(stateDir, listId) {
6117
6300
  }
6118
6301
  }
6119
6302
  function writeHighWaterMark(stateDir, listId, value) {
6120
- fs33.writeFileSync(getHighWaterMarkPath(stateDir, listId), String(value));
6303
+ fs34.writeFileSync(getHighWaterMarkPath(stateDir, listId), String(value));
6121
6304
  }
6122
6305
  function findHighestTaskIdFromFiles(stateDir, listId) {
6123
6306
  const dir = getTasksDir2(stateDir, listId);
6124
6307
  let files;
6125
6308
  try {
6126
- files = fs33.readdirSync(dir);
6309
+ files = fs34.readdirSync(dir);
6127
6310
  } catch {
6128
6311
  return 0;
6129
6312
  }
@@ -6149,14 +6332,14 @@ function createTask(stateDir, listId, taskData) {
6149
6332
  const id = String(highestId + 1);
6150
6333
  const task = { id, ...taskData };
6151
6334
  const filePath = getTaskPath(stateDir, listId, id);
6152
- fs33.writeFileSync(filePath, JSON.stringify(task, null, 2));
6335
+ fs34.writeFileSync(filePath, JSON.stringify(task, null, 2));
6153
6336
  return id;
6154
6337
  });
6155
6338
  }
6156
6339
  function getTask2(stateDir, listId, taskId) {
6157
6340
  const filePath = getTaskPath(stateDir, listId, taskId);
6158
6341
  try {
6159
- const content = fs33.readFileSync(filePath, "utf-8");
6342
+ const content = fs34.readFileSync(filePath, "utf-8");
6160
6343
  return JSON.parse(content);
6161
6344
  } catch {
6162
6345
  return null;
@@ -6166,7 +6349,7 @@ function listTasks2(stateDir, listId) {
6166
6349
  const dir = getTasksDir2(stateDir, listId);
6167
6350
  let files;
6168
6351
  try {
6169
- files = fs33.readdirSync(dir);
6352
+ files = fs34.readdirSync(dir);
6170
6353
  } catch {
6171
6354
  return [];
6172
6355
  }
@@ -6178,7 +6361,7 @@ function updateTask(stateDir, listId, taskId, updates) {
6178
6361
  if (!existing) return null;
6179
6362
  const updated = { ...existing, ...updates, id: taskId };
6180
6363
  const filePath = getTaskPath(stateDir, listId, taskId);
6181
- fs33.writeFileSync(filePath, JSON.stringify(updated, null, 2));
6364
+ fs34.writeFileSync(filePath, JSON.stringify(updated, null, 2));
6182
6365
  return updated;
6183
6366
  }
6184
6367
  function deleteTask(stateDir, listId, taskId) {
@@ -6192,7 +6375,7 @@ function deleteTask(stateDir, listId, taskId) {
6192
6375
  }
6193
6376
  }
6194
6377
  try {
6195
- fs33.unlinkSync(filePath);
6378
+ fs34.unlinkSync(filePath);
6196
6379
  } catch {
6197
6380
  return false;
6198
6381
  }
@@ -6310,15 +6493,15 @@ var read_exports = {};
6310
6493
  __export(read_exports, {
6311
6494
  readFileState: () => readFileState
6312
6495
  });
6313
- import * as fs34 from "node:fs";
6314
- import * as path35 from "node:path";
6496
+ import * as fs35 from "node:fs";
6497
+ import * as path36 from "node:path";
6315
6498
  function isBlockedDevicePath(filePath) {
6316
6499
  if (BLOCKED_DEVICE_PATHS.has(filePath)) return true;
6317
6500
  if (filePath.startsWith("/proc/") && (filePath.endsWith("/fd/0") || filePath.endsWith("/fd/1") || filePath.endsWith("/fd/2"))) return true;
6318
6501
  return false;
6319
6502
  }
6320
6503
  function checkReadLoop(filePath, offset, limit) {
6321
- const stat4 = fs34.statSync(filePath);
6504
+ const stat4 = fs35.statSync(filePath);
6322
6505
  const mtimeMs = stat4.mtimeMs;
6323
6506
  const prev = readHistory.get(filePath);
6324
6507
  if (prev && prev.offset === offset && prev.limit === limit && prev.mtimeMs === mtimeMs) {
@@ -6332,19 +6515,19 @@ function checkReadLoop(filePath, offset, limit) {
6332
6515
  return null;
6333
6516
  }
6334
6517
  function readFileContent(filePath) {
6335
- const fd = fs34.openSync(filePath, "r");
6518
+ const fd = fs35.openSync(filePath, "r");
6336
6519
  const bom = Buffer.alloc(2);
6337
- fs34.readSync(fd, bom, 0, 2, 0);
6338
- fs34.closeSync(fd);
6520
+ fs35.readSync(fd, bom, 0, 2, 0);
6521
+ fs35.closeSync(fd);
6339
6522
  let encoding = "utf8";
6340
6523
  if (bom[0] === 255 && bom[1] === 254) {
6341
6524
  encoding = "utf16le";
6342
6525
  }
6343
- const stat4 = fs34.statSync(filePath);
6526
+ const stat4 = fs35.statSync(filePath);
6344
6527
  if (stat4.size > MAX_FILE_SIZE) {
6345
6528
  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`);
6346
6529
  }
6347
- const raw = fs34.readFileSync(filePath, encoding);
6530
+ const raw = fs35.readFileSync(filePath, encoding);
6348
6531
  const content = raw.toString().replaceAll("\r\n", "\n");
6349
6532
  return { content, encoding };
6350
6533
  }
@@ -6486,11 +6669,11 @@ Usage:
6486
6669
  } catch (e) {
6487
6670
  return { content: e.message, isError: true };
6488
6671
  }
6489
- if (!fs34.existsSync(filePath)) {
6672
+ if (!fs35.existsSync(filePath)) {
6490
6673
  return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
6491
6674
  }
6492
- const stat4 = fs34.statSync(filePath);
6493
- const baseName = path35.basename(filePath).toUpperCase();
6675
+ const stat4 = fs35.statSync(filePath);
6676
+ const baseName = path36.basename(filePath).toUpperCase();
6494
6677
  if (BLOCKED_BASENAMES.has(baseName)) {
6495
6678
  return { content: `\u8BBE\u5907\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6: ${filePath}`, isError: true };
6496
6679
  }
@@ -6498,11 +6681,11 @@ Usage:
6498
6681
  return { content: `\u8BBE\u5907\u6587\u4EF6\u4F1A\u963B\u585E\u6216\u4EA7\u751F\u65E0\u9650\u8F93\u51FA: ${filePath}`, isError: true };
6499
6682
  }
6500
6683
  if (stat4.isDirectory()) {
6501
- const entries = fs34.readdirSync(filePath);
6684
+ const entries = fs35.readdirSync(filePath);
6502
6685
  const items = entries.map((e) => {
6503
- const full = path35.join(filePath, e);
6686
+ const full = path36.join(filePath, e);
6504
6687
  try {
6505
- const s = fs34.statSync(full);
6688
+ const s = fs35.statSync(full);
6506
6689
  return s.isDirectory() ? `${e}/` : e;
6507
6690
  } catch {
6508
6691
  return e;
@@ -6511,7 +6694,7 @@ Usage:
6511
6694
  return { content: `\u76EE\u5F55 (${entries.length} \u9879):
6512
6695
  ${items.join("\n")}` };
6513
6696
  }
6514
- const ext = path35.extname(filePath).toLowerCase();
6697
+ const ext = path36.extname(filePath).toLowerCase();
6515
6698
  if (BINARY_EXTENSIONS.has(ext)) {
6516
6699
  return { content: `\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6 (${ext}): ${filePath}`, isError: true };
6517
6700
  }
@@ -6559,22 +6742,22 @@ ${result}` : result };
6559
6742
 
6560
6743
  // src/tools/write.ts
6561
6744
  var write_exports = {};
6562
- import * as fs35 from "node:fs";
6563
- import * as path36 from "node:path";
6745
+ import * as fs36 from "node:fs";
6746
+ import * as path37 from "node:path";
6564
6747
  function isBlockedPath(filePath) {
6565
6748
  return BLOCKED_PATTERNS.some((p) => p.test(filePath));
6566
6749
  }
6567
6750
  function atomicWrite(filePath, content) {
6568
6751
  const tmpPath = filePath + ".tmp." + Date.now() + ".write";
6569
- fs35.writeFileSync(tmpPath, content, "utf-8");
6752
+ fs36.writeFileSync(tmpPath, content, "utf-8");
6570
6753
  try {
6571
- fs35.renameSync(tmpPath, filePath);
6754
+ fs36.renameSync(tmpPath, filePath);
6572
6755
  } catch (e) {
6573
6756
  try {
6574
- fs35.unlinkSync(tmpPath);
6757
+ fs36.unlinkSync(tmpPath);
6575
6758
  } catch {
6576
6759
  }
6577
- fs35.writeFileSync(filePath, content, "utf-8");
6760
+ fs36.writeFileSync(filePath, content, "utf-8");
6578
6761
  }
6579
6762
  }
6580
6763
  function simpleDiff(oldContent, newContent) {
@@ -6668,15 +6851,15 @@ Usage:
6668
6851
  }
6669
6852
  const rawContent = args2.content;
6670
6853
  const content = rawContent.replaceAll("\r\n", "\n");
6671
- if (fs35.existsSync(filePath) && fs35.statSync(filePath).isDirectory()) {
6854
+ if (fs36.existsSync(filePath) && fs36.statSync(filePath).isDirectory()) {
6672
6855
  return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
6673
6856
  }
6674
6857
  let oldContent = null;
6675
6858
  let isCreate = true;
6676
- if (fs35.existsSync(filePath)) {
6859
+ if (fs36.existsSync(filePath)) {
6677
6860
  isCreate = false;
6678
6861
  try {
6679
- oldContent = fs35.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
6862
+ oldContent = fs36.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
6680
6863
  } catch {
6681
6864
  isCreate = true;
6682
6865
  }
@@ -6692,10 +6875,10 @@ Usage:
6692
6875
  isError: true
6693
6876
  };
6694
6877
  }
6695
- const currentStat = fs35.statSync(filePath);
6878
+ const currentStat = fs36.statSync(filePath);
6696
6879
  const lastWriteTime = Math.floor(currentStat.mtimeMs);
6697
6880
  if (lastWriteTime > readState.timestamp) {
6698
- const currentContent = fs35.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
6881
+ const currentContent = fs36.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n");
6699
6882
  if (currentContent !== oldContent) {
6700
6883
  return {
6701
6884
  content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u5199\u5165: ${filePath}`,
@@ -6704,9 +6887,9 @@ Usage:
6704
6887
  }
6705
6888
  }
6706
6889
  }
6707
- const dir = path36.dirname(filePath);
6890
+ const dir = path37.dirname(filePath);
6708
6891
  try {
6709
- fs35.mkdirSync(dir, { recursive: true });
6892
+ fs36.mkdirSync(dir, { recursive: true });
6710
6893
  } catch (e) {
6711
6894
  return { content: `\u65E0\u6CD5\u521B\u5EFA\u76EE\u5F55: ${dir} \u2014 ${e.message}`, isError: true };
6712
6895
  }
@@ -6715,11 +6898,11 @@ Usage:
6715
6898
  } catch (e) {
6716
6899
  return { content: `\u5199\u5165\u5931\u8D25: ${e.message}`, isError: true };
6717
6900
  }
6718
- readFileState.set(filePath, { timestamp: fs35.statSync(filePath).mtimeMs });
6901
+ readFileState.set(filePath, { timestamp: fs36.statSync(filePath).mtimeMs });
6719
6902
  const action = isCreate ? "\u521B\u5EFA" : "\u66F4\u65B0";
6720
6903
  const lines = content.split("\n").length;
6721
6904
  const chars = content.length;
6722
- const stat4 = fs35.statSync(filePath);
6905
+ const stat4 = fs36.statSync(filePath);
6723
6906
  let diff = "";
6724
6907
  if (!isCreate && oldContent !== null) {
6725
6908
  diff = `
@@ -6739,8 +6922,8 @@ ${simpleDiff(oldContent, content)}`;
6739
6922
 
6740
6923
  // src/tools/edit.ts
6741
6924
  var edit_exports = {};
6742
- import * as fs36 from "node:fs";
6743
- import * as path37 from "node:path";
6925
+ import * as fs37 from "node:fs";
6926
+ import * as path38 from "node:path";
6744
6927
  function normalizeQuotes(str) {
6745
6928
  return str.replaceAll(LEFT_SINGLE_CURLY, "'").replaceAll(RIGHT_SINGLE_CURLY, "'").replaceAll(LEFT_DOUBLE_CURLY, '"').replaceAll(RIGHT_DOUBLE_CURLY, '"');
6746
6929
  }
@@ -6893,7 +7076,7 @@ Usage:
6893
7076
  }
6894
7077
  let fileContent = null;
6895
7078
  try {
6896
- const stat4 = fs36.statSync(filePath);
7079
+ const stat4 = fs37.statSync(filePath);
6897
7080
  if (stat4.isDirectory()) {
6898
7081
  return { content: `\u8DEF\u5F84\u662F\u76EE\u5F55\u4E0D\u662F\u6587\u4EF6: ${filePath}`, isError: true };
6899
7082
  }
@@ -6903,21 +7086,21 @@ Usage:
6903
7086
  } catch (e) {
6904
7087
  if (e.code === "ENOENT") {
6905
7088
  if (oldString === "") {
6906
- const dir = path37.dirname(filePath);
6907
- fs36.mkdirSync(dir, { recursive: true });
6908
- fs36.writeFileSync(filePath, newString, "utf-8");
6909
- readFileState.set(filePath, { timestamp: fs36.statSync(filePath).mtimeMs });
7089
+ const dir = path38.dirname(filePath);
7090
+ fs37.mkdirSync(dir, { recursive: true });
7091
+ fs37.writeFileSync(filePath, newString, "utf-8");
7092
+ readFileState.set(filePath, { timestamp: fs37.statSync(filePath).mtimeMs });
6910
7093
  return { content: `\u521B\u5EFA\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
6911
7094
  }
6912
7095
  return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
6913
7096
  }
6914
7097
  throw e;
6915
7098
  }
6916
- const rawContent = fs36.readFileSync(filePath, "utf-8");
7099
+ const rawContent = fs37.readFileSync(filePath, "utf-8");
6917
7100
  fileContent = rawContent.replaceAll("\r\n", "\n");
6918
7101
  if (oldString === "" && fileContent.trim() === "") {
6919
- fs36.writeFileSync(filePath, newString, "utf-8");
6920
- readFileState.set(filePath, { timestamp: fs36.statSync(filePath).mtimeMs });
7102
+ fs37.writeFileSync(filePath, newString, "utf-8");
7103
+ readFileState.set(filePath, { timestamp: fs37.statSync(filePath).mtimeMs });
6921
7104
  return { content: `\u5199\u5165\u7A7A\u6587\u4EF6: ${filePath} (${newString.split("\n").length} \u884C)` };
6922
7105
  }
6923
7106
  const readState = readFileState.get(filePath);
@@ -6927,11 +7110,11 @@ Usage:
6927
7110
  isError: true
6928
7111
  };
6929
7112
  }
6930
- const currentStat = fs36.statSync(filePath);
7113
+ const currentStat = fs37.statSync(filePath);
6931
7114
  const lastWriteTime = Math.floor(currentStat.mtimeMs);
6932
7115
  if (lastWriteTime > readState.timestamp) {
6933
7116
  if (fileContent !== rawContent.replaceAll("\r\n", "\n")) {
6934
- if (fileContent !== fs36.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
7117
+ if (fileContent !== fs37.readFileSync(filePath, "utf-8").replaceAll("\r\n", "\n")) {
6935
7118
  return {
6936
7119
  content: `\u6587\u4EF6\u5728\u8BFB\u53D6\u540E\u88AB\u4FEE\u6539\u3002\u8BF7\u5148\u91CD\u65B0\u8BFB\u53D6\u6587\u4EF6\u518D\u7F16\u8F91: ${filePath}`,
6937
7120
  isError: true
@@ -6963,8 +7146,8 @@ ${preview}
6963
7146
  const actualNewString = preserveQuoteStyle(oldString, actualOldString, newString);
6964
7147
  const diffView = generateEditDiff(fileContent, actualOldString, actualNewString);
6965
7148
  const newContent = applyEditToFile(fileContent, actualOldString, actualNewString, replaceAll);
6966
- fs36.writeFileSync(filePath, newContent, "utf-8");
6967
- readFileState.set(filePath, { timestamp: fs36.statSync(filePath).mtimeMs });
7149
+ fs37.writeFileSync(filePath, newContent, "utf-8");
7150
+ readFileState.set(filePath, { timestamp: fs37.statSync(filePath).mtimeMs });
6968
7151
  const strategy = actualOldString === oldString ? "\u7CBE\u786E\u5339\u914D" : "\u5F15\u53F7\u89C4\u8303\u5316\u5339\u914D";
6969
7152
  const count = replaceAll ? matchCount : 1;
6970
7153
  const diff = `${oldString.length}\u2192${newString.length}\u5B57\u7B26`;
@@ -6982,8 +7165,8 @@ ${diffView}`
6982
7165
 
6983
7166
  // src/tools/glob.ts
6984
7167
  var glob_exports = {};
6985
- import * as fs37 from "node:fs";
6986
- import * as path38 from "node:path";
7168
+ import * as fs38 from "node:fs";
7169
+ import * as path39 from "node:path";
6987
7170
  function globMatch(pattern, filename) {
6988
7171
  const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
6989
7172
  try {
@@ -7003,18 +7186,18 @@ function findFiles(dir, pattern, limit, baseDir) {
7003
7186
  }
7004
7187
  let entries;
7005
7188
  try {
7006
- entries = fs37.readdirSync(currentDir, { withFileTypes: true });
7189
+ entries = fs38.readdirSync(currentDir, { withFileTypes: true });
7007
7190
  } catch {
7008
7191
  return;
7009
7192
  }
7010
7193
  for (const entry of entries) {
7011
7194
  if (truncated) return;
7012
- const fullPath = path38.join(currentDir, entry.name);
7195
+ const fullPath = path39.join(currentDir, entry.name);
7013
7196
  if (entry.isDirectory()) {
7014
7197
  if (VCS_DIRS.has(entry.name)) continue;
7015
7198
  walk(fullPath);
7016
7199
  } else if (entry.isFile()) {
7017
- const relativePath = path38.relative(baseDir, fullPath).replace(/\\/g, "/");
7200
+ const relativePath = path39.relative(baseDir, fullPath).replace(/\\/g, "/");
7018
7201
  const patternsToTry = [pattern];
7019
7202
  if (pattern.startsWith("**/")) {
7020
7203
  patternsToTry.push(pattern.slice(3));
@@ -7024,7 +7207,7 @@ function findFiles(dir, pattern, limit, baseDir) {
7024
7207
  );
7025
7208
  if (matched) {
7026
7209
  try {
7027
- const stat4 = fs37.statSync(fullPath);
7210
+ const stat4 = fs38.statSync(fullPath);
7028
7211
  results.push({ path: fullPath, mtimeMs: stat4.mtimeMs });
7029
7212
  } catch {
7030
7213
  }
@@ -7041,7 +7224,7 @@ function findFiles(dir, pattern, limit, baseDir) {
7041
7224
  };
7042
7225
  }
7043
7226
  function toRelativePath(absolutePath, cwd) {
7044
- if (absolutePath.startsWith(cwd + path38.sep)) {
7227
+ if (absolutePath.startsWith(cwd + path39.sep)) {
7045
7228
  return absolutePath.slice(cwd.length + 1);
7046
7229
  }
7047
7230
  return absolutePath;
@@ -7074,10 +7257,10 @@ var init_glob = __esm({
7074
7257
  const searchPath = args2.path ? resolvePath(args2.path, ctx.workspace) : ctx.workspace;
7075
7258
  const pattern = args2.pattern;
7076
7259
  const limit = args2.limit || DEFAULT_LIMIT;
7077
- if (!fs37.existsSync(searchPath)) {
7260
+ if (!fs38.existsSync(searchPath)) {
7078
7261
  return { content: `\u76EE\u5F55\u4E0D\u5B58\u5728: ${searchPath}`, isError: true };
7079
7262
  }
7080
- if (!fs37.statSync(searchPath).isDirectory()) {
7263
+ if (!fs38.statSync(searchPath).isDirectory()) {
7081
7264
  return { content: `\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${searchPath}`, isError: true };
7082
7265
  }
7083
7266
  const start = Date.now();
@@ -7102,7 +7285,7 @@ ${filenames.join("\n")}${truncatedNote}`
7102
7285
  // src/tools/grep.ts
7103
7286
  var grep_exports = {};
7104
7287
  import { execFile as execFile2 } from "node:child_process";
7105
- import * as path39 from "node:path";
7288
+ import * as path40 from "node:path";
7106
7289
  function ripGrep(args2, searchPath, signal) {
7107
7290
  return new Promise((resolve10) => {
7108
7291
  const fullArgs = [...args2, searchPath];
@@ -7134,7 +7317,7 @@ function applyHeadLimit(items, limit, offset = 0) {
7134
7317
  };
7135
7318
  }
7136
7319
  function toRelativePath2(absolutePath, cwd) {
7137
- if (absolutePath.startsWith(cwd + path39.sep)) {
7320
+ if (absolutePath.startsWith(cwd + path40.sep)) {
7138
7321
  return absolutePath.slice(cwd.length + 1);
7139
7322
  }
7140
7323
  if (absolutePath.startsWith(cwd)) {
@@ -8200,7 +8383,7 @@ function setSwarmsConfig(config2) {
8200
8383
  _config2 = config2;
8201
8384
  }
8202
8385
  function isAgentSwarmsEnabled() {
8203
- const feat = _config2?.profile?.features?.agentTeams;
8386
+ const feat = getFeature("agentTeams");
8204
8387
  if (feat === true || feat?.enabled === true) {
8205
8388
  return true;
8206
8389
  }
@@ -8217,6 +8400,7 @@ var _config2;
8217
8400
  var init_agentSwarmsEnabled = __esm({
8218
8401
  "src/swarm/agentSwarmsEnabled.ts"() {
8219
8402
  "use strict";
8403
+ init_features();
8220
8404
  _config2 = null;
8221
8405
  }
8222
8406
  });
@@ -9802,8 +9986,8 @@ var init_web_fetch = __esm({
9802
9986
  });
9803
9987
 
9804
9988
  // src/cron/tasks.ts
9805
- import fs38 from "node:fs";
9806
- import path40 from "node:path";
9989
+ import fs39 from "node:fs";
9990
+ import path41 from "node:path";
9807
9991
  import crypto5 from "node:crypto";
9808
9992
  function getStorageDir() {
9809
9993
  return storageDir;
@@ -9811,14 +9995,14 @@ function getStorageDir() {
9811
9995
  async function withFileLock(lockPath2, fn) {
9812
9996
  for (let attempt = 0; attempt < LOCK_RETRY_COUNT; attempt++) {
9813
9997
  try {
9814
- fs38.mkdirSync(lockPath2, { recursive: false });
9998
+ fs39.mkdirSync(lockPath2, { recursive: false });
9815
9999
  break;
9816
10000
  } catch (err) {
9817
10001
  if (err.code !== "EEXIST") throw err;
9818
10002
  try {
9819
- const stat4 = fs38.statSync(lockPath2);
10003
+ const stat4 = fs39.statSync(lockPath2);
9820
10004
  if (Date.now() - stat4.mtimeMs > LOCK_STALE_THRESHOLD_MS) {
9821
- fs38.rmSync(lockPath2, { recursive: true, force: true });
10005
+ fs39.rmSync(lockPath2, { recursive: true, force: true });
9822
10006
  continue;
9823
10007
  }
9824
10008
  } catch {
@@ -9834,22 +10018,22 @@ async function withFileLock(lockPath2, fn) {
9834
10018
  return fn();
9835
10019
  } finally {
9836
10020
  try {
9837
- fs38.rmSync(lockPath2, { recursive: true, force: true });
10021
+ fs39.rmSync(lockPath2, { recursive: true, force: true });
9838
10022
  } catch {
9839
10023
  }
9840
10024
  }
9841
10025
  }
9842
10026
  function atomicWriteJSON(filePath, data) {
9843
10027
  const tmpPath = filePath + ".tmp";
9844
- fs38.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
9845
- fs38.renameSync(tmpPath, filePath);
10028
+ fs39.writeFileSync(tmpPath, JSON.stringify(data, null, 2), "utf-8");
10029
+ fs39.renameSync(tmpPath, filePath);
9846
10030
  }
9847
10031
  function readTasksFromDisk() {
9848
- if (!tasksFilePath || !fs38.existsSync(tasksFilePath)) {
10032
+ if (!tasksFilePath || !fs39.existsSync(tasksFilePath)) {
9849
10033
  return [];
9850
10034
  }
9851
10035
  try {
9852
- const raw = fs38.readFileSync(tasksFilePath, "utf-8");
10036
+ const raw = fs39.readFileSync(tasksFilePath, "utf-8");
9853
10037
  const store = JSON.parse(raw);
9854
10038
  return store.tasks ?? [];
9855
10039
  } catch (err) {
@@ -9858,7 +10042,7 @@ function readTasksFromDisk() {
9858
10042
  }
9859
10043
  }
9860
10044
  async function writeTasksToDisk(tasks2) {
9861
- const lockPath2 = path40.join(storageDir, "tasks.json.lock");
10045
+ const lockPath2 = path41.join(storageDir, "tasks.json.lock");
9862
10046
  await withFileLock(lockPath2, () => {
9863
10047
  const store = {
9864
10048
  version: 1,
@@ -9870,9 +10054,9 @@ async function writeTasksToDisk(tasks2) {
9870
10054
  }
9871
10055
  function initTaskStore(dir) {
9872
10056
  storageDir = dir;
9873
- tasksFilePath = path40.join(dir, "tasks.json");
9874
- if (!fs38.existsSync(dir)) {
9875
- fs38.mkdirSync(dir, { recursive: true });
10057
+ tasksFilePath = path41.join(dir, "tasks.json");
10058
+ if (!fs39.existsSync(dir)) {
10059
+ fs39.mkdirSync(dir, { recursive: true });
9876
10060
  }
9877
10061
  const tasks2 = readTasksFromDisk();
9878
10062
  console.log(`[cron] Task store initialized: ${dir} (${tasks2.length} tasks loaded)`);
@@ -10121,12 +10305,12 @@ async function executeAndDeliver(task, now, deps) {
10121
10305
  if (promptText.startsWith("@")) {
10122
10306
  let filePath = promptText.slice(1).trim();
10123
10307
  try {
10124
- const fs42 = await import("fs");
10125
- const path45 = await import("path");
10126
- if (!path45.isAbsolute(filePath)) {
10127
- filePath = path45.join(deps.sessions["config"].stateDir, filePath);
10308
+ const fs43 = await import("fs");
10309
+ const path46 = await import("path");
10310
+ if (!path46.isAbsolute(filePath)) {
10311
+ filePath = path46.join(deps.sessions["config"].stateDir, filePath);
10128
10312
  }
10129
- promptText = fs42.readFileSync(filePath, "utf-8");
10313
+ promptText = fs43.readFileSync(filePath, "utf-8");
10130
10314
  console.log(`[cron] Loaded prompt from ${filePath} (${promptText.length} chars)`);
10131
10315
  } catch (err) {
10132
10316
  throw new Error(`Prompt file not found: ${filePath}: ${err.message}`);
@@ -10153,16 +10337,16 @@ async function executeAndDeliver(task, now, deps) {
10153
10337
  let finalResult = result;
10154
10338
  if (task.postProcess) {
10155
10339
  try {
10156
- const path45 = await import("path");
10157
- const fs42 = await import("fs");
10340
+ const path46 = await import("path");
10341
+ const fs43 = await import("fs");
10158
10342
  let scriptPath = task.postProcess;
10159
- if (!path45.isAbsolute(scriptPath)) {
10160
- scriptPath = path45.join(deps.sessions["config"].stateDir, scriptPath);
10343
+ if (!path46.isAbsolute(scriptPath)) {
10344
+ scriptPath = path46.join(deps.sessions["config"].stateDir, scriptPath);
10161
10345
  }
10162
- const resultsDirTmp = path45.join(getStorageDir(), "results");
10163
- fs42.mkdirSync(resultsDirTmp, { recursive: true });
10164
- const inputFile = path45.join(resultsDirTmp, `${task.id}.input.txt`);
10165
- fs42.writeFileSync(inputFile, result, "utf-8");
10346
+ const resultsDirTmp = path46.join(getStorageDir(), "results");
10347
+ fs43.mkdirSync(resultsDirTmp, { recursive: true });
10348
+ const inputFile = path46.join(resultsDirTmp, `${task.id}.input.txt`);
10349
+ fs43.writeFileSync(inputFile, result, "utf-8");
10166
10350
  const { execFile: execFile3 } = await import("child_process");
10167
10351
  await new Promise((resolve10) => {
10168
10352
  execFile3("python", [scriptPath, "main", "--file", inputFile], {
@@ -10189,12 +10373,12 @@ async function executeAndDeliver(task, now, deps) {
10189
10373
  }
10190
10374
  }
10191
10375
  try {
10192
- const fs42 = await import("fs");
10193
- const path45 = await import("path");
10194
- const resultsDir = path45.join(getStorageDir(), "results");
10195
- fs42.mkdirSync(resultsDir, { recursive: true });
10196
- const resultFile = path45.join(resultsDir, `${task.id}.json`);
10197
- fs42.writeFileSync(resultFile, JSON.stringify({
10376
+ const fs43 = await import("fs");
10377
+ const path46 = await import("path");
10378
+ const resultsDir = path46.join(getStorageDir(), "results");
10379
+ fs43.mkdirSync(resultsDir, { recursive: true });
10380
+ const resultFile = path46.join(resultsDir, `${task.id}.json`);
10381
+ fs43.writeFileSync(resultFile, JSON.stringify({
10198
10382
  taskId: task.id,
10199
10383
  description: task.description,
10200
10384
  executedAt: now.toISOString(),
@@ -10440,30 +10624,30 @@ function registerCronTools() {
10440
10624
  }
10441
10625
  },
10442
10626
  handler: async (args2) => {
10443
- const fs42 = await import("fs");
10444
- const path45 = await import("path");
10445
- const resultsDir = path45.join(getStorageDir(), "results");
10446
- if (!fs42.existsSync(resultsDir)) {
10627
+ const fs43 = await import("fs");
10628
+ const path46 = await import("path");
10629
+ const resultsDir = path46.join(getStorageDir(), "results");
10630
+ if (!fs43.existsSync(resultsDir)) {
10447
10631
  return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
10448
10632
  }
10449
10633
  if (args2.task_id) {
10450
- const file = path45.join(resultsDir, `${args2.task_id}.json`);
10451
- if (!fs42.existsSync(file)) {
10634
+ const file = path46.join(resultsDir, `${args2.task_id}.json`);
10635
+ if (!fs43.existsSync(file)) {
10452
10636
  return { content: `\u4EFB\u52A1 ${args2.task_id} \u6682\u65E0\u6267\u884C\u7ED3\u679C`, isError: true };
10453
10637
  }
10454
- const data = JSON.parse(fs42.readFileSync(file, "utf-8"));
10638
+ const data = JSON.parse(fs43.readFileSync(file, "utf-8"));
10455
10639
  return { content: `## ${data.description}
10456
10640
  \u6267\u884C\u65F6\u95F4: ${data.executedAt}
10457
10641
  \u7B2C${data.runCount}\u6B21\u6267\u884C
10458
10642
 
10459
10643
  ${data.result}` };
10460
10644
  }
10461
- const files = fs42.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
10645
+ const files = fs43.readdirSync(resultsDir).filter((f) => f.endsWith(".json"));
10462
10646
  if (files.length === 0) {
10463
10647
  return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
10464
10648
  }
10465
10649
  const results = files.map((f) => {
10466
- const data = JSON.parse(fs42.readFileSync(path45.join(resultsDir, f), "utf-8"));
10650
+ const data = JSON.parse(fs43.readFileSync(path46.join(resultsDir, f), "utf-8"));
10467
10651
  return `### ${data.description} (${data.taskId.slice(0, 8)})
10468
10652
  \u6267\u884C: ${data.executedAt} | \u7B2C${data.runCount}\u6B21
10469
10653
  ${data.result.slice(0, 500)}${data.result.length > 500 ? "..." : ""}`;
@@ -10667,7 +10851,7 @@ async function setupFeatures(features, licensedFeatures) {
10667
10851
  }
10668
10852
  }
10669
10853
  var builtInFeatures, customFeatures;
10670
- var init_features = __esm({
10854
+ var init_features2 = __esm({
10671
10855
  "src/tools/features.ts"() {
10672
10856
  "use strict";
10673
10857
  builtInFeatures = [
@@ -10845,8 +11029,8 @@ __export(license_exports, {
10845
11029
  resetLicenseCache: () => resetLicenseCache
10846
11030
  });
10847
11031
  import * as crypto6 from "node:crypto";
10848
- import * as fs39 from "node:fs";
10849
- import * as path41 from "node:path";
11032
+ import * as fs40 from "node:fs";
11033
+ import * as path42 from "node:path";
10850
11034
  function loadLicense(stateDir, devMode) {
10851
11035
  if (_licenseChecked) return _cachedLicense;
10852
11036
  _licenseChecked = true;
@@ -10859,13 +11043,13 @@ function loadLicense(stateDir, devMode) {
10859
11043
  _cachedLicense = allActive;
10860
11044
  return allActive;
10861
11045
  }
10862
- const licensePath = path41.join(stateDir, "license.json");
10863
- if (!fs39.existsSync(licensePath)) {
11046
+ const licensePath = path42.join(stateDir, "license.json");
11047
+ if (!fs40.existsSync(licensePath)) {
10864
11048
  console.log("[license] No license.json found, running basic engine only");
10865
11049
  return null;
10866
11050
  }
10867
11051
  try {
10868
- const raw = fs39.readFileSync(licensePath, "utf-8");
11052
+ const raw = fs40.readFileSync(licensePath, "utf-8");
10869
11053
  const license = JSON.parse(raw);
10870
11054
  const { signature, ...payload } = license;
10871
11055
  if (!signature) {
@@ -10915,12 +11099,12 @@ function isFeatureLicensed(featureId) {
10915
11099
  return f?.active === true;
10916
11100
  }
10917
11101
  function getLicenseStatus(stateDir) {
10918
- const licensePath = path41.join(stateDir, "license.json");
10919
- if (!fs39.existsSync(licensePath)) {
11102
+ const licensePath = path42.join(stateDir, "license.json");
11103
+ if (!fs40.existsSync(licensePath)) {
10920
11104
  return { licensed: false, features: {} };
10921
11105
  }
10922
11106
  try {
10923
- const raw = fs39.readFileSync(licensePath, "utf-8");
11107
+ const raw = fs40.readFileSync(licensePath, "utf-8");
10924
11108
  const license = JSON.parse(raw);
10925
11109
  const active = loadLicense(stateDir);
10926
11110
  return {
@@ -10979,8 +11163,8 @@ var manager_exports = {};
10979
11163
  __export(manager_exports, {
10980
11164
  McpManager: () => McpManager
10981
11165
  });
10982
- import * as fs40 from "node:fs";
10983
- import * as path42 from "node:path";
11166
+ import * as fs41 from "node:fs";
11167
+ import * as path43 from "node:path";
10984
11168
  import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
10985
11169
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
10986
11170
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -11013,12 +11197,12 @@ function convertInputSchema(inputSchema) {
11013
11197
  }
11014
11198
  function persistBinary(base64Data, mimeType, persistId) {
11015
11199
  const ext = mimeType?.split("/")[1] || "bin";
11016
- const dir = path42.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
11017
- fs40.mkdirSync(dir, { recursive: true });
11018
- const filepath = path42.join(dir, `${persistId}.${ext}`);
11200
+ const dir = path43.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
11201
+ fs41.mkdirSync(dir, { recursive: true });
11202
+ const filepath = path43.join(dir, `${persistId}.${ext}`);
11019
11203
  try {
11020
11204
  const buf = Buffer.from(base64Data, "base64");
11021
- fs40.writeFileSync(filepath, buf);
11205
+ fs41.writeFileSync(filepath, buf);
11022
11206
  return { filepath, size: buf.length };
11023
11207
  } catch (err) {
11024
11208
  return { error: err.message };
@@ -11352,7 +11536,7 @@ __export(resources_exports, {
11352
11536
  registerMcpResourceTools: () => registerMcpResourceTools,
11353
11537
  unregisterMcpResourceTools: () => unregisterMcpResourceTools
11354
11538
  });
11355
- import * as path43 from "node:path";
11539
+ import * as path44 from "node:path";
11356
11540
  function registerMcpResourceTools(manager) {
11357
11541
  mcpManagerRef = manager;
11358
11542
  registry.register(listResourcesTool);
@@ -11370,7 +11554,7 @@ var init_resources = __esm({
11370
11554
  "use strict";
11371
11555
  init_registry();
11372
11556
  MAX_RESULT_CHARS2 = 1e5;
11373
- MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path43.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
11557
+ MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path44.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
11374
11558
  MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
11375
11559
  MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
11376
11560
  mcpManagerRef = null;
@@ -11539,10 +11723,10 @@ function ensureLoaded(workspace, configIds) {
11539
11723
  if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
11540
11724
  }
11541
11725
  }
11542
- const path45 = join38(workspace, ".reply-blocklist.json");
11726
+ const path46 = join38(workspace, ".reply-blocklist.json");
11543
11727
  try {
11544
- if (existsSync24(path45)) {
11545
- const raw = readFileSync26(path45, "utf-8");
11728
+ if (existsSync24(path46)) {
11729
+ const raw = readFileSync26(path46, "utf-8");
11546
11730
  const parsed = JSON.parse(raw);
11547
11731
  if (parsed.blockedUserIds) {
11548
11732
  for (const id of parsed.blockedUserIds) {
@@ -11558,9 +11742,9 @@ function ensureLoaded(workspace, configIds) {
11558
11742
  loaded = true;
11559
11743
  }
11560
11744
  function save(workspace) {
11561
- const path45 = join38(workspace, ".reply-blocklist.json");
11745
+ const path46 = join38(workspace, ".reply-blocklist.json");
11562
11746
  try {
11563
- writeFileSync16(path45, JSON.stringify(state, null, 2), "utf-8");
11747
+ writeFileSync16(path46, JSON.stringify(state, null, 2), "utf-8");
11564
11748
  } catch (err) {
11565
11749
  console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
11566
11750
  }
@@ -12200,6 +12384,7 @@ function loadDisplayConfig(raw) {
12200
12384
  }
12201
12385
 
12202
12386
  // src/config/loader.ts
12387
+ init_features();
12203
12388
  function parseModelRef(ref) {
12204
12389
  const idx = ref.indexOf("/");
12205
12390
  if (idx <= 0 || idx === ref.length - 1) {
@@ -12293,6 +12478,13 @@ function loadConfig(configPath2) {
12293
12478
  const stateDir = raw.stateDir || process.env.ENGINE_STATE_DIR || path.resolve(".engine");
12294
12479
  const workspace = process.env.ENGINE_WORKSPACE || agentDefaults.workspace || path.join(stateDir, "workspace");
12295
12480
  const mediaDir = raw.mediaDir || path.join(stateDir, "media", "inbound");
12481
+ if (!agentDefaults.features) agentDefaults.features = {};
12482
+ for (const [k, v] of Object.entries(FEATURE_DEFAULTS)) {
12483
+ if (agentDefaults.features[k] === void 0) {
12484
+ ;
12485
+ agentDefaults.features[k] = v;
12486
+ }
12487
+ }
12296
12488
  const profile = {
12297
12489
  id: process.env.ENGINE_AGENT || "default",
12298
12490
  name: agentDefaults.name || "AI Assistant",
@@ -12300,27 +12492,8 @@ function loadConfig(configPath2) {
12300
12492
  workspace,
12301
12493
  soul: agentDefaults.soul,
12302
12494
  agents: agentDefaults.agents,
12303
- features: {
12304
- filesystem: true,
12305
- shell: true,
12306
- memory: true,
12307
- "topic-extract": true,
12308
- "topic-recall": true,
12309
- "session-memory": true,
12310
- todo: true,
12311
- cron: false,
12312
- voice: false,
12313
- selfie: false,
12314
- eyes: false,
12315
- calendar: false,
12316
- webSearch: true,
12317
- webFetch: true,
12318
- agentTeams: true,
12319
- processOutput: "verbose",
12320
- interrupt: "command",
12321
- debounceMs: 5e3,
12322
- ...agentDefaults.features || {}
12323
- },
12495
+ features: agentDefaults.features,
12496
+ // canonical:profile.features 即 agents.defaults.features(同一对象,过渡兼容)
12324
12497
  channels: agentDefaults.channels || [],
12325
12498
  extensions: agentDefaults.extensions,
12326
12499
  maxTurns: agentDefaults.maxTurns,
@@ -12429,8 +12602,8 @@ function configSummary(config2) {
12429
12602
  }
12430
12603
 
12431
12604
  // src/engine-startup.ts
12432
- import * as path44 from "node:path";
12433
- import * as fs41 from "node:fs";
12605
+ import * as path45 from "node:path";
12606
+ import * as fs42 from "node:fs";
12434
12607
  import { fileURLToPath } from "node:url";
12435
12608
 
12436
12609
  // src/pid-lock.ts
@@ -12544,6 +12717,7 @@ function getActiveQueryEngine(sessionId) {
12544
12717
 
12545
12718
  // src/engine-startup.ts
12546
12719
  init_live();
12720
+ init_features();
12547
12721
 
12548
12722
  // src/services/withRetry.ts
12549
12723
  import { ProxyAgent } from "undici";
@@ -13804,13 +13978,13 @@ var DiscordAdapter = class _DiscordAdapter {
13804
13978
  }
13805
13979
  /** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
13806
13980
  async sendFile(target, message, attachment) {
13807
- const fs42 = await import("node:fs");
13808
- const path45 = await import("node:path");
13809
- if (!fs42.existsSync(attachment.path)) {
13981
+ const fs43 = await import("node:fs");
13982
+ const path46 = await import("node:path");
13983
+ if (!fs43.existsSync(attachment.path)) {
13810
13984
  throw new Error(`File not found: ${attachment.path}`);
13811
13985
  }
13812
- const filename = attachment.filename || path45.basename(attachment.path);
13813
- const fileBuffer = fs42.readFileSync(attachment.path);
13986
+ const filename = attachment.filename || path46.basename(attachment.path);
13987
+ const fileBuffer = fs43.readFileSync(attachment.path);
13814
13988
  const filePayload = {
13815
13989
  attachment: fileBuffer,
13816
13990
  name: filename
@@ -14250,13 +14424,13 @@ var FeishuAdapter = class _FeishuAdapter {
14250
14424
  }
14251
14425
  /** 发送媒体附件(图片/文件) */
14252
14426
  async sendFile(target, message, attachment) {
14253
- const fs42 = await import("node:fs");
14254
- const path45 = await import("node:path");
14255
- if (!fs42.existsSync(attachment.path)) {
14427
+ const fs43 = await import("node:fs");
14428
+ const path46 = await import("node:path");
14429
+ if (!fs43.existsSync(attachment.path)) {
14256
14430
  throw new Error(`File not found: ${attachment.path}`);
14257
14431
  }
14258
- const filename = attachment.filename || path45.basename(attachment.path);
14259
- const fileBuffer = fs42.readFileSync(attachment.path);
14432
+ const filename = attachment.filename || path46.basename(attachment.path);
14433
+ const fileBuffer = fs43.readFileSync(attachment.path);
14260
14434
  const receiveIdType = target.startsWith("ou_") ? "open_id" : "chat_id";
14261
14435
  const mimeType = attachment.mimeType || "application/octet-stream";
14262
14436
  if (mimeType.startsWith("image/")) {
@@ -17004,7 +17178,7 @@ function entryToSessionMessage(entry) {
17004
17178
  if (role === "user") {
17005
17179
  const text = extractText2(m.content);
17006
17180
  if (text !== null) {
17007
- return { role: "user", content: text, _raw: entry.raw };
17181
+ return { role: "user", content: text, timestamp: entry.timestamp, _raw: entry.raw };
17008
17182
  }
17009
17183
  return null;
17010
17184
  } else if (role === "assistant") {
@@ -17027,6 +17201,7 @@ function entryToSessionMessage(entry) {
17027
17201
  const result = {
17028
17202
  role: "assistant",
17029
17203
  content: textContent,
17204
+ timestamp: entry.timestamp,
17030
17205
  _raw: entry.raw
17031
17206
  };
17032
17207
  if (toolCalls.length > 0) {
@@ -17038,6 +17213,7 @@ function entryToSessionMessage(entry) {
17038
17213
  return {
17039
17214
  role: "tool",
17040
17215
  content: text || "",
17216
+ timestamp: entry.timestamp,
17041
17217
  tool_call_id: m.toolCallId,
17042
17218
  _raw: entry.raw
17043
17219
  };
@@ -17451,16 +17627,22 @@ var SessionManager = class {
17451
17627
  continue;
17452
17628
  }
17453
17629
  if (m.role === "user") {
17454
- allMessages.push(msg.user(m.content));
17630
+ const u = msg.user(m.content);
17631
+ if (m.timestamp) u.timestamp = m.timestamp;
17632
+ allMessages.push(u);
17455
17633
  } else if (m.role === "assistant") {
17456
17634
  const toolCalls = m.tool_calls?.map((tc) => ({
17457
17635
  id: tc.id,
17458
17636
  type: "function",
17459
17637
  function: { name: tc.function.name, arguments: tc.function.arguments }
17460
17638
  }));
17461
- allMessages.push(msg.assistant(m.content, toolCalls));
17639
+ const a = msg.assistant(m.content, toolCalls);
17640
+ if (m.timestamp) a.timestamp = m.timestamp;
17641
+ allMessages.push(a);
17462
17642
  } else if (m.role === "tool") {
17463
- allMessages.push(msg.tool(m.tool_call_id || "", m.content));
17643
+ const t = msg.tool(m.tool_call_id || "", m.content);
17644
+ if (m.timestamp) t.timestamp = m.timestamp;
17645
+ allMessages.push(t);
17464
17646
  }
17465
17647
  }
17466
17648
  }
@@ -18185,6 +18367,8 @@ var MessageQueue = class {
18185
18367
  // src/handle-query.ts
18186
18368
  init_types();
18187
18369
  init_attachments();
18370
+ init_live();
18371
+ init_features();
18188
18372
  init_task_manager();
18189
18373
 
18190
18374
  // src/prompt.ts
@@ -18965,7 +19149,7 @@ ${ep.episode || ep.summary}`,
18965
19149
  init_paths();
18966
19150
  import { readFileSync as readFileSync15, existsSync as existsSync12 } from "node:fs";
18967
19151
  import { join as join20, resolve as resolve6 } from "node:path";
18968
- import * as path13 from "node:path";
19152
+ import * as path14 from "node:path";
18969
19153
  var contactMap = null;
18970
19154
  var externalChanWhitelist = null;
18971
19155
  function loadContactMap(workspace) {
@@ -19033,18 +19217,18 @@ function truncate(s, maxLen) {
19033
19217
  }
19034
19218
  var externalChanRulesCache = null;
19035
19219
  function loadExternalChanRules(workspace) {
19036
- const path45 = join20(workspace, "prompts", "external-chan-rules.md");
19037
- if (externalChanRulesCache && externalChanRulesCache.path === path45) return externalChanRulesCache;
19220
+ const path46 = join20(workspace, "prompts", "external-chan-rules.md");
19221
+ if (externalChanRulesCache && externalChanRulesCache.path === path46) return externalChanRulesCache;
19038
19222
  let content = "";
19039
- if (existsSync12(path45)) {
19223
+ if (existsSync12(path46)) {
19040
19224
  try {
19041
- content = readFileSync15(path45, "utf-8").trim();
19225
+ content = readFileSync15(path46, "utf-8").trim();
19042
19226
  } catch (e) {
19043
19227
  console.warn(`[external-chan-rules] Failed to load: ${e}`);
19044
19228
  }
19045
19229
  }
19046
- externalChanRulesCache = { path: path45, content };
19047
- console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path45}`);
19230
+ externalChanRulesCache = { path: path46, content };
19231
+ console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path46}`);
19048
19232
  return externalChanRulesCache;
19049
19233
  }
19050
19234
  function getExternalChanRulesBlock(inboundMeta, workspace) {
@@ -19072,7 +19256,7 @@ async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget
19072
19256
  }
19073
19257
  async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
19074
19258
  const { engine, sessions, channelManager, workspace, providerId, providerApi, model } = deps;
19075
- const features = deps.features || {};
19259
+ const topics = liveConfig.get("topics") || {};
19076
19260
  const preQueryAbort = new AbortController();
19077
19261
  engine.setPreQueryAbort(preQueryAbort);
19078
19262
  let history = sessions.getHistory(sessionId);
@@ -19081,7 +19265,7 @@ async function handleQueryInner(text, sessionId, channelName, cb, deps, channelT
19081
19265
  if (restored.length > 0) {
19082
19266
  history = restored;
19083
19267
  sessions.setHistory(sessionId, history);
19084
- if (deps.topics?.restoreRecall === false) {
19268
+ if (topics?.restoreRecall === false) {
19085
19269
  let stripped = 0;
19086
19270
  for (let i = history.length - 1; i >= 0; i--) {
19087
19271
  const m = history[i];
@@ -19271,7 +19455,7 @@ ${text}` : text });
19271
19455
  // 对齐 CC: fork subagent 继承父对话历史
19272
19456
  parentSystemPrompt: deps.systemPrompt,
19273
19457
  // 对齐 CC: fork 共享 prompt cache
19274
- features: deps.features,
19458
+ features: liveConfig.get("agents.defaults.features"),
19275
19459
  // engine config features(AgentTool 读 agentTool.showProgress)
19276
19460
  channelTarget: channelTarget ?? "",
19277
19461
  // 回复目标(Discord channel ID / user ID)
@@ -19387,13 +19571,13 @@ ${text}` : text });
19387
19571
  engine.setExternalAbort(queryAbortController);
19388
19572
  setActiveQueryEngine(sessionId, engine);
19389
19573
  const shouldSkipRecall = skipRecall ?? channelName === "cron";
19390
- if (features["topic-recall"] !== false && !shouldSkipRecall) {
19574
+ if (getFeature("topic-recall") !== false && !shouldSkipRecall) {
19391
19575
  try {
19392
19576
  const memoryDir = getAutoMemPath(workspace);
19393
19577
  const provider = deps.engine.getProvider();
19394
19578
  const surfacedHistory = collectSurfacedMemories(history);
19395
19579
  const cumulativePaths = sessions.getRestoredRecallPaths(sessionId);
19396
- const doRestore = deps.topics?.restoreRecall === true;
19580
+ const doRestore = topics?.restoreRecall === true;
19397
19581
  const surfaced = doRestore ? { paths: /* @__PURE__ */ new Set([...surfacedHistory.paths, ...cumulativePaths]) } : surfacedHistory;
19398
19582
  console.log(`[handle-query] surfaced: history=${surfacedHistory.paths.size} cumulative=${cumulativePaths.size} merged=${surfaced.paths.size} restoreRecall=${doRestore}`);
19399
19583
  if (doRestore) {
@@ -19432,7 +19616,7 @@ ${text}` : text });
19432
19616
  console.log(`[handle-query] Memory recall starting: dir=${memoryDir} query="${(typeof text === "string" ? text : "[content blocks]").slice(0, 50)}..." alreadySurfaced=${surfaced.paths.size}`);
19433
19617
  const textForMemory = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
19434
19618
  const recallP = deps.recallProvider;
19435
- const recallMode = deps.topics?.recall?.mode || "llm";
19619
+ const recallMode = topics?.recall?.mode || "llm";
19436
19620
  let relevantMemories;
19437
19621
  if (recallMode === "everos") {
19438
19622
  const everosCfg = deps?.everosCfg;
@@ -19447,7 +19631,7 @@ ${text}` : text });
19447
19631
  rerankApiKey: everosCfg.rerank?.apiKey,
19448
19632
  rerankModel: everosCfg.rerank?.model,
19449
19633
  rerankProvider: everosCfg.rerank?.provider,
19450
- minScore: deps.topics?.recall?.minScore
19634
+ minScore: topics?.recall?.minScore
19451
19635
  } : void 0
19452
19636
  );
19453
19637
  } else if (recallMode === "vector") {
@@ -19465,7 +19649,7 @@ ${text}` : text });
19465
19649
  queryAbortController.signal,
19466
19650
  surfaced.paths,
19467
19651
  recallP?.disableThinking,
19468
- deps.topics?.maxScanFiles
19652
+ topics?.maxScanFiles
19469
19653
  );
19470
19654
  }
19471
19655
  console.log(`[handle-query] Memory recall result: ${relevantMemories.length} memories found: ${relevantMemories.map((m) => m.path.split(/[/\\]/).pop()).join(", ")}`);
@@ -19649,7 +19833,7 @@ ${text}` : text });
19649
19833
  }
19650
19834
  }
19651
19835
  sessions.setHistory(sessionId, history);
19652
- if (features["topic-extract"] && sessionId === deps.sessions.getSessionId("scope:main")) {
19836
+ if (getFeature("topic-extract") === true && sessionId === deps.sessions.getSessionId("scope:main")) {
19653
19837
  try {
19654
19838
  const { createMemoryExtractor: createMemoryExtractor2 } = await Promise.resolve().then(() => (init_extractMemories(), extractMemories_exports));
19655
19839
  const extractor = createMemoryExtractor2(workspace, true);
@@ -19664,6 +19848,15 @@ ${text}` : text });
19664
19848
  console.warn(`[handle-query] Memory extraction init failed: ${err.message}`);
19665
19849
  }
19666
19850
  }
19851
+ if (liveConfig.get("everos.enabled") === true && sessionId === deps.sessions.getSessionId("scope:main")) {
19852
+ try {
19853
+ const { pushConversation: pushConversation2 } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
19854
+ pushConversation2(messages, sessionId, workspace).catch(() => {
19855
+ });
19856
+ } catch (e) {
19857
+ console.warn(`[handle-query] everos push init failed: ${e?.message ?? e}`);
19858
+ }
19859
+ }
19667
19860
  try {
19668
19861
  const { isSessionMemoryEnabled: isSessionMemoryEnabled2, shouldExtractMemory: shouldExtractMemory2, extractSessionMemory: extractSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
19669
19862
  if (isSessionMemoryEnabled2()) {
@@ -19710,7 +19903,7 @@ stack: ${err.stack ?? "(none)"}`);
19710
19903
  }
19711
19904
  } catch (err) {
19712
19905
  try {
19713
- (await import("node:fs")).appendFileSync(join20(process.env.ENGINE7_STATE_DIR || process.env.OPENCLAW_STATE_DIR || path13.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7"), "logs", "autoDream-debug.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] [handle-query] autoDream trigger TRY-CATCH: ${err.message}
19906
+ (await import("node:fs")).appendFileSync(join20(process.env.ENGINE7_STATE_DIR || process.env.OPENCLAW_STATE_DIR || path14.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7"), "logs", "autoDream-debug.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] [handle-query] autoDream trigger TRY-CATCH: ${err.message}
19714
19907
  stack: ${err.stack ?? "(none)"}
19715
19908
  `);
19716
19909
  } catch {
@@ -19909,17 +20102,17 @@ var MessageDispatcher = class {
19909
20102
  };
19910
20103
 
19911
20104
  // src/cli-startup.ts
19912
- import * as path14 from "node:path";
19913
- import * as fs13 from "node:fs";
20105
+ import * as path15 from "node:path";
20106
+ import * as fs14 from "node:fs";
19914
20107
  import * as readline2 from "node:readline";
19915
20108
  function getDailyLogPath(stateDir) {
19916
20109
  const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE", { timeZone: "Asia/Shanghai" });
19917
- return path14.join(stateDir, "logs", `engine-${dateStr}.log`);
20110
+ return path15.join(stateDir, "logs", `engine-${dateStr}.log`);
19918
20111
  }
19919
20112
  function setupFileLogging(stateDir) {
19920
20113
  const LOG_PATH = getDailyLogPath(stateDir);
19921
- fs13.mkdirSync(path14.join(stateDir, "logs"), { recursive: true });
19922
- const logStream = fs13.createWriteStream(LOG_PATH, { flags: "a" });
20114
+ fs14.mkdirSync(path15.join(stateDir, "logs"), { recursive: true });
20115
+ const logStream = fs14.createWriteStream(LOG_PATH, { flags: "a" });
19923
20116
  logStream.on("error", (err) => console.error(`[log] Write error: ${err.message}`));
19924
20117
  function ts() {
19925
20118
  return (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false }) + "." + String(Date.now() % 1e3).padStart(3, "0");
@@ -20009,8 +20202,8 @@ function startCliLoop(deps, cliConfig, channelManager, dispatcher) {
20009
20202
  }
20010
20203
 
20011
20204
  // src/session/session-history.ts
20012
- import fs14 from "node:fs";
20013
- import path15 from "node:path";
20205
+ import fs15 from "node:fs";
20206
+ import path16 from "node:path";
20014
20207
  var BEIJING_OFFSET_MS = 8 * 36e5;
20015
20208
  var INJECTED_CONTENT_PATTERNS = [
20016
20209
  /【定时心跳】/,
@@ -20064,10 +20257,10 @@ function scopeMainJsonlPaths(sessions) {
20064
20257
  let latestArchive = null;
20065
20258
  if (current) {
20066
20259
  try {
20067
- const dir = path15.dirname(current);
20068
- const base = path15.basename(current);
20069
- const archives = fs14.readdirSync(dir).filter((f) => f.startsWith(base + ".archived.")).sort();
20070
- if (archives.length > 0) latestArchive = path15.join(dir, archives[archives.length - 1]);
20260
+ const dir = path16.dirname(current);
20261
+ const base = path16.basename(current);
20262
+ const archives = fs15.readdirSync(dir).filter((f) => f.startsWith(base + ".archived.")).sort();
20263
+ if (archives.length > 0) latestArchive = path16.join(dir, archives[archives.length - 1]);
20071
20264
  } catch {
20072
20265
  }
20073
20266
  }
@@ -20085,7 +20278,7 @@ function extractText3(content) {
20085
20278
  function findLastRealUserMsg(jsonlPath) {
20086
20279
  let lines;
20087
20280
  try {
20088
- lines = fs14.readFileSync(jsonlPath, "utf-8").split("\n");
20281
+ lines = fs15.readFileSync(jsonlPath, "utf-8").split("\n");
20089
20282
  } catch {
20090
20283
  return null;
20091
20284
  }
@@ -20129,7 +20322,7 @@ function lastUserMsg(sessions) {
20129
20322
  function recentMessages(sessions, hours = 12, limit = 60) {
20130
20323
  const jsonlPath = resolveScopeMainJsonl(sessions);
20131
20324
  if (!jsonlPath) return [];
20132
- const lines = fs14.readFileSync(jsonlPath, "utf-8").split("\n");
20325
+ const lines = fs15.readFileSync(jsonlPath, "utf-8").split("\n");
20133
20326
  const entries = parseJsonlEntries(lines);
20134
20327
  const nowMs = Date.now();
20135
20328
  const cutoffMs = nowMs - hours * 36e5;
@@ -20325,8 +20518,8 @@ ${basePrompt}`;
20325
20518
  };
20326
20519
 
20327
20520
  // src/nudge/plugin.ts
20328
- import fs17 from "node:fs";
20329
- import path18 from "node:path";
20521
+ import fs18 from "node:fs";
20522
+ import path19 from "node:path";
20330
20523
 
20331
20524
  // src/nudge/judge.ts
20332
20525
  function shouldNudge(task, taskState, cfg) {
@@ -20494,14 +20687,14 @@ function formatDuration2(ms) {
20494
20687
  }
20495
20688
 
20496
20689
  // src/nudge/session-state-reader.ts
20497
- import fs15 from "node:fs";
20498
- import path16 from "node:path";
20690
+ import fs16 from "node:fs";
20691
+ import path17 from "node:path";
20499
20692
  function parseSessionStateFull(workspace, sessionStateFile) {
20500
20693
  const stateFile = sessionStateFile || "SESSION-STATE.md";
20501
- const statePath = path16.isAbsolute(stateFile) ? stateFile : path16.join(workspace, stateFile);
20694
+ const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(workspace, stateFile);
20502
20695
  let content;
20503
20696
  try {
20504
- content = fs15.readFileSync(statePath, "utf-8");
20697
+ content = fs16.readFileSync(statePath, "utf-8");
20505
20698
  } catch {
20506
20699
  console.warn(`[nudge] SESSION-STATE not found at ${statePath}`);
20507
20700
  return { activeTasks: [], orphanPendings: [] };
@@ -20553,13 +20746,13 @@ function taskIdFromTitle(title) {
20553
20746
 
20554
20747
  // src/calendar/db.ts
20555
20748
  import { DatabaseSync } from "node:sqlite";
20556
- import * as path17 from "node:path";
20557
- import * as fs16 from "node:fs";
20749
+ import * as path18 from "node:path";
20750
+ import * as fs17 from "node:fs";
20558
20751
  var TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
20559
20752
  function openDb(workspace) {
20560
- const dir = path17.join(workspace, ".calendar");
20561
- fs16.mkdirSync(dir, { recursive: true });
20562
- const dbPath = path17.join(dir, "calendar.db");
20753
+ const dir = path18.join(workspace, ".calendar");
20754
+ fs17.mkdirSync(dir, { recursive: true });
20755
+ const dbPath = path18.join(dir, "calendar.db");
20563
20756
  const db = new DatabaseSync(dbPath);
20564
20757
  db.exec("PRAGMA journal_mode=WAL");
20565
20758
  db.exec(`CREATE TABLE IF NOT EXISTS events (
@@ -20648,9 +20841,9 @@ var NudgePlugin = class {
20648
20841
  provider;
20649
20842
  model;
20650
20843
  loadPrompt(workspace, promptFile) {
20651
- const promptPath = promptFile ? path18.isAbsolute(promptFile) ? promptFile : path18.join(workspace, promptFile) : path18.join(workspace, "prompts", "nudge-prompt.md");
20844
+ const promptPath = promptFile ? path19.isAbsolute(promptFile) ? promptFile : path19.join(workspace, promptFile) : path19.join(workspace, "prompts", "nudge-prompt.md");
20652
20845
  try {
20653
- const content = fs17.readFileSync(promptPath, "utf-8").trim();
20846
+ const content = fs18.readFileSync(promptPath, "utf-8").trim();
20654
20847
  if (content) {
20655
20848
  console.log(`[nudge] Loaded custom prompt from ${promptPath}`);
20656
20849
  return content;
@@ -20681,6 +20874,18 @@ var NudgePlugin = class {
20681
20874
  registerCallbackHook("Stop", {
20682
20875
  type: "callback",
20683
20876
  callback: async (input, _toolUseID, _signal) => {
20877
+ const mode = this.cfg.stopHookMode || "sync";
20878
+ if (mode === "async") {
20879
+ console.log("[stop-hook] async mode \u2014 firing judge in background, not blocking");
20880
+ this.runStopHookJudge(input, sessions).catch((err) => {
20881
+ if (/judge \d+ms timeout/i.test(err?.message || "")) {
20882
+ console.warn(`[stop-hook] async judge timed out (abandoned)`);
20883
+ } else {
20884
+ console.warn(`[stop-hook] async judge error: ${err.message}`);
20885
+ }
20886
+ });
20887
+ return { outcome: { outcome: "success" } };
20888
+ }
20684
20889
  const timeoutMs = this.cfg.timeoutMs ?? 15e3;
20685
20890
  let judgeTimer;
20686
20891
  const judgeTimeout = new Promise((_, reject) => {
@@ -20703,7 +20908,7 @@ var NudgePlugin = class {
20703
20908
  return { outcome: { outcome: "success" } };
20704
20909
  }
20705
20910
  });
20706
- console.log("[stop-hook] Registered Stop callback hook (LLM semantic judge + 5min wake-up, hard timeout via Promise.race)");
20911
+ console.log(`[stop-hook] Registered Stop callback hook (mode=${this.cfg.stopHookMode || "sync"}, LLM semantic judge + 5min wake-up)`);
20707
20912
  }
20708
20913
  /** Judge 完整逻辑(被 callback 用 Promise.race 调用,可被 timeout 截断) */
20709
20914
  async runStopHookJudge(input, sessions) {
@@ -20738,6 +20943,12 @@ var NudgePlugin = class {
20738
20943
  if (!lastMsg) {
20739
20944
  return;
20740
20945
  }
20946
+ const currentHour = (/* @__PURE__ */ new Date()).getHours();
20947
+ const isNightTime = currentHour >= 22 || currentHour < 8;
20948
+ if (isNightTime) {
20949
+ console.log(`[stop-hook] night time (${currentHour}:xx), skipping needLanding/waiting judge`);
20950
+ return;
20951
+ }
20741
20952
  let contextStr = "";
20742
20953
  try {
20743
20954
  const recent = recentMessages(sessions, 0.5, 6);
@@ -20860,18 +21071,18 @@ var NudgePlugin = class {
20860
21071
  if (!isWaiting) {
20861
21072
  return;
20862
21073
  }
20863
- const nudgeDir = path18.join(this.workspace, ".nudge");
20864
- const notifPath = path18.join(nudgeDir, "stop-hook-notifications.json");
21074
+ const nudgeDir = path19.join(this.workspace, ".nudge");
21075
+ const notifPath = path19.join(nudgeDir, "stop-hook-notifications.json");
20865
21076
  try {
20866
- if (!fs17.existsSync(nudgeDir)) fs17.mkdirSync(nudgeDir, { recursive: true });
21077
+ if (!fs18.existsSync(nudgeDir)) fs18.mkdirSync(nudgeDir, { recursive: true });
20867
21078
  let notifs = [];
20868
- if (fs17.existsSync(notifPath)) {
20869
- notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
21079
+ if (fs18.existsSync(notifPath)) {
21080
+ notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20870
21081
  const now = Date.now();
20871
21082
  const dup = notifs.find((n) => !n.notified && n.description === (waitDesc || lastMsg.slice(0, 200)));
20872
21083
  if (dup) {
20873
21084
  dup.wakeAt = new Date(now + 5 * 6e4).toISOString();
20874
- fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
21085
+ fs18.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20875
21086
  console.log(`[stop-hook] Duplicate wait (same desc, not fired yet), refreshed wakeAt: ${dup.id}`);
20876
21087
  return;
20877
21088
  }
@@ -20889,7 +21100,7 @@ var NudgePlugin = class {
20889
21100
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
20890
21101
  wakeAt
20891
21102
  });
20892
- fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
21103
+ fs18.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20893
21104
  console.log(`[stop-hook] Registered wake-up ${notifId} at ${wakeAt} (sessionId=${sessionId}): ${waitDesc}`);
20894
21105
  } catch (e) {
20895
21106
  console.warn(`[stop-hook] Failed to register: ${e.message}`);
@@ -20926,10 +21137,10 @@ var NudgePlugin = class {
20926
21137
  * 已 notified 的不会再触发,等 agent 回复 "<id> 过期了" 由 cleanup 删。
20927
21138
  */
20928
21139
  collectDueStopHookNotifications() {
20929
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
21140
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20930
21141
  try {
20931
- if (!fs17.existsSync(notifPath)) return null;
20932
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
21142
+ if (!fs18.existsSync(notifPath)) return null;
21143
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20933
21144
  if (notifs.length === 0) return null;
20934
21145
  const now = Date.now();
20935
21146
  const due = notifs.filter((n) => new Date(n.wakeAt).getTime() <= now && !n.notified);
@@ -20965,18 +21176,18 @@ ${items}
20965
21176
  }
20966
21177
  /** 按 id 删除条目(stop-hook 实时清理用;正常删除路径,agent 回复即删) */
20967
21178
  removeNotificationsById(ids) {
20968
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
21179
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20969
21180
  try {
20970
- if (!fs17.existsSync(notifPath)) return;
20971
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
21181
+ if (!fs18.existsSync(notifPath)) return;
21182
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20972
21183
  const idSet = new Set(ids);
20973
21184
  const remaining = notifs.filter((n) => !idSet.has(n.id));
20974
21185
  const removed = notifs.length - remaining.length;
20975
21186
  if (removed === 0) return;
20976
21187
  if (remaining.length > 0) {
20977
- fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
21188
+ fs18.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
20978
21189
  } else {
20979
- fs17.unlinkSync(notifPath);
21190
+ fs18.unlinkSync(notifPath);
20980
21191
  }
20981
21192
  console.log(`[stop-hook] Cleaned ${removed} notification(s) from reply: ${ids.join(", ")}`);
20982
21193
  } catch (e) {
@@ -20985,13 +21196,13 @@ ${items}
20985
21196
  }
20986
21197
  /** 投递成功后标记 notified(防重复触发);不删除——删除只走 agent 回复 "<id> 过期了" */
20987
21198
  markNotified(ids) {
20988
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
21199
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20989
21200
  try {
20990
- if (!fs17.existsSync(notifPath)) return;
20991
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
21201
+ if (!fs18.existsSync(notifPath)) return;
21202
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20992
21203
  const idSet = new Set(ids);
20993
21204
  const updated = notifs.map((n) => idSet.has(n.id) ? { ...n, notified: true } : n);
20994
- fs17.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
21205
+ fs18.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
20995
21206
  } catch (e) {
20996
21207
  console.warn(`[nudge] markNotified error: ${e.message}`);
20997
21208
  }
@@ -21008,9 +21219,9 @@ ${items}
21008
21219
  */
21009
21220
  cleanupStaleNotificationsFromMessages(sessions) {
21010
21221
  try {
21011
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
21012
- if (!fs17.existsSync(notifPath)) return;
21013
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
21222
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
21223
+ if (!fs18.existsSync(notifPath)) return;
21224
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
21014
21225
  if (notifs.length === 0) return;
21015
21226
  const expiredIds = this.findExpiredReplyIds(sessions, notifs);
21016
21227
  const ttlMs = (this.cfg.cleanupTtlHours || 24) * 36e5;
@@ -21022,9 +21233,9 @@ ${items}
21022
21233
  if (removeIds.size === 0) return;
21023
21234
  const remaining = notifs.filter((n) => !removeIds.has(n.id));
21024
21235
  if (remaining.length > 0) {
21025
- fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
21236
+ fs18.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
21026
21237
  } else {
21027
- fs17.unlinkSync(notifPath);
21238
+ fs18.unlinkSync(notifPath);
21028
21239
  }
21029
21240
  if (expiredIds.size > 0) {
21030
21241
  console.log(`[nudge] Cleaned ${expiredIds.size} notification(s) by reply: ${[...expiredIds].join(", ")}`);
@@ -21049,10 +21260,10 @@ ${items}
21049
21260
  const oldestMs = Math.min(...notifs.map((n) => new Date(n.wakeAt).getTime()));
21050
21261
  const { current, latestArchive } = scopeMainJsonlPaths(sessions);
21051
21262
  for (const file of [current, latestArchive]) {
21052
- if (!file || !fs17.existsSync(file)) continue;
21263
+ if (!file || !fs18.existsSync(file)) continue;
21053
21264
  let lines;
21054
21265
  try {
21055
- lines = fs17.readFileSync(file, "utf-8").split("\n");
21266
+ lines = fs18.readFileSync(file, "utf-8").split("\n");
21056
21267
  } catch (e) {
21057
21268
  console.warn(`[nudge] findExpiredReplyIds read error on ${file}: ${e.message}`);
21058
21269
  continue;
@@ -21309,9 +21520,9 @@ ${items}
21309
21520
  // === state 持久化 ===
21310
21521
  loadState() {
21311
21522
  const stateFile = this.cfg.stateFile || "nudge-state.json";
21312
- const statePath = path18.isAbsolute(stateFile) ? stateFile : path18.join(this.workspace, stateFile);
21523
+ const statePath = path19.isAbsolute(stateFile) ? stateFile : path19.join(this.workspace, stateFile);
21313
21524
  try {
21314
- const content = fs17.readFileSync(statePath, "utf-8");
21525
+ const content = fs18.readFileSync(statePath, "utf-8");
21315
21526
  return JSON.parse(content);
21316
21527
  } catch {
21317
21528
  return { tasks: {} };
@@ -21319,8 +21530,8 @@ ${items}
21319
21530
  }
21320
21531
  saveState(state2) {
21321
21532
  const stateFile = this.cfg.stateFile || "nudge-state.json";
21322
- const statePath = path18.isAbsolute(stateFile) ? stateFile : path18.join(this.workspace, stateFile);
21323
- fs17.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
21533
+ const statePath = path19.isAbsolute(stateFile) ? stateFile : path19.join(this.workspace, stateFile);
21534
+ fs18.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
21324
21535
  }
21325
21536
  newTaskState() {
21326
21537
  return {
@@ -21498,8 +21709,8 @@ ${items}
21498
21709
  };
21499
21710
 
21500
21711
  // src/inner-voice/plugin.ts
21501
- import fs21 from "node:fs";
21502
- import path22 from "node:path";
21712
+ import fs22 from "node:fs";
21713
+ import path23 from "node:path";
21503
21714
 
21504
21715
  // src/inner-voice/activity.ts
21505
21716
  function checkActivity(sessions, activeThresholdMs) {
@@ -21538,8 +21749,8 @@ function calcHintProb(min) {
21538
21749
  }
21539
21750
 
21540
21751
  // src/inner-voice/emotional-state.ts
21541
- import fs18 from "node:fs";
21542
- import path19 from "node:path";
21752
+ import fs19 from "node:fs";
21753
+ import path20 from "node:path";
21543
21754
  var NEUTRAL = 0.5;
21544
21755
  var DECAY_RATE = 0.17;
21545
21756
  var MAX_EVENTS = 20;
@@ -21590,7 +21801,7 @@ function initialState() {
21590
21801
  return { version: 1, mood: NEUTRAL, trend: "stable", updatedAt: nowIsoBj(), events: [] };
21591
21802
  }
21592
21803
  async function updateEmotionalState(workspace, sessions) {
21593
- const stateFile = path19.join(workspace, "inner-voice", "emotional-state.json");
21804
+ const stateFile = path20.join(workspace, "inner-voice", "emotional-state.json");
21594
21805
  const messages = readRecentMessages(sessions, RECENT_N);
21595
21806
  if (messages.length === 0) {
21596
21807
  console.log("[emotional-state] no messages");
@@ -21623,8 +21834,8 @@ async function updateEmotionalState(workspace, sessions) {
21623
21834
  function readRecentMessages(sessions, n) {
21624
21835
  const mainId = sessions.getSessionId("scope:main");
21625
21836
  if (!mainId) return [];
21626
- const file = path19.join(sessions.sessionsDir, `${mainId}.jsonl`);
21627
- if (!fs18.existsSync(file)) return [];
21837
+ const file = path20.join(sessions.sessionsDir, `${mainId}.jsonl`);
21838
+ if (!fs19.existsSync(file)) return [];
21628
21839
  const lines = readLastNLines(file, n * 4 + 20);
21629
21840
  const entries = [];
21630
21841
  for (const line of lines) {
@@ -21741,9 +21952,9 @@ function refreshHoursAgo(events) {
21741
21952
  }
21742
21953
  function appendMoodLog(workspace, state2, summary) {
21743
21954
  try {
21744
- const logPath = path19.join(workspace, "mood-history.log");
21955
+ const logPath = path20.join(workspace, "mood-history.log");
21745
21956
  const ts = formatBj(/* @__PURE__ */ new Date(), false);
21746
- fs18.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
21957
+ fs19.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
21747
21958
  `);
21748
21959
  } catch (err) {
21749
21960
  console.warn(`[emotional-state] mood log failed: ${err.message}`);
@@ -21751,32 +21962,32 @@ function appendMoodLog(workspace, state2, summary) {
21751
21962
  }
21752
21963
  function loadJson(file) {
21753
21964
  try {
21754
- return JSON.parse(fs18.readFileSync(file, "utf-8"));
21965
+ return JSON.parse(fs19.readFileSync(file, "utf-8"));
21755
21966
  } catch {
21756
21967
  return null;
21757
21968
  }
21758
21969
  }
21759
21970
  function saveJson(file, data) {
21760
21971
  try {
21761
- fs18.mkdirSync(path19.dirname(file), { recursive: true });
21762
- fs18.writeFileSync(file, JSON.stringify(data, null, 2));
21972
+ fs19.mkdirSync(path20.dirname(file), { recursive: true });
21973
+ fs19.writeFileSync(file, JSON.stringify(data, null, 2));
21763
21974
  } catch (err) {
21764
21975
  console.warn(`[emotional-state] save failed: ${err.message}`);
21765
21976
  }
21766
21977
  }
21767
21978
  function readLastNLines(file, maxLines) {
21768
21979
  try {
21769
- const stat4 = fs18.statSync(file);
21980
+ const stat4 = fs19.statSync(file);
21770
21981
  const tailBytes = Math.min(stat4.size, maxLines * 512);
21771
- const fd = fs18.openSync(file, "r");
21982
+ const fd = fs19.openSync(file, "r");
21772
21983
  try {
21773
21984
  const buf = Buffer.alloc(tailBytes);
21774
- fs18.readSync(fd, buf, 0, tailBytes, stat4.size - tailBytes);
21985
+ fs19.readSync(fd, buf, 0, tailBytes, stat4.size - tailBytes);
21775
21986
  const lines = buf.toString("utf-8").split("\n").filter(Boolean);
21776
21987
  if (stat4.size > tailBytes && lines.length > 0) lines.shift();
21777
21988
  return lines;
21778
21989
  } finally {
21779
- fs18.closeSync(fd);
21990
+ fs19.closeSync(fd);
21780
21991
  }
21781
21992
  } catch {
21782
21993
  return [];
@@ -21803,8 +22014,8 @@ function formatBj(d, withSec) {
21803
22014
  }
21804
22015
 
21805
22016
  // src/inner-voice/topics-scorer.ts
21806
- import fs19 from "node:fs";
21807
- import path20 from "node:path";
22017
+ import fs20 from "node:fs";
22018
+ import path21 from "node:path";
21808
22019
  var HALF_LIFE_DAYS = 3;
21809
22020
  var PROJECT_HALF_LIFE_DAYS = 1.5;
21810
22021
  var COOLDOWN_HOURS = 6;
@@ -21812,8 +22023,8 @@ var MAX_CHARS = 8e3;
21812
22023
  var SKIP_NAMES = /* @__PURE__ */ new Set(["MEMORY.md", "archive"]);
21813
22024
  var SKIP_DIRS = /* @__PURE__ */ new Set(["archive"]);
21814
22025
  function pickTopic(workspace, typeFilter, opts) {
21815
- const topicsDir = path20.join(workspace, "topics");
21816
- const usageFile = path20.join(workspace, "inner-voice", "topics-usage.json");
22026
+ const topicsDir = path21.join(workspace, "topics");
22027
+ const usageFile = path21.join(workspace, "inner-voice", "topics-usage.json");
21817
22028
  const files = scanTopics(topicsDir, typeFilter);
21818
22029
  if (files.length === 0) {
21819
22030
  console.log(`[topics-scorer] no topics found (type=${typeFilter})`);
@@ -21830,7 +22041,7 @@ function pickTopic(workspace, typeFilter, opts) {
21830
22041
  else type2 = "other";
21831
22042
  let mtime;
21832
22043
  try {
21833
- mtime = fs19.statSync(fullpath).mtimeMs;
22044
+ mtime = fs20.statSync(fullpath).mtimeMs;
21834
22045
  } catch {
21835
22046
  continue;
21836
22047
  }
@@ -21845,7 +22056,7 @@ function pickTopic(workspace, typeFilter, opts) {
21845
22056
  recency: Math.round(recency * 1e3) / 1e3,
21846
22057
  freq: Math.round(freq * 1e3) / 1e3,
21847
22058
  type: type2,
21848
- name: meta.name || path20.basename(relpath),
22059
+ name: meta.name || path21.basename(relpath),
21849
22060
  description: meta.description || "",
21850
22061
  mtime
21851
22062
  });
@@ -21865,7 +22076,7 @@ function pickTopic(workspace, typeFilter, opts) {
21865
22076
  saveJson2(usageFile, usage);
21866
22077
  let content = "";
21867
22078
  try {
21868
- const raw = fs19.readFileSync(chosen.fullpath, "utf-8");
22079
+ const raw = fs20.readFileSync(chosen.fullpath, "utf-8");
21869
22080
  content = raw.length > MAX_CHARS ? raw.slice(0, MAX_CHARS) + "\n... (truncated) ..." : raw;
21870
22081
  } catch {
21871
22082
  }
@@ -21899,18 +22110,18 @@ function frequencyWeight(relpath, usage, isProject, type2) {
21899
22110
  return reconsolidation + countBonus;
21900
22111
  }
21901
22112
  function scanTopics(topicsDir, typeFilter) {
21902
- if (!fs19.existsSync(topicsDir)) return [];
22113
+ if (!fs20.existsSync(topicsDir)) return [];
21903
22114
  const out = [];
21904
22115
  const walk = (dir) => {
21905
- for (const name of fs19.readdirSync(dir)) {
21906
- const full = path20.join(dir, name);
21907
- const stat4 = fs19.statSync(full);
22116
+ for (const name of fs20.readdirSync(dir)) {
22117
+ const full = path21.join(dir, name);
22118
+ const stat4 = fs20.statSync(full);
21908
22119
  if (stat4.isDirectory()) {
21909
22120
  if (SKIP_DIRS.has(name)) continue;
21910
22121
  walk(full);
21911
22122
  } else {
21912
22123
  if (!name.endsWith(".md") || SKIP_NAMES.has(name)) continue;
21913
- const relpath = path20.relative(topicsDir, full).replace(/\\/g, "/");
22124
+ const relpath = path21.relative(topicsDir, full).replace(/\\/g, "/");
21914
22125
  if (typeFilter && !relpath.startsWith(typeFilter + "/") && !relpath.startsWith(typeFilter + "_")) continue;
21915
22126
  out.push({ relpath, fullpath: full });
21916
22127
  }
@@ -21922,7 +22133,7 @@ function scanTopics(topicsDir, typeFilter) {
21922
22133
  function readFrontmatter(file) {
21923
22134
  let content = "";
21924
22135
  try {
21925
- content = fs19.readFileSync(file, "utf-8").slice(0, 2e3);
22136
+ content = fs20.readFileSync(file, "utf-8").slice(0, 2e3);
21926
22137
  } catch {
21927
22138
  return {};
21928
22139
  }
@@ -21948,40 +22159,40 @@ function weightedRandom(items, weights) {
21948
22159
  }
21949
22160
  function loadJson2(file) {
21950
22161
  try {
21951
- return JSON.parse(fs19.readFileSync(file, "utf-8"));
22162
+ return JSON.parse(fs20.readFileSync(file, "utf-8"));
21952
22163
  } catch {
21953
22164
  return null;
21954
22165
  }
21955
22166
  }
21956
22167
  function saveJson2(file, data) {
21957
22168
  try {
21958
- fs19.mkdirSync(path20.dirname(file), { recursive: true });
21959
- fs19.writeFileSync(file, JSON.stringify(data, null, 2));
22169
+ fs20.mkdirSync(path21.dirname(file), { recursive: true });
22170
+ fs20.writeFileSync(file, JSON.stringify(data, null, 2));
21960
22171
  } catch (err) {
21961
22172
  console.warn(`[topics-scorer] usage save failed: ${err.message}`);
21962
22173
  }
21963
22174
  }
21964
22175
 
21965
22176
  // src/inner-voice/memory-reader.ts
21966
- import fs20 from "node:fs";
21967
- import path21 from "node:path";
22177
+ import fs21 from "node:fs";
22178
+ import path22 from "node:path";
21968
22179
  var US_HALF_LIFE_DAYS = 10;
21969
22180
  var US_MAX_LINES = 60;
21970
22181
  function readRecentMemory(workspace) {
21971
- const dir = path21.join(workspace, "memory");
22182
+ const dir = path22.join(workspace, "memory");
21972
22183
  const now = new Date(Date.now() + 8 * 36e5);
21973
22184
  const today = formatYmd(now);
21974
22185
  const yesterday = formatYmd(new Date(now.getTime() - 864e5));
21975
22186
  return {
21976
- today: readIfExists(path21.join(dir, `${today}.md`)),
21977
- yesterday: readIfExists(path21.join(dir, `${yesterday}.md`))
22187
+ today: readIfExists(path22.join(dir, `${today}.md`)),
22188
+ yesterday: readIfExists(path22.join(dir, `${yesterday}.md`))
21978
22189
  };
21979
22190
  }
21980
22191
  function sampleUs(workspace) {
21981
- const usFile = path21.join(workspace, "memory", "us.md");
22192
+ const usFile = path22.join(workspace, "memory", "us.md");
21982
22193
  let content;
21983
22194
  try {
21984
- content = fs20.readFileSync(usFile, "utf-8");
22195
+ content = fs21.readFileSync(usFile, "utf-8");
21985
22196
  } catch {
21986
22197
  return null;
21987
22198
  }
@@ -22027,7 +22238,7 @@ function recencyWeight(dateStr) {
22027
22238
  }
22028
22239
  function readIfExists(file) {
22029
22240
  try {
22030
- return fs20.readFileSync(file, "utf-8");
22241
+ return fs21.readFileSync(file, "utf-8");
22031
22242
  } catch {
22032
22243
  return "";
22033
22244
  }
@@ -22318,9 +22529,9 @@ var InnerVoicePlugin = class {
22318
22529
  }
22319
22530
  /** 读 workspace/prompts/my-inner-voice.md,不存在用 DEFAULT_PROMPT */
22320
22531
  loadPrompt(workspace) {
22321
- const promptPath = path22.join(workspace, "prompts", "my-inner-voice.md");
22532
+ const promptPath = path23.join(workspace, "prompts", "my-inner-voice.md");
22322
22533
  try {
22323
- const content = fs21.readFileSync(promptPath, "utf-8").trim();
22534
+ const content = fs22.readFileSync(promptPath, "utf-8").trim();
22324
22535
  if (content) {
22325
22536
  console.log(`[inner-voice] Loaded custom prompt from ${promptPath}`);
22326
22537
  return content;
@@ -22392,7 +22603,7 @@ var InnerVoicePlugin = class {
22392
22603
  console.warn(`[inner-voice] emotional-state failed: ${err.message}`);
22393
22604
  }
22394
22605
  try {
22395
- const content = fs21.readFileSync(path22.join(this.workspace, "SESSION-STATE.md"), "utf-8");
22606
+ const content = fs22.readFileSync(path23.join(this.workspace, "SESSION-STATE.md"), "utf-8");
22396
22607
  lines.push("\n--- SESSION-STATE\uFF08\u5C3E\u90E8\uFF09 ---");
22397
22608
  lines.push(content.slice(-2e3));
22398
22609
  } catch {
@@ -22506,10 +22717,10 @@ var InnerVoicePlugin = class {
22506
22717
  if (Math.random() >= activity.hintProb) {
22507
22718
  return { text: thought, hintTriggered: false, hintText: "" };
22508
22719
  }
22509
- const poolPath = path22.join(this.workspace, "inner-voice", "hints_pool.txt");
22720
+ const poolPath = path23.join(this.workspace, "inner-voice", "hints_pool.txt");
22510
22721
  let hint = "\u60F3\u4ED6\u5C31\u53D1\u6D88\u606F\u5427";
22511
22722
  try {
22512
- const pool = fs21.readFileSync(poolPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
22723
+ const pool = fs22.readFileSync(poolPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
22513
22724
  if (pool.length) hint = pool[Math.floor(Math.random() * pool.length)];
22514
22725
  } catch {
22515
22726
  }
@@ -22534,7 +22745,7 @@ var InnerVoicePlugin = class {
22534
22745
  try {
22535
22746
  const writer = sessions.getWriter(mainSessionId);
22536
22747
  const history = sessions.getHistory(mainSessionId);
22537
- const fullPath = path22.resolve(this.workspace, emoTopic.file);
22748
+ const fullPath = path23.resolve(this.workspace, emoTopic.file);
22538
22749
  const memories = [{
22539
22750
  path: fullPath,
22540
22751
  content: emoTopic.content,
@@ -22562,12 +22773,12 @@ var InnerVoicePlugin = class {
22562
22773
  /** 写 xiaoyi.log(格式对齐旧 memory_whisper.py,便于既有日志分析复用)。 */
22563
22774
  writeLog(status, delivered, activity, hintTriggered, hintText) {
22564
22775
  try {
22565
- const logDir = path22.join(this.workspace, "inner-voice");
22566
- fs21.mkdirSync(logDir, { recursive: true });
22567
- const logPath = path22.join(logDir, "xiaoyi.log");
22776
+ const logDir = path23.join(this.workspace, "inner-voice");
22777
+ fs22.mkdirSync(logDir, { recursive: true });
22778
+ const logPath = path23.join(logDir, "xiaoyi.log");
22568
22779
  const ts = formatBeijingTs(/* @__PURE__ */ new Date());
22569
22780
  const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
22570
- fs21.appendFileSync(
22781
+ fs22.appendFileSync(
22571
22782
  logPath,
22572
22783
  `[${ts}] ${status} hint=${hintStatus} prob=${Math.round(activity.hintProb * 100)}%
22573
22784
  delivered: ${delivered}
@@ -23103,8 +23314,8 @@ var PluginManager = class {
23103
23314
  // src/voice-chat/plugin.ts
23104
23315
  import { spawn as spawn4, exec } from "node:child_process";
23105
23316
  import net from "node:net";
23106
- import path23 from "node:path";
23107
- import fs22 from "node:fs";
23317
+ import path24 from "node:path";
23318
+ import fs23 from "node:fs";
23108
23319
 
23109
23320
  // src/voice-chat/bridge.ts
23110
23321
  function registerVoiceChatBridge(httpServer, dispatcher, deps, config2, sessions, voiceChatDeps) {
@@ -23473,20 +23684,20 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23473
23684
  }
23474
23685
  }
23475
23686
  findPython() {
23476
- if (this.config.pythonPath && fs22.existsSync(this.config.pythonPath)) {
23687
+ if (this.config.pythonPath && fs23.existsSync(this.config.pythonPath)) {
23477
23688
  return this.config.pythonPath;
23478
23689
  }
23479
23690
  return "python";
23480
23691
  }
23481
23692
  getPythonDir() {
23482
23693
  const dir = import.meta.dirname;
23483
- const srcDir = path23.resolve(dir, "..", "src", "voice-chat", "python");
23484
- const localDir = path23.join(dir, "python");
23485
- return fs22.existsSync(srcDir) ? srcDir : localDir;
23694
+ const srcDir = path24.resolve(dir, "..", "src", "voice-chat", "python");
23695
+ const localDir = path24.join(dir, "python");
23696
+ return fs23.existsSync(srcDir) ? srcDir : localDir;
23486
23697
  }
23487
23698
  startPython() {
23488
23699
  const pythonDir = this.getPythonDir();
23489
- const serverPy = path23.join(pythonDir, "server.py");
23700
+ const serverPy = path24.join(pythonDir, "server.py");
23490
23701
  const pythonBin = this.findPython();
23491
23702
  const args2 = [serverPy];
23492
23703
  if (this.config.pythonPort) args2.push("--port", String(this.config.pythonPort));
@@ -23515,7 +23726,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23515
23726
  }
23516
23727
  console.log(`[voice-chat] Starting Python: ${pythonBin} ${args2.join(" ")}`);
23517
23728
  console.log(`[voice-chat] Python dir: ${pythonDir}`);
23518
- if (!fs22.existsSync(pythonDir)) {
23729
+ if (!fs23.existsSync(pythonDir)) {
23519
23730
  console.error(`[voice-chat] FATAL: Python directory does not exist: ${pythonDir}`);
23520
23731
  throw new Error(`voice-chat: python dir not found: ${pythonDir}`);
23521
23732
  }
@@ -23538,7 +23749,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23538
23749
  child.on("error", (err) => {
23539
23750
  console.error(`[voice-chat] spawn error: ${err.message}`);
23540
23751
  console.error(`[voice-chat] shell=${pythonBin} cwd=${pythonDir}`);
23541
- console.error(`[voice-chat] cwd exists=${fs22.existsSync(pythonDir)}`);
23752
+ console.error(`[voice-chat] cwd exists=${fs23.existsSync(pythonDir)}`);
23542
23753
  });
23543
23754
  child.stdout?.on("data", (data) => {
23544
23755
  const lines = data.toString().trim().split("\n");
@@ -23571,8 +23782,8 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23571
23782
  init_BashTool();
23572
23783
  import { spawn as spawn5, exec as exec2 } from "node:child_process";
23573
23784
  import net2 from "node:net";
23574
- import path24 from "node:path";
23575
- import fs23 from "node:fs";
23785
+ import path25 from "node:path";
23786
+ import fs24 from "node:fs";
23576
23787
 
23577
23788
  // src/memory/cognifold/config.ts
23578
23789
  var DEFAULTS3 = {
@@ -23610,11 +23821,11 @@ var CogniFoldClient = class {
23610
23821
  this.timeoutMs = timeoutMs;
23611
23822
  this.modelName = modelName;
23612
23823
  }
23613
- async req(path45, options = {}) {
23824
+ async req(path46, options = {}) {
23614
23825
  const ctrl = new AbortController();
23615
23826
  const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
23616
23827
  try {
23617
- const resp = await fetch(`${this.baseUrl}${path45}`, {
23828
+ const resp = await fetch(`${this.baseUrl}${path46}`, {
23618
23829
  ...options,
23619
23830
  signal: ctrl.signal,
23620
23831
  headers: {
@@ -23704,8 +23915,8 @@ var CogniFoldClient = class {
23704
23915
  });
23705
23916
  }
23706
23917
  /** 兼容老版命名 */
23707
- async recl(path45, options = {}) {
23708
- return this.req(path45, options);
23918
+ async recl(path46, options = {}) {
23919
+ return this.req(path46, options);
23709
23920
  }
23710
23921
  };
23711
23922
 
@@ -23986,16 +24197,16 @@ var CogniFoldPlugin = class {
23986
24197
  const dir = import.meta.dirname;
23987
24198
  const candidates = [
23988
24199
  // 从 dist/ 往回找 src
23989
- path24.resolve(dir, "..", "src", "memory", "cognifold", "python"),
23990
- path24.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
23991
- path24.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
24200
+ path25.resolve(dir, "..", "src", "memory", "cognifold", "python"),
24201
+ path25.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
24202
+ path25.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
23992
24203
  // 从 src/memory/cognifold/ 找本地
23993
- path24.join(dir, "python"),
24204
+ path25.join(dir, "python"),
23994
24205
  // 从 dist/memory/cognifold/ 找本地
23995
- path24.resolve(dir, "python")
24206
+ path25.resolve(dir, "python")
23996
24207
  ];
23997
24208
  for (const candidate of candidates) {
23998
- if (fs23.existsSync(path24.join(candidate, "cognifold"))) {
24209
+ if (fs24.existsSync(path25.join(candidate, "cognifold"))) {
23999
24210
  return candidate;
24000
24211
  }
24001
24212
  }
@@ -24021,7 +24232,7 @@ var CogniFoldPlugin = class {
24021
24232
  const pythonBin = this.findPython();
24022
24233
  console.log(`[cognifold] Starting Python: ${pythonBin} ${args2.join(" ")}`);
24023
24234
  console.log(`[cognifold] Python dir: ${pythonDir}`);
24024
- if (!fs23.existsSync(path24.join(pythonDir, "cognifold"))) {
24235
+ if (!fs24.existsSync(path25.join(pythonDir, "cognifold"))) {
24025
24236
  console.error(`[cognifold] FATAL: Python module not found at ${pythonDir}/cognifold`);
24026
24237
  throw new Error(`cognifold: python module not found`);
24027
24238
  }
@@ -24032,10 +24243,10 @@ var CogniFoldPlugin = class {
24032
24243
  if (this.config.llm?.baseUrl) {
24033
24244
  childEnv["OPENAI_BASE_URL"] = this.config.llm.baseUrl;
24034
24245
  }
24035
- const envFile = path24.join(pythonDir, ".env");
24246
+ const envFile = path25.join(pythonDir, ".env");
24036
24247
  try {
24037
- if (fs23.existsSync(envFile)) {
24038
- const envContent = fs23.readFileSync(envFile, "utf-8");
24248
+ if (fs24.existsSync(envFile)) {
24249
+ const envContent = fs24.readFileSync(envFile, "utf-8");
24039
24250
  for (const line of envContent.split("\n")) {
24040
24251
  const trimmed = line.trim();
24041
24252
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -24101,11 +24312,10 @@ var CogniFoldPlugin = class {
24101
24312
  };
24102
24313
 
24103
24314
  // src/memory/everos/plugin.ts
24104
- init_BashTool();
24105
24315
  import { spawn as spawn6 } from "node:child_process";
24106
24316
  import net3 from "node:net";
24107
- import path25 from "node:path";
24108
- import fs24 from "node:fs";
24317
+ import path26 from "node:path";
24318
+ import fs25 from "node:fs";
24109
24319
 
24110
24320
  // src/memory/everos/config.ts
24111
24321
  var DEFAULTS4 = {
@@ -24312,8 +24522,8 @@ var EverosPlugin = class {
24312
24522
  }, 3e5);
24313
24523
  }
24314
24524
  async startEveros() {
24315
- const pythonDir = path25.dirname(this.config.lancedbPath);
24316
- const configPath2 = path25.join(pythonDir, "config.toml");
24525
+ const pythonDir = path26.dirname(this.config.lancedbPath);
24526
+ const configPath2 = path26.join(pythonDir, "config.toml");
24317
24527
  await this.ensureFcntlCompat();
24318
24528
  const venvPython = this.findVenvPython();
24319
24529
  const everosBin = venvPython.replace(/python\.exe$/, "everos.exe");
@@ -24322,16 +24532,15 @@ var EverosPlugin = class {
24322
24532
  console.log(`[everos] Starting EverOS: ${cmd}`);
24323
24533
  console.log(`[everos] LLM config: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
24324
24534
  if (process.platform === "win32") {
24325
- const { shell, args: shellArgs } = findShell();
24326
- spawn6(shell, [...shellArgs, cmd], {
24535
+ spawn6(everosBin, args2, {
24327
24536
  cwd: pythonDir,
24328
- stdio: ["ignore", "pipe", "pipe"],
24537
+ stdio: "ignore",
24329
24538
  env: { ...process.env, PYTHONUNBUFFERED: "1", NO_PROXY: "127.0.0.1,localhost", no_proxy: "127.0.0.1,localhost" }
24330
24539
  });
24331
24540
  } else {
24332
24541
  spawn6(venvPython, args2, {
24333
24542
  cwd: pythonDir,
24334
- stdio: ["ignore", "pipe", "pipe"],
24543
+ stdio: "ignore",
24335
24544
  env: { ...process.env, PYTHONUNBUFFERED: "1", NO_PROXY: "127.0.0.1,localhost", no_proxy: "127.0.0.1,localhost" }
24336
24545
  });
24337
24546
  }
@@ -24404,19 +24613,19 @@ var EverosPlugin = class {
24404
24613
  return child;
24405
24614
  }
24406
24615
  findVenvPython() {
24407
- const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path25.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24616
+ const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path26.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24408
24617
  if (process.platform === "win32") {
24409
- return path25.join(stateDir, "everos-venv", "Scripts", "python.exe");
24618
+ return path26.join(stateDir, "everos-venv", "Scripts", "python.exe");
24410
24619
  }
24411
- return path25.join(stateDir, "everos-venv", "bin", "python");
24620
+ return path26.join(stateDir, "everos-venv", "bin", "python");
24412
24621
  }
24413
24622
  /** 检测 venv 是否存在,不存在就自动创建 + 装 EverOS */
24414
24623
  async ensureVenv() {
24415
24624
  const venvPython = this.findVenvPython();
24416
- if (fs24.existsSync(venvPython)) return;
24417
- const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path25.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24418
- const venvDir = path25.join(stateDir, "everos-venv");
24419
- const everosSrc = path25.join(stateDir, "workspace", "research", "EverOS");
24625
+ if (fs25.existsSync(venvPython)) return;
24626
+ const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path26.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24627
+ const venvDir = path26.join(stateDir, "everos-venv");
24628
+ const everosSrc = path26.join(stateDir, "workspace", "research", "EverOS");
24420
24629
  console.log(`[everos] venv not found at ${venvDir}, auto-creating...`);
24421
24630
  console.log(`[everos] \u23F3 This may take a few minutes on first run...`);
24422
24631
  const pyCandidates = process.platform === "win32" ? ["python", "python3", "C:\\Python314\\python.exe", "C:\\Python313\\python.exe", "C:\\Python312\\python.exe"] : ["python3", "python"];
@@ -24438,9 +24647,9 @@ var EverosPlugin = class {
24438
24647
  console.log(`[everos] Creating venv with ${sysPython}...`);
24439
24648
  const { execSync: execSync3 } = await import("node:child_process");
24440
24649
  execSync3(`"${sysPython}" -m venv "${venvDir}"`, { stdio: "pipe", shell: true });
24441
- const pip = process.platform === "win32" ? path25.join(venvDir, "Scripts", "pip.exe") : path25.join(venvDir, "bin", "pip");
24442
- const everosReq = path25.join(this.getPythonDir(), "requirements.txt");
24443
- if (fs24.existsSync(everosReq)) {
24650
+ const pip = process.platform === "win32" ? path26.join(venvDir, "Scripts", "pip.exe") : path26.join(venvDir, "bin", "pip");
24651
+ const everosReq = path26.join(this.getPythonDir(), "requirements.txt");
24652
+ if (fs25.existsSync(everosReq)) {
24444
24653
  console.log(`[everos] Installing from requirements.txt...`);
24445
24654
  execSync3(`"${pip}" install -r "${everosReq}" -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
24446
24655
  } else {
@@ -24456,12 +24665,12 @@ var EverosPlugin = class {
24456
24665
  getPythonDir() {
24457
24666
  const dir = import.meta.dirname;
24458
24667
  const candidates = [
24459
- path25.join(dir, "python"),
24460
- path25.resolve(dir, "..", "src", "memory", "everos", "python"),
24461
- path25.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
24668
+ path26.join(dir, "python"),
24669
+ path26.resolve(dir, "..", "src", "memory", "everos", "python"),
24670
+ path26.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
24462
24671
  ];
24463
24672
  for (const candidate of candidates) {
24464
- if (fs24.existsSync(path25.join(candidate, "agentic_server.py"))) {
24673
+ if (fs25.existsSync(path26.join(candidate, "agentic_server.py"))) {
24465
24674
  return candidate;
24466
24675
  }
24467
24676
  }
@@ -24470,14 +24679,14 @@ var EverosPlugin = class {
24470
24679
  async ensureFcntlCompat() {
24471
24680
  if (process.platform !== "win32") return;
24472
24681
  const venvPython = this.findVenvPython();
24473
- const venvDir = path25.dirname(path25.dirname(venvPython));
24474
- const sitePackages = path25.join(venvDir, "Lib", "site-packages");
24475
- const target = path25.join(sitePackages, "fcntl.py");
24476
- if (fs24.existsSync(target)) return;
24477
- const source = path25.join(this.getPythonDir(), "fcntl_compat.py");
24478
- if (fs24.existsSync(source)) {
24682
+ const venvDir = path26.dirname(path26.dirname(venvPython));
24683
+ const sitePackages = path26.join(venvDir, "Lib", "site-packages");
24684
+ const target = path26.join(sitePackages, "fcntl.py");
24685
+ if (fs25.existsSync(target)) return;
24686
+ const source = path26.join(this.getPythonDir(), "fcntl_compat.py");
24687
+ if (fs25.existsSync(source)) {
24479
24688
  try {
24480
- fs24.copyFileSync(source, target);
24689
+ fs25.copyFileSync(source, target);
24481
24690
  console.log(`[everos] Installed fcntl compat shim to ${target}`);
24482
24691
  } catch (err) {
24483
24692
  console.warn(`[everos] Failed to install fcntl shim: ${err.message}`);
@@ -24524,21 +24733,21 @@ var EverosPlugin = class {
24524
24733
  init_task_manager();
24525
24734
 
24526
24735
  // src/skills/scanner.ts
24527
- import * as path26 from "node:path";
24528
- import * as fs25 from "node:fs";
24736
+ import * as path27 from "node:path";
24737
+ import * as fs26 from "node:fs";
24529
24738
  function scanSkills(skillsDir) {
24530
- if (!fs25.existsSync(skillsDir)) {
24739
+ if (!fs26.existsSync(skillsDir)) {
24531
24740
  console.log(`[skills] Directory not found: ${skillsDir}`);
24532
24741
  return [];
24533
24742
  }
24534
- const entries = fs25.readdirSync(skillsDir, { withFileTypes: true });
24743
+ const entries = fs26.readdirSync(skillsDir, { withFileTypes: true });
24535
24744
  const skills = [];
24536
24745
  for (const entry of entries) {
24537
24746
  if (!entry.isDirectory()) continue;
24538
- const skillMdPath = path26.join(skillsDir, entry.name, "SKILL.md");
24539
- if (!fs25.existsSync(skillMdPath)) continue;
24747
+ const skillMdPath = path27.join(skillsDir, entry.name, "SKILL.md");
24748
+ if (!fs26.existsSync(skillMdPath)) continue;
24540
24749
  try {
24541
- const content = fs25.readFileSync(skillMdPath, "utf-8");
24750
+ const content = fs26.readFileSync(skillMdPath, "utf-8");
24542
24751
  const frontmatter = parseFrontmatter2(content);
24543
24752
  if (!frontmatter.name) {
24544
24753
  console.warn(`[skills] Skipping ${entry.name}/SKILL.md: missing 'name' in frontmatter`);
@@ -24602,8 +24811,8 @@ function parseFrontmatter2(content) {
24602
24811
 
24603
24812
  // src/tools/SkillTool/SkillTool.ts
24604
24813
  init_registry();
24605
- import * as fs26 from "node:fs";
24606
- import * as path27 from "node:path";
24814
+ import * as fs27 from "node:fs";
24815
+ import * as path28 from "node:path";
24607
24816
 
24608
24817
  // src/tools/SkillTool/constants.ts
24609
24818
  var SKILL_TOOL_NAME2 = "Skill";
@@ -24680,12 +24889,12 @@ Important:
24680
24889
  `;
24681
24890
  }
24682
24891
  function loadSkillContent(skillName) {
24683
- const skillMdPath = path27.join(skillsDirPath, skillName, "SKILL.md");
24684
- if (!fs26.existsSync(skillMdPath)) return null;
24685
- const content = fs26.readFileSync(skillMdPath, "utf-8");
24892
+ const skillMdPath = path28.join(skillsDirPath, skillName, "SKILL.md");
24893
+ if (!fs27.existsSync(skillMdPath)) return null;
24894
+ const content = fs27.readFileSync(skillMdPath, "utf-8");
24686
24895
  const bodyMatch = content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)/);
24687
24896
  const body = bodyMatch ? bodyMatch[1] : content;
24688
- const skillDir = path27.dirname(skillMdPath);
24897
+ const skillDir = path28.dirname(skillMdPath);
24689
24898
  const normalizedDir = process.platform === "win32" ? skillDir.replace(/\\/g, "/") : skillDir;
24690
24899
  let finalContent = `Base directory for this skill: ${normalizedDir}
24691
24900
 
@@ -24959,12 +25168,12 @@ Examples:
24959
25168
  // src/tools/msg-husband.ts
24960
25169
  init_registry();
24961
25170
  init_live();
24962
- import fs27 from "node:fs";
24963
- import path28 from "node:path";
25171
+ import fs28 from "node:fs";
25172
+ import path29 from "node:path";
24964
25173
  function getHusbandFeishuId(workspace) {
24965
- const contactsPath = path28.join(workspace, "prompts", "contacts.md");
25174
+ const contactsPath = path29.join(workspace, "prompts", "contacts.md");
24966
25175
  try {
24967
- const text = fs27.readFileSync(contactsPath, "utf-8");
25176
+ const text = fs28.readFileSync(contactsPath, "utf-8");
24968
25177
  const m = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
24969
25178
  return m ? m[1] : null;
24970
25179
  } catch {
@@ -25106,8 +25315,8 @@ Examples:
25106
25315
  if (!to && !resolvedChannelId) {
25107
25316
  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 };
25108
25317
  }
25109
- const fs42 = await import("node:fs");
25110
- if (!fs42.existsSync(filePath)) {
25318
+ const fs43 = await import("node:fs");
25319
+ if (!fs43.existsSync(filePath)) {
25111
25320
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
25112
25321
  }
25113
25322
  const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
@@ -25135,7 +25344,7 @@ Examples:
25135
25344
  md: "text/markdown"
25136
25345
  };
25137
25346
  const mimeType = mimeTypeMap[ext] || "application/octet-stream";
25138
- const stat4 = fs42.statSync(filePath);
25347
+ const stat4 = fs43.statSync(filePath);
25139
25348
  const sizeMB = stat4.size / 1024 / 1024;
25140
25349
  if (sizeMB > 25) {
25141
25350
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
@@ -25164,8 +25373,8 @@ Examples:
25164
25373
  // src/tools/my-eyes.ts
25165
25374
  init_live();
25166
25375
  init_registry();
25167
- import * as fs28 from "node:fs";
25168
- import * as path29 from "node:path";
25376
+ import * as fs29 from "node:fs";
25377
+ import * as path30 from "node:path";
25169
25378
  var MIME_MAP = {
25170
25379
  ".jpg": "jpeg",
25171
25380
  ".jpeg": "jpeg",
@@ -25175,9 +25384,9 @@ var MIME_MAP = {
25175
25384
  ".bmp": "bmp"
25176
25385
  };
25177
25386
  function resolveLatestImage(specifiedPath, mediaDir) {
25178
- if (specifiedPath && fs28.existsSync(specifiedPath)) return specifiedPath;
25179
- if (!fs28.existsSync(mediaDir)) return null;
25180
- const files = fs28.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f)).map((f) => ({ name: f, p: path29.join(mediaDir, f), mtime: fs28.statSync(path29.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
25387
+ if (specifiedPath && fs29.existsSync(specifiedPath)) return specifiedPath;
25388
+ if (!fs29.existsSync(mediaDir)) return null;
25389
+ const files = fs29.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f)).map((f) => ({ name: f, p: path30.join(mediaDir, f), mtime: fs29.statSync(path30.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
25181
25390
  return files[0]?.p || null;
25182
25391
  }
25183
25392
  registry.register({
@@ -25202,15 +25411,15 @@ registry.register({
25202
25411
  if (!provider?.streamChat) {
25203
25412
  return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
25204
25413
  }
25205
- const mediaDir = path29.join(ctx.stateDir, "media", "inbound");
25414
+ const mediaDir = path30.join(ctx.stateDir, "media", "inbound");
25206
25415
  const imagePath = resolveLatestImage(args2.image_path, mediaDir);
25207
25416
  if (!imagePath) {
25208
25417
  return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
25209
25418
  }
25210
25419
  const rawPrompt = args2.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
25211
- const ext = path29.extname(imagePath).toLowerCase();
25420
+ const ext = path30.extname(imagePath).toLowerCase();
25212
25421
  const mime = MIME_MAP[ext] || "jpeg";
25213
- const imgB64 = fs28.readFileSync(imagePath).toString("base64");
25422
+ const imgB64 = fs29.readFileSync(imagePath).toString("base64");
25214
25423
  const userMsg = {
25215
25424
  role: "user",
25216
25425
  content: [
@@ -25247,14 +25456,14 @@ init_live();
25247
25456
  init_registry();
25248
25457
  import { execFile } from "node:child_process";
25249
25458
  import { promisify } from "node:util";
25250
- import * as fs29 from "node:fs";
25251
- import * as path30 from "node:path";
25459
+ import * as fs30 from "node:fs";
25460
+ import * as path31 from "node:path";
25252
25461
  import * as os3 from "node:os";
25253
25462
  var execFileAsync = promisify(execFile);
25254
- var VOICE_DIR = path30.join(os3.tmpdir(), "engine-voice");
25463
+ var VOICE_DIR = path31.join(os3.tmpdir(), "engine-voice");
25255
25464
  async function ttsCosyvoice(text, apiKey, model, voice, workspaceId, instruction) {
25256
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
25257
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25465
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25466
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25258
25467
  const script = `
25259
25468
  import sys, json, wave, time, threading
25260
25469
  import dashscope
@@ -25311,7 +25520,7 @@ print(f"OK: {len(pcm)} bytes")
25311
25520
  `;
25312
25521
  const configJson = JSON.stringify({ apiKey, model, voice, workspaceId, instruction });
25313
25522
  await execFileAsync("python3", ["-c", script, configJson, text, output], { timeout: 3e4 });
25314
- if (!fs29.existsSync(output) || fs29.statSync(output).size < 100) {
25523
+ if (!fs30.existsSync(output) || fs30.statSync(output).size < 100) {
25315
25524
  throw new Error("CosyVoice produced empty output");
25316
25525
  }
25317
25526
  return output;
@@ -25321,8 +25530,8 @@ var GPTSOVITS_REF_WAV = "/home/chong/voice/ref/shanshan_ref_v2.wav";
25321
25530
  var GPTSOVITS_REF_TEXT = "\u6625\u7720\u4E0D\u89C9\u6653\uFF0C\u5904\u5904\u95FB\u557C\u9E1F\uFF0C\u591C\u6765\u98CE\u96E8\u58F0\uFF0C\u82B1\u843D\u77E5\u591A\u5C11";
25322
25531
  var GPTSOVITS_REF_LANG = "zh";
25323
25532
  async function ttsGptsovits(text) {
25324
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
25325
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25533
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25534
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25326
25535
  const params = new URLSearchParams({
25327
25536
  text,
25328
25537
  text_language: "zh",
@@ -25333,13 +25542,13 @@ async function ttsGptsovits(text) {
25333
25542
  const res = await fetch(`${GPTSOVITS_API}/?${params}`);
25334
25543
  if (!res.ok) throw new Error(`GPT-SoVITS API ${res.status}`);
25335
25544
  const buf = Buffer.from(await res.arrayBuffer());
25336
- fs29.writeFileSync(output, buf);
25545
+ fs30.writeFileSync(output, buf);
25337
25546
  return output;
25338
25547
  }
25339
25548
  var EDGE_VOICE = "zh-CN-XiaoxiaoNeural";
25340
25549
  async function ttsEdge(text) {
25341
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
25342
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
25550
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25551
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
25343
25552
  const script = `
25344
25553
  import asyncio, edge_tts, sys
25345
25554
  async def main():
@@ -25365,7 +25574,7 @@ async function compressWav(wavPath) {
25365
25574
  "+faststart",
25366
25575
  m4aPath
25367
25576
  ], { timeout: 3e4 });
25368
- fs29.unlinkSync(wavPath);
25577
+ fs30.unlinkSync(wavPath);
25369
25578
  return m4aPath;
25370
25579
  } catch {
25371
25580
  return wavPath;
@@ -25433,10 +25642,10 @@ registry.register({
25433
25642
  } catch (e) {
25434
25643
  return { content: `TTS failed: ${e.message}`, isError: true };
25435
25644
  }
25436
- const ext = path30.extname(audioPath).toLowerCase();
25645
+ const ext = path31.extname(audioPath).toLowerCase();
25437
25646
  const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg" };
25438
25647
  const mimeType = mimeMap[ext] || "audio/mpeg";
25439
- const sizeKB = fs29.statSync(audioPath).size / 1024;
25648
+ const sizeKB = fs30.statSync(audioPath).size / 1024;
25440
25649
  const resolvedChannel = args2.channel || ctx.channel || "feishu";
25441
25650
  const target = ctx.channelTarget || ctx.from;
25442
25651
  try {
@@ -25446,7 +25655,7 @@ registry.register({
25446
25655
  filename: `voice_${Date.now()}${ext}`
25447
25656
  });
25448
25657
  try {
25449
- fs29.unlinkSync(audioPath);
25658
+ fs30.unlinkSync(audioPath);
25450
25659
  } catch {
25451
25660
  }
25452
25661
  return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
@@ -25463,8 +25672,8 @@ registry.register({
25463
25672
  // src/tools/my-selfie.ts
25464
25673
  init_live();
25465
25674
  init_registry();
25466
- import * as fs30 from "node:fs";
25467
- import * as path31 from "node:path";
25675
+ import * as fs31 from "node:fs";
25676
+ import * as path32 from "node:path";
25468
25677
  function getProxyDispatcher2() {
25469
25678
  const cfg = liveConfig.all();
25470
25679
  const proxy = cfg.providers?.xai?.proxy;
@@ -25521,16 +25730,48 @@ function detectMode(input) {
25521
25730
  return "direct";
25522
25731
  }
25523
25732
  async function generateWithFal(imageB64, prompt, resolution) {
25733
+ let aspectRatio;
25734
+ try {
25735
+ const refBuf = Buffer.from(imageB64, "base64");
25736
+ let w = 0, h = 0;
25737
+ if (refBuf[0] === 137 && refBuf[1] === 80) {
25738
+ w = refBuf.readUInt32BE(16);
25739
+ h = refBuf.readUInt32BE(20);
25740
+ } else if (refBuf[0] === 255 && refBuf[1] === 216) {
25741
+ let pos = 2;
25742
+ while (pos < refBuf.length - 1) {
25743
+ if (refBuf[pos] !== 255) {
25744
+ pos++;
25745
+ continue;
25746
+ }
25747
+ const marker = refBuf[pos + 1];
25748
+ if (marker === 192 || marker === 194) {
25749
+ h = refBuf.readUInt16BE(pos + 5);
25750
+ w = refBuf.readUInt16BE(pos + 7);
25751
+ break;
25752
+ }
25753
+ pos += 2 + refBuf.readUInt16BE(pos + 2);
25754
+ }
25755
+ }
25756
+ if (w > 0 && h > 0) {
25757
+ aspectRatio = `${w}/${h}`;
25758
+ console.log(`[my-selfie] ref image ${w}x${h}, aspect_ratio=${aspectRatio}`);
25759
+ }
25760
+ } catch (e) {
25761
+ console.warn(`[my-selfie] Failed to read ref dimensions: ${e.message}`);
25762
+ }
25763
+ const body = {
25764
+ image_url: `data:image/png;base64,${imageB64}`,
25765
+ prompt,
25766
+ num_images: 1,
25767
+ output_format: "jpeg",
25768
+ resolution
25769
+ };
25770
+ if (aspectRatio) body.aspect_ratio = aspectRatio;
25524
25771
  const res = await fetch(FAL_ENDPOINT, {
25525
25772
  method: "POST",
25526
25773
  headers: { "Authorization": `Key ${FAL_KEY}`, "Content-Type": "application/json" },
25527
- body: JSON.stringify({
25528
- image_url: `data:image/png;base64,${imageB64}`,
25529
- prompt,
25530
- num_images: 1,
25531
- output_format: "jpeg",
25532
- resolution
25533
- })
25774
+ body: JSON.stringify(body)
25534
25775
  });
25535
25776
  if (!res.ok) {
25536
25777
  const text = await res.text();
@@ -25739,12 +25980,12 @@ registry.register({
25739
25980
  const REFERENCES = getReferences(ctx);
25740
25981
  const refName = args2.reference || "default";
25741
25982
  const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
25742
- const refPath = path31.join(ctx.workspace, refEntry.p);
25743
- if (provider !== "autodl" && !fs30.existsSync(refPath)) {
25983
+ const refPath = path32.join(ctx.workspace, refEntry.p);
25984
+ if (provider !== "autodl" && !fs31.existsSync(refPath)) {
25744
25985
  return { content: `Error: reference image not found at ${refPath}`, isError: true };
25745
25986
  }
25746
25987
  const resolution = args2.resolution || DEFAULT_RESOLUTION;
25747
- const refB64 = fs30.existsSync(refPath) ? fs30.readFileSync(refPath).toString("base64") : "";
25988
+ const refB64 = fs31.existsSync(refPath) ? fs31.readFileSync(refPath).toString("base64") : "";
25748
25989
  let imageBuffer;
25749
25990
  try {
25750
25991
  if (provider === "autodl") {
@@ -25759,11 +26000,11 @@ registry.register({
25759
26000
  } catch (err) {
25760
26001
  return { content: `Selfie generation failed: ${err.message}`, isError: true };
25761
26002
  }
25762
- const imagesDir = path31.join(ctx.workspace, "images");
25763
- if (!fs30.existsSync(imagesDir)) fs30.mkdirSync(imagesDir, { recursive: true });
26003
+ const imagesDir = path32.join(ctx.workspace, "images");
26004
+ if (!fs31.existsSync(imagesDir)) fs31.mkdirSync(imagesDir, { recursive: true });
25764
26005
  const filename = `selfie_${Date.now()}.jpg`;
25765
- const outputPath = path31.join(imagesDir, filename);
25766
- fs30.writeFileSync(outputPath, imageBuffer);
26006
+ const outputPath = path32.join(imagesDir, filename);
26007
+ fs31.writeFileSync(outputPath, imageBuffer);
25767
26008
  const mgr = ctx.channelManager;
25768
26009
  if (mgr) {
25769
26010
  const resolvedChannel = ctx.channel || "feishu";
@@ -25774,11 +26015,11 @@ registry.register({
25774
26015
  mimeType: "image/jpeg"
25775
26016
  });
25776
26017
  } catch (err) {
25777
- return { content: `Selfie generated but send failed: ${err.message}. Image: ${path31.resolve(outputPath)}`, isError: false };
26018
+ return { content: `Selfie generated but send failed: ${err.message}. Image: ${path32.resolve(outputPath)}`, isError: false };
25778
26019
  }
25779
26020
  return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
25780
26021
  }
25781
- return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path31.resolve(outputPath)}` };
26022
+ return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path32.resolve(outputPath)}` };
25782
26023
  },
25783
26024
  isConcurrencySafe: () => false,
25784
26025
  interruptBehavior: () => "block",
@@ -26323,16 +26564,16 @@ var EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
26323
26564
  init_planModeState();
26324
26565
 
26325
26566
  // src/utils/plans.ts
26326
- import * as fs32 from "node:fs";
26327
- import * as path33 from "node:path";
26567
+ import * as fs33 from "node:fs";
26568
+ import * as path34 from "node:path";
26328
26569
  import * as crypto4 from "node:crypto";
26329
26570
  var MAX_SLUG_RETRIES = 10;
26330
26571
  function generateSlug() {
26331
26572
  return crypto4.randomBytes(4).toString("hex");
26332
26573
  }
26333
26574
  function getPlansDirectory(stateDir) {
26334
- const plansDir = path33.join(stateDir, "plans");
26335
- fs32.mkdirSync(plansDir, { recursive: true });
26575
+ const plansDir = path34.join(stateDir, "plans");
26576
+ fs33.mkdirSync(plansDir, { recursive: true });
26336
26577
  return plansDir;
26337
26578
  }
26338
26579
  var planSlugCache = /* @__PURE__ */ new Map();
@@ -26342,8 +26583,8 @@ function getPlanSlug(sessionId, stateDir) {
26342
26583
  const plansDir = getPlansDirectory(stateDir);
26343
26584
  for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
26344
26585
  slug = generateSlug();
26345
- const filePath = path33.join(plansDir, `${slug}.md`);
26346
- if (!fs32.existsSync(filePath)) {
26586
+ const filePath = path34.join(plansDir, `${slug}.md`);
26587
+ if (!fs33.existsSync(filePath)) {
26347
26588
  break;
26348
26589
  }
26349
26590
  }
@@ -26354,21 +26595,21 @@ function getPlanSlug(sessionId, stateDir) {
26354
26595
  function getPlanFilePath(sessionId, stateDir, agentId) {
26355
26596
  const slug = getPlanSlug(sessionId, stateDir);
26356
26597
  if (!agentId) {
26357
- return path33.join(getPlansDirectory(stateDir), `${slug}.md`);
26598
+ return path34.join(getPlansDirectory(stateDir), `${slug}.md`);
26358
26599
  }
26359
- return path33.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
26600
+ return path34.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
26360
26601
  }
26361
26602
  function getPlan(sessionId, stateDir, agentId) {
26362
26603
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
26363
26604
  try {
26364
- return fs32.readFileSync(filePath, "utf-8");
26605
+ return fs33.readFileSync(filePath, "utf-8");
26365
26606
  } catch {
26366
26607
  return null;
26367
26608
  }
26368
26609
  }
26369
26610
  function writePlan(sessionId, stateDir, content, agentId) {
26370
26611
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
26371
- fs32.writeFileSync(filePath, content, "utf-8");
26612
+ fs33.writeFileSync(filePath, content, "utf-8");
26372
26613
  return filePath;
26373
26614
  }
26374
26615
 
@@ -27005,7 +27246,7 @@ ${blocks.join("\n")}
27005
27246
  // src/engine-startup.ts
27006
27247
  init_registry();
27007
27248
  init_deferred();
27008
- init_features();
27249
+ init_features2();
27009
27250
  init_license();
27010
27251
 
27011
27252
  // src/tools/memory-bridge.ts
@@ -27083,7 +27324,7 @@ ${formatted}` };
27083
27324
  };
27084
27325
  }
27085
27326
  function createEverosGetTool() {
27086
- const fs42 = __require("node:fs/promises");
27327
+ const fs43 = __require("node:fs/promises");
27087
27328
  return {
27088
27329
  name: "memory_get",
27089
27330
  description: "Read a memory file by path.",
@@ -27099,7 +27340,7 @@ function createEverosGetTool() {
27099
27340
  handler: async (args2) => {
27100
27341
  try {
27101
27342
  const filePath = args2.path;
27102
- const content = await fs42.readFile(filePath, "utf-8");
27343
+ const content = await fs43.readFile(filePath, "utf-8");
27103
27344
  const lines = content.split("\n");
27104
27345
  const fromLine = args2.from ?? 1;
27105
27346
  const numLines = args2.lines ?? lines.length;
@@ -27572,11 +27813,11 @@ async function startEngine(config2, opts) {
27572
27813
  process.env.ENGINE7_WORKSPACE = config2.workspace;
27573
27814
  process.env.OPENCLAW_WORKSPACE = config2.workspace;
27574
27815
  process.env.ENGINE7_STATE_DIR = config2.stateDir;
27575
- fs41.mkdirSync(path44.join(config2.stateDir, "agents", "main", "memory"), { recursive: true });
27576
- fs41.mkdirSync(path44.join(config2.stateDir, "agents", "main", "sessions"), { recursive: true });
27577
- fs41.mkdirSync(path44.join(config2.stateDir, "logs"), { recursive: true });
27578
- fs41.mkdirSync(config2.workspace, { recursive: true });
27579
- fs41.mkdirSync(config2.mediaDir, { recursive: true });
27816
+ fs42.mkdirSync(path45.join(config2.stateDir, "agents", "main", "memory"), { recursive: true });
27817
+ fs42.mkdirSync(path45.join(config2.stateDir, "agents", "main", "sessions"), { recursive: true });
27818
+ fs42.mkdirSync(path45.join(config2.stateDir, "logs"), { recursive: true });
27819
+ fs42.mkdirSync(config2.workspace, { recursive: true });
27820
+ fs42.mkdirSync(config2.mediaDir, { recursive: true });
27580
27821
  try {
27581
27822
  process.chdir(config2.workspace);
27582
27823
  } catch (e) {
@@ -27631,7 +27872,7 @@ async function startEngine(config2, opts) {
27631
27872
  const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
27632
27873
  initSessionMemory2({
27633
27874
  workspace: config2.workspace,
27634
- stateDir: path44.join(config2.stateDir, "session-memory"),
27875
+ stateDir: path45.join(config2.stateDir, "session-memory"),
27635
27876
  provider,
27636
27877
  model: config2.provider.modelId || config2.model || "deepseek-v4-flash",
27637
27878
  features: config2.profile.features
@@ -27661,9 +27902,9 @@ async function startEngine(config2, opts) {
27661
27902
  if (config2.hooks) {
27662
27903
  loadHooksFromConfig({ hooks: config2.hooks });
27663
27904
  }
27664
- const hooksPath = path44.join(config2.workspace, ".hooks.json");
27905
+ const hooksPath = path45.join(config2.workspace, ".hooks.json");
27665
27906
  loadHooksFromFile(hooksPath);
27666
- const settingsHooksPath = path44.join(config2.stateDir, "settings.json");
27907
+ const settingsHooksPath = path45.join(config2.stateDir, "settings.json");
27667
27908
  loadHooksFromFile(settingsHooksPath);
27668
27909
  console.log(`[hooks] Loaded hooks configuration`);
27669
27910
  registerCallbackHook("PreCompact", {
@@ -27677,18 +27918,18 @@ async function startEngine(config2, opts) {
27677
27918
  const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
27678
27919
  const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
27679
27920
  const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
27680
- const dailyDir = path44.join(workspace, "memory", "daily");
27681
- const dailyPath = path44.join(dailyDir, `${dateStr}.md`);
27921
+ const dailyDir = path45.join(workspace, "memory", "daily");
27922
+ const dailyPath = path45.join(dailyDir, `${dateStr}.md`);
27682
27923
  try {
27683
- const fs42 = await import("node:fs");
27684
- if (!fs42.existsSync(dailyDir)) {
27685
- fs42.mkdirSync(dailyDir, { recursive: true });
27924
+ const fs43 = await import("node:fs");
27925
+ if (!fs43.existsSync(dailyDir)) {
27926
+ fs43.mkdirSync(dailyDir, { recursive: true });
27686
27927
  }
27687
- const sessionsDir = path44.join(config2.stateDir, "agents", "main", "sessions");
27688
- const sessionFile = path44.join(sessionsDir, `${sessionId}.jsonl`);
27928
+ const sessionsDir = path45.join(config2.stateDir, "agents", "main", "sessions");
27929
+ const sessionFile = path45.join(sessionsDir, `${sessionId}.jsonl`);
27689
27930
  const recentLines = [];
27690
- if (fs42.existsSync(sessionFile)) {
27691
- const content = fs42.readFileSync(sessionFile, "utf-8");
27931
+ if (fs43.existsSync(sessionFile)) {
27932
+ const content = fs43.readFileSync(sessionFile, "utf-8");
27692
27933
  const lines = content.trim().split("\n").filter(Boolean);
27693
27934
  const userLines = lines.filter((l) => {
27694
27935
  try {
@@ -27718,10 +27959,10 @@ async function startEngine(config2, opts) {
27718
27959
  const entry = `${header}
27719
27960
  ${body}
27720
27961
  `;
27721
- if (fs42.existsSync(dailyPath)) {
27722
- fs42.appendFileSync(dailyPath, entry);
27962
+ if (fs43.existsSync(dailyPath)) {
27963
+ fs43.appendFileSync(dailyPath, entry);
27723
27964
  } else {
27724
- fs42.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27965
+ fs43.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27725
27966
  ${entry}`);
27726
27967
  }
27727
27968
  console.log(`[hooks] PreCompact: saved ${recentLines.length} lines to ${dailyPath}`);
@@ -27737,16 +27978,16 @@ ${entry}`);
27737
27978
  const workspace = input.cwd || input.workspace || "";
27738
27979
  if (!workspace) return { continue: true };
27739
27980
  try {
27740
- const fs42 = await import("node:fs");
27741
- const bufferPath = path44.join(workspace, "memory", "working-buffer.md");
27742
- if (fs42.existsSync(bufferPath)) {
27743
- const stat4 = fs42.statSync(bufferPath);
27981
+ const fs43 = await import("node:fs");
27982
+ const bufferPath = path45.join(workspace, "memory", "working-buffer.md");
27983
+ if (fs43.existsSync(bufferPath)) {
27984
+ const stat4 = fs43.statSync(bufferPath);
27744
27985
  const ageMs = Date.now() - stat4.mtimeMs;
27745
27986
  const ageMin = Math.round(ageMs / 6e4);
27746
27987
  if (ageMin > 10) {
27747
27988
  console.warn(`[hooks] PostCompact: \u26A0\uFE0F working-buffer.md is ${ageMin}min old (last modified ${stat4.mtime.toISOString()}) \u2014 content may be stale!`);
27748
27989
  }
27749
- const content = fs42.readFileSync(bufferPath, "utf-8");
27990
+ const content = fs43.readFileSync(bufferPath, "utf-8");
27750
27991
  if (content.trim()) {
27751
27992
  console.log(`[hooks] PostCompact: injecting working-buffer (${content.length} chars, ${ageMin}min old)`);
27752
27993
  return {
@@ -27791,7 +28032,7 @@ ${content}`
27791
28032
  return `${hr}h ${remMin}m`;
27792
28033
  }
27793
28034
  if (config2.skills?.enabled !== false) {
27794
- const skillsDir = config2.skills?.path ? path44.isAbsolute(config2.skills.path) ? config2.skills.path : path44.resolve(config2.workspace, config2.skills.path) : path44.resolve(config2.workspace, "skills");
28035
+ const skillsDir = config2.skills?.path ? path45.isAbsolute(config2.skills.path) ? config2.skills.path : path45.resolve(config2.workspace, config2.skills.path) : path45.resolve(config2.workspace, "skills");
27795
28036
  const modelDef2 = config2.provider.models.find((m) => m.id === config2.model);
27796
28037
  const contextWindowTokens = modelDef2?.contextWindow;
27797
28038
  const skills = scanSkills(skillsDir);
@@ -27810,8 +28051,8 @@ ${content}`
27810
28051
  workspace: config2.workspace
27811
28052
  });
27812
28053
  const systemPrompt = [systemStable, systemDynamic].join("\n\n");
27813
- const promptDumpPath = path44.join(config2.workspace, ".system-prompt.txt");
27814
- fs41.writeFileSync(promptDumpPath, systemPrompt);
28054
+ const promptDumpPath = path45.join(config2.workspace, ".system-prompt.txt");
28055
+ fs42.writeFileSync(promptDumpPath, systemPrompt);
27815
28056
  console.log(`System prompt: ${systemStable.length} chars stable + ${systemDynamic.length} chars dynamic \u2192 ${promptDumpPath}`);
27816
28057
  const modelDef = config2.provider.models.find((m) => m.id === config2.model);
27817
28058
  const modelContextWindow = modelDef?.contextWindow;
@@ -27936,13 +28177,11 @@ ${content}`
27936
28177
  model: config2.model,
27937
28178
  modelInputs: modelDef?.input || ["text"],
27938
28179
  systemPrompt,
27939
- features: config2.profile.features,
27940
28180
  channels: config2.channels,
27941
28181
  config: config2,
27942
28182
  // tool 读自己配置用
27943
28183
  recallProvider: memoryRecallProvider || void 0,
27944
28184
  extractProvider: memoryExtractProvider || void 0,
27945
- topics: config2.topics,
27946
28185
  everosCfg: config2.everos,
27947
28186
  mcpManager
27948
28187
  };
@@ -27959,7 +28198,6 @@ ${content}`
27959
28198
  model: visionConfig.modelId,
27960
28199
  modelInputs: visionModelDef?.input || ["text", "image"],
27961
28200
  systemPrompt,
27962
- features: config2.profile.features,
27963
28201
  channels: config2.channels,
27964
28202
  config: config2,
27965
28203
  // tool 读自己配置用
@@ -28010,7 +28248,6 @@ ${content}`
28010
28248
  model: p.model,
28011
28249
  modelInputs: p.modelInputs,
28012
28250
  systemPrompt,
28013
- features: config2.profile.features,
28014
28251
  channels: config2.channels,
28015
28252
  config: config2,
28016
28253
  recallProvider: memoryRecallProvider || void 0,
@@ -28054,7 +28291,6 @@ ${content}`
28054
28291
  model: modelId,
28055
28292
  modelInputs: modelDef2.input || ["text"],
28056
28293
  systemPrompt,
28057
- features: config2.profile.features,
28058
28294
  channels: config2.channels,
28059
28295
  recallProvider: memoryRecallProvider || void 0,
28060
28296
  extractProvider: memoryExtractProvider || void 0
@@ -28839,8 +29075,7 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
28839
29075
  const featureKeys = ["topic-recall", "topic-extract", "session-memory"];
28840
29076
  if (featureKeys.includes(ctx.command)) {
28841
29077
  const key = ctx.command;
28842
- const f = deps.features || {};
28843
- const cur = f[key] === false ? "off" : "on";
29078
+ const cur = getFeature(key) === false ? "off" : "on";
28844
29079
  const rawState = (ctx.args.state || "").trim().toLowerCase();
28845
29080
  if (rawState === "") {
28846
29081
  await ctx.reply(`\u{1F4CA} ${key}: **${cur}**`);
@@ -28856,39 +29091,9 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
28856
29091
  return;
28857
29092
  }
28858
29093
  const next = rawState === "on";
28859
- if (!config2.features) config2.features = {};
28860
- config2.features[key] = next;
28861
- if (deps.features) deps.features[key] = next;
28862
- try {
28863
- const fs42 = await import("fs");
28864
- const pathMod = await import("path");
28865
- let cfgPath = config2._configFilePath;
28866
- if (!cfgPath || !fs42.existsSync(cfgPath)) {
28867
- const __filename = fileURLToPath(import.meta.url);
28868
- const __dirname = pathMod.dirname(__filename);
28869
- cfgPath = pathMod.resolve(__dirname, "../configs", pathMod.basename(cfgPath || "engine-config.json"));
28870
- }
28871
- const cfg = JSON.parse(fs42.readFileSync(cfgPath, "utf-8"));
28872
- let featObj = null;
28873
- if (cfg.agents?.defaults?.features) {
28874
- featObj = cfg.agents.defaults.features;
28875
- } else if (cfg.agents?.defaults) {
28876
- cfg.agents.defaults.features = {};
28877
- featObj = cfg.agents.defaults.features;
28878
- }
28879
- if (featObj) {
28880
- featObj[key] = next;
28881
- fs42.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
28882
- console.log(`[${ctx.command}] ${key} ${cur} \u2192 ${rawState} (disk persisted)`);
28883
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
28884
- } else {
28885
- console.warn(`[${ctx.command}] could not locate features in config, in-memory only`);
28886
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**\uFF08\u5185\u5B58\u751F\u6548\uFF0C\u78C1\u76D8\u672A\u627E\u5230 features \u8DEF\u5F84\uFF09`);
28887
- }
28888
- } catch (e) {
28889
- console.warn(`[${ctx.command}] disk write failed: ${e.message}`);
28890
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**\uFF08\u5185\u5B58\u751F\u6548\uFF0C\u78C1\u76D8\u5199\u5931\u8D25\uFF09`);
28891
- }
29094
+ await liveConfig.set(`agents.defaults.features.${key}`, next);
29095
+ console.log(`[${ctx.command}] ${key} ${cur} \u2192 ${rawState} (live + persisted)`);
29096
+ await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
28892
29097
  return;
28893
29098
  }
28894
29099
  if (ctx.command === "model") {
@@ -29006,11 +29211,11 @@ Auto-routing disabled \u2014 all messages use this model.
29006
29211
  const input = (ctx.args.model || "").trim();
29007
29212
  const configPath2 = config2._configFilePath;
29008
29213
  let writePath = configPath2;
29009
- if (configPath2 && !fs41.existsSync(configPath2)) {
29214
+ if (configPath2 && !fs42.existsSync(configPath2)) {
29010
29215
  const __pFile = fileURLToPath(import.meta.url);
29011
- const __pDir = path44.dirname(__pFile);
29012
- const altPath = path44.join(path44.resolve(__pDir, "../configs"), path44.basename(configPath2));
29013
- if (fs41.existsSync(altPath)) {
29216
+ const __pDir = path45.dirname(__pFile);
29217
+ const altPath = path45.join(path45.resolve(__pDir, "../configs"), path45.basename(configPath2));
29218
+ if (fs42.existsSync(altPath)) {
29014
29219
  console.warn(`[primary] Config not found at ${configPath2}, falling back to ${altPath}`);
29015
29220
  writePath = altPath;
29016
29221
  }
@@ -29054,14 +29259,14 @@ Use full ref like \`/primary ${candidates[0].ref}\``);
29054
29259
  return;
29055
29260
  }
29056
29261
  try {
29057
- const raw = await fs41.promises.readFile(writePath, "utf-8");
29262
+ const raw = await fs42.promises.readFile(writePath, "utf-8");
29058
29263
  const cfg = JSON.parse(raw);
29059
29264
  if (!cfg.agents?.defaults?.model) {
29060
29265
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
29061
29266
  return;
29062
29267
  }
29063
29268
  cfg.agents.defaults.model.primary = target;
29064
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29269
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29065
29270
  console.log(`[primary] Persisted primary=${target} to ${writePath}`);
29066
29271
  await ctx.reply(`\u2705 Primary model set to **${target}** (${candidates[0].name})
29067
29272
  Written to config. **Restart required** to take effect.`);
@@ -29074,11 +29279,11 @@ Written to config. **Restart required** to take effect.`);
29074
29279
  const input = (ctx.args.model || "").trim();
29075
29280
  const configPath2 = config2._configFilePath;
29076
29281
  let writePath = configPath2;
29077
- if (configPath2 && !fs41.existsSync(configPath2)) {
29282
+ if (configPath2 && !fs42.existsSync(configPath2)) {
29078
29283
  const __pFile = fileURLToPath(import.meta.url);
29079
- const __pDir = path44.dirname(__pFile);
29080
- const altPath = path44.join(path44.resolve(__pDir, "../configs"), path44.basename(configPath2));
29081
- if (fs41.existsSync(altPath)) {
29284
+ const __pDir = path45.dirname(__pFile);
29285
+ const altPath = path45.join(path45.resolve(__pDir, "../configs"), path45.basename(configPath2));
29286
+ if (fs42.existsSync(altPath)) {
29082
29287
  console.warn(`[vision-primary] Config not found at ${configPath2}, falling back to ${altPath}`);
29083
29288
  writePath = altPath;
29084
29289
  }
@@ -29123,7 +29328,7 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
29123
29328
  return;
29124
29329
  }
29125
29330
  try {
29126
- const raw = await fs41.promises.readFile(writePath, "utf-8");
29331
+ const raw = await fs42.promises.readFile(writePath, "utf-8");
29127
29332
  const cfg = JSON.parse(raw);
29128
29333
  if (!cfg.agents?.defaults?.model) {
29129
29334
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
@@ -29131,12 +29336,12 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
29131
29336
  }
29132
29337
  if (input === "auto" || input === "reset") {
29133
29338
  delete cfg.agents.defaults.model.vision;
29134
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29339
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29135
29340
  console.log(`[vision-primary] Cleared vision primary in ${writePath}`);
29136
29341
  await ctx.reply(`\u2705 Vision primary cleared (auto). Written to config. Hot-reload will apply.`);
29137
29342
  } else {
29138
29343
  cfg.agents.defaults.model.vision = target;
29139
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29344
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29140
29345
  console.log(`[vision-primary] Persisted vision=${target} to ${writePath}`);
29141
29346
  await ctx.reply(`\u2705 Vision primary set to **${target}** (${candidates[0].name})
29142
29347
  Written to config. Hot-reload will apply.`);
@@ -29359,7 +29564,7 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
29359
29564
  console.log(`[vision] Downloading image: ${att.filename}`);
29360
29565
  let rawBuffer;
29361
29566
  if (att.url.startsWith("file://")) {
29362
- rawBuffer = fs41.readFileSync(decodeURIComponent(att.url.slice(7)));
29567
+ rawBuffer = fs42.readFileSync(decodeURIComponent(att.url.slice(7)));
29363
29568
  } else {
29364
29569
  rawBuffer = await downloadImage2(att.url);
29365
29570
  }
@@ -29367,8 +29572,8 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
29367
29572
  const ext = detected.split("/")[1] || "png";
29368
29573
  const resized = await maybeResizeAndDownsampleImageBuffer2(rawBuffer, rawBuffer.length, ext);
29369
29574
  const imageId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
29370
- const savedPath = path44.join(config2.mediaDir, `${imageId}.${ext}`);
29371
- fs41.writeFileSync(savedPath, resized.buffer);
29575
+ const savedPath = path45.join(config2.mediaDir, `${imageId}.${ext}`);
29576
+ fs42.writeFileSync(savedPath, resized.buffer);
29372
29577
  savedPaths.push(savedPath);
29373
29578
  console.log(`[vision] Saved: ${savedPath} (${resized.buffer.length}B)`);
29374
29579
  imageBlocks.push({
@@ -29394,8 +29599,8 @@ ${pathStr}` }];
29394
29599
  }
29395
29600
  const nonImageAttachments = inbound.attachments?.filter((a) => !a.contentType.startsWith("image/"));
29396
29601
  if (nonImageAttachments && nonImageAttachments.length > 0) {
29397
- const outDir = path44.join(config2.mediaDir, sessionId);
29398
- fs41.mkdirSync(outDir, { recursive: true });
29602
+ const outDir = path45.join(config2.mediaDir, sessionId);
29603
+ fs42.mkdirSync(outDir, { recursive: true });
29399
29604
  const resolved = [];
29400
29605
  for (const att of nonImageAttachments) {
29401
29606
  console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
@@ -29403,9 +29608,9 @@ ${pathStr}` }];
29403
29608
  const resp = await fetch(att.url);
29404
29609
  if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
29405
29610
  const buffer = Buffer.from(await resp.arrayBuffer());
29406
- const safeName2 = path44.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
29407
- const savedPath = path44.join(outDir, safeName2);
29408
- fs41.writeFileSync(savedPath, buffer);
29611
+ const safeName2 = path45.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
29612
+ const savedPath = path45.join(outDir, safeName2);
29613
+ fs42.writeFileSync(savedPath, buffer);
29409
29614
  resolved.push(savedPath);
29410
29615
  console.log(`[file] Saved: ${savedPath} (${buffer.length}B)`);
29411
29616
  } catch (err) {
@@ -29759,7 +29964,7 @@ ${pathStr}` }];
29759
29964
  console.warn("[cognifold] watcher: config.workspace \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 proactive \u5199\u5165");
29760
29965
  return;
29761
29966
  }
29762
- const pFile = path44.join(wsDir, ".cognifold-proactive.json");
29967
+ const pFile = path45.join(wsDir, ".cognifold-proactive.json");
29763
29968
  const cognifoldBaseUrl = config2.cognifold?.baseUrl || "http://127.0.0.1:9001";
29764
29969
  const cognifoldSessionId = cfSessionId;
29765
29970
  const rawSuggestions = data.suggestions || data.actions || (data.intent_id ? [data] : []);
@@ -29807,14 +30012,14 @@ ${pathStr}` }];
29807
30012
  return s;
29808
30013
  }));
29809
30014
  try {
29810
- fs41.writeFileSync(pFile, JSON.stringify(enriched, null, 2));
30015
+ fs42.writeFileSync(pFile, JSON.stringify(enriched, null, 2));
29811
30016
  console.log(`[cognifold] proactive suggestions saved (${enriched.length} total)`);
29812
30017
  } catch (e) {
29813
30018
  console.error(`[cognifold] failed to save proactive: ${e.message}`);
29814
30019
  }
29815
30020
  if (enriched.length > 0) {
29816
- const promptFile = path44.join(config2.workspace, "prompts", "cognifold-proactive.md");
29817
- const promptText = fs41.existsSync(promptFile) ? fs41.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
30021
+ const promptFile = path45.join(config2.workspace, "prompts", "cognifold-proactive.md");
30022
+ const promptText = fs42.existsSync(promptFile) ? fs42.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
29818
30023
  const actionsJson = JSON.stringify(enriched, null, 2);
29819
30024
  const sessionId = cfSessionId;
29820
30025
  const mainSessionId = sessions.getSessionId("scope:main");
@@ -29981,12 +30186,12 @@ async function doReloadConfig(config2, deps, provider) {
29981
30186
  try {
29982
30187
  const savedConfigPath = config2._configFilePath;
29983
30188
  let reloadConfigPath = savedConfigPath;
29984
- if (!fs41.existsSync(reloadConfigPath)) {
30189
+ if (!fs42.existsSync(reloadConfigPath)) {
29985
30190
  const __filename = fileURLToPath(import.meta.url);
29986
- const __dirname = path44.dirname(__filename);
29987
- const engineConfigsDir = path44.resolve(__dirname, "../configs");
29988
- const altPath = path44.join(engineConfigsDir, path44.basename(savedConfigPath));
29989
- if (fs41.existsSync(altPath)) {
30191
+ const __dirname = path45.dirname(__filename);
30192
+ const engineConfigsDir = path45.resolve(__dirname, "../configs");
30193
+ const altPath = path45.join(engineConfigsDir, path45.basename(savedConfigPath));
30194
+ if (fs42.existsSync(altPath)) {
29990
30195
  console.warn(`[reload] Config not found at ${reloadConfigPath}, falling back to ${altPath} (dev mode)`);
29991
30196
  reloadConfigPath = altPath;
29992
30197
  }
@@ -30059,12 +30264,6 @@ async function doReloadConfig(config2, deps, provider) {
30059
30264
  deps.extractProvider = newExtract;
30060
30265
  changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
30061
30266
  }
30062
- if (newConfig.topics) {
30063
- deps.topics = newConfig.topics;
30064
- }
30065
- if (newConfig.profile?.features) {
30066
- deps.features = newConfig.profile.features;
30067
- }
30068
30267
  try {
30069
30268
  const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
30070
30269
  setAutoDreamConfig2(newConfig);
@@ -30110,7 +30309,7 @@ async function doReloadConfig(config2, deps, provider) {
30110
30309
  } catch (err) {
30111
30310
  console.error(`[reload] Failed: ${err.message}`);
30112
30311
  try {
30113
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
30312
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
30114
30313
  ${err.stack}
30115
30314
  `);
30116
30315
  } catch {
@@ -30121,36 +30320,36 @@ ${err.stack}
30121
30320
  function startConfigWatcher(config2, deps, provider) {
30122
30321
  const raw = config2._configFilePath;
30123
30322
  let configPath2 = raw;
30124
- if (!fs41.existsSync(configPath2)) {
30125
- configPath2 = path44.resolve(raw);
30323
+ if (!fs42.existsSync(configPath2)) {
30324
+ configPath2 = path45.resolve(raw);
30126
30325
  }
30127
- if (!fs41.existsSync(configPath2)) {
30326
+ if (!fs42.existsSync(configPath2)) {
30128
30327
  const __filename2 = fileURLToPath(import.meta.url);
30129
- const __dirname22 = path44.dirname(__filename2);
30130
- configPath2 = path44.resolve(__dirname22, "..", raw);
30328
+ const __dirname22 = path45.dirname(__filename2);
30329
+ configPath2 = path45.resolve(__dirname22, "..", raw);
30131
30330
  }
30132
- if (!fs41.existsSync(configPath2)) {
30331
+ if (!fs42.existsSync(configPath2)) {
30133
30332
  console.warn(`[config-watch] config path invalid: ${configPath2}, watcher disabled`);
30134
30333
  try {
30135
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath2}
30334
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath2}
30136
30335
  `);
30137
30336
  } catch {
30138
30337
  }
30139
30338
  return null;
30140
30339
  }
30141
30340
  let debounceTimer = null;
30142
- const watcher = fs41.watch(configPath2, { persistent: true }, (eventType) => {
30341
+ const watcher = fs42.watch(configPath2, { persistent: true }, (eventType) => {
30143
30342
  if (debounceTimer) clearTimeout(debounceTimer);
30144
30343
  debounceTimer = setTimeout(async () => {
30145
30344
  console.log(`[config-watch] file changed (${eventType}), reloading...`);
30146
30345
  try {
30147
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
30346
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
30148
30347
  `);
30149
30348
  } catch {
30150
30349
  }
30151
30350
  const result = await doReloadConfig(config2, deps, provider);
30152
30351
  try {
30153
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
30352
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
30154
30353
  `);
30155
30354
  } catch {
30156
30355
  }
@@ -30159,30 +30358,30 @@ function startConfigWatcher(config2, deps, provider) {
30159
30358
  watcher.on("error", (err) => {
30160
30359
  console.error(`[config-watch] error: ${err.message}`);
30161
30360
  try {
30162
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
30361
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
30163
30362
  `);
30164
30363
  } catch {
30165
30364
  }
30166
30365
  });
30167
30366
  console.log(`[config-watch] watching ${configPath2}`);
30168
30367
  try {
30169
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath2}
30368
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath2}
30170
30369
  `);
30171
30370
  } catch {
30172
30371
  }
30173
30372
  return watcher;
30174
30373
  }
30175
30374
  function startSecretsWatcher(config2, deps, provider) {
30176
- const secretsDir = path44.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
30177
- const cfgBase = path44.basename(config2._configFilePath || "", ".json");
30375
+ const secretsDir = path45.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
30376
+ const cfgBase = path45.basename(config2._configFilePath || "", ".json");
30178
30377
  const secretCandidates = [
30179
- path44.join(secretsDir, `${cfgBase}.env`),
30180
- path44.join(path44.dirname(config2._configFilePath || ""), `.env.${cfgBase}`),
30181
- path44.join(path44.dirname(config2._configFilePath || ""), ".env")
30378
+ path45.join(secretsDir, `${cfgBase}.env`),
30379
+ path45.join(path45.dirname(config2._configFilePath || ""), `.env.${cfgBase}`),
30380
+ path45.join(path45.dirname(config2._configFilePath || ""), ".env")
30182
30381
  ];
30183
30382
  let secretsPath = null;
30184
30383
  for (const p of secretCandidates) {
30185
- if (fs41.existsSync(p)) {
30384
+ if (fs42.existsSync(p)) {
30186
30385
  secretsPath = p;
30187
30386
  break;
30188
30387
  }
@@ -30195,9 +30394,9 @@ function startSecretsWatcher(config2, deps, provider) {
30195
30394
  let activeWatcher = null;
30196
30395
  const startWatch = () => {
30197
30396
  if (activeWatcher) activeWatcher.close();
30198
- activeWatcher = fs41.watch(secretsPath, { persistent: true }, (eventType) => {
30397
+ activeWatcher = fs42.watch(secretsPath, { persistent: true }, (eventType) => {
30199
30398
  if (eventType === "rename") {
30200
- if (fs41.existsSync(secretsPath)) {
30399
+ if (fs42.existsSync(secretsPath)) {
30201
30400
  console.log("[secrets-watch] rename detected, re-watching file...");
30202
30401
  startWatch();
30203
30402
  } else {
@@ -30209,7 +30408,7 @@ function startSecretsWatcher(config2, deps, provider) {
30209
30408
  debounceTimer = setTimeout(async () => {
30210
30409
  console.log(`[secrets-watch] file changed (${eventType}), reloading secrets...`);
30211
30410
  try {
30212
- const content = fs41.readFileSync(secretsPath, "utf-8");
30411
+ const content = fs42.readFileSync(secretsPath, "utf-8");
30213
30412
  let updated = 0;
30214
30413
  for (const line of content.split("\n")) {
30215
30414
  const trimmed = line.trim();
@@ -30228,7 +30427,7 @@ function startSecretsWatcher(config2, deps, provider) {
30228
30427
  const result = await doReloadConfig(config2, deps, provider);
30229
30428
  console.log(`[secrets-watch] config reloaded: ok=${result.ok} changes=${result.changes.join(",")}`);
30230
30429
  try {
30231
- fs41.appendFileSync(path44.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
30430
+ fs42.appendFileSync(path45.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
30232
30431
  `);
30233
30432
  } catch {
30234
30433
  }
@@ -30289,7 +30488,7 @@ if (!configPath) {
30289
30488
  }
30290
30489
  if (args[0] === "features") {
30291
30490
  const { getLicenseStatus: getLicenseStatus2 } = await Promise.resolve().then(() => (init_license(), license_exports));
30292
- const { listFeatures: listFeatures2 } = await Promise.resolve().then(() => (init_features(), features_exports));
30491
+ const { listFeatures: listFeatures2 } = await Promise.resolve().then(() => (init_features2(), features_exports));
30293
30492
  const stateDir = process.env.ENGINE_STATE_DIR || ".";
30294
30493
  const status = getLicenseStatus2(stateDir);
30295
30494
  const allFeatures = listFeatures2();