engine7 7.1.35 → 7.1.37

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.
@@ -174,17 +174,19 @@ var init_types = __esm({
174
174
  "src/messages/types.ts"() {
175
175
  "use strict";
176
176
  msg = {
177
- system: (content) => ({ role: "system", content }),
178
- user: (content) => ({ role: "user", content }),
177
+ system: (content) => ({ role: "system", content, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
178
+ user: (content) => ({ role: "user", content, timestamp: (/* @__PURE__ */ new Date()).toISOString() }),
179
179
  assistant: (content, tool_calls) => ({
180
180
  role: "assistant",
181
181
  content,
182
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
182
183
  ...tool_calls ? { tool_calls } : {}
183
184
  }),
184
185
  tool: (tool_call_id, content, isError) => ({
185
186
  role: "tool",
186
187
  tool_call_id,
187
188
  content,
189
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
188
190
  ...isError ? { is_error: true } : {}
189
191
  })
190
192
  };
@@ -2139,9 +2141,9 @@ function isAutoMemPath(absolutePath, workspace) {
2139
2141
  return normalizedPath.startsWith(getAutoMemPath(workspace));
2140
2142
  }
2141
2143
  async function ensureMemoryDirExists(memoryDir) {
2142
- const fs42 = await import("node:fs");
2144
+ const fs43 = await import("node:fs");
2143
2145
  try {
2144
- await fs42.promises.mkdir(memoryDir, { recursive: true });
2146
+ await fs43.promises.mkdir(memoryDir, { recursive: true });
2145
2147
  } catch (e) {
2146
2148
  const code = e?.code;
2147
2149
  if (code !== "EEXIST") {
@@ -3343,6 +3345,41 @@ var init_live = __esm({
3343
3345
  }
3344
3346
  Object.assign(this.current, newConfig);
3345
3347
  }
3348
+ /** 改活树上的值;文件承载路径同步持久化(read-modify-write 回 config 文件) */
3349
+ async set(dotPath, val) {
3350
+ if (!this.current) throw new Error("[liveConfig] not initialized \u2014 call liveConfig.init() first");
3351
+ const keys = dotPath.split(".");
3352
+ let obj = this.current;
3353
+ for (let i = 0; i < keys.length - 1; i++) {
3354
+ if (obj[keys[i]] == null) obj[keys[i]] = {};
3355
+ obj = obj[keys[i]];
3356
+ }
3357
+ obj[keys[keys.length - 1]] = val;
3358
+ await this.persistToFile(dotPath, val);
3359
+ }
3360
+ /** 把改动写回 config 文件对应段(找不到文件路径则只内存生效) */
3361
+ async persistToFile(dotPath, val) {
3362
+ const fs43 = await import("node:fs");
3363
+ const cfgPath = this.current?._configFilePath;
3364
+ if (!cfgPath || !fs43.existsSync(cfgPath)) {
3365
+ console.warn(`[liveConfig] set: no config file path, in-memory only (${dotPath})`);
3366
+ return;
3367
+ }
3368
+ try {
3369
+ const raw = JSON.parse(fs43.readFileSync(cfgPath, "utf-8"));
3370
+ const keys = dotPath.split(".");
3371
+ let o = raw;
3372
+ for (let i = 0; i < keys.length - 1; i++) {
3373
+ if (o[keys[i]] == null) o[keys[i]] = {};
3374
+ o = o[keys[i]];
3375
+ }
3376
+ o[keys[keys.length - 1]] = val;
3377
+ fs43.writeFileSync(cfgPath, JSON.stringify(raw, null, 2) + "\n", "utf-8");
3378
+ console.log(`[liveConfig] persisted ${dotPath} = ${JSON.stringify(val)} to ${cfgPath}`);
3379
+ } catch (e) {
3380
+ console.warn(`[liveConfig] persist failed (${dotPath}): ${e.message}`);
3381
+ }
3382
+ }
3346
3383
  /** 是否已初始化 */
3347
3384
  isReady() {
3348
3385
  return this.current !== null;
@@ -3352,6 +3389,40 @@ var init_live = __esm({
3352
3389
  }
3353
3390
  });
3354
3391
 
3392
+ // src/config/features.ts
3393
+ function getFeature(key) {
3394
+ const v = liveConfig.get(`agents.defaults.features.${key}`);
3395
+ return v === void 0 ? FEATURE_DEFAULTS[key] : v;
3396
+ }
3397
+ var FEATURE_DEFAULTS;
3398
+ var init_features = __esm({
3399
+ "src/config/features.ts"() {
3400
+ "use strict";
3401
+ init_live();
3402
+ FEATURE_DEFAULTS = {
3403
+ filesystem: true,
3404
+ shell: true,
3405
+ memory: true,
3406
+ "topic-extract": true,
3407
+ "topic-recall": true,
3408
+ "session-memory": true,
3409
+ todo: true,
3410
+ cron: false,
3411
+ voice: false,
3412
+ selfie: false,
3413
+ eyes: false,
3414
+ calendar: false,
3415
+ webSearch: true,
3416
+ webFetch: true,
3417
+ agentTeams: true,
3418
+ autoDream: true,
3419
+ processOutput: "verbose",
3420
+ interrupt: "command",
3421
+ debounceMs: 5e3
3422
+ };
3423
+ }
3424
+ });
3425
+
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(config) {
5558
5749
  _config = config;
5559
- const c = config;
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 = args.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 = args.path ? resolvePath(args.path, ctx.workspace) : ctx.workspace;
7075
7258
  const pattern = args.pattern;
7076
7259
  const limit = args.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(args, searchPath, signal) {
7107
7290
  return new Promise((resolve10) => {
7108
7291
  const fullArgs = [...args, 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(config) {
8200
8383
  _config2 = config;
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 (args) => {
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 (args.task_id) {
10450
- const file = path45.join(resultsDir, `${args.task_id}.json`);
10451
- if (!fs42.existsSync(file)) {
10634
+ const file = path46.join(resultsDir, `${args.task_id}.json`);
10635
+ if (!fs43.existsSync(file)) {
10452
10636
  return { content: `\u4EFB\u52A1 ${args.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 ? "..." : ""}`;
@@ -10626,8 +10810,8 @@ var manager_exports = {};
10626
10810
  __export(manager_exports, {
10627
10811
  McpManager: () => McpManager
10628
10812
  });
10629
- import * as fs40 from "node:fs";
10630
- import * as path42 from "node:path";
10813
+ import * as fs41 from "node:fs";
10814
+ import * as path43 from "node:path";
10631
10815
  import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
10632
10816
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
10633
10817
  import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
@@ -10660,12 +10844,12 @@ function convertInputSchema(inputSchema) {
10660
10844
  }
10661
10845
  function persistBinary(base64Data, mimeType, persistId) {
10662
10846
  const ext = mimeType?.split("/")[1] || "bin";
10663
- const dir = path42.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
10664
- fs40.mkdirSync(dir, { recursive: true });
10665
- const filepath = path42.join(dir, `${persistId}.${ext}`);
10847
+ const dir = path43.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
10848
+ fs41.mkdirSync(dir, { recursive: true });
10849
+ const filepath = path43.join(dir, `${persistId}.${ext}`);
10666
10850
  try {
10667
10851
  const buf = Buffer.from(base64Data, "base64");
10668
- fs40.writeFileSync(filepath, buf);
10852
+ fs41.writeFileSync(filepath, buf);
10669
10853
  return { filepath, size: buf.length };
10670
10854
  } catch (err) {
10671
10855
  return { error: err.message };
@@ -10999,7 +11183,7 @@ __export(resources_exports, {
10999
11183
  registerMcpResourceTools: () => registerMcpResourceTools,
11000
11184
  unregisterMcpResourceTools: () => unregisterMcpResourceTools
11001
11185
  });
11002
- import * as path43 from "node:path";
11186
+ import * as path44 from "node:path";
11003
11187
  function registerMcpResourceTools(manager) {
11004
11188
  mcpManagerRef = manager;
11005
11189
  registry.register(listResourcesTool);
@@ -11017,7 +11201,7 @@ var init_resources = __esm({
11017
11201
  "use strict";
11018
11202
  init_registry();
11019
11203
  MAX_RESULT_CHARS2 = 1e5;
11020
- MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path43.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
11204
+ MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path44.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
11021
11205
  MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
11022
11206
  MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
11023
11207
  mcpManagerRef = null;
@@ -11186,10 +11370,10 @@ function ensureLoaded(workspace, configIds) {
11186
11370
  if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
11187
11371
  }
11188
11372
  }
11189
- const path45 = join38(workspace, ".reply-blocklist.json");
11373
+ const path46 = join38(workspace, ".reply-blocklist.json");
11190
11374
  try {
11191
- if (existsSync24(path45)) {
11192
- const raw = readFileSync26(path45, "utf-8");
11375
+ if (existsSync24(path46)) {
11376
+ const raw = readFileSync26(path46, "utf-8");
11193
11377
  const parsed = JSON.parse(raw);
11194
11378
  if (parsed.blockedUserIds) {
11195
11379
  for (const id of parsed.blockedUserIds) {
@@ -11205,9 +11389,9 @@ function ensureLoaded(workspace, configIds) {
11205
11389
  loaded = true;
11206
11390
  }
11207
11391
  function save(workspace) {
11208
- const path45 = join38(workspace, ".reply-blocklist.json");
11392
+ const path46 = join38(workspace, ".reply-blocklist.json");
11209
11393
  try {
11210
- writeFileSync16(path45, JSON.stringify(state, null, 2), "utf-8");
11394
+ writeFileSync16(path46, JSON.stringify(state, null, 2), "utf-8");
11211
11395
  } catch (err) {
11212
11396
  console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
11213
11397
  }
@@ -11804,8 +11988,8 @@ var init_cognifold_intent_watcher = __esm({
11804
11988
  });
11805
11989
 
11806
11990
  // src/engine-startup.ts
11807
- import * as path44 from "node:path";
11808
- import * as fs41 from "node:fs";
11991
+ import * as path45 from "node:path";
11992
+ import * as fs42 from "node:fs";
11809
11993
  import { fileURLToPath } from "node:url";
11810
11994
 
11811
11995
  // src/pid-lock.ts
@@ -11961,6 +12145,7 @@ function loadDisplayConfig(raw) {
11961
12145
  }
11962
12146
 
11963
12147
  // src/config/loader.ts
12148
+ init_features();
11964
12149
  function parseModelRef(ref) {
11965
12150
  const idx = ref.indexOf("/");
11966
12151
  if (idx <= 0 || idx === ref.length - 1) {
@@ -12054,6 +12239,13 @@ function loadConfig(configPath) {
12054
12239
  const stateDir = raw.stateDir || process.env.ENGINE_STATE_DIR || path3.resolve(".engine");
12055
12240
  const workspace = process.env.ENGINE_WORKSPACE || agentDefaults.workspace || path3.join(stateDir, "workspace");
12056
12241
  const mediaDir = raw.mediaDir || path3.join(stateDir, "media", "inbound");
12242
+ if (!agentDefaults.features) agentDefaults.features = {};
12243
+ for (const [k, v] of Object.entries(FEATURE_DEFAULTS)) {
12244
+ if (agentDefaults.features[k] === void 0) {
12245
+ ;
12246
+ agentDefaults.features[k] = v;
12247
+ }
12248
+ }
12057
12249
  const profile = {
12058
12250
  id: process.env.ENGINE_AGENT || "default",
12059
12251
  name: agentDefaults.name || "AI Assistant",
@@ -12061,27 +12253,8 @@ function loadConfig(configPath) {
12061
12253
  workspace,
12062
12254
  soul: agentDefaults.soul,
12063
12255
  agents: agentDefaults.agents,
12064
- features: {
12065
- filesystem: true,
12066
- shell: true,
12067
- memory: true,
12068
- "topic-extract": true,
12069
- "topic-recall": true,
12070
- "session-memory": true,
12071
- todo: true,
12072
- cron: false,
12073
- voice: false,
12074
- selfie: false,
12075
- eyes: false,
12076
- calendar: false,
12077
- webSearch: true,
12078
- webFetch: true,
12079
- agentTeams: true,
12080
- processOutput: "verbose",
12081
- interrupt: "command",
12082
- debounceMs: 5e3,
12083
- ...agentDefaults.features || {}
12084
- },
12256
+ features: agentDefaults.features,
12257
+ // canonical:profile.features 即 agents.defaults.features(同一对象,过渡兼容)
12085
12258
  channels: agentDefaults.channels || [],
12086
12259
  extensions: agentDefaults.extensions,
12087
12260
  maxTurns: agentDefaults.maxTurns,
@@ -12170,6 +12343,7 @@ function loadConfig(configPath) {
12170
12343
 
12171
12344
  // src/engine-startup.ts
12172
12345
  init_live();
12346
+ init_features();
12173
12347
 
12174
12348
  // src/services/withRetry.ts
12175
12349
  import { ProxyAgent } from "undici";
@@ -13430,13 +13604,13 @@ var DiscordAdapter = class _DiscordAdapter {
13430
13604
  }
13431
13605
  /** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
13432
13606
  async sendFile(target, message, attachment) {
13433
- const fs42 = await import("node:fs");
13434
- const path45 = await import("node:path");
13435
- if (!fs42.existsSync(attachment.path)) {
13607
+ const fs43 = await import("node:fs");
13608
+ const path46 = await import("node:path");
13609
+ if (!fs43.existsSync(attachment.path)) {
13436
13610
  throw new Error(`File not found: ${attachment.path}`);
13437
13611
  }
13438
- const filename = attachment.filename || path45.basename(attachment.path);
13439
- const fileBuffer = fs42.readFileSync(attachment.path);
13612
+ const filename = attachment.filename || path46.basename(attachment.path);
13613
+ const fileBuffer = fs43.readFileSync(attachment.path);
13440
13614
  const filePayload = {
13441
13615
  attachment: fileBuffer,
13442
13616
  name: filename
@@ -13876,13 +14050,13 @@ var FeishuAdapter = class _FeishuAdapter {
13876
14050
  }
13877
14051
  /** 发送媒体附件(图片/文件) */
13878
14052
  async sendFile(target, message, attachment) {
13879
- const fs42 = await import("node:fs");
13880
- const path45 = await import("node:path");
13881
- if (!fs42.existsSync(attachment.path)) {
14053
+ const fs43 = await import("node:fs");
14054
+ const path46 = await import("node:path");
14055
+ if (!fs43.existsSync(attachment.path)) {
13882
14056
  throw new Error(`File not found: ${attachment.path}`);
13883
14057
  }
13884
- const filename = attachment.filename || path45.basename(attachment.path);
13885
- const fileBuffer = fs42.readFileSync(attachment.path);
14058
+ const filename = attachment.filename || path46.basename(attachment.path);
14059
+ const fileBuffer = fs43.readFileSync(attachment.path);
13886
14060
  const receiveIdType = target.startsWith("ou_") ? "open_id" : "chat_id";
13887
14061
  const mimeType = attachment.mimeType || "application/octet-stream";
13888
14062
  if (mimeType.startsWith("image/")) {
@@ -14159,6 +14333,7 @@ var FeishuAdapter = class _FeishuAdapter {
14159
14333
  const message = data?.message;
14160
14334
  if (!sender || !message) return;
14161
14335
  const messageId = message.message_id || "";
14336
+ console.log(`[feishu] recv msg_type=${message.message_type} chat=${message.chat_type} from=${sender.sender_id?.open_id?.slice(0, 10)}`);
14162
14337
  if (this.recentMessageIds.has(messageId)) return;
14163
14338
  this.recentMessageIds.add(messageId);
14164
14339
  if (this.recentMessageIds.size > 500) {
@@ -14273,6 +14448,13 @@ var FeishuAdapter = class _FeishuAdapter {
14273
14448
  if (msgType === "file") {
14274
14449
  return { text: `[\u6587\u4EF6: ${parsed.file_name || "\u672A\u77E5\u6587\u4EF6"}]`, imageKeys, fileKey: parsed.file_key, fileName: parsed.file_name };
14275
14450
  }
14451
+ if (msgType === "share_location" || msgType === "location") {
14452
+ const name = parsed.name || "";
14453
+ const lat = parsed.latitude || "";
14454
+ const lng = parsed.longitude || "";
14455
+ const text = `[\u4F4D\u7F6E: ${name}${lat && lng ? ` (${lat}, ${lng})` : ""}]`;
14456
+ return { text, imageKeys };
14457
+ }
14276
14458
  if (msgType === "post") {
14277
14459
  const lines = [];
14278
14460
  if (Array.isArray(parsed.content)) {
@@ -16630,7 +16812,7 @@ function entryToSessionMessage(entry) {
16630
16812
  if (role === "user") {
16631
16813
  const text = extractText2(m.content);
16632
16814
  if (text !== null) {
16633
- return { role: "user", content: text, _raw: entry.raw };
16815
+ return { role: "user", content: text, timestamp: entry.timestamp, _raw: entry.raw };
16634
16816
  }
16635
16817
  return null;
16636
16818
  } else if (role === "assistant") {
@@ -16653,6 +16835,7 @@ function entryToSessionMessage(entry) {
16653
16835
  const result = {
16654
16836
  role: "assistant",
16655
16837
  content: textContent,
16838
+ timestamp: entry.timestamp,
16656
16839
  _raw: entry.raw
16657
16840
  };
16658
16841
  if (toolCalls.length > 0) {
@@ -16664,6 +16847,7 @@ function entryToSessionMessage(entry) {
16664
16847
  return {
16665
16848
  role: "tool",
16666
16849
  content: text || "",
16850
+ timestamp: entry.timestamp,
16667
16851
  tool_call_id: m.toolCallId,
16668
16852
  _raw: entry.raw
16669
16853
  };
@@ -17077,16 +17261,22 @@ var SessionManager = class {
17077
17261
  continue;
17078
17262
  }
17079
17263
  if (m.role === "user") {
17080
- allMessages.push(msg.user(m.content));
17264
+ const u = msg.user(m.content);
17265
+ if (m.timestamp) u.timestamp = m.timestamp;
17266
+ allMessages.push(u);
17081
17267
  } else if (m.role === "assistant") {
17082
17268
  const toolCalls = m.tool_calls?.map((tc) => ({
17083
17269
  id: tc.id,
17084
17270
  type: "function",
17085
17271
  function: { name: tc.function.name, arguments: tc.function.arguments }
17086
17272
  }));
17087
- allMessages.push(msg.assistant(m.content, toolCalls));
17273
+ const a = msg.assistant(m.content, toolCalls);
17274
+ if (m.timestamp) a.timestamp = m.timestamp;
17275
+ allMessages.push(a);
17088
17276
  } else if (m.role === "tool") {
17089
- allMessages.push(msg.tool(m.tool_call_id || "", m.content));
17277
+ const t = msg.tool(m.tool_call_id || "", m.content);
17278
+ if (m.timestamp) t.timestamp = m.timestamp;
17279
+ allMessages.push(t);
17090
17280
  }
17091
17281
  }
17092
17282
  }
@@ -17811,6 +18001,8 @@ var MessageQueue = class {
17811
18001
  // src/handle-query.ts
17812
18002
  init_types();
17813
18003
  init_attachments();
18004
+ init_live();
18005
+ init_features();
17814
18006
  init_task_manager();
17815
18007
 
17816
18008
  // src/prompt.ts
@@ -18591,7 +18783,7 @@ ${ep.episode || ep.summary}`,
18591
18783
  init_paths();
18592
18784
  import { readFileSync as readFileSync15, existsSync as existsSync12 } from "node:fs";
18593
18785
  import { join as join20, resolve as resolve6 } from "node:path";
18594
- import * as path13 from "node:path";
18786
+ import * as path14 from "node:path";
18595
18787
  var contactMap = null;
18596
18788
  var externalChanWhitelist = null;
18597
18789
  function loadContactMap(workspace) {
@@ -18659,18 +18851,18 @@ function truncate(s, maxLen) {
18659
18851
  }
18660
18852
  var externalChanRulesCache = null;
18661
18853
  function loadExternalChanRules(workspace) {
18662
- const path45 = join20(workspace, "prompts", "external-chan-rules.md");
18663
- if (externalChanRulesCache && externalChanRulesCache.path === path45) return externalChanRulesCache;
18854
+ const path46 = join20(workspace, "prompts", "external-chan-rules.md");
18855
+ if (externalChanRulesCache && externalChanRulesCache.path === path46) return externalChanRulesCache;
18664
18856
  let content = "";
18665
- if (existsSync12(path45)) {
18857
+ if (existsSync12(path46)) {
18666
18858
  try {
18667
- content = readFileSync15(path45, "utf-8").trim();
18859
+ content = readFileSync15(path46, "utf-8").trim();
18668
18860
  } catch (e) {
18669
18861
  console.warn(`[external-chan-rules] Failed to load: ${e}`);
18670
18862
  }
18671
18863
  }
18672
- externalChanRulesCache = { path: path45, content };
18673
- console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path45}`);
18864
+ externalChanRulesCache = { path: path46, content };
18865
+ console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path46}`);
18674
18866
  return externalChanRulesCache;
18675
18867
  }
18676
18868
  function getExternalChanRulesBlock(inboundMeta, workspace) {
@@ -18698,7 +18890,7 @@ async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget
18698
18890
  }
18699
18891
  async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
18700
18892
  const { engine, sessions, channelManager, workspace, providerId, providerApi, model } = deps;
18701
- const features = deps.features || {};
18893
+ const topics = liveConfig.get("topics") || {};
18702
18894
  const preQueryAbort = new AbortController();
18703
18895
  engine.setPreQueryAbort(preQueryAbort);
18704
18896
  let history = sessions.getHistory(sessionId);
@@ -18707,7 +18899,7 @@ async function handleQueryInner(text, sessionId, channelName, cb, deps, channelT
18707
18899
  if (restored.length > 0) {
18708
18900
  history = restored;
18709
18901
  sessions.setHistory(sessionId, history);
18710
- if (deps.topics?.restoreRecall === false) {
18902
+ if (topics?.restoreRecall === false) {
18711
18903
  let stripped = 0;
18712
18904
  for (let i = history.length - 1; i >= 0; i--) {
18713
18905
  const m = history[i];
@@ -18897,7 +19089,7 @@ ${text}` : text });
18897
19089
  // 对齐 CC: fork subagent 继承父对话历史
18898
19090
  parentSystemPrompt: deps.systemPrompt,
18899
19091
  // 对齐 CC: fork 共享 prompt cache
18900
- features: deps.features,
19092
+ features: liveConfig.get("agents.defaults.features"),
18901
19093
  // engine config features(AgentTool 读 agentTool.showProgress)
18902
19094
  channelTarget: channelTarget ?? "",
18903
19095
  // 回复目标(Discord channel ID / user ID)
@@ -19013,13 +19205,13 @@ ${text}` : text });
19013
19205
  engine.setExternalAbort(queryAbortController);
19014
19206
  setActiveQueryEngine(sessionId, engine);
19015
19207
  const shouldSkipRecall = skipRecall ?? channelName === "cron";
19016
- if (features["topic-recall"] !== false && !shouldSkipRecall) {
19208
+ if (getFeature("topic-recall") !== false && !shouldSkipRecall) {
19017
19209
  try {
19018
19210
  const memoryDir = getAutoMemPath(workspace);
19019
19211
  const provider = deps.engine.getProvider();
19020
19212
  const surfacedHistory = collectSurfacedMemories(history);
19021
19213
  const cumulativePaths = sessions.getRestoredRecallPaths(sessionId);
19022
- const doRestore = deps.topics?.restoreRecall === true;
19214
+ const doRestore = topics?.restoreRecall === true;
19023
19215
  const surfaced = doRestore ? { paths: /* @__PURE__ */ new Set([...surfacedHistory.paths, ...cumulativePaths]) } : surfacedHistory;
19024
19216
  console.log(`[handle-query] surfaced: history=${surfacedHistory.paths.size} cumulative=${cumulativePaths.size} merged=${surfaced.paths.size} restoreRecall=${doRestore}`);
19025
19217
  if (doRestore) {
@@ -19058,7 +19250,7 @@ ${text}` : text });
19058
19250
  console.log(`[handle-query] Memory recall starting: dir=${memoryDir} query="${(typeof text === "string" ? text : "[content blocks]").slice(0, 50)}..." alreadySurfaced=${surfaced.paths.size}`);
19059
19251
  const textForMemory = typeof text === "string" ? text : text.filter((b) => b.type === "text").map((b) => b.text).join(" ");
19060
19252
  const recallP = deps.recallProvider;
19061
- const recallMode = deps.topics?.recall?.mode || "llm";
19253
+ const recallMode = topics?.recall?.mode || "llm";
19062
19254
  let relevantMemories;
19063
19255
  if (recallMode === "everos") {
19064
19256
  const everosCfg = deps?.everosCfg;
@@ -19073,7 +19265,7 @@ ${text}` : text });
19073
19265
  rerankApiKey: everosCfg.rerank?.apiKey,
19074
19266
  rerankModel: everosCfg.rerank?.model,
19075
19267
  rerankProvider: everosCfg.rerank?.provider,
19076
- minScore: deps.topics?.recall?.minScore
19268
+ minScore: topics?.recall?.minScore
19077
19269
  } : void 0
19078
19270
  );
19079
19271
  } else if (recallMode === "vector") {
@@ -19091,7 +19283,7 @@ ${text}` : text });
19091
19283
  queryAbortController.signal,
19092
19284
  surfaced.paths,
19093
19285
  recallP?.disableThinking,
19094
- deps.topics?.maxScanFiles
19286
+ topics?.maxScanFiles
19095
19287
  );
19096
19288
  }
19097
19289
  console.log(`[handle-query] Memory recall result: ${relevantMemories.length} memories found: ${relevantMemories.map((m) => m.path.split(/[/\\]/).pop()).join(", ")}`);
@@ -19275,7 +19467,7 @@ ${text}` : text });
19275
19467
  }
19276
19468
  }
19277
19469
  sessions.setHistory(sessionId, history);
19278
- if (features["topic-extract"] && sessionId === deps.sessions.getSessionId("scope:main")) {
19470
+ if (getFeature("topic-extract") === true && sessionId === deps.sessions.getSessionId("scope:main")) {
19279
19471
  try {
19280
19472
  const { createMemoryExtractor: createMemoryExtractor2 } = await Promise.resolve().then(() => (init_extractMemories(), extractMemories_exports));
19281
19473
  const extractor = createMemoryExtractor2(workspace, true);
@@ -19290,6 +19482,15 @@ ${text}` : text });
19290
19482
  console.warn(`[handle-query] Memory extraction init failed: ${err.message}`);
19291
19483
  }
19292
19484
  }
19485
+ if (liveConfig.get("everos.enabled") === true && sessionId === deps.sessions.getSessionId("scope:main")) {
19486
+ try {
19487
+ const { pushConversation: pushConversation2 } = await Promise.resolve().then(() => (init_ingest(), ingest_exports));
19488
+ pushConversation2(messages, sessionId, workspace).catch(() => {
19489
+ });
19490
+ } catch (e) {
19491
+ console.warn(`[handle-query] everos push init failed: ${e?.message ?? e}`);
19492
+ }
19493
+ }
19293
19494
  try {
19294
19495
  const { isSessionMemoryEnabled: isSessionMemoryEnabled2, shouldExtractMemory: shouldExtractMemory2, extractSessionMemory: extractSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
19295
19496
  if (isSessionMemoryEnabled2()) {
@@ -19336,7 +19537,7 @@ stack: ${err.stack ?? "(none)"}`);
19336
19537
  }
19337
19538
  } catch (err) {
19338
19539
  try {
19339
- (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}
19540
+ (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}
19340
19541
  stack: ${err.stack ?? "(none)"}
19341
19542
  `);
19342
19543
  } catch {
@@ -19535,17 +19736,17 @@ var MessageDispatcher = class {
19535
19736
  };
19536
19737
 
19537
19738
  // src/cli-startup.ts
19538
- import * as path14 from "node:path";
19539
- import * as fs13 from "node:fs";
19739
+ import * as path15 from "node:path";
19740
+ import * as fs14 from "node:fs";
19540
19741
  import * as readline2 from "node:readline";
19541
19742
  function getDailyLogPath(stateDir) {
19542
19743
  const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString("sv-SE", { timeZone: "Asia/Shanghai" });
19543
- return path14.join(stateDir, "logs", `engine-${dateStr}.log`);
19744
+ return path15.join(stateDir, "logs", `engine-${dateStr}.log`);
19544
19745
  }
19545
19746
  function setupFileLogging(stateDir) {
19546
19747
  const LOG_PATH = getDailyLogPath(stateDir);
19547
- fs13.mkdirSync(path14.join(stateDir, "logs"), { recursive: true });
19548
- const logStream = fs13.createWriteStream(LOG_PATH, { flags: "a" });
19748
+ fs14.mkdirSync(path15.join(stateDir, "logs"), { recursive: true });
19749
+ const logStream = fs14.createWriteStream(LOG_PATH, { flags: "a" });
19549
19750
  logStream.on("error", (err) => console.error(`[log] Write error: ${err.message}`));
19550
19751
  function ts() {
19551
19752
  return (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false }) + "." + String(Date.now() % 1e3).padStart(3, "0");
@@ -19635,8 +19836,8 @@ function startCliLoop(deps, cliConfig, channelManager, dispatcher) {
19635
19836
  }
19636
19837
 
19637
19838
  // src/session/session-history.ts
19638
- import fs14 from "node:fs";
19639
- import path15 from "node:path";
19839
+ import fs15 from "node:fs";
19840
+ import path16 from "node:path";
19640
19841
  var BEIJING_OFFSET_MS = 8 * 36e5;
19641
19842
  var INJECTED_CONTENT_PATTERNS = [
19642
19843
  /【定时心跳】/,
@@ -19690,10 +19891,10 @@ function scopeMainJsonlPaths(sessions) {
19690
19891
  let latestArchive = null;
19691
19892
  if (current) {
19692
19893
  try {
19693
- const dir = path15.dirname(current);
19694
- const base = path15.basename(current);
19695
- const archives = fs14.readdirSync(dir).filter((f) => f.startsWith(base + ".archived.")).sort();
19696
- if (archives.length > 0) latestArchive = path15.join(dir, archives[archives.length - 1]);
19894
+ const dir = path16.dirname(current);
19895
+ const base = path16.basename(current);
19896
+ const archives = fs15.readdirSync(dir).filter((f) => f.startsWith(base + ".archived.")).sort();
19897
+ if (archives.length > 0) latestArchive = path16.join(dir, archives[archives.length - 1]);
19697
19898
  } catch {
19698
19899
  }
19699
19900
  }
@@ -19711,7 +19912,7 @@ function extractText3(content) {
19711
19912
  function findLastRealUserMsg(jsonlPath) {
19712
19913
  let lines;
19713
19914
  try {
19714
- lines = fs14.readFileSync(jsonlPath, "utf-8").split("\n");
19915
+ lines = fs15.readFileSync(jsonlPath, "utf-8").split("\n");
19715
19916
  } catch {
19716
19917
  return null;
19717
19918
  }
@@ -19755,7 +19956,7 @@ function lastUserMsg(sessions) {
19755
19956
  function recentMessages(sessions, hours = 12, limit = 60) {
19756
19957
  const jsonlPath = resolveScopeMainJsonl(sessions);
19757
19958
  if (!jsonlPath) return [];
19758
- const lines = fs14.readFileSync(jsonlPath, "utf-8").split("\n");
19959
+ const lines = fs15.readFileSync(jsonlPath, "utf-8").split("\n");
19759
19960
  const entries = parseJsonlEntries(lines);
19760
19961
  const nowMs = Date.now();
19761
19962
  const cutoffMs = nowMs - hours * 36e5;
@@ -19951,8 +20152,8 @@ ${basePrompt}`;
19951
20152
  };
19952
20153
 
19953
20154
  // src/nudge/plugin.ts
19954
- import fs17 from "node:fs";
19955
- import path18 from "node:path";
20155
+ import fs18 from "node:fs";
20156
+ import path19 from "node:path";
19956
20157
 
19957
20158
  // src/nudge/judge.ts
19958
20159
  function shouldNudge(task, taskState, cfg) {
@@ -20120,14 +20321,14 @@ function formatDuration2(ms) {
20120
20321
  }
20121
20322
 
20122
20323
  // src/nudge/session-state-reader.ts
20123
- import fs15 from "node:fs";
20124
- import path16 from "node:path";
20324
+ import fs16 from "node:fs";
20325
+ import path17 from "node:path";
20125
20326
  function parseSessionStateFull(workspace, sessionStateFile) {
20126
20327
  const stateFile = sessionStateFile || "SESSION-STATE.md";
20127
- const statePath = path16.isAbsolute(stateFile) ? stateFile : path16.join(workspace, stateFile);
20328
+ const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(workspace, stateFile);
20128
20329
  let content;
20129
20330
  try {
20130
- content = fs15.readFileSync(statePath, "utf-8");
20331
+ content = fs16.readFileSync(statePath, "utf-8");
20131
20332
  } catch {
20132
20333
  console.warn(`[nudge] SESSION-STATE not found at ${statePath}`);
20133
20334
  return { activeTasks: [], orphanPendings: [] };
@@ -20179,13 +20380,13 @@ function taskIdFromTitle(title) {
20179
20380
 
20180
20381
  // src/calendar/db.ts
20181
20382
  import { DatabaseSync } from "node:sqlite";
20182
- import * as path17 from "node:path";
20183
- import * as fs16 from "node:fs";
20383
+ import * as path18 from "node:path";
20384
+ import * as fs17 from "node:fs";
20184
20385
  var TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
20185
20386
  function openDb(workspace) {
20186
- const dir = path17.join(workspace, ".calendar");
20187
- fs16.mkdirSync(dir, { recursive: true });
20188
- const dbPath = path17.join(dir, "calendar.db");
20387
+ const dir = path18.join(workspace, ".calendar");
20388
+ fs17.mkdirSync(dir, { recursive: true });
20389
+ const dbPath = path18.join(dir, "calendar.db");
20189
20390
  const db = new DatabaseSync(dbPath);
20190
20391
  db.exec("PRAGMA journal_mode=WAL");
20191
20392
  db.exec(`CREATE TABLE IF NOT EXISTS events (
@@ -20274,9 +20475,9 @@ var NudgePlugin = class {
20274
20475
  provider;
20275
20476
  model;
20276
20477
  loadPrompt(workspace, promptFile) {
20277
- const promptPath = promptFile ? path18.isAbsolute(promptFile) ? promptFile : path18.join(workspace, promptFile) : path18.join(workspace, "prompts", "nudge-prompt.md");
20478
+ const promptPath = promptFile ? path19.isAbsolute(promptFile) ? promptFile : path19.join(workspace, promptFile) : path19.join(workspace, "prompts", "nudge-prompt.md");
20278
20479
  try {
20279
- const content = fs17.readFileSync(promptPath, "utf-8").trim();
20480
+ const content = fs18.readFileSync(promptPath, "utf-8").trim();
20280
20481
  if (content) {
20281
20482
  console.log(`[nudge] Loaded custom prompt from ${promptPath}`);
20282
20483
  return content;
@@ -20307,6 +20508,18 @@ var NudgePlugin = class {
20307
20508
  registerCallbackHook("Stop", {
20308
20509
  type: "callback",
20309
20510
  callback: async (input, _toolUseID, _signal) => {
20511
+ const mode = this.cfg.stopHookMode || "sync";
20512
+ if (mode === "async") {
20513
+ console.log("[stop-hook] async mode \u2014 firing judge in background, not blocking");
20514
+ this.runStopHookJudge(input, sessions).catch((err) => {
20515
+ if (/judge \d+ms timeout/i.test(err?.message || "")) {
20516
+ console.warn(`[stop-hook] async judge timed out (abandoned)`);
20517
+ } else {
20518
+ console.warn(`[stop-hook] async judge error: ${err.message}`);
20519
+ }
20520
+ });
20521
+ return { outcome: { outcome: "success" } };
20522
+ }
20310
20523
  const timeoutMs = this.cfg.timeoutMs ?? 15e3;
20311
20524
  let judgeTimer;
20312
20525
  const judgeTimeout = new Promise((_, reject) => {
@@ -20329,7 +20542,7 @@ var NudgePlugin = class {
20329
20542
  return { outcome: { outcome: "success" } };
20330
20543
  }
20331
20544
  });
20332
- console.log("[stop-hook] Registered Stop callback hook (LLM semantic judge + 5min wake-up, hard timeout via Promise.race)");
20545
+ console.log(`[stop-hook] Registered Stop callback hook (mode=${this.cfg.stopHookMode || "sync"}, LLM semantic judge + 5min wake-up)`);
20333
20546
  }
20334
20547
  /** Judge 完整逻辑(被 callback 用 Promise.race 调用,可被 timeout 截断) */
20335
20548
  async runStopHookJudge(input, sessions) {
@@ -20364,6 +20577,12 @@ var NudgePlugin = class {
20364
20577
  if (!lastMsg) {
20365
20578
  return;
20366
20579
  }
20580
+ const currentHour = (/* @__PURE__ */ new Date()).getHours();
20581
+ const isNightTime = currentHour >= 22 || currentHour < 8;
20582
+ if (isNightTime) {
20583
+ console.log(`[stop-hook] night time (${currentHour}:xx), skipping needLanding/waiting judge`);
20584
+ return;
20585
+ }
20367
20586
  let contextStr = "";
20368
20587
  try {
20369
20588
  const recent = recentMessages(sessions, 0.5, 6);
@@ -20486,18 +20705,18 @@ var NudgePlugin = class {
20486
20705
  if (!isWaiting) {
20487
20706
  return;
20488
20707
  }
20489
- const nudgeDir = path18.join(this.workspace, ".nudge");
20490
- const notifPath = path18.join(nudgeDir, "stop-hook-notifications.json");
20708
+ const nudgeDir = path19.join(this.workspace, ".nudge");
20709
+ const notifPath = path19.join(nudgeDir, "stop-hook-notifications.json");
20491
20710
  try {
20492
- if (!fs17.existsSync(nudgeDir)) fs17.mkdirSync(nudgeDir, { recursive: true });
20711
+ if (!fs18.existsSync(nudgeDir)) fs18.mkdirSync(nudgeDir, { recursive: true });
20493
20712
  let notifs = [];
20494
- if (fs17.existsSync(notifPath)) {
20495
- notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
20713
+ if (fs18.existsSync(notifPath)) {
20714
+ notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20496
20715
  const now = Date.now();
20497
20716
  const dup = notifs.find((n) => !n.notified && n.description === (waitDesc || lastMsg.slice(0, 200)));
20498
20717
  if (dup) {
20499
20718
  dup.wakeAt = new Date(now + 5 * 6e4).toISOString();
20500
- fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20719
+ fs18.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20501
20720
  console.log(`[stop-hook] Duplicate wait (same desc, not fired yet), refreshed wakeAt: ${dup.id}`);
20502
20721
  return;
20503
20722
  }
@@ -20515,7 +20734,7 @@ var NudgePlugin = class {
20515
20734
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
20516
20735
  wakeAt
20517
20736
  });
20518
- fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20737
+ fs18.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
20519
20738
  console.log(`[stop-hook] Registered wake-up ${notifId} at ${wakeAt} (sessionId=${sessionId}): ${waitDesc}`);
20520
20739
  } catch (e) {
20521
20740
  console.warn(`[stop-hook] Failed to register: ${e.message}`);
@@ -20552,10 +20771,10 @@ var NudgePlugin = class {
20552
20771
  * 已 notified 的不会再触发,等 agent 回复 "<id> 过期了" 由 cleanup 删。
20553
20772
  */
20554
20773
  collectDueStopHookNotifications() {
20555
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20774
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20556
20775
  try {
20557
- if (!fs17.existsSync(notifPath)) return null;
20558
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
20776
+ if (!fs18.existsSync(notifPath)) return null;
20777
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20559
20778
  if (notifs.length === 0) return null;
20560
20779
  const now = Date.now();
20561
20780
  const due = notifs.filter((n) => new Date(n.wakeAt).getTime() <= now && !n.notified);
@@ -20591,18 +20810,18 @@ ${items}
20591
20810
  }
20592
20811
  /** 按 id 删除条目(stop-hook 实时清理用;正常删除路径,agent 回复即删) */
20593
20812
  removeNotificationsById(ids) {
20594
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20813
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20595
20814
  try {
20596
- if (!fs17.existsSync(notifPath)) return;
20597
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
20815
+ if (!fs18.existsSync(notifPath)) return;
20816
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20598
20817
  const idSet = new Set(ids);
20599
20818
  const remaining = notifs.filter((n) => !idSet.has(n.id));
20600
20819
  const removed = notifs.length - remaining.length;
20601
20820
  if (removed === 0) return;
20602
20821
  if (remaining.length > 0) {
20603
- fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
20822
+ fs18.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
20604
20823
  } else {
20605
- fs17.unlinkSync(notifPath);
20824
+ fs18.unlinkSync(notifPath);
20606
20825
  }
20607
20826
  console.log(`[stop-hook] Cleaned ${removed} notification(s) from reply: ${ids.join(", ")}`);
20608
20827
  } catch (e) {
@@ -20611,13 +20830,13 @@ ${items}
20611
20830
  }
20612
20831
  /** 投递成功后标记 notified(防重复触发);不删除——删除只走 agent 回复 "<id> 过期了" */
20613
20832
  markNotified(ids) {
20614
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20833
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20615
20834
  try {
20616
- if (!fs17.existsSync(notifPath)) return;
20617
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
20835
+ if (!fs18.existsSync(notifPath)) return;
20836
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20618
20837
  const idSet = new Set(ids);
20619
20838
  const updated = notifs.map((n) => idSet.has(n.id) ? { ...n, notified: true } : n);
20620
- fs17.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
20839
+ fs18.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
20621
20840
  } catch (e) {
20622
20841
  console.warn(`[nudge] markNotified error: ${e.message}`);
20623
20842
  }
@@ -20634,9 +20853,9 @@ ${items}
20634
20853
  */
20635
20854
  cleanupStaleNotificationsFromMessages(sessions) {
20636
20855
  try {
20637
- const notifPath = path18.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20638
- if (!fs17.existsSync(notifPath)) return;
20639
- const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
20856
+ const notifPath = path19.join(this.workspace, ".nudge", "stop-hook-notifications.json");
20857
+ if (!fs18.existsSync(notifPath)) return;
20858
+ const notifs = JSON.parse(fs18.readFileSync(notifPath, "utf-8"));
20640
20859
  if (notifs.length === 0) return;
20641
20860
  const expiredIds = this.findExpiredReplyIds(sessions, notifs);
20642
20861
  const ttlMs = (this.cfg.cleanupTtlHours || 24) * 36e5;
@@ -20648,9 +20867,9 @@ ${items}
20648
20867
  if (removeIds.size === 0) return;
20649
20868
  const remaining = notifs.filter((n) => !removeIds.has(n.id));
20650
20869
  if (remaining.length > 0) {
20651
- fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
20870
+ fs18.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
20652
20871
  } else {
20653
- fs17.unlinkSync(notifPath);
20872
+ fs18.unlinkSync(notifPath);
20654
20873
  }
20655
20874
  if (expiredIds.size > 0) {
20656
20875
  console.log(`[nudge] Cleaned ${expiredIds.size} notification(s) by reply: ${[...expiredIds].join(", ")}`);
@@ -20675,10 +20894,10 @@ ${items}
20675
20894
  const oldestMs = Math.min(...notifs.map((n) => new Date(n.wakeAt).getTime()));
20676
20895
  const { current, latestArchive } = scopeMainJsonlPaths(sessions);
20677
20896
  for (const file of [current, latestArchive]) {
20678
- if (!file || !fs17.existsSync(file)) continue;
20897
+ if (!file || !fs18.existsSync(file)) continue;
20679
20898
  let lines;
20680
20899
  try {
20681
- lines = fs17.readFileSync(file, "utf-8").split("\n");
20900
+ lines = fs18.readFileSync(file, "utf-8").split("\n");
20682
20901
  } catch (e) {
20683
20902
  console.warn(`[nudge] findExpiredReplyIds read error on ${file}: ${e.message}`);
20684
20903
  continue;
@@ -20935,9 +21154,9 @@ ${items}
20935
21154
  // === state 持久化 ===
20936
21155
  loadState() {
20937
21156
  const stateFile = this.cfg.stateFile || "nudge-state.json";
20938
- const statePath = path18.isAbsolute(stateFile) ? stateFile : path18.join(this.workspace, stateFile);
21157
+ const statePath = path19.isAbsolute(stateFile) ? stateFile : path19.join(this.workspace, stateFile);
20939
21158
  try {
20940
- const content = fs17.readFileSync(statePath, "utf-8");
21159
+ const content = fs18.readFileSync(statePath, "utf-8");
20941
21160
  return JSON.parse(content);
20942
21161
  } catch {
20943
21162
  return { tasks: {} };
@@ -20945,8 +21164,8 @@ ${items}
20945
21164
  }
20946
21165
  saveState(state2) {
20947
21166
  const stateFile = this.cfg.stateFile || "nudge-state.json";
20948
- const statePath = path18.isAbsolute(stateFile) ? stateFile : path18.join(this.workspace, stateFile);
20949
- fs17.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
21167
+ const statePath = path19.isAbsolute(stateFile) ? stateFile : path19.join(this.workspace, stateFile);
21168
+ fs18.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
20950
21169
  }
20951
21170
  newTaskState() {
20952
21171
  return {
@@ -21124,8 +21343,8 @@ ${items}
21124
21343
  };
21125
21344
 
21126
21345
  // src/inner-voice/plugin.ts
21127
- import fs21 from "node:fs";
21128
- import path22 from "node:path";
21346
+ import fs22 from "node:fs";
21347
+ import path23 from "node:path";
21129
21348
 
21130
21349
  // src/inner-voice/activity.ts
21131
21350
  function checkActivity(sessions, activeThresholdMs) {
@@ -21164,8 +21383,8 @@ function calcHintProb(min) {
21164
21383
  }
21165
21384
 
21166
21385
  // src/inner-voice/emotional-state.ts
21167
- import fs18 from "node:fs";
21168
- import path19 from "node:path";
21386
+ import fs19 from "node:fs";
21387
+ import path20 from "node:path";
21169
21388
  var NEUTRAL = 0.5;
21170
21389
  var DECAY_RATE = 0.17;
21171
21390
  var MAX_EVENTS = 20;
@@ -21216,7 +21435,7 @@ function initialState() {
21216
21435
  return { version: 1, mood: NEUTRAL, trend: "stable", updatedAt: nowIsoBj(), events: [] };
21217
21436
  }
21218
21437
  async function updateEmotionalState(workspace, sessions) {
21219
- const stateFile = path19.join(workspace, "inner-voice", "emotional-state.json");
21438
+ const stateFile = path20.join(workspace, "inner-voice", "emotional-state.json");
21220
21439
  const messages = readRecentMessages(sessions, RECENT_N);
21221
21440
  if (messages.length === 0) {
21222
21441
  console.log("[emotional-state] no messages");
@@ -21249,8 +21468,8 @@ async function updateEmotionalState(workspace, sessions) {
21249
21468
  function readRecentMessages(sessions, n) {
21250
21469
  const mainId = sessions.getSessionId("scope:main");
21251
21470
  if (!mainId) return [];
21252
- const file = path19.join(sessions.sessionsDir, `${mainId}.jsonl`);
21253
- if (!fs18.existsSync(file)) return [];
21471
+ const file = path20.join(sessions.sessionsDir, `${mainId}.jsonl`);
21472
+ if (!fs19.existsSync(file)) return [];
21254
21473
  const lines = readLastNLines(file, n * 4 + 20);
21255
21474
  const entries = [];
21256
21475
  for (const line of lines) {
@@ -21367,9 +21586,9 @@ function refreshHoursAgo(events) {
21367
21586
  }
21368
21587
  function appendMoodLog(workspace, state2, summary) {
21369
21588
  try {
21370
- const logPath = path19.join(workspace, "mood-history.log");
21589
+ const logPath = path20.join(workspace, "mood-history.log");
21371
21590
  const ts = formatBj(/* @__PURE__ */ new Date(), false);
21372
- fs18.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
21591
+ fs19.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
21373
21592
  `);
21374
21593
  } catch (err) {
21375
21594
  console.warn(`[emotional-state] mood log failed: ${err.message}`);
@@ -21377,32 +21596,32 @@ function appendMoodLog(workspace, state2, summary) {
21377
21596
  }
21378
21597
  function loadJson(file) {
21379
21598
  try {
21380
- return JSON.parse(fs18.readFileSync(file, "utf-8"));
21599
+ return JSON.parse(fs19.readFileSync(file, "utf-8"));
21381
21600
  } catch {
21382
21601
  return null;
21383
21602
  }
21384
21603
  }
21385
21604
  function saveJson(file, data) {
21386
21605
  try {
21387
- fs18.mkdirSync(path19.dirname(file), { recursive: true });
21388
- fs18.writeFileSync(file, JSON.stringify(data, null, 2));
21606
+ fs19.mkdirSync(path20.dirname(file), { recursive: true });
21607
+ fs19.writeFileSync(file, JSON.stringify(data, null, 2));
21389
21608
  } catch (err) {
21390
21609
  console.warn(`[emotional-state] save failed: ${err.message}`);
21391
21610
  }
21392
21611
  }
21393
21612
  function readLastNLines(file, maxLines) {
21394
21613
  try {
21395
- const stat4 = fs18.statSync(file);
21614
+ const stat4 = fs19.statSync(file);
21396
21615
  const tailBytes = Math.min(stat4.size, maxLines * 512);
21397
- const fd = fs18.openSync(file, "r");
21616
+ const fd = fs19.openSync(file, "r");
21398
21617
  try {
21399
21618
  const buf = Buffer.alloc(tailBytes);
21400
- fs18.readSync(fd, buf, 0, tailBytes, stat4.size - tailBytes);
21619
+ fs19.readSync(fd, buf, 0, tailBytes, stat4.size - tailBytes);
21401
21620
  const lines = buf.toString("utf-8").split("\n").filter(Boolean);
21402
21621
  if (stat4.size > tailBytes && lines.length > 0) lines.shift();
21403
21622
  return lines;
21404
21623
  } finally {
21405
- fs18.closeSync(fd);
21624
+ fs19.closeSync(fd);
21406
21625
  }
21407
21626
  } catch {
21408
21627
  return [];
@@ -21429,8 +21648,8 @@ function formatBj(d, withSec) {
21429
21648
  }
21430
21649
 
21431
21650
  // src/inner-voice/topics-scorer.ts
21432
- import fs19 from "node:fs";
21433
- import path20 from "node:path";
21651
+ import fs20 from "node:fs";
21652
+ import path21 from "node:path";
21434
21653
  var HALF_LIFE_DAYS = 3;
21435
21654
  var PROJECT_HALF_LIFE_DAYS = 1.5;
21436
21655
  var COOLDOWN_HOURS = 6;
@@ -21438,8 +21657,8 @@ var MAX_CHARS = 8e3;
21438
21657
  var SKIP_NAMES = /* @__PURE__ */ new Set(["MEMORY.md", "archive"]);
21439
21658
  var SKIP_DIRS = /* @__PURE__ */ new Set(["archive"]);
21440
21659
  function pickTopic(workspace, typeFilter, opts) {
21441
- const topicsDir = path20.join(workspace, "topics");
21442
- const usageFile = path20.join(workspace, "inner-voice", "topics-usage.json");
21660
+ const topicsDir = path21.join(workspace, "topics");
21661
+ const usageFile = path21.join(workspace, "inner-voice", "topics-usage.json");
21443
21662
  const files = scanTopics(topicsDir, typeFilter);
21444
21663
  if (files.length === 0) {
21445
21664
  console.log(`[topics-scorer] no topics found (type=${typeFilter})`);
@@ -21456,7 +21675,7 @@ function pickTopic(workspace, typeFilter, opts) {
21456
21675
  else type2 = "other";
21457
21676
  let mtime;
21458
21677
  try {
21459
- mtime = fs19.statSync(fullpath).mtimeMs;
21678
+ mtime = fs20.statSync(fullpath).mtimeMs;
21460
21679
  } catch {
21461
21680
  continue;
21462
21681
  }
@@ -21471,7 +21690,7 @@ function pickTopic(workspace, typeFilter, opts) {
21471
21690
  recency: Math.round(recency * 1e3) / 1e3,
21472
21691
  freq: Math.round(freq * 1e3) / 1e3,
21473
21692
  type: type2,
21474
- name: meta.name || path20.basename(relpath),
21693
+ name: meta.name || path21.basename(relpath),
21475
21694
  description: meta.description || "",
21476
21695
  mtime
21477
21696
  });
@@ -21491,7 +21710,7 @@ function pickTopic(workspace, typeFilter, opts) {
21491
21710
  saveJson2(usageFile, usage);
21492
21711
  let content = "";
21493
21712
  try {
21494
- const raw = fs19.readFileSync(chosen.fullpath, "utf-8");
21713
+ const raw = fs20.readFileSync(chosen.fullpath, "utf-8");
21495
21714
  content = raw.length > MAX_CHARS ? raw.slice(0, MAX_CHARS) + "\n... (truncated) ..." : raw;
21496
21715
  } catch {
21497
21716
  }
@@ -21525,18 +21744,18 @@ function frequencyWeight(relpath, usage, isProject, type2) {
21525
21744
  return reconsolidation + countBonus;
21526
21745
  }
21527
21746
  function scanTopics(topicsDir, typeFilter) {
21528
- if (!fs19.existsSync(topicsDir)) return [];
21747
+ if (!fs20.existsSync(topicsDir)) return [];
21529
21748
  const out = [];
21530
21749
  const walk = (dir) => {
21531
- for (const name of fs19.readdirSync(dir)) {
21532
- const full = path20.join(dir, name);
21533
- const stat4 = fs19.statSync(full);
21750
+ for (const name of fs20.readdirSync(dir)) {
21751
+ const full = path21.join(dir, name);
21752
+ const stat4 = fs20.statSync(full);
21534
21753
  if (stat4.isDirectory()) {
21535
21754
  if (SKIP_DIRS.has(name)) continue;
21536
21755
  walk(full);
21537
21756
  } else {
21538
21757
  if (!name.endsWith(".md") || SKIP_NAMES.has(name)) continue;
21539
- const relpath = path20.relative(topicsDir, full).replace(/\\/g, "/");
21758
+ const relpath = path21.relative(topicsDir, full).replace(/\\/g, "/");
21540
21759
  if (typeFilter && !relpath.startsWith(typeFilter + "/") && !relpath.startsWith(typeFilter + "_")) continue;
21541
21760
  out.push({ relpath, fullpath: full });
21542
21761
  }
@@ -21548,7 +21767,7 @@ function scanTopics(topicsDir, typeFilter) {
21548
21767
  function readFrontmatter(file) {
21549
21768
  let content = "";
21550
21769
  try {
21551
- content = fs19.readFileSync(file, "utf-8").slice(0, 2e3);
21770
+ content = fs20.readFileSync(file, "utf-8").slice(0, 2e3);
21552
21771
  } catch {
21553
21772
  return {};
21554
21773
  }
@@ -21574,40 +21793,40 @@ function weightedRandom(items, weights) {
21574
21793
  }
21575
21794
  function loadJson2(file) {
21576
21795
  try {
21577
- return JSON.parse(fs19.readFileSync(file, "utf-8"));
21796
+ return JSON.parse(fs20.readFileSync(file, "utf-8"));
21578
21797
  } catch {
21579
21798
  return null;
21580
21799
  }
21581
21800
  }
21582
21801
  function saveJson2(file, data) {
21583
21802
  try {
21584
- fs19.mkdirSync(path20.dirname(file), { recursive: true });
21585
- fs19.writeFileSync(file, JSON.stringify(data, null, 2));
21803
+ fs20.mkdirSync(path21.dirname(file), { recursive: true });
21804
+ fs20.writeFileSync(file, JSON.stringify(data, null, 2));
21586
21805
  } catch (err) {
21587
21806
  console.warn(`[topics-scorer] usage save failed: ${err.message}`);
21588
21807
  }
21589
21808
  }
21590
21809
 
21591
21810
  // src/inner-voice/memory-reader.ts
21592
- import fs20 from "node:fs";
21593
- import path21 from "node:path";
21811
+ import fs21 from "node:fs";
21812
+ import path22 from "node:path";
21594
21813
  var US_HALF_LIFE_DAYS = 10;
21595
21814
  var US_MAX_LINES = 60;
21596
21815
  function readRecentMemory(workspace) {
21597
- const dir = path21.join(workspace, "memory");
21816
+ const dir = path22.join(workspace, "memory");
21598
21817
  const now = new Date(Date.now() + 8 * 36e5);
21599
21818
  const today = formatYmd(now);
21600
21819
  const yesterday = formatYmd(new Date(now.getTime() - 864e5));
21601
21820
  return {
21602
- today: readIfExists(path21.join(dir, `${today}.md`)),
21603
- yesterday: readIfExists(path21.join(dir, `${yesterday}.md`))
21821
+ today: readIfExists(path22.join(dir, `${today}.md`)),
21822
+ yesterday: readIfExists(path22.join(dir, `${yesterday}.md`))
21604
21823
  };
21605
21824
  }
21606
21825
  function sampleUs(workspace) {
21607
- const usFile = path21.join(workspace, "memory", "us.md");
21826
+ const usFile = path22.join(workspace, "memory", "us.md");
21608
21827
  let content;
21609
21828
  try {
21610
- content = fs20.readFileSync(usFile, "utf-8");
21829
+ content = fs21.readFileSync(usFile, "utf-8");
21611
21830
  } catch {
21612
21831
  return null;
21613
21832
  }
@@ -21653,7 +21872,7 @@ function recencyWeight(dateStr) {
21653
21872
  }
21654
21873
  function readIfExists(file) {
21655
21874
  try {
21656
- return fs20.readFileSync(file, "utf-8");
21875
+ return fs21.readFileSync(file, "utf-8");
21657
21876
  } catch {
21658
21877
  return "";
21659
21878
  }
@@ -21944,9 +22163,9 @@ var InnerVoicePlugin = class {
21944
22163
  }
21945
22164
  /** 读 workspace/prompts/my-inner-voice.md,不存在用 DEFAULT_PROMPT */
21946
22165
  loadPrompt(workspace) {
21947
- const promptPath = path22.join(workspace, "prompts", "my-inner-voice.md");
22166
+ const promptPath = path23.join(workspace, "prompts", "my-inner-voice.md");
21948
22167
  try {
21949
- const content = fs21.readFileSync(promptPath, "utf-8").trim();
22168
+ const content = fs22.readFileSync(promptPath, "utf-8").trim();
21950
22169
  if (content) {
21951
22170
  console.log(`[inner-voice] Loaded custom prompt from ${promptPath}`);
21952
22171
  return content;
@@ -22018,7 +22237,7 @@ var InnerVoicePlugin = class {
22018
22237
  console.warn(`[inner-voice] emotional-state failed: ${err.message}`);
22019
22238
  }
22020
22239
  try {
22021
- const content = fs21.readFileSync(path22.join(this.workspace, "SESSION-STATE.md"), "utf-8");
22240
+ const content = fs22.readFileSync(path23.join(this.workspace, "SESSION-STATE.md"), "utf-8");
22022
22241
  lines.push("\n--- SESSION-STATE\uFF08\u5C3E\u90E8\uFF09 ---");
22023
22242
  lines.push(content.slice(-2e3));
22024
22243
  } catch {
@@ -22132,10 +22351,10 @@ var InnerVoicePlugin = class {
22132
22351
  if (Math.random() >= activity.hintProb) {
22133
22352
  return { text: thought, hintTriggered: false, hintText: "" };
22134
22353
  }
22135
- const poolPath = path22.join(this.workspace, "inner-voice", "hints_pool.txt");
22354
+ const poolPath = path23.join(this.workspace, "inner-voice", "hints_pool.txt");
22136
22355
  let hint = "\u60F3\u4ED6\u5C31\u53D1\u6D88\u606F\u5427";
22137
22356
  try {
22138
- const pool = fs21.readFileSync(poolPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
22357
+ const pool = fs22.readFileSync(poolPath, "utf-8").split("\n").map((s) => s.trim()).filter(Boolean);
22139
22358
  if (pool.length) hint = pool[Math.floor(Math.random() * pool.length)];
22140
22359
  } catch {
22141
22360
  }
@@ -22160,7 +22379,7 @@ var InnerVoicePlugin = class {
22160
22379
  try {
22161
22380
  const writer = sessions.getWriter(mainSessionId);
22162
22381
  const history = sessions.getHistory(mainSessionId);
22163
- const fullPath = path22.resolve(this.workspace, emoTopic.file);
22382
+ const fullPath = path23.resolve(this.workspace, emoTopic.file);
22164
22383
  const memories = [{
22165
22384
  path: fullPath,
22166
22385
  content: emoTopic.content,
@@ -22188,12 +22407,12 @@ var InnerVoicePlugin = class {
22188
22407
  /** 写 xiaoyi.log(格式对齐旧 memory_whisper.py,便于既有日志分析复用)。 */
22189
22408
  writeLog(status, delivered, activity, hintTriggered, hintText) {
22190
22409
  try {
22191
- const logDir = path22.join(this.workspace, "inner-voice");
22192
- fs21.mkdirSync(logDir, { recursive: true });
22193
- const logPath = path22.join(logDir, "xiaoyi.log");
22410
+ const logDir = path23.join(this.workspace, "inner-voice");
22411
+ fs22.mkdirSync(logDir, { recursive: true });
22412
+ const logPath = path23.join(logDir, "xiaoyi.log");
22194
22413
  const ts = formatBeijingTs(/* @__PURE__ */ new Date());
22195
22414
  const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
22196
- fs21.appendFileSync(
22415
+ fs22.appendFileSync(
22197
22416
  logPath,
22198
22417
  `[${ts}] ${status} hint=${hintStatus} prob=${Math.round(activity.hintProb * 100)}%
22199
22418
  delivered: ${delivered}
@@ -22729,8 +22948,8 @@ var PluginManager = class {
22729
22948
  // src/voice-chat/plugin.ts
22730
22949
  import { spawn as spawn4, exec } from "node:child_process";
22731
22950
  import net from "node:net";
22732
- import path23 from "node:path";
22733
- import fs22 from "node:fs";
22951
+ import path24 from "node:path";
22952
+ import fs23 from "node:fs";
22734
22953
 
22735
22954
  // src/voice-chat/bridge.ts
22736
22955
  function registerVoiceChatBridge(httpServer, dispatcher, deps, config, sessions, voiceChatDeps) {
@@ -23099,20 +23318,20 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23099
23318
  }
23100
23319
  }
23101
23320
  findPython() {
23102
- if (this.config.pythonPath && fs22.existsSync(this.config.pythonPath)) {
23321
+ if (this.config.pythonPath && fs23.existsSync(this.config.pythonPath)) {
23103
23322
  return this.config.pythonPath;
23104
23323
  }
23105
23324
  return "python";
23106
23325
  }
23107
23326
  getPythonDir() {
23108
23327
  const dir = import.meta.dirname;
23109
- const srcDir = path23.resolve(dir, "..", "src", "voice-chat", "python");
23110
- const localDir = path23.join(dir, "python");
23111
- return fs22.existsSync(srcDir) ? srcDir : localDir;
23328
+ const srcDir = path24.resolve(dir, "..", "src", "voice-chat", "python");
23329
+ const localDir = path24.join(dir, "python");
23330
+ return fs23.existsSync(srcDir) ? srcDir : localDir;
23112
23331
  }
23113
23332
  startPython() {
23114
23333
  const pythonDir = this.getPythonDir();
23115
- const serverPy = path23.join(pythonDir, "server.py");
23334
+ const serverPy = path24.join(pythonDir, "server.py");
23116
23335
  const pythonBin = this.findPython();
23117
23336
  const args = [serverPy];
23118
23337
  if (this.config.pythonPort) args.push("--port", String(this.config.pythonPort));
@@ -23141,7 +23360,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23141
23360
  }
23142
23361
  console.log(`[voice-chat] Starting Python: ${pythonBin} ${args.join(" ")}`);
23143
23362
  console.log(`[voice-chat] Python dir: ${pythonDir}`);
23144
- if (!fs22.existsSync(pythonDir)) {
23363
+ if (!fs23.existsSync(pythonDir)) {
23145
23364
  console.error(`[voice-chat] FATAL: Python directory does not exist: ${pythonDir}`);
23146
23365
  throw new Error(`voice-chat: python dir not found: ${pythonDir}`);
23147
23366
  }
@@ -23164,7 +23383,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23164
23383
  child.on("error", (err) => {
23165
23384
  console.error(`[voice-chat] spawn error: ${err.message}`);
23166
23385
  console.error(`[voice-chat] shell=${pythonBin} cwd=${pythonDir}`);
23167
- console.error(`[voice-chat] cwd exists=${fs22.existsSync(pythonDir)}`);
23386
+ console.error(`[voice-chat] cwd exists=${fs23.existsSync(pythonDir)}`);
23168
23387
  });
23169
23388
  child.stdout?.on("data", (data) => {
23170
23389
  const lines = data.toString().trim().split("\n");
@@ -23197,8 +23416,8 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
23197
23416
  init_BashTool();
23198
23417
  import { spawn as spawn5, exec as exec2 } from "node:child_process";
23199
23418
  import net2 from "node:net";
23200
- import path24 from "node:path";
23201
- import fs23 from "node:fs";
23419
+ import path25 from "node:path";
23420
+ import fs24 from "node:fs";
23202
23421
 
23203
23422
  // src/memory/cognifold/config.ts
23204
23423
  var DEFAULTS3 = {
@@ -23236,11 +23455,11 @@ var CogniFoldClient = class {
23236
23455
  this.timeoutMs = timeoutMs;
23237
23456
  this.modelName = modelName;
23238
23457
  }
23239
- async req(path45, options = {}) {
23458
+ async req(path46, options = {}) {
23240
23459
  const ctrl = new AbortController();
23241
23460
  const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
23242
23461
  try {
23243
- const resp = await fetch(`${this.baseUrl}${path45}`, {
23462
+ const resp = await fetch(`${this.baseUrl}${path46}`, {
23244
23463
  ...options,
23245
23464
  signal: ctrl.signal,
23246
23465
  headers: {
@@ -23330,8 +23549,8 @@ var CogniFoldClient = class {
23330
23549
  });
23331
23550
  }
23332
23551
  /** 兼容老版命名 */
23333
- async recl(path45, options = {}) {
23334
- return this.req(path45, options);
23552
+ async recl(path46, options = {}) {
23553
+ return this.req(path46, options);
23335
23554
  }
23336
23555
  };
23337
23556
 
@@ -23612,16 +23831,16 @@ var CogniFoldPlugin = class {
23612
23831
  const dir = import.meta.dirname;
23613
23832
  const candidates = [
23614
23833
  // 从 dist/ 往回找 src
23615
- path24.resolve(dir, "..", "src", "memory", "cognifold", "python"),
23616
- path24.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
23617
- path24.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
23834
+ path25.resolve(dir, "..", "src", "memory", "cognifold", "python"),
23835
+ path25.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
23836
+ path25.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
23618
23837
  // 从 src/memory/cognifold/ 找本地
23619
- path24.join(dir, "python"),
23838
+ path25.join(dir, "python"),
23620
23839
  // 从 dist/memory/cognifold/ 找本地
23621
- path24.resolve(dir, "python")
23840
+ path25.resolve(dir, "python")
23622
23841
  ];
23623
23842
  for (const candidate of candidates) {
23624
- if (fs23.existsSync(path24.join(candidate, "cognifold"))) {
23843
+ if (fs24.existsSync(path25.join(candidate, "cognifold"))) {
23625
23844
  return candidate;
23626
23845
  }
23627
23846
  }
@@ -23647,7 +23866,7 @@ var CogniFoldPlugin = class {
23647
23866
  const pythonBin = this.findPython();
23648
23867
  console.log(`[cognifold] Starting Python: ${pythonBin} ${args.join(" ")}`);
23649
23868
  console.log(`[cognifold] Python dir: ${pythonDir}`);
23650
- if (!fs23.existsSync(path24.join(pythonDir, "cognifold"))) {
23869
+ if (!fs24.existsSync(path25.join(pythonDir, "cognifold"))) {
23651
23870
  console.error(`[cognifold] FATAL: Python module not found at ${pythonDir}/cognifold`);
23652
23871
  throw new Error(`cognifold: python module not found`);
23653
23872
  }
@@ -23658,10 +23877,10 @@ var CogniFoldPlugin = class {
23658
23877
  if (this.config.llm?.baseUrl) {
23659
23878
  childEnv["OPENAI_BASE_URL"] = this.config.llm.baseUrl;
23660
23879
  }
23661
- const envFile = path24.join(pythonDir, ".env");
23880
+ const envFile = path25.join(pythonDir, ".env");
23662
23881
  try {
23663
- if (fs23.existsSync(envFile)) {
23664
- const envContent = fs23.readFileSync(envFile, "utf-8");
23882
+ if (fs24.existsSync(envFile)) {
23883
+ const envContent = fs24.readFileSync(envFile, "utf-8");
23665
23884
  for (const line of envContent.split("\n")) {
23666
23885
  const trimmed = line.trim();
23667
23886
  if (!trimmed || trimmed.startsWith("#")) continue;
@@ -23727,11 +23946,10 @@ var CogniFoldPlugin = class {
23727
23946
  };
23728
23947
 
23729
23948
  // src/memory/everos/plugin.ts
23730
- init_BashTool();
23731
23949
  import { spawn as spawn6 } from "node:child_process";
23732
23950
  import net3 from "node:net";
23733
- import path25 from "node:path";
23734
- import fs24 from "node:fs";
23951
+ import path26 from "node:path";
23952
+ import fs25 from "node:fs";
23735
23953
 
23736
23954
  // src/memory/everos/config.ts
23737
23955
  var DEFAULTS4 = {
@@ -23938,8 +24156,8 @@ var EverosPlugin = class {
23938
24156
  }, 3e5);
23939
24157
  }
23940
24158
  async startEveros() {
23941
- const pythonDir = path25.dirname(this.config.lancedbPath);
23942
- const configPath = path25.join(pythonDir, "config.toml");
24159
+ const pythonDir = path26.dirname(this.config.lancedbPath);
24160
+ const configPath = path26.join(pythonDir, "config.toml");
23943
24161
  await this.ensureFcntlCompat();
23944
24162
  const venvPython = this.findVenvPython();
23945
24163
  const everosBin = venvPython.replace(/python\.exe$/, "everos.exe");
@@ -23948,16 +24166,15 @@ var EverosPlugin = class {
23948
24166
  console.log(`[everos] Starting EverOS: ${cmd}`);
23949
24167
  console.log(`[everos] LLM config: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
23950
24168
  if (process.platform === "win32") {
23951
- const { shell, args: shellArgs } = findShell();
23952
- spawn6(shell, [...shellArgs, cmd], {
24169
+ spawn6(everosBin, args, {
23953
24170
  cwd: pythonDir,
23954
- stdio: ["ignore", "pipe", "pipe"],
24171
+ stdio: "ignore",
23955
24172
  env: { ...process.env, PYTHONUNBUFFERED: "1", NO_PROXY: "127.0.0.1,localhost", no_proxy: "127.0.0.1,localhost" }
23956
24173
  });
23957
24174
  } else {
23958
24175
  spawn6(venvPython, args, {
23959
24176
  cwd: pythonDir,
23960
- stdio: ["ignore", "pipe", "pipe"],
24177
+ stdio: "ignore",
23961
24178
  env: { ...process.env, PYTHONUNBUFFERED: "1", NO_PROXY: "127.0.0.1,localhost", no_proxy: "127.0.0.1,localhost" }
23962
24179
  });
23963
24180
  }
@@ -24030,19 +24247,19 @@ var EverosPlugin = class {
24030
24247
  return child;
24031
24248
  }
24032
24249
  findVenvPython() {
24033
- const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path25.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24250
+ const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path26.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24034
24251
  if (process.platform === "win32") {
24035
- return path25.join(stateDir, "everos-venv", "Scripts", "python.exe");
24252
+ return path26.join(stateDir, "everos-venv", "Scripts", "python.exe");
24036
24253
  }
24037
- return path25.join(stateDir, "everos-venv", "bin", "python");
24254
+ return path26.join(stateDir, "everos-venv", "bin", "python");
24038
24255
  }
24039
24256
  /** 检测 venv 是否存在,不存在就自动创建 + 装 EverOS */
24040
24257
  async ensureVenv() {
24041
24258
  const venvPython = this.findVenvPython();
24042
- if (fs24.existsSync(venvPython)) return;
24043
- const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path25.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24044
- const venvDir = path25.join(stateDir, "everos-venv");
24045
- const everosSrc = path25.join(stateDir, "workspace", "research", "EverOS");
24259
+ if (fs25.existsSync(venvPython)) return;
24260
+ const stateDir = (process.env.ENGINE7_STATE_DIR ?? process.env.OPENCLAW_STATE_DIR) || path26.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
24261
+ const venvDir = path26.join(stateDir, "everos-venv");
24262
+ const everosSrc = path26.join(stateDir, "workspace", "research", "EverOS");
24046
24263
  console.log(`[everos] venv not found at ${venvDir}, auto-creating...`);
24047
24264
  console.log(`[everos] \u23F3 This may take a few minutes on first run...`);
24048
24265
  const pyCandidates = process.platform === "win32" ? ["python", "python3", "C:\\Python314\\python.exe", "C:\\Python313\\python.exe", "C:\\Python312\\python.exe"] : ["python3", "python"];
@@ -24064,9 +24281,9 @@ var EverosPlugin = class {
24064
24281
  console.log(`[everos] Creating venv with ${sysPython}...`);
24065
24282
  const { execSync: execSync3 } = await import("node:child_process");
24066
24283
  execSync3(`"${sysPython}" -m venv "${venvDir}"`, { stdio: "pipe", shell: true });
24067
- const pip = process.platform === "win32" ? path25.join(venvDir, "Scripts", "pip.exe") : path25.join(venvDir, "bin", "pip");
24068
- const everosReq = path25.join(this.getPythonDir(), "requirements.txt");
24069
- if (fs24.existsSync(everosReq)) {
24284
+ const pip = process.platform === "win32" ? path26.join(venvDir, "Scripts", "pip.exe") : path26.join(venvDir, "bin", "pip");
24285
+ const everosReq = path26.join(this.getPythonDir(), "requirements.txt");
24286
+ if (fs25.existsSync(everosReq)) {
24070
24287
  console.log(`[everos] Installing from requirements.txt...`);
24071
24288
  execSync3(`"${pip}" install -r "${everosReq}" -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
24072
24289
  } else {
@@ -24082,12 +24299,12 @@ var EverosPlugin = class {
24082
24299
  getPythonDir() {
24083
24300
  const dir = import.meta.dirname;
24084
24301
  const candidates = [
24085
- path25.join(dir, "python"),
24086
- path25.resolve(dir, "..", "src", "memory", "everos", "python"),
24087
- path25.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
24302
+ path26.join(dir, "python"),
24303
+ path26.resolve(dir, "..", "src", "memory", "everos", "python"),
24304
+ path26.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
24088
24305
  ];
24089
24306
  for (const candidate of candidates) {
24090
- if (fs24.existsSync(path25.join(candidate, "agentic_server.py"))) {
24307
+ if (fs25.existsSync(path26.join(candidate, "agentic_server.py"))) {
24091
24308
  return candidate;
24092
24309
  }
24093
24310
  }
@@ -24096,14 +24313,14 @@ var EverosPlugin = class {
24096
24313
  async ensureFcntlCompat() {
24097
24314
  if (process.platform !== "win32") return;
24098
24315
  const venvPython = this.findVenvPython();
24099
- const venvDir = path25.dirname(path25.dirname(venvPython));
24100
- const sitePackages = path25.join(venvDir, "Lib", "site-packages");
24101
- const target = path25.join(sitePackages, "fcntl.py");
24102
- if (fs24.existsSync(target)) return;
24103
- const source = path25.join(this.getPythonDir(), "fcntl_compat.py");
24104
- if (fs24.existsSync(source)) {
24316
+ const venvDir = path26.dirname(path26.dirname(venvPython));
24317
+ const sitePackages = path26.join(venvDir, "Lib", "site-packages");
24318
+ const target = path26.join(sitePackages, "fcntl.py");
24319
+ if (fs25.existsSync(target)) return;
24320
+ const source = path26.join(this.getPythonDir(), "fcntl_compat.py");
24321
+ if (fs25.existsSync(source)) {
24105
24322
  try {
24106
- fs24.copyFileSync(source, target);
24323
+ fs25.copyFileSync(source, target);
24107
24324
  console.log(`[everos] Installed fcntl compat shim to ${target}`);
24108
24325
  } catch (err) {
24109
24326
  console.warn(`[everos] Failed to install fcntl shim: ${err.message}`);
@@ -24150,21 +24367,21 @@ var EverosPlugin = class {
24150
24367
  init_task_manager();
24151
24368
 
24152
24369
  // src/skills/scanner.ts
24153
- import * as path26 from "node:path";
24154
- import * as fs25 from "node:fs";
24370
+ import * as path27 from "node:path";
24371
+ import * as fs26 from "node:fs";
24155
24372
  function scanSkills(skillsDir) {
24156
- if (!fs25.existsSync(skillsDir)) {
24373
+ if (!fs26.existsSync(skillsDir)) {
24157
24374
  console.log(`[skills] Directory not found: ${skillsDir}`);
24158
24375
  return [];
24159
24376
  }
24160
- const entries = fs25.readdirSync(skillsDir, { withFileTypes: true });
24377
+ const entries = fs26.readdirSync(skillsDir, { withFileTypes: true });
24161
24378
  const skills = [];
24162
24379
  for (const entry of entries) {
24163
24380
  if (!entry.isDirectory()) continue;
24164
- const skillMdPath = path26.join(skillsDir, entry.name, "SKILL.md");
24165
- if (!fs25.existsSync(skillMdPath)) continue;
24381
+ const skillMdPath = path27.join(skillsDir, entry.name, "SKILL.md");
24382
+ if (!fs26.existsSync(skillMdPath)) continue;
24166
24383
  try {
24167
- const content = fs25.readFileSync(skillMdPath, "utf-8");
24384
+ const content = fs26.readFileSync(skillMdPath, "utf-8");
24168
24385
  const frontmatter = parseFrontmatter2(content);
24169
24386
  if (!frontmatter.name) {
24170
24387
  console.warn(`[skills] Skipping ${entry.name}/SKILL.md: missing 'name' in frontmatter`);
@@ -24228,8 +24445,8 @@ function parseFrontmatter2(content) {
24228
24445
 
24229
24446
  // src/tools/SkillTool/SkillTool.ts
24230
24447
  init_registry();
24231
- import * as fs26 from "node:fs";
24232
- import * as path27 from "node:path";
24448
+ import * as fs27 from "node:fs";
24449
+ import * as path28 from "node:path";
24233
24450
 
24234
24451
  // src/tools/SkillTool/constants.ts
24235
24452
  var SKILL_TOOL_NAME2 = "Skill";
@@ -24306,12 +24523,12 @@ Important:
24306
24523
  `;
24307
24524
  }
24308
24525
  function loadSkillContent(skillName) {
24309
- const skillMdPath = path27.join(skillsDirPath, skillName, "SKILL.md");
24310
- if (!fs26.existsSync(skillMdPath)) return null;
24311
- const content = fs26.readFileSync(skillMdPath, "utf-8");
24526
+ const skillMdPath = path28.join(skillsDirPath, skillName, "SKILL.md");
24527
+ if (!fs27.existsSync(skillMdPath)) return null;
24528
+ const content = fs27.readFileSync(skillMdPath, "utf-8");
24312
24529
  const bodyMatch = content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)/);
24313
24530
  const body = bodyMatch ? bodyMatch[1] : content;
24314
- const skillDir = path27.dirname(skillMdPath);
24531
+ const skillDir = path28.dirname(skillMdPath);
24315
24532
  const normalizedDir = process.platform === "win32" ? skillDir.replace(/\\/g, "/") : skillDir;
24316
24533
  let finalContent = `Base directory for this skill: ${normalizedDir}
24317
24534
 
@@ -24585,12 +24802,12 @@ Examples:
24585
24802
  // src/tools/msg-husband.ts
24586
24803
  init_registry();
24587
24804
  init_live();
24588
- import fs27 from "node:fs";
24589
- import path28 from "node:path";
24805
+ import fs28 from "node:fs";
24806
+ import path29 from "node:path";
24590
24807
  function getHusbandFeishuId(workspace) {
24591
- const contactsPath = path28.join(workspace, "prompts", "contacts.md");
24808
+ const contactsPath = path29.join(workspace, "prompts", "contacts.md");
24592
24809
  try {
24593
- const text = fs27.readFileSync(contactsPath, "utf-8");
24810
+ const text = fs28.readFileSync(contactsPath, "utf-8");
24594
24811
  const m = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
24595
24812
  return m ? m[1] : null;
24596
24813
  } catch {
@@ -24732,8 +24949,8 @@ Examples:
24732
24949
  if (!to && !resolvedChannelId) {
24733
24950
  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 };
24734
24951
  }
24735
- const fs42 = await import("node:fs");
24736
- if (!fs42.existsSync(filePath)) {
24952
+ const fs43 = await import("node:fs");
24953
+ if (!fs43.existsSync(filePath)) {
24737
24954
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6\u4E0D\u5B58\u5728 ${filePath}`, isError: true };
24738
24955
  }
24739
24956
  const toIds = to ? to.split(",").map((s) => s.trim()).filter(Boolean) : [];
@@ -24761,7 +24978,7 @@ Examples:
24761
24978
  md: "text/markdown"
24762
24979
  };
24763
24980
  const mimeType = mimeTypeMap[ext] || "application/octet-stream";
24764
- const stat4 = fs42.statSync(filePath);
24981
+ const stat4 = fs43.statSync(filePath);
24765
24982
  const sizeMB = stat4.size / 1024 / 1024;
24766
24983
  if (sizeMB > 25) {
24767
24984
  return { content: `\u53D1\u9001\u5931\u8D25: \u6587\u4EF6 ${sizeMB.toFixed(1)}MB \u8D85\u8FC7 Discord 25MB \u9650\u5236`, isError: true };
@@ -24790,8 +25007,8 @@ Examples:
24790
25007
  // src/tools/my-eyes.ts
24791
25008
  init_live();
24792
25009
  init_registry();
24793
- import * as fs28 from "node:fs";
24794
- import * as path29 from "node:path";
25010
+ import * as fs29 from "node:fs";
25011
+ import * as path30 from "node:path";
24795
25012
  var MIME_MAP = {
24796
25013
  ".jpg": "jpeg",
24797
25014
  ".jpeg": "jpeg",
@@ -24801,9 +25018,9 @@ var MIME_MAP = {
24801
25018
  ".bmp": "bmp"
24802
25019
  };
24803
25020
  function resolveLatestImage(specifiedPath, mediaDir) {
24804
- if (specifiedPath && fs28.existsSync(specifiedPath)) return specifiedPath;
24805
- if (!fs28.existsSync(mediaDir)) return null;
24806
- 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);
25021
+ if (specifiedPath && fs29.existsSync(specifiedPath)) return specifiedPath;
25022
+ if (!fs29.existsSync(mediaDir)) return null;
25023
+ 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);
24807
25024
  return files[0]?.p || null;
24808
25025
  }
24809
25026
  registry.register({
@@ -24828,15 +25045,15 @@ registry.register({
24828
25045
  if (!provider?.streamChat) {
24829
25046
  return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
24830
25047
  }
24831
- const mediaDir = path29.join(ctx.stateDir, "media", "inbound");
25048
+ const mediaDir = path30.join(ctx.stateDir, "media", "inbound");
24832
25049
  const imagePath = resolveLatestImage(args.image_path, mediaDir);
24833
25050
  if (!imagePath) {
24834
25051
  return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
24835
25052
  }
24836
25053
  const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
24837
- const ext = path29.extname(imagePath).toLowerCase();
25054
+ const ext = path30.extname(imagePath).toLowerCase();
24838
25055
  const mime = MIME_MAP[ext] || "jpeg";
24839
- const imgB64 = fs28.readFileSync(imagePath).toString("base64");
25056
+ const imgB64 = fs29.readFileSync(imagePath).toString("base64");
24840
25057
  const userMsg = {
24841
25058
  role: "user",
24842
25059
  content: [
@@ -24873,14 +25090,14 @@ init_live();
24873
25090
  init_registry();
24874
25091
  import { execFile } from "node:child_process";
24875
25092
  import { promisify } from "node:util";
24876
- import * as fs29 from "node:fs";
24877
- import * as path30 from "node:path";
25093
+ import * as fs30 from "node:fs";
25094
+ import * as path31 from "node:path";
24878
25095
  import * as os3 from "node:os";
24879
25096
  var execFileAsync = promisify(execFile);
24880
- var VOICE_DIR = path30.join(os3.tmpdir(), "engine-voice");
25097
+ var VOICE_DIR = path31.join(os3.tmpdir(), "engine-voice");
24881
25098
  async function ttsCosyvoice(text, apiKey, model, voice, workspaceId, instruction) {
24882
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
24883
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25099
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25100
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.wav`);
24884
25101
  const script = `
24885
25102
  import sys, json, wave, time, threading
24886
25103
  import dashscope
@@ -24937,7 +25154,7 @@ print(f"OK: {len(pcm)} bytes")
24937
25154
  `;
24938
25155
  const configJson = JSON.stringify({ apiKey, model, voice, workspaceId, instruction });
24939
25156
  await execFileAsync("python3", ["-c", script, configJson, text, output], { timeout: 3e4 });
24940
- if (!fs29.existsSync(output) || fs29.statSync(output).size < 100) {
25157
+ if (!fs30.existsSync(output) || fs30.statSync(output).size < 100) {
24941
25158
  throw new Error("CosyVoice produced empty output");
24942
25159
  }
24943
25160
  return output;
@@ -24947,8 +25164,8 @@ var GPTSOVITS_REF_WAV = "/home/chong/voice/ref/shanshan_ref_v2.wav";
24947
25164
  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";
24948
25165
  var GPTSOVITS_REF_LANG = "zh";
24949
25166
  async function ttsGptsovits(text) {
24950
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
24951
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.wav`);
25167
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25168
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.wav`);
24952
25169
  const params = new URLSearchParams({
24953
25170
  text,
24954
25171
  text_language: "zh",
@@ -24959,13 +25176,13 @@ async function ttsGptsovits(text) {
24959
25176
  const res = await fetch(`${GPTSOVITS_API}/?${params}`);
24960
25177
  if (!res.ok) throw new Error(`GPT-SoVITS API ${res.status}`);
24961
25178
  const buf = Buffer.from(await res.arrayBuffer());
24962
- fs29.writeFileSync(output, buf);
25179
+ fs30.writeFileSync(output, buf);
24963
25180
  return output;
24964
25181
  }
24965
25182
  var EDGE_VOICE = "zh-CN-XiaoxiaoNeural";
24966
25183
  async function ttsEdge(text) {
24967
- fs29.mkdirSync(VOICE_DIR, { recursive: true });
24968
- const output = path30.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
25184
+ fs30.mkdirSync(VOICE_DIR, { recursive: true });
25185
+ const output = path31.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
24969
25186
  const script = `
24970
25187
  import asyncio, edge_tts, sys
24971
25188
  async def main():
@@ -24991,7 +25208,7 @@ async function compressWav(wavPath) {
24991
25208
  "+faststart",
24992
25209
  m4aPath
24993
25210
  ], { timeout: 3e4 });
24994
- fs29.unlinkSync(wavPath);
25211
+ fs30.unlinkSync(wavPath);
24995
25212
  return m4aPath;
24996
25213
  } catch {
24997
25214
  return wavPath;
@@ -25059,10 +25276,10 @@ registry.register({
25059
25276
  } catch (e) {
25060
25277
  return { content: `TTS failed: ${e.message}`, isError: true };
25061
25278
  }
25062
- const ext = path30.extname(audioPath).toLowerCase();
25279
+ const ext = path31.extname(audioPath).toLowerCase();
25063
25280
  const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg" };
25064
25281
  const mimeType = mimeMap[ext] || "audio/mpeg";
25065
- const sizeKB = fs29.statSync(audioPath).size / 1024;
25282
+ const sizeKB = fs30.statSync(audioPath).size / 1024;
25066
25283
  const resolvedChannel = args.channel || ctx.channel || "feishu";
25067
25284
  const target = ctx.channelTarget || ctx.from;
25068
25285
  try {
@@ -25072,7 +25289,7 @@ registry.register({
25072
25289
  filename: `voice_${Date.now()}${ext}`
25073
25290
  });
25074
25291
  try {
25075
- fs29.unlinkSync(audioPath);
25292
+ fs30.unlinkSync(audioPath);
25076
25293
  } catch {
25077
25294
  }
25078
25295
  return { content: `Voice sent! (${actualEngine}, ${sizeKB.toFixed(0)}KB, ${resolvedChannel})` };
@@ -25089,8 +25306,8 @@ registry.register({
25089
25306
  // src/tools/my-selfie.ts
25090
25307
  init_live();
25091
25308
  init_registry();
25092
- import * as fs30 from "node:fs";
25093
- import * as path31 from "node:path";
25309
+ import * as fs31 from "node:fs";
25310
+ import * as path32 from "node:path";
25094
25311
  function getProxyDispatcher2() {
25095
25312
  const cfg = liveConfig.all();
25096
25313
  const proxy = cfg.providers?.xai?.proxy;
@@ -25147,16 +25364,48 @@ function detectMode(input) {
25147
25364
  return "direct";
25148
25365
  }
25149
25366
  async function generateWithFal(imageB64, prompt, resolution) {
25367
+ let aspectRatio;
25368
+ try {
25369
+ const refBuf = Buffer.from(imageB64, "base64");
25370
+ let w = 0, h = 0;
25371
+ if (refBuf[0] === 137 && refBuf[1] === 80) {
25372
+ w = refBuf.readUInt32BE(16);
25373
+ h = refBuf.readUInt32BE(20);
25374
+ } else if (refBuf[0] === 255 && refBuf[1] === 216) {
25375
+ let pos = 2;
25376
+ while (pos < refBuf.length - 1) {
25377
+ if (refBuf[pos] !== 255) {
25378
+ pos++;
25379
+ continue;
25380
+ }
25381
+ const marker = refBuf[pos + 1];
25382
+ if (marker === 192 || marker === 194) {
25383
+ h = refBuf.readUInt16BE(pos + 5);
25384
+ w = refBuf.readUInt16BE(pos + 7);
25385
+ break;
25386
+ }
25387
+ pos += 2 + refBuf.readUInt16BE(pos + 2);
25388
+ }
25389
+ }
25390
+ if (w > 0 && h > 0) {
25391
+ aspectRatio = `${w}/${h}`;
25392
+ console.log(`[my-selfie] ref image ${w}x${h}, aspect_ratio=${aspectRatio}`);
25393
+ }
25394
+ } catch (e) {
25395
+ console.warn(`[my-selfie] Failed to read ref dimensions: ${e.message}`);
25396
+ }
25397
+ const body = {
25398
+ image_url: `data:image/png;base64,${imageB64}`,
25399
+ prompt,
25400
+ num_images: 1,
25401
+ output_format: "jpeg",
25402
+ resolution
25403
+ };
25404
+ if (aspectRatio) body.aspect_ratio = aspectRatio;
25150
25405
  const res = await fetch(FAL_ENDPOINT, {
25151
25406
  method: "POST",
25152
25407
  headers: { "Authorization": `Key ${FAL_KEY}`, "Content-Type": "application/json" },
25153
- body: JSON.stringify({
25154
- image_url: `data:image/png;base64,${imageB64}`,
25155
- prompt,
25156
- num_images: 1,
25157
- output_format: "jpeg",
25158
- resolution
25159
- })
25408
+ body: JSON.stringify(body)
25160
25409
  });
25161
25410
  if (!res.ok) {
25162
25411
  const text = await res.text();
@@ -25365,12 +25614,12 @@ registry.register({
25365
25614
  const REFERENCES = getReferences(ctx);
25366
25615
  const refName = args.reference || "default";
25367
25616
  const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
25368
- const refPath = path31.join(ctx.workspace, refEntry.p);
25369
- if (provider !== "autodl" && !fs30.existsSync(refPath)) {
25617
+ const refPath = path32.join(ctx.workspace, refEntry.p);
25618
+ if (provider !== "autodl" && !fs31.existsSync(refPath)) {
25370
25619
  return { content: `Error: reference image not found at ${refPath}`, isError: true };
25371
25620
  }
25372
25621
  const resolution = args.resolution || DEFAULT_RESOLUTION;
25373
- const refB64 = fs30.existsSync(refPath) ? fs30.readFileSync(refPath).toString("base64") : "";
25622
+ const refB64 = fs31.existsSync(refPath) ? fs31.readFileSync(refPath).toString("base64") : "";
25374
25623
  let imageBuffer;
25375
25624
  try {
25376
25625
  if (provider === "autodl") {
@@ -25385,11 +25634,11 @@ registry.register({
25385
25634
  } catch (err) {
25386
25635
  return { content: `Selfie generation failed: ${err.message}`, isError: true };
25387
25636
  }
25388
- const imagesDir = path31.join(ctx.workspace, "images");
25389
- if (!fs30.existsSync(imagesDir)) fs30.mkdirSync(imagesDir, { recursive: true });
25637
+ const imagesDir = path32.join(ctx.workspace, "images");
25638
+ if (!fs31.existsSync(imagesDir)) fs31.mkdirSync(imagesDir, { recursive: true });
25390
25639
  const filename = `selfie_${Date.now()}.jpg`;
25391
- const outputPath = path31.join(imagesDir, filename);
25392
- fs30.writeFileSync(outputPath, imageBuffer);
25640
+ const outputPath = path32.join(imagesDir, filename);
25641
+ fs31.writeFileSync(outputPath, imageBuffer);
25393
25642
  const mgr = ctx.channelManager;
25394
25643
  if (mgr) {
25395
25644
  const resolvedChannel = ctx.channel || "feishu";
@@ -25400,11 +25649,11 @@ registry.register({
25400
25649
  mimeType: "image/jpeg"
25401
25650
  });
25402
25651
  } catch (err) {
25403
- return { content: `Selfie generated but send failed: ${err.message}. Image: ${path31.resolve(outputPath)}`, isError: false };
25652
+ return { content: `Selfie generated but send failed: ${err.message}. Image: ${path32.resolve(outputPath)}`, isError: false };
25404
25653
  }
25405
25654
  return { content: `Selfie sent! Mode: ${mode}, Provider: ${provider}, Ref: ${refEntry.name}` };
25406
25655
  }
25407
- return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path31.resolve(outputPath)}` };
25656
+ return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path32.resolve(outputPath)}` };
25408
25657
  },
25409
25658
  isConcurrencySafe: () => false,
25410
25659
  interruptBehavior: () => "block",
@@ -25949,16 +26198,16 @@ var EXIT_PLAN_MODE_TOOL_NAME = "ExitPlanMode";
25949
26198
  init_planModeState();
25950
26199
 
25951
26200
  // src/utils/plans.ts
25952
- import * as fs32 from "node:fs";
25953
- import * as path33 from "node:path";
26201
+ import * as fs33 from "node:fs";
26202
+ import * as path34 from "node:path";
25954
26203
  import * as crypto4 from "node:crypto";
25955
26204
  var MAX_SLUG_RETRIES = 10;
25956
26205
  function generateSlug() {
25957
26206
  return crypto4.randomBytes(4).toString("hex");
25958
26207
  }
25959
26208
  function getPlansDirectory(stateDir) {
25960
- const plansDir = path33.join(stateDir, "plans");
25961
- fs32.mkdirSync(plansDir, { recursive: true });
26209
+ const plansDir = path34.join(stateDir, "plans");
26210
+ fs33.mkdirSync(plansDir, { recursive: true });
25962
26211
  return plansDir;
25963
26212
  }
25964
26213
  var planSlugCache = /* @__PURE__ */ new Map();
@@ -25968,8 +26217,8 @@ function getPlanSlug(sessionId, stateDir) {
25968
26217
  const plansDir = getPlansDirectory(stateDir);
25969
26218
  for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
25970
26219
  slug = generateSlug();
25971
- const filePath = path33.join(plansDir, `${slug}.md`);
25972
- if (!fs32.existsSync(filePath)) {
26220
+ const filePath = path34.join(plansDir, `${slug}.md`);
26221
+ if (!fs33.existsSync(filePath)) {
25973
26222
  break;
25974
26223
  }
25975
26224
  }
@@ -25980,21 +26229,21 @@ function getPlanSlug(sessionId, stateDir) {
25980
26229
  function getPlanFilePath(sessionId, stateDir, agentId) {
25981
26230
  const slug = getPlanSlug(sessionId, stateDir);
25982
26231
  if (!agentId) {
25983
- return path33.join(getPlansDirectory(stateDir), `${slug}.md`);
26232
+ return path34.join(getPlansDirectory(stateDir), `${slug}.md`);
25984
26233
  }
25985
- return path33.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
26234
+ return path34.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
25986
26235
  }
25987
26236
  function getPlan(sessionId, stateDir, agentId) {
25988
26237
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
25989
26238
  try {
25990
- return fs32.readFileSync(filePath, "utf-8");
26239
+ return fs33.readFileSync(filePath, "utf-8");
25991
26240
  } catch {
25992
26241
  return null;
25993
26242
  }
25994
26243
  }
25995
26244
  function writePlan(sessionId, stateDir, content, agentId) {
25996
26245
  const filePath = getPlanFilePath(sessionId, stateDir, agentId);
25997
- fs32.writeFileSync(filePath, content, "utf-8");
26246
+ fs33.writeFileSync(filePath, content, "utf-8");
25998
26247
  return filePath;
25999
26248
  }
26000
26249
 
@@ -26830,8 +27079,8 @@ async function setupFeatures(features, licensedFeatures) {
26830
27079
 
26831
27080
  // src/license/license.ts
26832
27081
  import * as crypto6 from "node:crypto";
26833
- import * as fs39 from "node:fs";
26834
- import * as path41 from "node:path";
27082
+ import * as fs40 from "node:fs";
27083
+ import * as path42 from "node:path";
26835
27084
  var EMBEDDED_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
26836
27085
  MCowBQYDK2VwAyEAaKBEX+e8+D59qwtidazsu7WYDglApyvsVI3APwFoakA=
26837
27086
  -----END PUBLIC KEY-----`;
@@ -26862,13 +27111,13 @@ function loadLicense(stateDir, devMode) {
26862
27111
  _cachedLicense = allActive;
26863
27112
  return allActive;
26864
27113
  }
26865
- const licensePath = path41.join(stateDir, "license.json");
26866
- if (!fs39.existsSync(licensePath)) {
27114
+ const licensePath = path42.join(stateDir, "license.json");
27115
+ if (!fs40.existsSync(licensePath)) {
26867
27116
  console.log("[license] No license.json found, running basic engine only");
26868
27117
  return null;
26869
27118
  }
26870
27119
  try {
26871
- const raw = fs39.readFileSync(licensePath, "utf-8");
27120
+ const raw = fs40.readFileSync(licensePath, "utf-8");
26872
27121
  const license = JSON.parse(raw);
26873
27122
  const { signature, ...payload } = license;
26874
27123
  if (!signature) {
@@ -26999,7 +27248,7 @@ ${formatted}` };
26999
27248
  };
27000
27249
  }
27001
27250
  function createEverosGetTool() {
27002
- const fs42 = __require("node:fs/promises");
27251
+ const fs43 = __require("node:fs/promises");
27003
27252
  return {
27004
27253
  name: "memory_get",
27005
27254
  description: "Read a memory file by path.",
@@ -27015,7 +27264,7 @@ function createEverosGetTool() {
27015
27264
  handler: async (args) => {
27016
27265
  try {
27017
27266
  const filePath = args.path;
27018
- const content = await fs42.readFile(filePath, "utf-8");
27267
+ const content = await fs43.readFile(filePath, "utf-8");
27019
27268
  const lines = content.split("\n");
27020
27269
  const fromLine = args.from ?? 1;
27021
27270
  const numLines = args.lines ?? lines.length;
@@ -27488,11 +27737,11 @@ async function startEngine(config, opts) {
27488
27737
  process.env.ENGINE7_WORKSPACE = config.workspace;
27489
27738
  process.env.OPENCLAW_WORKSPACE = config.workspace;
27490
27739
  process.env.ENGINE7_STATE_DIR = config.stateDir;
27491
- fs41.mkdirSync(path44.join(config.stateDir, "agents", "main", "memory"), { recursive: true });
27492
- fs41.mkdirSync(path44.join(config.stateDir, "agents", "main", "sessions"), { recursive: true });
27493
- fs41.mkdirSync(path44.join(config.stateDir, "logs"), { recursive: true });
27494
- fs41.mkdirSync(config.workspace, { recursive: true });
27495
- fs41.mkdirSync(config.mediaDir, { recursive: true });
27740
+ fs42.mkdirSync(path45.join(config.stateDir, "agents", "main", "memory"), { recursive: true });
27741
+ fs42.mkdirSync(path45.join(config.stateDir, "agents", "main", "sessions"), { recursive: true });
27742
+ fs42.mkdirSync(path45.join(config.stateDir, "logs"), { recursive: true });
27743
+ fs42.mkdirSync(config.workspace, { recursive: true });
27744
+ fs42.mkdirSync(config.mediaDir, { recursive: true });
27496
27745
  try {
27497
27746
  process.chdir(config.workspace);
27498
27747
  } catch (e) {
@@ -27547,7 +27796,7 @@ async function startEngine(config, opts) {
27547
27796
  const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
27548
27797
  initSessionMemory2({
27549
27798
  workspace: config.workspace,
27550
- stateDir: path44.join(config.stateDir, "session-memory"),
27799
+ stateDir: path45.join(config.stateDir, "session-memory"),
27551
27800
  provider,
27552
27801
  model: config.provider.modelId || config.model || "deepseek-v4-flash",
27553
27802
  features: config.profile.features
@@ -27577,9 +27826,9 @@ async function startEngine(config, opts) {
27577
27826
  if (config.hooks) {
27578
27827
  loadHooksFromConfig({ hooks: config.hooks });
27579
27828
  }
27580
- const hooksPath = path44.join(config.workspace, ".hooks.json");
27829
+ const hooksPath = path45.join(config.workspace, ".hooks.json");
27581
27830
  loadHooksFromFile(hooksPath);
27582
- const settingsHooksPath = path44.join(config.stateDir, "settings.json");
27831
+ const settingsHooksPath = path45.join(config.stateDir, "settings.json");
27583
27832
  loadHooksFromFile(settingsHooksPath);
27584
27833
  console.log(`[hooks] Loaded hooks configuration`);
27585
27834
  registerCallbackHook("PreCompact", {
@@ -27593,18 +27842,18 @@ async function startEngine(config, opts) {
27593
27842
  const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
27594
27843
  const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
27595
27844
  const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
27596
- const dailyDir = path44.join(workspace, "memory", "daily");
27597
- const dailyPath = path44.join(dailyDir, `${dateStr}.md`);
27845
+ const dailyDir = path45.join(workspace, "memory", "daily");
27846
+ const dailyPath = path45.join(dailyDir, `${dateStr}.md`);
27598
27847
  try {
27599
- const fs42 = await import("node:fs");
27600
- if (!fs42.existsSync(dailyDir)) {
27601
- fs42.mkdirSync(dailyDir, { recursive: true });
27848
+ const fs43 = await import("node:fs");
27849
+ if (!fs43.existsSync(dailyDir)) {
27850
+ fs43.mkdirSync(dailyDir, { recursive: true });
27602
27851
  }
27603
- const sessionsDir = path44.join(config.stateDir, "agents", "main", "sessions");
27604
- const sessionFile = path44.join(sessionsDir, `${sessionId}.jsonl`);
27852
+ const sessionsDir = path45.join(config.stateDir, "agents", "main", "sessions");
27853
+ const sessionFile = path45.join(sessionsDir, `${sessionId}.jsonl`);
27605
27854
  const recentLines = [];
27606
- if (fs42.existsSync(sessionFile)) {
27607
- const content = fs42.readFileSync(sessionFile, "utf-8");
27855
+ if (fs43.existsSync(sessionFile)) {
27856
+ const content = fs43.readFileSync(sessionFile, "utf-8");
27608
27857
  const lines = content.trim().split("\n").filter(Boolean);
27609
27858
  const userLines = lines.filter((l) => {
27610
27859
  try {
@@ -27634,10 +27883,10 @@ async function startEngine(config, opts) {
27634
27883
  const entry = `${header}
27635
27884
  ${body}
27636
27885
  `;
27637
- if (fs42.existsSync(dailyPath)) {
27638
- fs42.appendFileSync(dailyPath, entry);
27886
+ if (fs43.existsSync(dailyPath)) {
27887
+ fs43.appendFileSync(dailyPath, entry);
27639
27888
  } else {
27640
- fs42.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27889
+ fs43.writeFileSync(dailyPath, `# ${dateStr} \u65E5\u5FD7
27641
27890
  ${entry}`);
27642
27891
  }
27643
27892
  console.log(`[hooks] PreCompact: saved ${recentLines.length} lines to ${dailyPath}`);
@@ -27653,16 +27902,16 @@ ${entry}`);
27653
27902
  const workspace = input.cwd || input.workspace || "";
27654
27903
  if (!workspace) return { continue: true };
27655
27904
  try {
27656
- const fs42 = await import("node:fs");
27657
- const bufferPath = path44.join(workspace, "memory", "working-buffer.md");
27658
- if (fs42.existsSync(bufferPath)) {
27659
- const stat4 = fs42.statSync(bufferPath);
27905
+ const fs43 = await import("node:fs");
27906
+ const bufferPath = path45.join(workspace, "memory", "working-buffer.md");
27907
+ if (fs43.existsSync(bufferPath)) {
27908
+ const stat4 = fs43.statSync(bufferPath);
27660
27909
  const ageMs = Date.now() - stat4.mtimeMs;
27661
27910
  const ageMin = Math.round(ageMs / 6e4);
27662
27911
  if (ageMin > 10) {
27663
27912
  console.warn(`[hooks] PostCompact: \u26A0\uFE0F working-buffer.md is ${ageMin}min old (last modified ${stat4.mtime.toISOString()}) \u2014 content may be stale!`);
27664
27913
  }
27665
- const content = fs42.readFileSync(bufferPath, "utf-8");
27914
+ const content = fs43.readFileSync(bufferPath, "utf-8");
27666
27915
  if (content.trim()) {
27667
27916
  console.log(`[hooks] PostCompact: injecting working-buffer (${content.length} chars, ${ageMin}min old)`);
27668
27917
  return {
@@ -27707,7 +27956,7 @@ ${content}`
27707
27956
  return `${hr}h ${remMin}m`;
27708
27957
  }
27709
27958
  if (config.skills?.enabled !== false) {
27710
- const skillsDir = config.skills?.path ? path44.isAbsolute(config.skills.path) ? config.skills.path : path44.resolve(config.workspace, config.skills.path) : path44.resolve(config.workspace, "skills");
27959
+ const skillsDir = config.skills?.path ? path45.isAbsolute(config.skills.path) ? config.skills.path : path45.resolve(config.workspace, config.skills.path) : path45.resolve(config.workspace, "skills");
27711
27960
  const modelDef2 = config.provider.models.find((m) => m.id === config.model);
27712
27961
  const contextWindowTokens = modelDef2?.contextWindow;
27713
27962
  const skills = scanSkills(skillsDir);
@@ -27726,8 +27975,8 @@ ${content}`
27726
27975
  workspace: config.workspace
27727
27976
  });
27728
27977
  const systemPrompt = [systemStable, systemDynamic].join("\n\n");
27729
- const promptDumpPath = path44.join(config.workspace, ".system-prompt.txt");
27730
- fs41.writeFileSync(promptDumpPath, systemPrompt);
27978
+ const promptDumpPath = path45.join(config.workspace, ".system-prompt.txt");
27979
+ fs42.writeFileSync(promptDumpPath, systemPrompt);
27731
27980
  console.log(`System prompt: ${systemStable.length} chars stable + ${systemDynamic.length} chars dynamic \u2192 ${promptDumpPath}`);
27732
27981
  const modelDef = config.provider.models.find((m) => m.id === config.model);
27733
27982
  const modelContextWindow = modelDef?.contextWindow;
@@ -27852,13 +28101,11 @@ ${content}`
27852
28101
  model: config.model,
27853
28102
  modelInputs: modelDef?.input || ["text"],
27854
28103
  systemPrompt,
27855
- features: config.profile.features,
27856
28104
  channels: config.channels,
27857
28105
  config,
27858
28106
  // tool 读自己配置用
27859
28107
  recallProvider: memoryRecallProvider || void 0,
27860
28108
  extractProvider: memoryExtractProvider || void 0,
27861
- topics: config.topics,
27862
28109
  everosCfg: config.everos,
27863
28110
  mcpManager
27864
28111
  };
@@ -27875,7 +28122,6 @@ ${content}`
27875
28122
  model: visionConfig.modelId,
27876
28123
  modelInputs: visionModelDef?.input || ["text", "image"],
27877
28124
  systemPrompt,
27878
- features: config.profile.features,
27879
28125
  channels: config.channels,
27880
28126
  config,
27881
28127
  // tool 读自己配置用
@@ -27926,7 +28172,6 @@ ${content}`
27926
28172
  model: p.model,
27927
28173
  modelInputs: p.modelInputs,
27928
28174
  systemPrompt,
27929
- features: config.profile.features,
27930
28175
  channels: config.channels,
27931
28176
  config,
27932
28177
  recallProvider: memoryRecallProvider || void 0,
@@ -27970,7 +28215,6 @@ ${content}`
27970
28215
  model: modelId,
27971
28216
  modelInputs: modelDef2.input || ["text"],
27972
28217
  systemPrompt,
27973
- features: config.profile.features,
27974
28218
  channels: config.channels,
27975
28219
  recallProvider: memoryRecallProvider || void 0,
27976
28220
  extractProvider: memoryExtractProvider || void 0
@@ -28755,8 +28999,7 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
28755
28999
  const featureKeys = ["topic-recall", "topic-extract", "session-memory"];
28756
29000
  if (featureKeys.includes(ctx.command)) {
28757
29001
  const key = ctx.command;
28758
- const f = deps.features || {};
28759
- const cur = f[key] === false ? "off" : "on";
29002
+ const cur = getFeature(key) === false ? "off" : "on";
28760
29003
  const rawState = (ctx.args.state || "").trim().toLowerCase();
28761
29004
  if (rawState === "") {
28762
29005
  await ctx.reply(`\u{1F4CA} ${key}: **${cur}**`);
@@ -28772,39 +29015,9 @@ ${result.changes.map((c) => `- ${c}`).join("\n")}` : `\u274C Reload failed: ${re
28772
29015
  return;
28773
29016
  }
28774
29017
  const next = rawState === "on";
28775
- if (!config.features) config.features = {};
28776
- config.features[key] = next;
28777
- if (deps.features) deps.features[key] = next;
28778
- try {
28779
- const fs42 = await import("fs");
28780
- const pathMod = await import("path");
28781
- let cfgPath = config._configFilePath;
28782
- if (!cfgPath || !fs42.existsSync(cfgPath)) {
28783
- const __filename = fileURLToPath(import.meta.url);
28784
- const __dirname = pathMod.dirname(__filename);
28785
- cfgPath = pathMod.resolve(__dirname, "../configs", pathMod.basename(cfgPath || "engine-config.json"));
28786
- }
28787
- const cfg = JSON.parse(fs42.readFileSync(cfgPath, "utf-8"));
28788
- let featObj = null;
28789
- if (cfg.agents?.defaults?.features) {
28790
- featObj = cfg.agents.defaults.features;
28791
- } else if (cfg.agents?.defaults) {
28792
- cfg.agents.defaults.features = {};
28793
- featObj = cfg.agents.defaults.features;
28794
- }
28795
- if (featObj) {
28796
- featObj[key] = next;
28797
- fs42.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
28798
- console.log(`[${ctx.command}] ${key} ${cur} \u2192 ${rawState} (disk persisted)`);
28799
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
28800
- } else {
28801
- console.warn(`[${ctx.command}] could not locate features in config, in-memory only`);
28802
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**\uFF08\u5185\u5B58\u751F\u6548\uFF0C\u78C1\u76D8\u672A\u627E\u5230 features \u8DEF\u5F84\uFF09`);
28803
- }
28804
- } catch (e) {
28805
- console.warn(`[${ctx.command}] disk write failed: ${e.message}`);
28806
- await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**\uFF08\u5185\u5B58\u751F\u6548\uFF0C\u78C1\u76D8\u5199\u5931\u8D25\uFF09`);
28807
- }
29018
+ await liveConfig.set(`agents.defaults.features.${key}`, next);
29019
+ console.log(`[${ctx.command}] ${key} ${cur} \u2192 ${rawState} (live + persisted)`);
29020
+ await ctx.reply(`\u2705 ${key}: **${cur}** \u2192 **${rawState}**`);
28808
29021
  return;
28809
29022
  }
28810
29023
  if (ctx.command === "model") {
@@ -28922,11 +29135,11 @@ Auto-routing disabled \u2014 all messages use this model.
28922
29135
  const input = (ctx.args.model || "").trim();
28923
29136
  const configPath = config._configFilePath;
28924
29137
  let writePath = configPath;
28925
- if (configPath && !fs41.existsSync(configPath)) {
29138
+ if (configPath && !fs42.existsSync(configPath)) {
28926
29139
  const __pFile = fileURLToPath(import.meta.url);
28927
- const __pDir = path44.dirname(__pFile);
28928
- const altPath = path44.join(path44.resolve(__pDir, "../configs"), path44.basename(configPath));
28929
- if (fs41.existsSync(altPath)) {
29140
+ const __pDir = path45.dirname(__pFile);
29141
+ const altPath = path45.join(path45.resolve(__pDir, "../configs"), path45.basename(configPath));
29142
+ if (fs42.existsSync(altPath)) {
28930
29143
  console.warn(`[primary] Config not found at ${configPath}, falling back to ${altPath}`);
28931
29144
  writePath = altPath;
28932
29145
  }
@@ -28970,14 +29183,14 @@ Use full ref like \`/primary ${candidates[0].ref}\``);
28970
29183
  return;
28971
29184
  }
28972
29185
  try {
28973
- const raw = await fs41.promises.readFile(writePath, "utf-8");
29186
+ const raw = await fs42.promises.readFile(writePath, "utf-8");
28974
29187
  const cfg = JSON.parse(raw);
28975
29188
  if (!cfg.agents?.defaults?.model) {
28976
29189
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
28977
29190
  return;
28978
29191
  }
28979
29192
  cfg.agents.defaults.model.primary = target;
28980
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29193
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
28981
29194
  console.log(`[primary] Persisted primary=${target} to ${writePath}`);
28982
29195
  await ctx.reply(`\u2705 Primary model set to **${target}** (${candidates[0].name})
28983
29196
  Written to config. **Restart required** to take effect.`);
@@ -28990,11 +29203,11 @@ Written to config. **Restart required** to take effect.`);
28990
29203
  const input = (ctx.args.model || "").trim();
28991
29204
  const configPath = config._configFilePath;
28992
29205
  let writePath = configPath;
28993
- if (configPath && !fs41.existsSync(configPath)) {
29206
+ if (configPath && !fs42.existsSync(configPath)) {
28994
29207
  const __pFile = fileURLToPath(import.meta.url);
28995
- const __pDir = path44.dirname(__pFile);
28996
- const altPath = path44.join(path44.resolve(__pDir, "../configs"), path44.basename(configPath));
28997
- if (fs41.existsSync(altPath)) {
29208
+ const __pDir = path45.dirname(__pFile);
29209
+ const altPath = path45.join(path45.resolve(__pDir, "../configs"), path45.basename(configPath));
29210
+ if (fs42.existsSync(altPath)) {
28998
29211
  console.warn(`[vision-primary] Config not found at ${configPath}, falling back to ${altPath}`);
28999
29212
  writePath = altPath;
29000
29213
  }
@@ -29039,7 +29252,7 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
29039
29252
  return;
29040
29253
  }
29041
29254
  try {
29042
- const raw = await fs41.promises.readFile(writePath, "utf-8");
29255
+ const raw = await fs42.promises.readFile(writePath, "utf-8");
29043
29256
  const cfg = JSON.parse(raw);
29044
29257
  if (!cfg.agents?.defaults?.model) {
29045
29258
  await ctx.reply(`\u26A0\uFE0F Config structure mismatch: agents.defaults.model not found`);
@@ -29047,12 +29260,12 @@ Use full ref like \`/vision-primary ${candidates[0].ref}\``);
29047
29260
  }
29048
29261
  if (input === "auto" || input === "reset") {
29049
29262
  delete cfg.agents.defaults.model.vision;
29050
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29263
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29051
29264
  console.log(`[vision-primary] Cleared vision primary in ${writePath}`);
29052
29265
  await ctx.reply(`\u2705 Vision primary cleared (auto). Written to config. Hot-reload will apply.`);
29053
29266
  } else {
29054
29267
  cfg.agents.defaults.model.vision = target;
29055
- await fs41.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29268
+ await fs42.promises.writeFile(writePath, JSON.stringify(cfg, null, 2), "utf-8");
29056
29269
  console.log(`[vision-primary] Persisted vision=${target} to ${writePath}`);
29057
29270
  await ctx.reply(`\u2705 Vision primary set to **${target}** (${candidates[0].name})
29058
29271
  Written to config. Hot-reload will apply.`);
@@ -29275,7 +29488,7 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
29275
29488
  console.log(`[vision] Downloading image: ${att.filename}`);
29276
29489
  let rawBuffer;
29277
29490
  if (att.url.startsWith("file://")) {
29278
- rawBuffer = fs41.readFileSync(decodeURIComponent(att.url.slice(7)));
29491
+ rawBuffer = fs42.readFileSync(decodeURIComponent(att.url.slice(7)));
29279
29492
  } else {
29280
29493
  rawBuffer = await downloadImage2(att.url);
29281
29494
  }
@@ -29283,8 +29496,8 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
29283
29496
  const ext = detected.split("/")[1] || "png";
29284
29497
  const resized = await maybeResizeAndDownsampleImageBuffer2(rawBuffer, rawBuffer.length, ext);
29285
29498
  const imageId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
29286
- const savedPath = path44.join(config.mediaDir, `${imageId}.${ext}`);
29287
- fs41.writeFileSync(savedPath, resized.buffer);
29499
+ const savedPath = path45.join(config.mediaDir, `${imageId}.${ext}`);
29500
+ fs42.writeFileSync(savedPath, resized.buffer);
29288
29501
  savedPaths.push(savedPath);
29289
29502
  console.log(`[vision] Saved: ${savedPath} (${resized.buffer.length}B)`);
29290
29503
  imageBlocks.push({
@@ -29310,8 +29523,8 @@ ${pathStr}` }];
29310
29523
  }
29311
29524
  const nonImageAttachments = inbound.attachments?.filter((a) => !a.contentType.startsWith("image/"));
29312
29525
  if (nonImageAttachments && nonImageAttachments.length > 0) {
29313
- const outDir = path44.join(config.mediaDir, sessionId);
29314
- fs41.mkdirSync(outDir, { recursive: true });
29526
+ const outDir = path45.join(config.mediaDir, sessionId);
29527
+ fs42.mkdirSync(outDir, { recursive: true });
29315
29528
  const resolved = [];
29316
29529
  for (const att of nonImageAttachments) {
29317
29530
  console.log(`[file] Downloading: ${att.filename} (${att.contentType}, ${att.size}B)`);
@@ -29319,9 +29532,9 @@ ${pathStr}` }];
29319
29532
  const resp = await fetch(att.url);
29320
29533
  if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
29321
29534
  const buffer = Buffer.from(await resp.arrayBuffer());
29322
- const safeName2 = path44.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
29323
- const savedPath = path44.join(outDir, safeName2);
29324
- fs41.writeFileSync(savedPath, buffer);
29535
+ const safeName2 = path45.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
29536
+ const savedPath = path45.join(outDir, safeName2);
29537
+ fs42.writeFileSync(savedPath, buffer);
29325
29538
  resolved.push(savedPath);
29326
29539
  console.log(`[file] Saved: ${savedPath} (${buffer.length}B)`);
29327
29540
  } catch (err) {
@@ -29675,7 +29888,7 @@ ${pathStr}` }];
29675
29888
  console.warn("[cognifold] watcher: config.workspace \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 proactive \u5199\u5165");
29676
29889
  return;
29677
29890
  }
29678
- const pFile = path44.join(wsDir, ".cognifold-proactive.json");
29891
+ const pFile = path45.join(wsDir, ".cognifold-proactive.json");
29679
29892
  const cognifoldBaseUrl = config.cognifold?.baseUrl || "http://127.0.0.1:9001";
29680
29893
  const cognifoldSessionId = cfSessionId;
29681
29894
  const rawSuggestions = data.suggestions || data.actions || (data.intent_id ? [data] : []);
@@ -29723,14 +29936,14 @@ ${pathStr}` }];
29723
29936
  return s;
29724
29937
  }));
29725
29938
  try {
29726
- fs41.writeFileSync(pFile, JSON.stringify(enriched, null, 2));
29939
+ fs42.writeFileSync(pFile, JSON.stringify(enriched, null, 2));
29727
29940
  console.log(`[cognifold] proactive suggestions saved (${enriched.length} total)`);
29728
29941
  } catch (e) {
29729
29942
  console.error(`[cognifold] failed to save proactive: ${e.message}`);
29730
29943
  }
29731
29944
  if (enriched.length > 0) {
29732
- const promptFile = path44.join(config.workspace, "prompts", "cognifold-proactive.md");
29733
- const promptText = fs41.existsSync(promptFile) ? fs41.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
29945
+ const promptFile = path45.join(config.workspace, "prompts", "cognifold-proactive.md");
29946
+ const promptText = fs42.existsSync(promptFile) ? fs42.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
29734
29947
  const actionsJson = JSON.stringify(enriched, null, 2);
29735
29948
  const sessionId = cfSessionId;
29736
29949
  const mainSessionId = sessions.getSessionId("scope:main");
@@ -29897,12 +30110,12 @@ async function doReloadConfig(config, deps, provider) {
29897
30110
  try {
29898
30111
  const savedConfigPath = config._configFilePath;
29899
30112
  let reloadConfigPath = savedConfigPath;
29900
- if (!fs41.existsSync(reloadConfigPath)) {
30113
+ if (!fs42.existsSync(reloadConfigPath)) {
29901
30114
  const __filename = fileURLToPath(import.meta.url);
29902
- const __dirname = path44.dirname(__filename);
29903
- const engineConfigsDir = path44.resolve(__dirname, "../configs");
29904
- const altPath = path44.join(engineConfigsDir, path44.basename(savedConfigPath));
29905
- if (fs41.existsSync(altPath)) {
30115
+ const __dirname = path45.dirname(__filename);
30116
+ const engineConfigsDir = path45.resolve(__dirname, "../configs");
30117
+ const altPath = path45.join(engineConfigsDir, path45.basename(savedConfigPath));
30118
+ if (fs42.existsSync(altPath)) {
29906
30119
  console.warn(`[reload] Config not found at ${reloadConfigPath}, falling back to ${altPath} (dev mode)`);
29907
30120
  reloadConfigPath = altPath;
29908
30121
  }
@@ -29975,12 +30188,6 @@ async function doReloadConfig(config, deps, provider) {
29975
30188
  deps.extractProvider = newExtract;
29976
30189
  changes.push(`extract \u2192 ${newConfig.topics?.extract?.provider}/${newConfig.topics?.extract?.model}`);
29977
30190
  }
29978
- if (newConfig.topics) {
29979
- deps.topics = newConfig.topics;
29980
- }
29981
- if (newConfig.profile?.features) {
29982
- deps.features = newConfig.profile.features;
29983
- }
29984
30191
  try {
29985
30192
  const { setAutoDreamConfig: setAutoDreamConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
29986
30193
  setAutoDreamConfig2(newConfig);
@@ -30026,7 +30233,7 @@ async function doReloadConfig(config, deps, provider) {
30026
30233
  } catch (err) {
30027
30234
  console.error(`[reload] Failed: ${err.message}`);
30028
30235
  try {
30029
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
30236
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
30030
30237
  ${err.stack}
30031
30238
  `);
30032
30239
  } catch {
@@ -30037,36 +30244,36 @@ ${err.stack}
30037
30244
  function startConfigWatcher(config, deps, provider) {
30038
30245
  const raw = config._configFilePath;
30039
30246
  let configPath = raw;
30040
- if (!fs41.existsSync(configPath)) {
30041
- configPath = path44.resolve(raw);
30247
+ if (!fs42.existsSync(configPath)) {
30248
+ configPath = path45.resolve(raw);
30042
30249
  }
30043
- if (!fs41.existsSync(configPath)) {
30250
+ if (!fs42.existsSync(configPath)) {
30044
30251
  const __filename2 = fileURLToPath(import.meta.url);
30045
- const __dirname2 = path44.dirname(__filename2);
30046
- configPath = path44.resolve(__dirname2, "..", raw);
30252
+ const __dirname2 = path45.dirname(__filename2);
30253
+ configPath = path45.resolve(__dirname2, "..", raw);
30047
30254
  }
30048
- if (!fs41.existsSync(configPath)) {
30255
+ if (!fs42.existsSync(configPath)) {
30049
30256
  console.warn(`[config-watch] config path invalid: ${configPath}, watcher disabled`);
30050
30257
  try {
30051
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath}
30258
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath}
30052
30259
  `);
30053
30260
  } catch {
30054
30261
  }
30055
30262
  return null;
30056
30263
  }
30057
30264
  let debounceTimer = null;
30058
- const watcher = fs41.watch(configPath, { persistent: true }, (eventType) => {
30265
+ const watcher = fs42.watch(configPath, { persistent: true }, (eventType) => {
30059
30266
  if (debounceTimer) clearTimeout(debounceTimer);
30060
30267
  debounceTimer = setTimeout(async () => {
30061
30268
  console.log(`[config-watch] file changed (${eventType}), reloading...`);
30062
30269
  try {
30063
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
30270
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
30064
30271
  `);
30065
30272
  } catch {
30066
30273
  }
30067
30274
  const result = await doReloadConfig(config, deps, provider);
30068
30275
  try {
30069
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
30276
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
30070
30277
  `);
30071
30278
  } catch {
30072
30279
  }
@@ -30075,30 +30282,30 @@ function startConfigWatcher(config, deps, provider) {
30075
30282
  watcher.on("error", (err) => {
30076
30283
  console.error(`[config-watch] error: ${err.message}`);
30077
30284
  try {
30078
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
30285
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
30079
30286
  `);
30080
30287
  } catch {
30081
30288
  }
30082
30289
  });
30083
30290
  console.log(`[config-watch] watching ${configPath}`);
30084
30291
  try {
30085
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath}
30292
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath}
30086
30293
  `);
30087
30294
  } catch {
30088
30295
  }
30089
30296
  return watcher;
30090
30297
  }
30091
30298
  function startSecretsWatcher(config, deps, provider) {
30092
- const secretsDir = path44.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
30093
- const cfgBase = path44.basename(config._configFilePath || "", ".json");
30299
+ const secretsDir = path45.join(process.env.HOME || process.env.USERPROFILE || ".", ".engine7-secrets");
30300
+ const cfgBase = path45.basename(config._configFilePath || "", ".json");
30094
30301
  const secretCandidates = [
30095
- path44.join(secretsDir, `${cfgBase}.env`),
30096
- path44.join(path44.dirname(config._configFilePath || ""), `.env.${cfgBase}`),
30097
- path44.join(path44.dirname(config._configFilePath || ""), ".env")
30302
+ path45.join(secretsDir, `${cfgBase}.env`),
30303
+ path45.join(path45.dirname(config._configFilePath || ""), `.env.${cfgBase}`),
30304
+ path45.join(path45.dirname(config._configFilePath || ""), ".env")
30098
30305
  ];
30099
30306
  let secretsPath = null;
30100
30307
  for (const p of secretCandidates) {
30101
- if (fs41.existsSync(p)) {
30308
+ if (fs42.existsSync(p)) {
30102
30309
  secretsPath = p;
30103
30310
  break;
30104
30311
  }
@@ -30111,9 +30318,9 @@ function startSecretsWatcher(config, deps, provider) {
30111
30318
  let activeWatcher = null;
30112
30319
  const startWatch = () => {
30113
30320
  if (activeWatcher) activeWatcher.close();
30114
- activeWatcher = fs41.watch(secretsPath, { persistent: true }, (eventType) => {
30321
+ activeWatcher = fs42.watch(secretsPath, { persistent: true }, (eventType) => {
30115
30322
  if (eventType === "rename") {
30116
- if (fs41.existsSync(secretsPath)) {
30323
+ if (fs42.existsSync(secretsPath)) {
30117
30324
  console.log("[secrets-watch] rename detected, re-watching file...");
30118
30325
  startWatch();
30119
30326
  } else {
@@ -30125,7 +30332,7 @@ function startSecretsWatcher(config, deps, provider) {
30125
30332
  debounceTimer = setTimeout(async () => {
30126
30333
  console.log(`[secrets-watch] file changed (${eventType}), reloading secrets...`);
30127
30334
  try {
30128
- const content = fs41.readFileSync(secretsPath, "utf-8");
30335
+ const content = fs42.readFileSync(secretsPath, "utf-8");
30129
30336
  let updated = 0;
30130
30337
  for (const line of content.split("\n")) {
30131
30338
  const trimmed = line.trim();
@@ -30144,7 +30351,7 @@ function startSecretsWatcher(config, deps, provider) {
30144
30351
  const result = await doReloadConfig(config, deps, provider);
30145
30352
  console.log(`[secrets-watch] config reloaded: ok=${result.ok} changes=${result.changes.join(",")}`);
30146
30353
  try {
30147
- fs41.appendFileSync(path44.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
30354
+ fs42.appendFileSync(path45.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] SECRETS RELOAD: ok=${result.ok} keys=${updated}
30148
30355
  `);
30149
30356
  } catch {
30150
30357
  }