engine7 7.1.23 → 7.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +15 -5
- package/dist/engine-startup.mjs +786 -311
- package/dist/main.mjs +787 -312
- package/package.json +2 -1
- package/src/memory/everos/python/__pycache__/agentic_search.cpython-314.pyc +0 -0
- package/src/memory/everos/python/__pycache__/agentic_server.cpython-314.pyc +0 -0
- package/src/memory/everos/python/agentic_search.py +980 -0
- package/src/memory/everos/python/agentic_server.py +383 -0
- package/src/memory/everos/python/fcntl_compat.py +23 -0
- package/src/memory/everos/python/requirements.txt +7 -0
package/dist/engine-startup.mjs
CHANGED
|
@@ -725,8 +725,10 @@ async function executeStopHooks(ctx, signal, lastAssistantMessage) {
|
|
|
725
725
|
cwd: ctx.cwd,
|
|
726
726
|
stop_hook_active: false,
|
|
727
727
|
last_assistant_message: lastAssistantMessage,
|
|
728
|
-
channel: ctx.channel
|
|
728
|
+
channel: ctx.channel,
|
|
729
729
|
// 让 callback hook 能判断来源
|
|
730
|
+
source: ctx.source || ""
|
|
731
|
+
// 消息来源(heartbeat/cron/system 等注入 turn 可据此跳过)
|
|
730
732
|
};
|
|
731
733
|
return executeHooks("Stop", hookInput, ctx, signal);
|
|
732
734
|
}
|
|
@@ -1078,6 +1080,7 @@ function collectSurfacedMemories(messages) {
|
|
|
1078
1080
|
return { paths };
|
|
1079
1081
|
}
|
|
1080
1082
|
function normalizePath(p2) {
|
|
1083
|
+
if (p2.startsWith("everos://")) return p2;
|
|
1081
1084
|
return pathResolve(p2);
|
|
1082
1085
|
}
|
|
1083
1086
|
function memoryHeader(filePath, mtimeMs) {
|
|
@@ -2524,10 +2527,12 @@ function breakdownMessages(messages) {
|
|
|
2524
2527
|
if (memCount === 0) console.log(`[context-analyzer] WARNING: relevant_memories attachment with 0 memories! keys=${Object.keys(m2.attachment).join(",")}`);
|
|
2525
2528
|
for (const mem of m2.attachment.memories || []) {
|
|
2526
2529
|
const fileName = mem.path.split(/[/\\]/).pop() || mem.path;
|
|
2530
|
+
const scoreMatch = mem.header?.match(/score=([\d.]+)/);
|
|
2527
2531
|
recalledTopics.push({
|
|
2528
2532
|
path: fileName,
|
|
2529
2533
|
tokens: roughTokenCountEstimation(mem.content),
|
|
2530
|
-
stable: false
|
|
2534
|
+
stable: false,
|
|
2535
|
+
score: scoreMatch ? scoreMatch[1] : void 0
|
|
2531
2536
|
});
|
|
2532
2537
|
}
|
|
2533
2538
|
}
|
|
@@ -2738,13 +2743,25 @@ function formatContextReport(report) {
|
|
|
2738
2743
|
output += `### Recalled Memories (${rt.length})
|
|
2739
2744
|
|
|
2740
2745
|
`;
|
|
2741
|
-
|
|
2746
|
+
const hasScore = rt.some((t) => t.score);
|
|
2747
|
+
if (hasScore) {
|
|
2748
|
+
output += `| File | Score | Tokens |
|
|
2742
2749
|
`;
|
|
2743
|
-
|
|
2750
|
+
output += `|------|-------|--------|
|
|
2751
|
+
`;
|
|
2752
|
+
for (const t of rt) {
|
|
2753
|
+
output += `| ${t.path} | ${t.score ?? "-"} | ${formatTokens(t.tokens)} |
|
|
2754
|
+
`;
|
|
2755
|
+
}
|
|
2756
|
+
} else {
|
|
2757
|
+
output += `| File | Tokens |
|
|
2758
|
+
`;
|
|
2759
|
+
output += `|------|--------|
|
|
2744
2760
|
`;
|
|
2745
|
-
|
|
2746
|
-
|
|
2761
|
+
for (const t of rt) {
|
|
2762
|
+
output += `| ${t.path} | ${formatTokens(t.tokens)} |
|
|
2747
2763
|
`;
|
|
2764
|
+
}
|
|
2748
2765
|
}
|
|
2749
2766
|
output += "\n";
|
|
2750
2767
|
}
|
|
@@ -3082,7 +3099,8 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3082
3099
|
sessionId: context?.sessionId || "default",
|
|
3083
3100
|
workspace: context?.workspace || "",
|
|
3084
3101
|
channel: context?.channel || "",
|
|
3085
|
-
cwd: context?.workspace || ""
|
|
3102
|
+
cwd: context?.workspace || "",
|
|
3103
|
+
source: typeof context?.source === "string" ? context.source : ""
|
|
3086
3104
|
};
|
|
3087
3105
|
const stopResult = await executeStopHooks(stopCtx, ac.signal, textContent);
|
|
3088
3106
|
if (stopResult.preventContinuation) {
|
|
@@ -5729,11 +5747,11 @@ async function readLastConsolidatedAt(memoryDir) {
|
|
|
5729
5747
|
}
|
|
5730
5748
|
}
|
|
5731
5749
|
async function tryAcquireConsolidationLock(memoryDir) {
|
|
5732
|
-
const
|
|
5750
|
+
const path44 = lockPath(memoryDir);
|
|
5733
5751
|
let mtimeMs;
|
|
5734
5752
|
let holderPid;
|
|
5735
5753
|
try {
|
|
5736
|
-
const [s2, raw] = await Promise.all([stat3(
|
|
5754
|
+
const [s2, raw] = await Promise.all([stat3(path44), readFile5(path44, "utf8")]);
|
|
5737
5755
|
mtimeMs = s2.mtimeMs;
|
|
5738
5756
|
const parsed = parseInt(raw.trim(), 10);
|
|
5739
5757
|
holderPid = Number.isFinite(parsed) ? parsed : void 0;
|
|
@@ -5746,10 +5764,10 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
5746
5764
|
}
|
|
5747
5765
|
}
|
|
5748
5766
|
await mkdir3(memoryDir, { recursive: true });
|
|
5749
|
-
await writeFile4(
|
|
5767
|
+
await writeFile4(path44, String(process.pid));
|
|
5750
5768
|
let verify2;
|
|
5751
5769
|
try {
|
|
5752
|
-
verify2 = await readFile5(
|
|
5770
|
+
verify2 = await readFile5(path44, "utf8");
|
|
5753
5771
|
} catch {
|
|
5754
5772
|
return null;
|
|
5755
5773
|
}
|
|
@@ -5757,15 +5775,15 @@ async function tryAcquireConsolidationLock(memoryDir) {
|
|
|
5757
5775
|
return mtimeMs ?? 0;
|
|
5758
5776
|
}
|
|
5759
5777
|
async function rollbackConsolidationLock(memoryDir, priorMtime) {
|
|
5760
|
-
const
|
|
5778
|
+
const path44 = lockPath(memoryDir);
|
|
5761
5779
|
try {
|
|
5762
5780
|
if (priorMtime === 0) {
|
|
5763
|
-
await unlink(
|
|
5781
|
+
await unlink(path44);
|
|
5764
5782
|
return;
|
|
5765
5783
|
}
|
|
5766
|
-
await writeFile4(
|
|
5784
|
+
await writeFile4(path44, "");
|
|
5767
5785
|
const t = priorMtime / 1e3;
|
|
5768
|
-
await utimes(
|
|
5786
|
+
await utimes(path44, t, t);
|
|
5769
5787
|
} catch (e) {
|
|
5770
5788
|
console.log(`[autoDream] rollback failed: ${e.message} \u2014 next trigger delayed to minHours`);
|
|
5771
5789
|
}
|
|
@@ -6098,15 +6116,15 @@ __export(TodoWriteTool_exports, {
|
|
|
6098
6116
|
loadTodos: () => loadTodos
|
|
6099
6117
|
});
|
|
6100
6118
|
import fs31 from "node:fs";
|
|
6101
|
-
import
|
|
6119
|
+
import path31 from "node:path";
|
|
6102
6120
|
function initTodoStore(stateDir) {
|
|
6103
|
-
todosDir =
|
|
6121
|
+
todosDir = path31.join(stateDir, "todos");
|
|
6104
6122
|
if (!fs31.existsSync(todosDir)) {
|
|
6105
6123
|
fs31.mkdirSync(todosDir, { recursive: true });
|
|
6106
6124
|
}
|
|
6107
6125
|
}
|
|
6108
6126
|
function todoFilePath(sessionId) {
|
|
6109
|
-
return
|
|
6127
|
+
return path31.join(todosDir, `${sessionId}.json`);
|
|
6110
6128
|
}
|
|
6111
6129
|
function loadTodos(sessionId) {
|
|
6112
6130
|
if (!todosDir) return [];
|
|
@@ -6202,15 +6220,15 @@ __export(tasks_exports, {
|
|
|
6202
6220
|
updateTask: () => updateTask
|
|
6203
6221
|
});
|
|
6204
6222
|
import * as fs33 from "node:fs";
|
|
6205
|
-
import * as
|
|
6223
|
+
import * as path33 from "node:path";
|
|
6206
6224
|
function sanitizePathComponent2(input) {
|
|
6207
6225
|
return input.replace(/[^a-zA-Z0-9_-]/g, "-");
|
|
6208
6226
|
}
|
|
6209
6227
|
function getTasksDir2(stateDir, listId) {
|
|
6210
|
-
return
|
|
6228
|
+
return path33.join(stateDir, "tasks", sanitizePathComponent2(listId));
|
|
6211
6229
|
}
|
|
6212
6230
|
function getTaskPath(stateDir, listId, taskId) {
|
|
6213
|
-
return
|
|
6231
|
+
return path33.join(getTasksDir2(stateDir, listId), `${sanitizePathComponent2(taskId)}.json`);
|
|
6214
6232
|
}
|
|
6215
6233
|
function ensureTasksDir2(stateDir, listId) {
|
|
6216
6234
|
const dir = getTasksDir2(stateDir, listId);
|
|
@@ -6218,7 +6236,7 @@ function ensureTasksDir2(stateDir, listId) {
|
|
|
6218
6236
|
return dir;
|
|
6219
6237
|
}
|
|
6220
6238
|
function getHighWaterMarkPath(stateDir, listId) {
|
|
6221
|
-
return
|
|
6239
|
+
return path33.join(getTasksDir2(stateDir, listId), HIGH_WATER_MARK_FILE);
|
|
6222
6240
|
}
|
|
6223
6241
|
function readHighWaterMark(stateDir, listId) {
|
|
6224
6242
|
try {
|
|
@@ -6424,7 +6442,7 @@ __export(read_exports, {
|
|
|
6424
6442
|
readFileState: () => readFileState
|
|
6425
6443
|
});
|
|
6426
6444
|
import * as fs34 from "node:fs";
|
|
6427
|
-
import * as
|
|
6445
|
+
import * as path34 from "node:path";
|
|
6428
6446
|
function isBlockedDevicePath(filePath) {
|
|
6429
6447
|
if (BLOCKED_DEVICE_PATHS.has(filePath)) return true;
|
|
6430
6448
|
if (filePath.startsWith("/proc/") && (filePath.endsWith("/fd/0") || filePath.endsWith("/fd/1") || filePath.endsWith("/fd/2"))) return true;
|
|
@@ -6603,7 +6621,7 @@ Usage:
|
|
|
6603
6621
|
return { content: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${filePath}`, isError: true };
|
|
6604
6622
|
}
|
|
6605
6623
|
const stat4 = fs34.statSync(filePath);
|
|
6606
|
-
const baseName =
|
|
6624
|
+
const baseName = path34.basename(filePath).toUpperCase();
|
|
6607
6625
|
if (BLOCKED_BASENAMES.has(baseName)) {
|
|
6608
6626
|
return { content: `\u8BBE\u5907\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6: ${filePath}`, isError: true };
|
|
6609
6627
|
}
|
|
@@ -6613,7 +6631,7 @@ Usage:
|
|
|
6613
6631
|
if (stat4.isDirectory()) {
|
|
6614
6632
|
const entries = fs34.readdirSync(filePath);
|
|
6615
6633
|
const items = entries.map((e) => {
|
|
6616
|
-
const full =
|
|
6634
|
+
const full = path34.join(filePath, e);
|
|
6617
6635
|
try {
|
|
6618
6636
|
const s2 = fs34.statSync(full);
|
|
6619
6637
|
return s2.isDirectory() ? `${e}/` : e;
|
|
@@ -6624,7 +6642,7 @@ Usage:
|
|
|
6624
6642
|
return { content: `\u76EE\u5F55 (${entries.length} \u9879):
|
|
6625
6643
|
${items.join("\n")}` };
|
|
6626
6644
|
}
|
|
6627
|
-
const ext =
|
|
6645
|
+
const ext = path34.extname(filePath).toLowerCase();
|
|
6628
6646
|
if (BINARY_EXTENSIONS.has(ext)) {
|
|
6629
6647
|
return { content: `\u4E8C\u8FDB\u5236\u6587\u4EF6\u4E0D\u652F\u6301\u8BFB\u53D6 (${ext}): ${filePath}`, isError: true };
|
|
6630
6648
|
}
|
|
@@ -6673,7 +6691,7 @@ ${result}` : result };
|
|
|
6673
6691
|
// src/tools/write.ts
|
|
6674
6692
|
var write_exports = {};
|
|
6675
6693
|
import * as fs35 from "node:fs";
|
|
6676
|
-
import * as
|
|
6694
|
+
import * as path35 from "node:path";
|
|
6677
6695
|
function isBlockedPath(filePath) {
|
|
6678
6696
|
return BLOCKED_PATTERNS.some((p2) => p2.test(filePath));
|
|
6679
6697
|
}
|
|
@@ -6817,7 +6835,7 @@ Usage:
|
|
|
6817
6835
|
}
|
|
6818
6836
|
}
|
|
6819
6837
|
}
|
|
6820
|
-
const dir =
|
|
6838
|
+
const dir = path35.dirname(filePath);
|
|
6821
6839
|
try {
|
|
6822
6840
|
fs35.mkdirSync(dir, { recursive: true });
|
|
6823
6841
|
} catch (e) {
|
|
@@ -6853,7 +6871,7 @@ ${simpleDiff(oldContent, content)}`;
|
|
|
6853
6871
|
// src/tools/edit.ts
|
|
6854
6872
|
var edit_exports = {};
|
|
6855
6873
|
import * as fs36 from "node:fs";
|
|
6856
|
-
import * as
|
|
6874
|
+
import * as path36 from "node:path";
|
|
6857
6875
|
function normalizeQuotes(str) {
|
|
6858
6876
|
return str.replaceAll(LEFT_SINGLE_CURLY, "'").replaceAll(RIGHT_SINGLE_CURLY, "'").replaceAll(LEFT_DOUBLE_CURLY, '"').replaceAll(RIGHT_DOUBLE_CURLY, '"');
|
|
6859
6877
|
}
|
|
@@ -7016,7 +7034,7 @@ Usage:
|
|
|
7016
7034
|
} catch (e) {
|
|
7017
7035
|
if (e.code === "ENOENT") {
|
|
7018
7036
|
if (oldString === "") {
|
|
7019
|
-
const dir =
|
|
7037
|
+
const dir = path36.dirname(filePath);
|
|
7020
7038
|
fs36.mkdirSync(dir, { recursive: true });
|
|
7021
7039
|
fs36.writeFileSync(filePath, newString, "utf-8");
|
|
7022
7040
|
readFileState.set(filePath, { timestamp: fs36.statSync(filePath).mtimeMs });
|
|
@@ -7096,7 +7114,7 @@ ${diffView}`
|
|
|
7096
7114
|
// src/tools/glob.ts
|
|
7097
7115
|
var glob_exports = {};
|
|
7098
7116
|
import * as fs37 from "node:fs";
|
|
7099
|
-
import * as
|
|
7117
|
+
import * as path37 from "node:path";
|
|
7100
7118
|
function globMatch(pattern, filename) {
|
|
7101
7119
|
const regexStr = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
7102
7120
|
try {
|
|
@@ -7122,12 +7140,12 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
7122
7140
|
}
|
|
7123
7141
|
for (const entry of entries) {
|
|
7124
7142
|
if (truncated) return;
|
|
7125
|
-
const fullPath =
|
|
7143
|
+
const fullPath = path37.join(currentDir, entry.name);
|
|
7126
7144
|
if (entry.isDirectory()) {
|
|
7127
7145
|
if (VCS_DIRS.has(entry.name)) continue;
|
|
7128
7146
|
walk(fullPath);
|
|
7129
7147
|
} else if (entry.isFile()) {
|
|
7130
|
-
const relativePath =
|
|
7148
|
+
const relativePath = path37.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
7131
7149
|
const patternsToTry = [pattern];
|
|
7132
7150
|
if (pattern.startsWith("**/")) {
|
|
7133
7151
|
patternsToTry.push(pattern.slice(3));
|
|
@@ -7154,7 +7172,7 @@ function findFiles(dir, pattern, limit, baseDir) {
|
|
|
7154
7172
|
};
|
|
7155
7173
|
}
|
|
7156
7174
|
function toRelativePath(absolutePath, cwd) {
|
|
7157
|
-
if (absolutePath.startsWith(cwd +
|
|
7175
|
+
if (absolutePath.startsWith(cwd + path37.sep)) {
|
|
7158
7176
|
return absolutePath.slice(cwd.length + 1);
|
|
7159
7177
|
}
|
|
7160
7178
|
return absolutePath;
|
|
@@ -7215,7 +7233,7 @@ ${filenames.join("\n")}${truncatedNote}`
|
|
|
7215
7233
|
// src/tools/grep.ts
|
|
7216
7234
|
var grep_exports = {};
|
|
7217
7235
|
import { execFile as execFile2 } from "node:child_process";
|
|
7218
|
-
import * as
|
|
7236
|
+
import * as path38 from "node:path";
|
|
7219
7237
|
function ripGrep(args, searchPath, signal) {
|
|
7220
7238
|
return new Promise((resolve10) => {
|
|
7221
7239
|
const fullArgs = [...args, searchPath];
|
|
@@ -7247,7 +7265,7 @@ function applyHeadLimit(items, limit, offset = 0) {
|
|
|
7247
7265
|
};
|
|
7248
7266
|
}
|
|
7249
7267
|
function toRelativePath2(absolutePath, cwd) {
|
|
7250
|
-
if (absolutePath.startsWith(cwd +
|
|
7268
|
+
if (absolutePath.startsWith(cwd + path38.sep)) {
|
|
7251
7269
|
return absolutePath.slice(cwd.length + 1);
|
|
7252
7270
|
}
|
|
7253
7271
|
if (absolutePath.startsWith(cwd)) {
|
|
@@ -9916,7 +9934,7 @@ var init_web_fetch = __esm({
|
|
|
9916
9934
|
|
|
9917
9935
|
// src/cron/tasks.ts
|
|
9918
9936
|
import fs38 from "node:fs";
|
|
9919
|
-
import
|
|
9937
|
+
import path39 from "node:path";
|
|
9920
9938
|
import crypto5 from "node:crypto";
|
|
9921
9939
|
function getStorageDir() {
|
|
9922
9940
|
return storageDir;
|
|
@@ -9971,7 +9989,7 @@ function readTasksFromDisk() {
|
|
|
9971
9989
|
}
|
|
9972
9990
|
}
|
|
9973
9991
|
async function writeTasksToDisk(tasks2) {
|
|
9974
|
-
const lockPath2 =
|
|
9992
|
+
const lockPath2 = path39.join(storageDir, "tasks.json.lock");
|
|
9975
9993
|
await withFileLock(lockPath2, () => {
|
|
9976
9994
|
const store = {
|
|
9977
9995
|
version: 1,
|
|
@@ -9983,7 +10001,7 @@ async function writeTasksToDisk(tasks2) {
|
|
|
9983
10001
|
}
|
|
9984
10002
|
function initTaskStore(dir) {
|
|
9985
10003
|
storageDir = dir;
|
|
9986
|
-
tasksFilePath =
|
|
10004
|
+
tasksFilePath = path39.join(dir, "tasks.json");
|
|
9987
10005
|
if (!fs38.existsSync(dir)) {
|
|
9988
10006
|
fs38.mkdirSync(dir, { recursive: true });
|
|
9989
10007
|
}
|
|
@@ -10897,9 +10915,9 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
10897
10915
|
let filePath = promptText.slice(1).trim();
|
|
10898
10916
|
try {
|
|
10899
10917
|
const fs42 = await import("fs");
|
|
10900
|
-
const
|
|
10901
|
-
if (!
|
|
10902
|
-
filePath =
|
|
10918
|
+
const path44 = await import("path");
|
|
10919
|
+
if (!path44.isAbsolute(filePath)) {
|
|
10920
|
+
filePath = path44.join(deps.sessions["config"].stateDir, filePath);
|
|
10903
10921
|
}
|
|
10904
10922
|
promptText = fs42.readFileSync(filePath, "utf-8");
|
|
10905
10923
|
console.log(`[cron] Loaded prompt from ${filePath} (${promptText.length} chars)`);
|
|
@@ -10928,15 +10946,15 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
10928
10946
|
let finalResult = result;
|
|
10929
10947
|
if (task.postProcess) {
|
|
10930
10948
|
try {
|
|
10931
|
-
const
|
|
10949
|
+
const path44 = await import("path");
|
|
10932
10950
|
const fs42 = await import("fs");
|
|
10933
10951
|
let scriptPath = task.postProcess;
|
|
10934
|
-
if (!
|
|
10935
|
-
scriptPath =
|
|
10952
|
+
if (!path44.isAbsolute(scriptPath)) {
|
|
10953
|
+
scriptPath = path44.join(deps.sessions["config"].stateDir, scriptPath);
|
|
10936
10954
|
}
|
|
10937
|
-
const resultsDirTmp =
|
|
10955
|
+
const resultsDirTmp = path44.join(getStorageDir(), "results");
|
|
10938
10956
|
fs42.mkdirSync(resultsDirTmp, { recursive: true });
|
|
10939
|
-
const inputFile =
|
|
10957
|
+
const inputFile = path44.join(resultsDirTmp, `${task.id}.input.txt`);
|
|
10940
10958
|
fs42.writeFileSync(inputFile, result, "utf-8");
|
|
10941
10959
|
const { execFile: execFile3 } = await import("child_process");
|
|
10942
10960
|
await new Promise((resolve10) => {
|
|
@@ -10965,10 +10983,10 @@ async function executeAndDeliver(task, now, deps) {
|
|
|
10965
10983
|
}
|
|
10966
10984
|
try {
|
|
10967
10985
|
const fs42 = await import("fs");
|
|
10968
|
-
const
|
|
10969
|
-
const resultsDir =
|
|
10986
|
+
const path44 = await import("path");
|
|
10987
|
+
const resultsDir = path44.join(getStorageDir(), "results");
|
|
10970
10988
|
fs42.mkdirSync(resultsDir, { recursive: true });
|
|
10971
|
-
const resultFile =
|
|
10989
|
+
const resultFile = path44.join(resultsDir, `${task.id}.json`);
|
|
10972
10990
|
fs42.writeFileSync(resultFile, JSON.stringify({
|
|
10973
10991
|
taskId: task.id,
|
|
10974
10992
|
description: task.description,
|
|
@@ -11217,13 +11235,13 @@ function registerCronTools() {
|
|
|
11217
11235
|
},
|
|
11218
11236
|
handler: async (args) => {
|
|
11219
11237
|
const fs42 = await import("fs");
|
|
11220
|
-
const
|
|
11221
|
-
const resultsDir =
|
|
11238
|
+
const path44 = await import("path");
|
|
11239
|
+
const resultsDir = path44.join(getStorageDir(), "results");
|
|
11222
11240
|
if (!fs42.existsSync(resultsDir)) {
|
|
11223
11241
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
11224
11242
|
}
|
|
11225
11243
|
if (args.task_id) {
|
|
11226
|
-
const file =
|
|
11244
|
+
const file = path44.join(resultsDir, `${args.task_id}.json`);
|
|
11227
11245
|
if (!fs42.existsSync(file)) {
|
|
11228
11246
|
return { content: `\u4EFB\u52A1 ${args.task_id} \u6682\u65E0\u6267\u884C\u7ED3\u679C`, isError: true };
|
|
11229
11247
|
}
|
|
@@ -11239,7 +11257,7 @@ ${data.result}` };
|
|
|
11239
11257
|
return { content: "\u6682\u65E0cron\u6267\u884C\u7ED3\u679C" };
|
|
11240
11258
|
}
|
|
11241
11259
|
const results = files.map((f2) => {
|
|
11242
|
-
const data = JSON.parse(fs42.readFileSync(
|
|
11260
|
+
const data = JSON.parse(fs42.readFileSync(path44.join(resultsDir, f2), "utf-8"));
|
|
11243
11261
|
return `### ${data.description} (${data.taskId.slice(0, 8)})
|
|
11244
11262
|
\u6267\u884C: ${data.executedAt} | \u7B2C${data.runCount}\u6B21
|
|
11245
11263
|
${data.result.slice(0, 500)}${data.result.length > 500 ? "..." : ""}`;
|
|
@@ -11401,7 +11419,7 @@ __export(manager_exports, {
|
|
|
11401
11419
|
McpManager: () => McpManager
|
|
11402
11420
|
});
|
|
11403
11421
|
import * as fs40 from "node:fs";
|
|
11404
|
-
import * as
|
|
11422
|
+
import * as path41 from "node:path";
|
|
11405
11423
|
import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
11406
11424
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
11407
11425
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -11434,9 +11452,9 @@ function convertInputSchema(inputSchema) {
|
|
|
11434
11452
|
}
|
|
11435
11453
|
function persistBinary(base64Data, mimeType, persistId) {
|
|
11436
11454
|
const ext = mimeType?.split("/")[1] || "bin";
|
|
11437
|
-
const dir =
|
|
11455
|
+
const dir = path41.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
|
|
11438
11456
|
fs40.mkdirSync(dir, { recursive: true });
|
|
11439
|
-
const filepath =
|
|
11457
|
+
const filepath = path41.join(dir, `${persistId}.${ext}`);
|
|
11440
11458
|
try {
|
|
11441
11459
|
const buf = Buffer.from(base64Data, "base64");
|
|
11442
11460
|
fs40.writeFileSync(filepath, buf);
|
|
@@ -11773,7 +11791,7 @@ __export(resources_exports, {
|
|
|
11773
11791
|
registerMcpResourceTools: () => registerMcpResourceTools,
|
|
11774
11792
|
unregisterMcpResourceTools: () => unregisterMcpResourceTools
|
|
11775
11793
|
});
|
|
11776
|
-
import * as
|
|
11794
|
+
import * as path42 from "node:path";
|
|
11777
11795
|
function registerMcpResourceTools(manager) {
|
|
11778
11796
|
mcpManagerRef = manager;
|
|
11779
11797
|
registry.register(listResourcesTool);
|
|
@@ -11791,7 +11809,7 @@ var init_resources = __esm({
|
|
|
11791
11809
|
"use strict";
|
|
11792
11810
|
init_registry();
|
|
11793
11811
|
MAX_RESULT_CHARS2 = 1e5;
|
|
11794
|
-
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR ||
|
|
11812
|
+
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path42.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
|
|
11795
11813
|
MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
|
|
11796
11814
|
MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
|
|
11797
11815
|
mcpManagerRef = null;
|
|
@@ -11881,11 +11899,21 @@ var everos_sync_exports = {};
|
|
|
11881
11899
|
__export(everos_sync_exports, {
|
|
11882
11900
|
createEverosSync: () => createEverosSync
|
|
11883
11901
|
});
|
|
11902
|
+
function parseMeta(text) {
|
|
11903
|
+
const m2 = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
|
|
11904
|
+
if (!m2) return null;
|
|
11905
|
+
return { senderName: m2[1].trim(), senderId: m2[2].trim(), platform: m2[3].trim() };
|
|
11906
|
+
}
|
|
11884
11907
|
function createEverosSync(cfg) {
|
|
11885
|
-
const { enabled, url, appId, userId } = cfg;
|
|
11908
|
+
const { enabled, url, appId, userId, agentName } = cfg;
|
|
11886
11909
|
async function push(event) {
|
|
11887
11910
|
if (!enabled) return;
|
|
11888
11911
|
if (!event.text.trim()) return;
|
|
11912
|
+
return;
|
|
11913
|
+
const meta = event.role === "user" ? parseMeta(event.text) : null;
|
|
11914
|
+
const senderId = appId;
|
|
11915
|
+
const senderName = meta?.senderName ?? (event.role === "assistant" ? agentName : void 0) ?? event.senderName ?? event.role;
|
|
11916
|
+
console.log(`[everos-sync] role=${event.role} sender_id=${senderId} sender_name=${senderName} metaParsed=${!!meta} textLen=${event.text.length}`);
|
|
11889
11917
|
try {
|
|
11890
11918
|
const resp = await fetch(`${url}/api/v1/memory/add`, {
|
|
11891
11919
|
method: "POST",
|
|
@@ -11895,8 +11923,8 @@ function createEverosSync(cfg) {
|
|
|
11895
11923
|
app_id: appId,
|
|
11896
11924
|
project_id: "default",
|
|
11897
11925
|
messages: [{
|
|
11898
|
-
sender_id:
|
|
11899
|
-
sender_name:
|
|
11926
|
+
sender_id: senderId,
|
|
11927
|
+
sender_name: senderName,
|
|
11900
11928
|
role: event.role,
|
|
11901
11929
|
timestamp: event.timestamp,
|
|
11902
11930
|
content: event.text
|
|
@@ -11920,13 +11948,17 @@ function createEverosSync(cfg) {
|
|
|
11920
11948
|
session_id: events[0].sessionId,
|
|
11921
11949
|
app_id: appId,
|
|
11922
11950
|
project_id: "default",
|
|
11923
|
-
messages: events.map((e) =>
|
|
11924
|
-
|
|
11925
|
-
|
|
11926
|
-
|
|
11927
|
-
|
|
11928
|
-
|
|
11929
|
-
|
|
11951
|
+
messages: events.map((e) => {
|
|
11952
|
+
const meta = e.role === "user" ? parseMeta(e.text) : null;
|
|
11953
|
+
const name = meta?.senderName ?? (e.role === "assistant" ? agentName : void 0) ?? e.senderName ?? e.role;
|
|
11954
|
+
return {
|
|
11955
|
+
sender_id: appId,
|
|
11956
|
+
sender_name: name,
|
|
11957
|
+
role: e.role,
|
|
11958
|
+
timestamp: e.timestamp,
|
|
11959
|
+
content: e.text
|
|
11960
|
+
};
|
|
11961
|
+
})
|
|
11930
11962
|
}),
|
|
11931
11963
|
signal: AbortSignal.timeout(3e4)
|
|
11932
11964
|
});
|
|
@@ -12028,10 +12060,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12028
12060
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
12029
12061
|
}
|
|
12030
12062
|
}
|
|
12031
|
-
const
|
|
12063
|
+
const path44 = join36(workspace, ".reply-blocklist.json");
|
|
12032
12064
|
try {
|
|
12033
|
-
if (existsSync24(
|
|
12034
|
-
const raw = readFileSync26(
|
|
12065
|
+
if (existsSync24(path44)) {
|
|
12066
|
+
const raw = readFileSync26(path44, "utf-8");
|
|
12035
12067
|
const parsed = JSON.parse(raw);
|
|
12036
12068
|
if (parsed.blockedUserIds) {
|
|
12037
12069
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -12047,9 +12079,9 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12047
12079
|
loaded = true;
|
|
12048
12080
|
}
|
|
12049
12081
|
function save(workspace) {
|
|
12050
|
-
const
|
|
12082
|
+
const path44 = join36(workspace, ".reply-blocklist.json");
|
|
12051
12083
|
try {
|
|
12052
|
-
writeFileSync15(
|
|
12084
|
+
writeFileSync15(path44, JSON.stringify(state, null, 2), "utf-8");
|
|
12053
12085
|
} catch (err) {
|
|
12054
12086
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
12055
12087
|
}
|
|
@@ -12646,7 +12678,7 @@ var init_cognifold_intent_watcher = __esm({
|
|
|
12646
12678
|
});
|
|
12647
12679
|
|
|
12648
12680
|
// src/engine-startup.ts
|
|
12649
|
-
import * as
|
|
12681
|
+
import * as path43 from "node:path";
|
|
12650
12682
|
import * as fs41 from "node:fs";
|
|
12651
12683
|
import { fileURLToPath } from "node:url";
|
|
12652
12684
|
|
|
@@ -13990,11 +14022,11 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
13990
14022
|
/** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
|
|
13991
14023
|
async sendFile(target, message, attachment) {
|
|
13992
14024
|
const fs42 = await import("node:fs");
|
|
13993
|
-
const
|
|
14025
|
+
const path44 = await import("node:path");
|
|
13994
14026
|
if (!fs42.existsSync(attachment.path)) {
|
|
13995
14027
|
throw new Error(`File not found: ${attachment.path}`);
|
|
13996
14028
|
}
|
|
13997
|
-
const filename = attachment.filename ||
|
|
14029
|
+
const filename = attachment.filename || path44.basename(attachment.path);
|
|
13998
14030
|
const fileBuffer = fs42.readFileSync(attachment.path);
|
|
13999
14031
|
const filePayload = {
|
|
14000
14032
|
attachment: fileBuffer,
|
|
@@ -14436,11 +14468,11 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14436
14468
|
/** 发送媒体附件(图片/文件) */
|
|
14437
14469
|
async sendFile(target, message, attachment) {
|
|
14438
14470
|
const fs42 = await import("node:fs");
|
|
14439
|
-
const
|
|
14471
|
+
const path44 = await import("node:path");
|
|
14440
14472
|
if (!fs42.existsSync(attachment.path)) {
|
|
14441
14473
|
throw new Error(`File not found: ${attachment.path}`);
|
|
14442
14474
|
}
|
|
14443
|
-
const filename = attachment.filename ||
|
|
14475
|
+
const filename = attachment.filename || path44.basename(attachment.path);
|
|
14444
14476
|
const fileBuffer = fs42.readFileSync(attachment.path);
|
|
14445
14477
|
const receiveIdType = target.startsWith("ou_") ? "open_id" : "chat_id";
|
|
14446
14478
|
const mimeType = attachment.mimeType || "application/octet-stream";
|
|
@@ -18052,7 +18084,8 @@ ${skillsListing}`);
|
|
|
18052
18084
|
parts.push(getEnvInfoSection(options.workspace));
|
|
18053
18085
|
const now = /* @__PURE__ */ new Date();
|
|
18054
18086
|
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
18055
|
-
parts.push(
|
|
18087
|
+
parts.push(`# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
18088
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`);
|
|
18056
18089
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
18057
18090
|
return parts.join("\n\n");
|
|
18058
18091
|
}
|
|
@@ -18354,6 +18387,169 @@ async function findRelevantMemoriesVector(query, memoryDir, alreadySurfaced = /*
|
|
|
18354
18387
|
}));
|
|
18355
18388
|
}
|
|
18356
18389
|
|
|
18390
|
+
// src/memory/memdir/findRelevantMemoriesEveros.ts
|
|
18391
|
+
var ROUND1_TOP_N = 30;
|
|
18392
|
+
var RERANK_BATCH_SIZE = 100;
|
|
18393
|
+
function formatEverosHeader(ep) {
|
|
18394
|
+
const score = ep.score.toFixed(3);
|
|
18395
|
+
const ts = ep.timestamp?.slice(0, 10) ?? "";
|
|
18396
|
+
let ageLabel = "";
|
|
18397
|
+
if (ts) {
|
|
18398
|
+
const days = Math.floor((Date.now() - new Date(ts).getTime()) / 864e5);
|
|
18399
|
+
if (days <= 1) ageLabel = "\u4ECA\u5929";
|
|
18400
|
+
else if (days <= 3) ageLabel = `${days}\u5929\u524D`;
|
|
18401
|
+
else if (days <= 14) ageLabel = `${days}\u5929\u524D`;
|
|
18402
|
+
else if (days <= 30) ageLabel = `~${Math.ceil(days / 7)}\u5468\u524D`;
|
|
18403
|
+
else ageLabel = `~${Math.ceil(days / 30)}\u4E2A\u6708\u524D`;
|
|
18404
|
+
}
|
|
18405
|
+
return `[EverOS score=${score} ${ts} (${ageLabel})]`;
|
|
18406
|
+
}
|
|
18407
|
+
async function hybridSearch(query, everosUrl, userId, topK) {
|
|
18408
|
+
const resp = await fetch(`${everosUrl}/api/v1/memory/search`, {
|
|
18409
|
+
method: "POST",
|
|
18410
|
+
headers: { "Content-Type": "application/json" },
|
|
18411
|
+
body: JSON.stringify({
|
|
18412
|
+
query: query.slice(0, 2e3),
|
|
18413
|
+
user_id: userId,
|
|
18414
|
+
app_id: userId,
|
|
18415
|
+
project_id: "default",
|
|
18416
|
+
top_k: topK,
|
|
18417
|
+
method: "hybrid"
|
|
18418
|
+
}),
|
|
18419
|
+
signal: AbortSignal.timeout(15e3)
|
|
18420
|
+
});
|
|
18421
|
+
if (!resp.ok) {
|
|
18422
|
+
console.warn(`[memdir] everos hybrid: search failed ${resp.status}`);
|
|
18423
|
+
return [];
|
|
18424
|
+
}
|
|
18425
|
+
const body = await resp.json();
|
|
18426
|
+
const episodes = body.data?.episodes ?? [];
|
|
18427
|
+
return episodes;
|
|
18428
|
+
}
|
|
18429
|
+
async function deepinfraRerank(query, episodes, rerankUrl, rerankApiKey, rerankModel, provider) {
|
|
18430
|
+
if (episodes.length === 0) return [];
|
|
18431
|
+
const documents = episodes.map(
|
|
18432
|
+
(ep) => ep.episode?.slice(0, 500) || ep.summary?.slice(0, 500) || ep.subject
|
|
18433
|
+
);
|
|
18434
|
+
const allScores = [];
|
|
18435
|
+
const isDashscope = provider === "dashscope";
|
|
18436
|
+
for (let i = 0; i < documents.length; i += RERANK_BATCH_SIZE) {
|
|
18437
|
+
const batch = documents.slice(i, i + RERANK_BATCH_SIZE);
|
|
18438
|
+
const body = isDashscope ? JSON.stringify({
|
|
18439
|
+
model: rerankModel || "qwen3-rerank",
|
|
18440
|
+
input: { query, documents: batch },
|
|
18441
|
+
parameters: { return_documents: false, top_n: batch.length }
|
|
18442
|
+
}) : JSON.stringify({ queries: [query], documents: batch });
|
|
18443
|
+
const makeRequest = () => fetch(rerankUrl, {
|
|
18444
|
+
method: "POST",
|
|
18445
|
+
headers: {
|
|
18446
|
+
"Authorization": `Bearer ${rerankApiKey}`,
|
|
18447
|
+
"Content-Type": "application/json"
|
|
18448
|
+
},
|
|
18449
|
+
body,
|
|
18450
|
+
signal: AbortSignal.timeout(3e4)
|
|
18451
|
+
});
|
|
18452
|
+
let resp = await makeRequest();
|
|
18453
|
+
if (resp.status === 429) {
|
|
18454
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
18455
|
+
resp = await makeRequest();
|
|
18456
|
+
}
|
|
18457
|
+
if (!resp.ok) {
|
|
18458
|
+
console.warn(`[memdir] everos rerank: failed ${resp.status}`);
|
|
18459
|
+
return episodes;
|
|
18460
|
+
}
|
|
18461
|
+
if (isDashscope) {
|
|
18462
|
+
const data = await resp.json();
|
|
18463
|
+
const results = data.output?.results ?? [];
|
|
18464
|
+
const scoreMap = new Array(batch.length).fill(0);
|
|
18465
|
+
for (const r of results) {
|
|
18466
|
+
scoreMap[r.index] = r.relevance_score;
|
|
18467
|
+
}
|
|
18468
|
+
allScores.push(...scoreMap);
|
|
18469
|
+
} else {
|
|
18470
|
+
const data = await resp.json();
|
|
18471
|
+
let batchScores = data.scores ?? [];
|
|
18472
|
+
if (Array.isArray(batchScores) && batchScores.length > 0 && Array.isArray(batchScores[0])) {
|
|
18473
|
+
batchScores = batchScores[0];
|
|
18474
|
+
}
|
|
18475
|
+
allScores.push(...batchScores);
|
|
18476
|
+
}
|
|
18477
|
+
}
|
|
18478
|
+
const ranked = episodes.map((ep, idx) => ({ ep, score: allScores[idx] ?? 0 })).sort((a, b2) => b2.score - a.score);
|
|
18479
|
+
for (const { ep, score } of ranked) {
|
|
18480
|
+
ep.score = score;
|
|
18481
|
+
}
|
|
18482
|
+
return ranked.map((r) => r.ep);
|
|
18483
|
+
}
|
|
18484
|
+
var DEFAULT_MIN_SCORE2 = 0.5;
|
|
18485
|
+
async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /* @__PURE__ */ new Set(), options) {
|
|
18486
|
+
const everosUrl = options?.everosUrl ?? "http://127.0.0.1:8100";
|
|
18487
|
+
const userId = options?.userId ?? "xiaomei";
|
|
18488
|
+
const topK = options?.topK ?? 3;
|
|
18489
|
+
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
18490
|
+
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} userId=${userId} topK=${topK}`);
|
|
18491
|
+
const t0 = Date.now();
|
|
18492
|
+
try {
|
|
18493
|
+
const tH1 = Date.now();
|
|
18494
|
+
let episodes = await hybridSearch(query, everosUrl, userId, ROUND1_TOP_N);
|
|
18495
|
+
const tH2 = Date.now();
|
|
18496
|
+
console.log(`[memdir] everos recall: hybrid ${episodes.length} candidates in ${tH2 - tH1}ms`);
|
|
18497
|
+
if (episodes.length === 0) return [];
|
|
18498
|
+
const rerankUrl = options?.rerankUrl;
|
|
18499
|
+
const rerankApiKey = options?.rerankApiKey;
|
|
18500
|
+
if (rerankUrl && rerankApiKey) {
|
|
18501
|
+
const tR1 = Date.now();
|
|
18502
|
+
episodes = await deepinfraRerank(
|
|
18503
|
+
query,
|
|
18504
|
+
episodes,
|
|
18505
|
+
rerankUrl,
|
|
18506
|
+
rerankApiKey,
|
|
18507
|
+
options?.rerankModel,
|
|
18508
|
+
options?.rerankProvider
|
|
18509
|
+
);
|
|
18510
|
+
const tR2 = Date.now();
|
|
18511
|
+
console.log(`[memdir] everos recall: rerank done in ${tR2 - tR1}ms (${options?.rerankProvider || "deepinfra"})`);
|
|
18512
|
+
} else {
|
|
18513
|
+
console.log(`[memdir] everos recall: no rerank key, using hybrid scores as-is`);
|
|
18514
|
+
}
|
|
18515
|
+
const ms = Date.now() - t0;
|
|
18516
|
+
console.log(`[memdir] everos recall: ${episodes.length} episodes in ${ms}ms total (hybrid+rerank)`);
|
|
18517
|
+
const surfacedSubjects = /* @__PURE__ */ new Set();
|
|
18518
|
+
for (const p2 of alreadySurfaced) {
|
|
18519
|
+
if (p2.startsWith("everos://")) {
|
|
18520
|
+
surfacedSubjects.add(p2.slice(8));
|
|
18521
|
+
} else {
|
|
18522
|
+
}
|
|
18523
|
+
}
|
|
18524
|
+
const result = [];
|
|
18525
|
+
const seenSubjects = /* @__PURE__ */ new Set();
|
|
18526
|
+
for (const ep of episodes) {
|
|
18527
|
+
if (ep.score < minScore) continue;
|
|
18528
|
+
const subject = (ep.subject || ep.id).slice(0, 80).replace(/[\n\r]/g, " ");
|
|
18529
|
+
const virtualPath = `everos://${subject}`;
|
|
18530
|
+
if (alreadySurfaced.has(virtualPath)) continue;
|
|
18531
|
+
if (surfacedSubjects.has(subject)) continue;
|
|
18532
|
+
if (seenSubjects.has(subject)) continue;
|
|
18533
|
+
seenSubjects.add(subject);
|
|
18534
|
+
result.push({
|
|
18535
|
+
path: virtualPath,
|
|
18536
|
+
mtimeMs: ep.timestamp ? new Date(ep.timestamp).getTime() : Date.now(),
|
|
18537
|
+
content: `### ${ep.subject}
|
|
18538
|
+
|
|
18539
|
+
${ep.episode || ep.summary}`,
|
|
18540
|
+
header: formatEverosHeader(ep)
|
|
18541
|
+
});
|
|
18542
|
+
if (result.length >= topK) break;
|
|
18543
|
+
}
|
|
18544
|
+
console.log(`[memdir] everos recall: returning ${result.length} memories (after dedup)`);
|
|
18545
|
+
return result;
|
|
18546
|
+
} catch (e) {
|
|
18547
|
+
const ms = Date.now() - t0;
|
|
18548
|
+
console.warn(`[memdir] everos recall: error after ${ms}ms: ${e?.message ?? e}`);
|
|
18549
|
+
return [];
|
|
18550
|
+
}
|
|
18551
|
+
}
|
|
18552
|
+
|
|
18357
18553
|
// src/handle-query.ts
|
|
18358
18554
|
init_paths();
|
|
18359
18555
|
import { readFileSync as readFileSync15, existsSync as existsSync12 } from "node:fs";
|
|
@@ -18426,18 +18622,18 @@ function truncate(s2, maxLen) {
|
|
|
18426
18622
|
}
|
|
18427
18623
|
var externalChanRulesCache = null;
|
|
18428
18624
|
function loadExternalChanRules(workspace) {
|
|
18429
|
-
const
|
|
18430
|
-
if (externalChanRulesCache && externalChanRulesCache.path ===
|
|
18625
|
+
const path44 = join20(workspace, "prompts", "external-chan-rules.md");
|
|
18626
|
+
if (externalChanRulesCache && externalChanRulesCache.path === path44) return externalChanRulesCache;
|
|
18431
18627
|
let content = "";
|
|
18432
|
-
if (existsSync12(
|
|
18628
|
+
if (existsSync12(path44)) {
|
|
18433
18629
|
try {
|
|
18434
|
-
content = readFileSync15(
|
|
18630
|
+
content = readFileSync15(path44, "utf-8").trim();
|
|
18435
18631
|
} catch (e) {
|
|
18436
18632
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
18437
18633
|
}
|
|
18438
18634
|
}
|
|
18439
|
-
externalChanRulesCache = { path:
|
|
18440
|
-
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${
|
|
18635
|
+
externalChanRulesCache = { path: path44, content };
|
|
18636
|
+
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path44}`);
|
|
18441
18637
|
return externalChanRulesCache;
|
|
18442
18638
|
}
|
|
18443
18639
|
function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
@@ -18460,10 +18656,10 @@ function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
|
18460
18656
|
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
18461
18657
|
return externalChanWhitelist;
|
|
18462
18658
|
}
|
|
18463
|
-
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall) {
|
|
18464
|
-
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall);
|
|
18659
|
+
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
18660
|
+
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
18465
18661
|
}
|
|
18466
|
-
async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall) {
|
|
18662
|
+
async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
18467
18663
|
const { engine, sessions, channelManager, workspace, providerId, providerApi, model } = deps;
|
|
18468
18664
|
const features = deps.features || {};
|
|
18469
18665
|
const preQueryAbort = new AbortController();
|
|
@@ -18640,6 +18836,8 @@ ${text}` : text });
|
|
|
18640
18836
|
const toolContext = {
|
|
18641
18837
|
sessionId,
|
|
18642
18838
|
channel: channelName === "cli" ? "console" : channelName,
|
|
18839
|
+
source: source || "",
|
|
18840
|
+
// 消息来源(user/inbox/heartbeat/cron/system/inner-voice),Stop hook 用来区分注入 turn
|
|
18643
18841
|
workspace,
|
|
18644
18842
|
stateDir: deps.stateDir || workspace,
|
|
18645
18843
|
channelManager,
|
|
@@ -18814,7 +19012,23 @@ ${text}` : text });
|
|
|
18814
19012
|
const recallP = deps.recallProvider;
|
|
18815
19013
|
const recallMode = deps.topics?.recall?.mode || "llm";
|
|
18816
19014
|
let relevantMemories;
|
|
18817
|
-
if (recallMode === "
|
|
19015
|
+
if (recallMode === "everos") {
|
|
19016
|
+
const everosCfg = deps?.everosCfg;
|
|
19017
|
+
relevantMemories = await findRelevantMemoriesEveros(
|
|
19018
|
+
textForMemory,
|
|
19019
|
+
memoryDir,
|
|
19020
|
+
surfaced.paths,
|
|
19021
|
+
everosCfg ? {
|
|
19022
|
+
everosUrl: everosCfg.everosUrl || "http://127.0.0.1:8100",
|
|
19023
|
+
userId: everosCfg.userId || "xiaomei",
|
|
19024
|
+
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
19025
|
+
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
19026
|
+
rerankModel: everosCfg.rerank?.model,
|
|
19027
|
+
rerankProvider: everosCfg.rerank?.provider,
|
|
19028
|
+
minScore: deps.topics?.recall?.minScore
|
|
19029
|
+
} : void 0
|
|
19030
|
+
);
|
|
19031
|
+
} else if (recallMode === "vector") {
|
|
18818
19032
|
relevantMemories = await findRelevantMemoriesVector(
|
|
18819
19033
|
textForMemory,
|
|
18820
19034
|
memoryDir,
|
|
@@ -18837,8 +19051,8 @@ ${text}` : text });
|
|
|
18837
19051
|
const attachmentMemories = [];
|
|
18838
19052
|
for (const mem of relevantMemories) {
|
|
18839
19053
|
try {
|
|
18840
|
-
const content = readFileSync15(mem.path, "utf-8");
|
|
18841
|
-
const header = memoryHeader(mem.path, mem.mtimeMs);
|
|
19054
|
+
const content = mem.content ?? readFileSync15(mem.path, "utf-8");
|
|
19055
|
+
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
18842
19056
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
18843
19057
|
} catch {
|
|
18844
19058
|
}
|
|
@@ -19386,7 +19600,9 @@ function registerCognifoldBridge(config) {
|
|
|
19386
19600
|
messageId: ctx.inbound.messageId
|
|
19387
19601
|
}
|
|
19388
19602
|
};
|
|
19389
|
-
|
|
19603
|
+
const sm = globalThis.__cognifoldSessions;
|
|
19604
|
+
const dynamicSessionId = sm?.getSessionId?.("main") || cognifoldConfig.sessionId;
|
|
19605
|
+
void enqueueEvent(dynamicSessionId, event);
|
|
19390
19606
|
return null;
|
|
19391
19607
|
}, 80);
|
|
19392
19608
|
}
|
|
@@ -19710,7 +19926,8 @@ var MessageDispatcher = class {
|
|
|
19710
19926
|
msg2.deps,
|
|
19711
19927
|
msg2.channelTarget,
|
|
19712
19928
|
msg2.inboundMeta,
|
|
19713
|
-
msg2.skipRecall
|
|
19929
|
+
msg2.skipRecall,
|
|
19930
|
+
msg2.source
|
|
19714
19931
|
);
|
|
19715
19932
|
} catch (err) {
|
|
19716
19933
|
console.error(`[dispatcher] Query error (session=${msg2.sessionId}): ${err.message}`);
|
|
@@ -19870,13 +20087,19 @@ function startCliLoop(deps, cliConfig, channelManager, dispatcher) {
|
|
|
19870
20087
|
|
|
19871
20088
|
// src/session/session-history.ts
|
|
19872
20089
|
import fs14 from "node:fs";
|
|
20090
|
+
import path14 from "node:path";
|
|
19873
20091
|
var BEIJING_OFFSET_MS = 8 * 36e5;
|
|
19874
20092
|
var INJECTED_CONTENT_PATTERNS = [
|
|
19875
20093
|
/【定时心跳】/,
|
|
19876
20094
|
/\[内心对话测试\]/,
|
|
19877
20095
|
/\[inner-voice\]/,
|
|
19878
20096
|
/\[微信巡检\]/,
|
|
19879
|
-
/\[plugin\]
|
|
20097
|
+
/\[plugin\]/,
|
|
20098
|
+
/<nudge-notification>/,
|
|
20099
|
+
/<task-notification>/,
|
|
20100
|
+
/<calendar-notification>/,
|
|
20101
|
+
/## Actions \(\d+\s*个\)/
|
|
20102
|
+
// CogniFold proactive 注入(block[0] 固定格式,engine-startup 拼的)
|
|
19880
20103
|
];
|
|
19881
20104
|
function parseJsonlEntries(lines) {
|
|
19882
20105
|
const entries = [];
|
|
@@ -19911,6 +20134,20 @@ function resolveScopeMainJsonl(sessions) {
|
|
|
19911
20134
|
if (!sessionId) return null;
|
|
19912
20135
|
return sessions.getSessionFilePath(sessionId);
|
|
19913
20136
|
}
|
|
20137
|
+
function scopeMainJsonlPaths(sessions) {
|
|
20138
|
+
const current = resolveScopeMainJsonl(sessions);
|
|
20139
|
+
let latestArchive = null;
|
|
20140
|
+
if (current) {
|
|
20141
|
+
try {
|
|
20142
|
+
const dir = path14.dirname(current);
|
|
20143
|
+
const base = path14.basename(current);
|
|
20144
|
+
const archives = fs14.readdirSync(dir).filter((f2) => f2.startsWith(base + ".archived.")).sort();
|
|
20145
|
+
if (archives.length > 0) latestArchive = path14.join(dir, archives[archives.length - 1]);
|
|
20146
|
+
} catch {
|
|
20147
|
+
}
|
|
20148
|
+
}
|
|
20149
|
+
return { current, latestArchive };
|
|
20150
|
+
}
|
|
19914
20151
|
function cleanText(rawText) {
|
|
19915
20152
|
let clean = rawText.replace(/<system-reminder>.*?<\/system-reminder>/gs, "").replace(/(?:Sender|Conversation info|Replied message) \(untrusted[^)]*\):\s*```json\s*\{[^}]*\}\s*```/g, "").replace(/\[\w{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}(?::\d{2})? GMT[+-]\d+\]/g, "").replace(/\[message_id:\s*\S+\]/g, "").replace(/\[\[reply_to_current\]\]/g, "").replace(/\[\[reply_to:\S+\]\]/g, "").replace(/<@\d+>/g, "").replace(/^System:.*$/gm, "").replace(/Reply target.*?```json\s*\{[^}]*\}\s*```/gs, "").replace(/\[media attached:.*?\]/g, "[\u56FE\u7247]");
|
|
19916
20153
|
return clean.trim();
|
|
@@ -20001,6 +20238,7 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
20001
20238
|
time: `${p2(bj.getHours())}:${p2(bj.getMinutes())}`,
|
|
20002
20239
|
role,
|
|
20003
20240
|
text: clean.slice(0, 80),
|
|
20241
|
+
timestamp: dtMs,
|
|
20004
20242
|
_utc: dtMs
|
|
20005
20243
|
});
|
|
20006
20244
|
}
|
|
@@ -20132,6 +20370,8 @@ ${basePrompt}`;
|
|
|
20132
20370
|
channelName: "heartbeat",
|
|
20133
20371
|
source: "heartbeat",
|
|
20134
20372
|
priority: "later",
|
|
20373
|
+
skipRecall: true,
|
|
20374
|
+
// 心跳不需要记忆召回,避免重复注入心跳相关记忆
|
|
20135
20375
|
callbacks: {
|
|
20136
20376
|
onResult: () => resolveDone()
|
|
20137
20377
|
},
|
|
@@ -20149,7 +20389,7 @@ ${basePrompt}`;
|
|
|
20149
20389
|
|
|
20150
20390
|
// src/nudge/plugin.ts
|
|
20151
20391
|
import fs17 from "node:fs";
|
|
20152
|
-
import
|
|
20392
|
+
import path17 from "node:path";
|
|
20153
20393
|
|
|
20154
20394
|
// src/nudge/judge.ts
|
|
20155
20395
|
function shouldNudge(task, taskState, cfg) {
|
|
@@ -20318,10 +20558,10 @@ function formatDuration2(ms) {
|
|
|
20318
20558
|
|
|
20319
20559
|
// src/nudge/session-state-reader.ts
|
|
20320
20560
|
import fs15 from "node:fs";
|
|
20321
|
-
import
|
|
20561
|
+
import path15 from "node:path";
|
|
20322
20562
|
function parseSessionStateFull(workspace, sessionStateFile) {
|
|
20323
20563
|
const stateFile = sessionStateFile || "SESSION-STATE.md";
|
|
20324
|
-
const statePath =
|
|
20564
|
+
const statePath = path15.isAbsolute(stateFile) ? stateFile : path15.join(workspace, stateFile);
|
|
20325
20565
|
let content;
|
|
20326
20566
|
try {
|
|
20327
20567
|
content = fs15.readFileSync(statePath, "utf-8");
|
|
@@ -20376,13 +20616,13 @@ function taskIdFromTitle(title) {
|
|
|
20376
20616
|
|
|
20377
20617
|
// src/calendar/db.ts
|
|
20378
20618
|
import { DatabaseSync } from "node:sqlite";
|
|
20379
|
-
import * as
|
|
20619
|
+
import * as path16 from "node:path";
|
|
20380
20620
|
import * as fs16 from "node:fs";
|
|
20381
20621
|
var TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
20382
20622
|
function openDb(workspace) {
|
|
20383
|
-
const dir =
|
|
20623
|
+
const dir = path16.join(workspace, ".calendar");
|
|
20384
20624
|
fs16.mkdirSync(dir, { recursive: true });
|
|
20385
|
-
const dbPath =
|
|
20625
|
+
const dbPath = path16.join(dir, "calendar.db");
|
|
20386
20626
|
const db = new DatabaseSync(dbPath);
|
|
20387
20627
|
db.exec("PRAGMA journal_mode=WAL");
|
|
20388
20628
|
db.exec(`CREATE TABLE IF NOT EXISTS events (
|
|
@@ -20471,7 +20711,7 @@ var NudgePlugin = class {
|
|
|
20471
20711
|
provider;
|
|
20472
20712
|
model;
|
|
20473
20713
|
loadPrompt(workspace, promptFile) {
|
|
20474
|
-
const promptPath = promptFile ?
|
|
20714
|
+
const promptPath = promptFile ? path17.isAbsolute(promptFile) ? promptFile : path17.join(workspace, promptFile) : path17.join(workspace, "prompts", "nudge-prompt.md");
|
|
20475
20715
|
try {
|
|
20476
20716
|
const content = fs17.readFileSync(promptPath, "utf-8").trim();
|
|
20477
20717
|
if (content) {
|
|
@@ -20508,11 +20748,20 @@ var NudgePlugin = class {
|
|
|
20508
20748
|
const lastMsg = input?.last_assistant_message || "";
|
|
20509
20749
|
const sessionId = input?.session_id || "";
|
|
20510
20750
|
console.log(`[stop-hook] lastMsg len=${lastMsg.length}, text="${lastMsg.slice(0, 80)}"`);
|
|
20751
|
+
const repliedIds = this.extractWakeReplyIds(lastMsg);
|
|
20752
|
+
if (repliedIds.length > 0) {
|
|
20753
|
+
this.removeNotificationsById(repliedIds);
|
|
20754
|
+
}
|
|
20511
20755
|
const msgChannel = input?.channel || "";
|
|
20512
20756
|
if (sessionId.includes("voice-chat") || msgChannel === "voice-chat") {
|
|
20513
20757
|
console.log(`[stop-hook] skipping voice-chat (channel=${msgChannel})`);
|
|
20514
20758
|
return { outcome: { outcome: "success" } };
|
|
20515
20759
|
}
|
|
20760
|
+
const msgSource = input?.source || "";
|
|
20761
|
+
if (msgSource && msgSource !== "user" && msgSource !== "inbox") {
|
|
20762
|
+
console.log(`[stop-hook] skipping non-conversation turn (source=${msgSource})`);
|
|
20763
|
+
return { outcome: { outcome: "success" } };
|
|
20764
|
+
}
|
|
20516
20765
|
if (!lastMsg) {
|
|
20517
20766
|
return { outcome: { outcome: "success" } };
|
|
20518
20767
|
}
|
|
@@ -20585,8 +20834,8 @@ var NudgePlugin = class {
|
|
|
20585
20834
|
if (pushedDecision && waitDesc) {
|
|
20586
20835
|
console.log(`[stop-hook] DETECTED pushedDecision! Injecting corrective message to ${sessionId}`);
|
|
20587
20836
|
try {
|
|
20588
|
-
const correctiveMsg = [
|
|
20589
|
-
"
|
|
20837
|
+
const correctiveMsg = buildNudgeNotification("prompt", [
|
|
20838
|
+
"[stop-hook \u81EA\u4E3B\u6267\u884C\u7EA0\u6B63] \u4F60\u521A\u624D\u628A\u4E00\u4E2A\u81EA\u5DF1\u80FD\u5B9A\u7684\u51B3\u5B9A\u63A8\u7ED9\u4E86\u5BF9\u65B9\u3002",
|
|
20590
20839
|
"",
|
|
20591
20840
|
`\u8BCA\u65AD\uFF1A${waitDesc}`,
|
|
20592
20841
|
"",
|
|
@@ -20599,9 +20848,10 @@ var NudgePlugin = class {
|
|
|
20599
20848
|
'3. \u6267\u884C\u5B8C\u6C47\u62A5\u7ED3\u679C\uFF08"\u5DF2\u5904\u7406" / "\u5DF2 commit" / "\u5DF2 archive"\uFF09',
|
|
20600
20849
|
"",
|
|
20601
20850
|
"\u5982\u679C\u4E0D\u662F\u5FC5\u987B\u95EE\u7684\uFF08\u6D89\u53CA\u82B1\u94B1/\u5BF9\u5916\u53D1\u5E03/\u91CD\u5927\u51B3\u7B56\uFF09\uFF0C\u4E0D\u8981\u95EE\u3002"
|
|
20602
|
-
].join("\n");
|
|
20603
|
-
|
|
20604
|
-
|
|
20851
|
+
].join("\n"));
|
|
20852
|
+
const route = this.getRoute(sessions);
|
|
20853
|
+
if (route) {
|
|
20854
|
+
enqueueNotification(correctiveMsg, route);
|
|
20605
20855
|
}
|
|
20606
20856
|
} catch (e) {
|
|
20607
20857
|
console.warn(`[stop-hook] Failed to inject corrective message: ${e.message}`);
|
|
@@ -20610,14 +20860,21 @@ var NudgePlugin = class {
|
|
|
20610
20860
|
if (!isWaiting) {
|
|
20611
20861
|
return { outcome: { outcome: "success" } };
|
|
20612
20862
|
}
|
|
20613
|
-
const nudgeDir =
|
|
20614
|
-
const notifPath =
|
|
20863
|
+
const nudgeDir = path17.join(this.workspace, ".nudge");
|
|
20864
|
+
const notifPath = path17.join(nudgeDir, "stop-hook-notifications.json");
|
|
20615
20865
|
try {
|
|
20616
20866
|
if (!fs17.existsSync(nudgeDir)) fs17.mkdirSync(nudgeDir, { recursive: true });
|
|
20617
20867
|
let notifs = [];
|
|
20618
20868
|
if (fs17.existsSync(notifPath)) {
|
|
20619
20869
|
notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
20620
20870
|
const now = Date.now();
|
|
20871
|
+
const dup = notifs.find((n) => !n.notified && n.description === (waitDesc || lastMsg.slice(0, 200)));
|
|
20872
|
+
if (dup) {
|
|
20873
|
+
dup.wakeAt = new Date(now + 5 * 6e4).toISOString();
|
|
20874
|
+
fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
|
|
20875
|
+
console.log(`[stop-hook] Duplicate wait (same desc, not fired yet), refreshed wakeAt: ${dup.id}`);
|
|
20876
|
+
return { outcome: { outcome: "success" } };
|
|
20877
|
+
}
|
|
20621
20878
|
const recentReg = notifs.find((n) => now - new Date(n.createdAt).getTime() < 3 * 6e4);
|
|
20622
20879
|
if (recentReg) {
|
|
20623
20880
|
console.log(`[stop-hook] Skip (recent registration within 3min)`);
|
|
@@ -20666,9 +20923,14 @@ var NudgePlugin = class {
|
|
|
20666
20923
|
return null;
|
|
20667
20924
|
}
|
|
20668
20925
|
}
|
|
20669
|
-
/**
|
|
20670
|
-
|
|
20671
|
-
|
|
20926
|
+
/**
|
|
20927
|
+
* 收集到期的 stop-hook notifications,批量构建一条 wake 消息。
|
|
20928
|
+
* 不标 notified——投递成功后由 tick 调 markNotified 标(route 拿不到时保留原样下个 tick 重试,
|
|
20929
|
+
* 避免"消息没投出去但已标 notified"的死账)。
|
|
20930
|
+
* 已 notified 的不会再触发,等 agent 回复 "<id> 过期了" 由 cleanup 删。
|
|
20931
|
+
*/
|
|
20932
|
+
collectDueStopHookNotifications() {
|
|
20933
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
20672
20934
|
try {
|
|
20673
20935
|
if (!fs17.existsSync(notifPath)) return null;
|
|
20674
20936
|
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
@@ -20676,54 +20938,161 @@ var NudgePlugin = class {
|
|
|
20676
20938
|
const now = Date.now();
|
|
20677
20939
|
const due = notifs.filter((n) => new Date(n.wakeAt).getTime() <= now && !n.notified);
|
|
20678
20940
|
if (due.length === 0) return null;
|
|
20679
|
-
|
|
20680
|
-
|
|
20681
|
-
|
|
20682
|
-
|
|
20683
|
-
|
|
20941
|
+
console.log(`[nudge] ${due.length} stop-hook notification(s) due: ${due.map((n) => n.id).join(", ")}`);
|
|
20942
|
+
const items = due.map((n) => `[\u901A\u77E5ID: ${n.id}]
|
|
20943
|
+
\u4E0A\u6B21\u8BF4\uFF1A${n.description}`).join("\n\n");
|
|
20944
|
+
const desc = due.length === 1 ? `\u4F60\u4E4B\u524D\u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF0C\u65F6\u95F4\u5230\u4E86\uFF0C\u56DE\u53BB\u68C0\u67E5\uFF01
|
|
20945
|
+
|
|
20946
|
+
${items}
|
|
20684
20947
|
|
|
20685
|
-
[\
|
|
20686
|
-
\u4E0A\u6B21\u8BF4\uFF1A${latest.description}
|
|
20948
|
+
\u68C0\u67E5\u6761\u4EF6\u662F\u5426\u6EE1\u8DB3\uFF0C\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D"${due[0].id} \u8FC7\u671F\u4E86"\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u8FD9\u6761\u3002` : `\u4F60\u4E4B\u524D\u6709 ${due.length} \u4E2A\u7B49\u5F85\u4E2D\u7684\u5916\u90E8\u6761\u4EF6\u90FD\u5230\u671F\u4E86\uFF0C\u56DE\u53BB\u9010\u4E2A\u68C0\u67E5\uFF01
|
|
20687
20949
|
|
|
20688
|
-
|
|
20950
|
+
${items}
|
|
20951
|
+
|
|
20952
|
+
\u5BF9\u6BCF\u4E00\u6761\uFF1A\u6761\u4EF6\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D\u5BF9\u5E94\u7684"<\u901A\u77E5ID> \u8FC7\u671F\u4E86"\u544A\u8BC9 nudge \u7CBE\u786E\u6E05\u7406\u3002`;
|
|
20953
|
+
return { message: buildNudgeNotification("wake", desc), ids: due.map((n) => n.id) };
|
|
20689
20954
|
} catch (e) {
|
|
20690
|
-
console.warn(`[nudge]
|
|
20955
|
+
console.warn(`[nudge] collectDueStopHookNotifications error: ${e.message}`);
|
|
20691
20956
|
return null;
|
|
20692
20957
|
}
|
|
20693
20958
|
}
|
|
20694
|
-
/**
|
|
20959
|
+
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个) */
|
|
20960
|
+
extractWakeReplyIds(text) {
|
|
20961
|
+
if (!text) return [];
|
|
20962
|
+
const ids = [];
|
|
20963
|
+
const re = /(wake-\d+-[a-z0-9]+)\s*过期了/g;
|
|
20964
|
+
let m2;
|
|
20965
|
+
while ((m2 = re.exec(text)) !== null) {
|
|
20966
|
+
if (!ids.includes(m2[1])) ids.push(m2[1]);
|
|
20967
|
+
}
|
|
20968
|
+
return ids;
|
|
20969
|
+
}
|
|
20970
|
+
/** 按 id 删除条目(stop-hook 实时清理用;正常删除路径,agent 回复即删) */
|
|
20971
|
+
removeNotificationsById(ids) {
|
|
20972
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
20973
|
+
try {
|
|
20974
|
+
if (!fs17.existsSync(notifPath)) return;
|
|
20975
|
+
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
20976
|
+
const idSet = new Set(ids);
|
|
20977
|
+
const remaining = notifs.filter((n) => !idSet.has(n.id));
|
|
20978
|
+
const removed = notifs.length - remaining.length;
|
|
20979
|
+
if (removed === 0) return;
|
|
20980
|
+
if (remaining.length > 0) {
|
|
20981
|
+
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
20982
|
+
} else {
|
|
20983
|
+
fs17.unlinkSync(notifPath);
|
|
20984
|
+
}
|
|
20985
|
+
console.log(`[stop-hook] Cleaned ${removed} notification(s) from reply: ${ids.join(", ")}`);
|
|
20986
|
+
} catch (e) {
|
|
20987
|
+
console.warn(`[stop-hook] removeNotificationsById error: ${e.message}`);
|
|
20988
|
+
}
|
|
20989
|
+
}
|
|
20990
|
+
/** 投递成功后标记 notified(防重复触发);不删除——删除只走 agent 回复 "<id> 过期了" */
|
|
20991
|
+
markNotified(ids) {
|
|
20992
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
20993
|
+
try {
|
|
20994
|
+
if (!fs17.existsSync(notifPath)) return;
|
|
20995
|
+
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
20996
|
+
const idSet = new Set(ids);
|
|
20997
|
+
const updated = notifs.map((n) => idSet.has(n.id) ? { ...n, notified: true } : n);
|
|
20998
|
+
fs17.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
|
|
20999
|
+
} catch (e) {
|
|
21000
|
+
console.warn(`[nudge] markNotified error: ${e.message}`);
|
|
21001
|
+
}
|
|
21002
|
+
}
|
|
21003
|
+
/**
|
|
21004
|
+
* tick 兜底清理。正常删除走 stop-hook 实时路径(agent 回复 "<id> 过期了" 当 turn 就删,
|
|
21005
|
+
* 见 registerStopHook 第 0 步),这里只接两种漏网:
|
|
21006
|
+
* ① 回复已落盘但 stop-hook 没来得及执行(进程中途崩等边缘情况)→ 扫 jsonl 补删;
|
|
21007
|
+
* ② TTL 清道夫:wakeAt 超过 cleanupTtlHours(默认 24h)仍无回复 → 回复永远来不了,删。
|
|
21008
|
+
*
|
|
21009
|
+
* 扫描不走 recentMessages()——它截断 80 字符、会过滤"对注入消息的回复"(wake 回复恰好
|
|
21010
|
+
* 被过滤掉)、限 20 条窗口。直接读 jsonl 原始条目:倒序扫、扫过最老 pending 的 wakeAt
|
|
21011
|
+
* 即停、全命中提前退、archive 只在 current 被 2MB 轮转切断时才读。
|
|
21012
|
+
*/
|
|
20695
21013
|
cleanupStaleNotificationsFromMessages(sessions) {
|
|
20696
21014
|
try {
|
|
20697
|
-
const
|
|
20698
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
20699
|
-
const recentTexts = recent.filter((r) => new Date(r.timestamp || r.createdAt || Date.now()).getTime() > fiveMinAgo).map((r) => r.text);
|
|
20700
|
-
const notifPath = path16.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21015
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
20701
21016
|
if (!fs17.existsSync(notifPath)) return;
|
|
20702
21017
|
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
20703
21018
|
if (notifs.length === 0) return;
|
|
20704
|
-
const expiredIds =
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
|
|
20709
|
-
|
|
20710
|
-
|
|
21019
|
+
const expiredIds = this.findExpiredReplyIds(sessions, notifs);
|
|
21020
|
+
const ttlMs = (this.cfg.cleanupTtlHours || 24) * 36e5;
|
|
21021
|
+
const now = Date.now();
|
|
21022
|
+
const ttlIds = new Set(
|
|
21023
|
+
notifs.filter((n) => now - new Date(n.wakeAt).getTime() > ttlMs && !expiredIds.has(n.id)).map((n) => n.id)
|
|
21024
|
+
);
|
|
21025
|
+
const removeIds = /* @__PURE__ */ new Set([...expiredIds, ...ttlIds]);
|
|
21026
|
+
if (removeIds.size === 0) return;
|
|
21027
|
+
const remaining = notifs.filter((n) => !removeIds.has(n.id));
|
|
21028
|
+
if (remaining.length > 0) {
|
|
21029
|
+
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
21030
|
+
} else {
|
|
21031
|
+
fs17.unlinkSync(notifPath);
|
|
20711
21032
|
}
|
|
20712
|
-
if (expiredIds.size
|
|
20713
|
-
|
|
20714
|
-
|
|
20715
|
-
if (
|
|
20716
|
-
|
|
20717
|
-
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
20718
|
-
} else {
|
|
20719
|
-
fs17.unlinkSync(notifPath);
|
|
20720
|
-
}
|
|
20721
|
-
console.log(`[nudge] Cleaned ${cleaned} stale notification(s) by explicit id: ${[...expiredIds].join(", ")}`);
|
|
21033
|
+
if (expiredIds.size > 0) {
|
|
21034
|
+
console.log(`[nudge] Cleaned ${expiredIds.size} notification(s) by reply: ${[...expiredIds].join(", ")}`);
|
|
21035
|
+
}
|
|
21036
|
+
if (ttlIds.size > 0) {
|
|
21037
|
+
console.log(`[nudge] Cleaned ${ttlIds.size} zombie notification(s) by TTL (>${this.cfg.cleanupTtlHours || 24}h no reply): ${[...ttlIds].join(", ")}`);
|
|
20722
21038
|
}
|
|
20723
21039
|
} catch (e) {
|
|
20724
21040
|
console.warn(`[nudge] cleanupStaleNotificationsFromMessages error: ${e.message}`);
|
|
20725
21041
|
}
|
|
20726
21042
|
}
|
|
21043
|
+
/**
|
|
21044
|
+
* 扫 "<id> 过期了" 回复,返回匹配到的 id 集合。
|
|
21045
|
+
* 不做全文扫描:倒序扫(回复紧跟在 fire 之后,通常就在尾部几条);
|
|
21046
|
+
* 扫过最老 pending 条目的 wakeAt 就停(回复不可能早于触发时间);
|
|
21047
|
+
* 全部命中提前退出;archive 只在 current 没覆盖时间范围(被 2MB 轮转切断)时才读。
|
|
21048
|
+
* 典型开销:解析几十条而不是上千条。
|
|
21049
|
+
*/
|
|
21050
|
+
findExpiredReplyIds(sessions, notifs) {
|
|
21051
|
+
const found = /* @__PURE__ */ new Set();
|
|
21052
|
+
if (notifs.length === 0) return found;
|
|
21053
|
+
const oldestMs = Math.min(...notifs.map((n) => new Date(n.wakeAt).getTime()));
|
|
21054
|
+
const { current, latestArchive } = scopeMainJsonlPaths(sessions);
|
|
21055
|
+
for (const file of [current, latestArchive]) {
|
|
21056
|
+
if (!file || !fs17.existsSync(file)) continue;
|
|
21057
|
+
let lines;
|
|
21058
|
+
try {
|
|
21059
|
+
lines = fs17.readFileSync(file, "utf-8").split("\n");
|
|
21060
|
+
} catch (e) {
|
|
21061
|
+
console.warn(`[nudge] findExpiredReplyIds read error on ${file}: ${e.message}`);
|
|
21062
|
+
continue;
|
|
21063
|
+
}
|
|
21064
|
+
let coveredOldest = false;
|
|
21065
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
21066
|
+
const trimmed = lines[i].trim();
|
|
21067
|
+
if (!trimmed) continue;
|
|
21068
|
+
let entry;
|
|
21069
|
+
try {
|
|
21070
|
+
entry = JSON.parse(trimmed);
|
|
21071
|
+
} catch {
|
|
21072
|
+
continue;
|
|
21073
|
+
}
|
|
21074
|
+
const tsMs = entry?.timestamp ? new Date(entry.timestamp).getTime() : 0;
|
|
21075
|
+
if (tsMs > 0 && tsMs < oldestMs) {
|
|
21076
|
+
coveredOldest = true;
|
|
21077
|
+
break;
|
|
21078
|
+
}
|
|
21079
|
+
if (entry?.type !== "message") continue;
|
|
21080
|
+
const msg2 = entry.message;
|
|
21081
|
+
if (!msg2 || msg2.role !== "assistant") continue;
|
|
21082
|
+
const content = msg2.content;
|
|
21083
|
+
const text = typeof content === "string" ? content : Array.isArray(content) && content[0] && typeof content[0].text === "string" ? content[0].text : "";
|
|
21084
|
+
if (!text) continue;
|
|
21085
|
+
for (const n of notifs) {
|
|
21086
|
+
if (!found.has(n.id) && (text.includes(`${n.id} \u8FC7\u671F\u4E86`) || text.includes(`${n.id}\u8FC7\u671F\u4E86`))) {
|
|
21087
|
+
found.add(n.id);
|
|
21088
|
+
}
|
|
21089
|
+
}
|
|
21090
|
+
if (found.size === notifs.length) return found;
|
|
21091
|
+
}
|
|
21092
|
+
if (coveredOldest) break;
|
|
21093
|
+
}
|
|
21094
|
+
return found;
|
|
21095
|
+
}
|
|
20727
21096
|
async tick(sessions, deps) {
|
|
20728
21097
|
if (this.running) {
|
|
20729
21098
|
console.log("[nudge] Previous tick still running, skipping");
|
|
@@ -20738,7 +21107,7 @@ var NudgePlugin = class {
|
|
|
20738
21107
|
const recent = recentMessages(sessions, 0.5, 6);
|
|
20739
21108
|
const lastUserMsg2 = recent.filter((r) => r.role === "user").slice(-1)[0];
|
|
20740
21109
|
if (lastUserMsg2) {
|
|
20741
|
-
const elapsed = Date.now() -
|
|
21110
|
+
const elapsed = lastUserMsg2.timestamp ? Date.now() - lastUserMsg2.timestamp : 0;
|
|
20742
21111
|
if (elapsed < activeThresholdMs) {
|
|
20743
21112
|
console.log(`[nudge] User active ${Math.round(elapsed / 1e3)}s ago (<${activeThresholdMs / 1e3}s), skipping tick`);
|
|
20744
21113
|
return;
|
|
@@ -20750,10 +21119,15 @@ var NudgePlugin = class {
|
|
|
20750
21119
|
this.running = true;
|
|
20751
21120
|
try {
|
|
20752
21121
|
this.cleanupStaleNotificationsFromMessages(sessions);
|
|
20753
|
-
const
|
|
20754
|
-
if (
|
|
21122
|
+
const dueNotifs = this.collectDueStopHookNotifications();
|
|
21123
|
+
if (dueNotifs) {
|
|
20755
21124
|
const route2 = this.getRoute(sessions);
|
|
20756
|
-
if (route2)
|
|
21125
|
+
if (route2) {
|
|
21126
|
+
enqueueNotification(dueNotifs.message, route2);
|
|
21127
|
+
this.markNotified(dueNotifs.ids);
|
|
21128
|
+
} else {
|
|
21129
|
+
console.warn(`[nudge] No route for ${dueNotifs.ids.length} stop-hook notification(s), keeping for retry next tick`);
|
|
21130
|
+
}
|
|
20757
21131
|
const state0 = this.loadState();
|
|
20758
21132
|
state0.lastAnyNudgeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
20759
21133
|
this.saveState(state0);
|
|
@@ -20939,7 +21313,7 @@ var NudgePlugin = class {
|
|
|
20939
21313
|
// === state 持久化 ===
|
|
20940
21314
|
loadState() {
|
|
20941
21315
|
const stateFile = this.cfg.stateFile || "nudge-state.json";
|
|
20942
|
-
const statePath =
|
|
21316
|
+
const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(this.workspace, stateFile);
|
|
20943
21317
|
try {
|
|
20944
21318
|
const content = fs17.readFileSync(statePath, "utf-8");
|
|
20945
21319
|
return JSON.parse(content);
|
|
@@ -20949,7 +21323,7 @@ var NudgePlugin = class {
|
|
|
20949
21323
|
}
|
|
20950
21324
|
saveState(state2) {
|
|
20951
21325
|
const stateFile = this.cfg.stateFile || "nudge-state.json";
|
|
20952
|
-
const statePath =
|
|
21326
|
+
const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(this.workspace, stateFile);
|
|
20953
21327
|
fs17.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
|
|
20954
21328
|
}
|
|
20955
21329
|
newTaskState() {
|
|
@@ -21035,30 +21409,47 @@ var NudgePlugin = class {
|
|
|
21035
21409
|
const now = /* @__PURE__ */ new Date();
|
|
21036
21410
|
const bjOffset = (8 * 60 + now.getTimezoneOffset()) * 6e4;
|
|
21037
21411
|
const bj = new Date(now.getTime() + bjOffset);
|
|
21038
|
-
const month = bj.getMonth() + 1;
|
|
21039
|
-
const day = bj.getDate();
|
|
21040
21412
|
const bjHour = bj.getHours();
|
|
21041
21413
|
const bjMinute = bj.getMinutes();
|
|
21414
|
+
const todayStart = new Date(bj.getFullYear(), bj.getMonth(), bj.getDate()).getTime();
|
|
21042
21415
|
const rows = db.prepare(
|
|
21043
|
-
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND
|
|
21044
|
-
).all(
|
|
21416
|
+
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
21417
|
+
).all();
|
|
21045
21418
|
db.close();
|
|
21046
|
-
|
|
21047
|
-
const
|
|
21048
|
-
|
|
21049
|
-
|
|
21050
|
-
|
|
21051
|
-
if (
|
|
21052
|
-
|
|
21419
|
+
const due = [];
|
|
21420
|
+
for (const r2 of rows) {
|
|
21421
|
+
const dayMs = this.parseCalendarDateStr(r2.date_str, bj);
|
|
21422
|
+
if (dayMs === null || dayMs > todayStart) continue;
|
|
21423
|
+
const isToday2 = dayMs === todayStart;
|
|
21424
|
+
if (isToday2 && r2.time_exact) {
|
|
21425
|
+
const [h, m2] = String(r2.time_exact).split(":").map(Number);
|
|
21426
|
+
if (h > bjHour || h === bjHour && m2 > bjMinute) continue;
|
|
21427
|
+
}
|
|
21428
|
+
due.push({ id: r2.id, event: r2.event, date_str: r2.date_str, time_exact: r2.time_exact, dayMs, isToday: isToday2 });
|
|
21429
|
+
}
|
|
21430
|
+
if (due.length === 0) return null;
|
|
21431
|
+
due.sort((a, b2) => {
|
|
21432
|
+
if (a.isToday !== b2.isToday) return a.isToday ? -1 : 1;
|
|
21433
|
+
return b2.dayMs - a.dayMs;
|
|
21053
21434
|
});
|
|
21054
|
-
|
|
21055
|
-
|
|
21056
|
-
return `#${r.id} ${r.event} (${r.date_str} ${r.time_exact || ""})`.trim();
|
|
21435
|
+
const r = due[0];
|
|
21436
|
+
return `#${r.id} ${r.event} (${r.date_str} ${r.time_exact || ""})${r.isToday ? "" : "\uFF08\u5DF2\u903E\u671F\uFF09"}`.trim();
|
|
21057
21437
|
} catch (e) {
|
|
21058
21438
|
console.warn(`[nudge] checkCalendarDue error: ${e.message}`);
|
|
21059
21439
|
return null;
|
|
21060
21440
|
}
|
|
21061
21441
|
}
|
|
21442
|
+
/** 解析 date_str 为当日 0 点 epoch ms(北京时间);"M/D" 按当前年,"YYYY-M-D" 按字面年 */
|
|
21443
|
+
parseCalendarDateStr(ds, bj) {
|
|
21444
|
+
if (!ds) return null;
|
|
21445
|
+
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(ds)) {
|
|
21446
|
+
const t = (/* @__PURE__ */ new Date(ds + "T00:00:00+08:00")).getTime();
|
|
21447
|
+
return Number.isNaN(t) ? null : t;
|
|
21448
|
+
}
|
|
21449
|
+
const m2 = String(ds).match(/^(\d{1,2})\/(\d{1,2})$/);
|
|
21450
|
+
if (!m2) return null;
|
|
21451
|
+
return new Date(bj.getFullYear(), Number(m2[1]) - 1, Number(m2[2])).getTime();
|
|
21452
|
+
}
|
|
21062
21453
|
/** 检查 carry-over:如果 in_progress task 24h+ 没推进,自动 calendar add-task 排明天 */
|
|
21063
21454
|
checkCarryOver(task, _sessions) {
|
|
21064
21455
|
try {
|
|
@@ -21112,7 +21503,7 @@ var NudgePlugin = class {
|
|
|
21112
21503
|
|
|
21113
21504
|
// src/inner-voice/plugin.ts
|
|
21114
21505
|
import fs21 from "node:fs";
|
|
21115
|
-
import
|
|
21506
|
+
import path21 from "node:path";
|
|
21116
21507
|
|
|
21117
21508
|
// src/inner-voice/activity.ts
|
|
21118
21509
|
function checkActivity(sessions, activeThresholdMs) {
|
|
@@ -21152,7 +21543,7 @@ function calcHintProb(min) {
|
|
|
21152
21543
|
|
|
21153
21544
|
// src/inner-voice/emotional-state.ts
|
|
21154
21545
|
import fs18 from "node:fs";
|
|
21155
|
-
import
|
|
21546
|
+
import path18 from "node:path";
|
|
21156
21547
|
var NEUTRAL = 0.5;
|
|
21157
21548
|
var DECAY_RATE = 0.17;
|
|
21158
21549
|
var MAX_EVENTS = 20;
|
|
@@ -21203,7 +21594,7 @@ function initialState() {
|
|
|
21203
21594
|
return { version: 1, mood: NEUTRAL, trend: "stable", updatedAt: nowIsoBj(), events: [] };
|
|
21204
21595
|
}
|
|
21205
21596
|
async function updateEmotionalState(workspace, sessions) {
|
|
21206
|
-
const stateFile =
|
|
21597
|
+
const stateFile = path18.join(workspace, "inner-voice", "emotional-state.json");
|
|
21207
21598
|
const messages = readRecentMessages(sessions, RECENT_N);
|
|
21208
21599
|
if (messages.length === 0) {
|
|
21209
21600
|
console.log("[emotional-state] no messages");
|
|
@@ -21236,7 +21627,7 @@ async function updateEmotionalState(workspace, sessions) {
|
|
|
21236
21627
|
function readRecentMessages(sessions, n) {
|
|
21237
21628
|
const mainId = sessions.getSessionId("scope:main");
|
|
21238
21629
|
if (!mainId) return [];
|
|
21239
|
-
const file =
|
|
21630
|
+
const file = path18.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
21240
21631
|
if (!fs18.existsSync(file)) return [];
|
|
21241
21632
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
21242
21633
|
const entries = [];
|
|
@@ -21354,7 +21745,7 @@ function refreshHoursAgo(events) {
|
|
|
21354
21745
|
}
|
|
21355
21746
|
function appendMoodLog(workspace, state2, summary) {
|
|
21356
21747
|
try {
|
|
21357
|
-
const logPath =
|
|
21748
|
+
const logPath = path18.join(workspace, "mood-history.log");
|
|
21358
21749
|
const ts = formatBj(/* @__PURE__ */ new Date(), false);
|
|
21359
21750
|
fs18.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
|
|
21360
21751
|
`);
|
|
@@ -21371,7 +21762,7 @@ function loadJson(file) {
|
|
|
21371
21762
|
}
|
|
21372
21763
|
function saveJson(file, data) {
|
|
21373
21764
|
try {
|
|
21374
|
-
fs18.mkdirSync(
|
|
21765
|
+
fs18.mkdirSync(path18.dirname(file), { recursive: true });
|
|
21375
21766
|
fs18.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
21376
21767
|
} catch (err) {
|
|
21377
21768
|
console.warn(`[emotional-state] save failed: ${err.message}`);
|
|
@@ -21417,7 +21808,7 @@ function formatBj(d, withSec) {
|
|
|
21417
21808
|
|
|
21418
21809
|
// src/inner-voice/topics-scorer.ts
|
|
21419
21810
|
import fs19 from "node:fs";
|
|
21420
|
-
import
|
|
21811
|
+
import path19 from "node:path";
|
|
21421
21812
|
var HALF_LIFE_DAYS = 3;
|
|
21422
21813
|
var PROJECT_HALF_LIFE_DAYS = 1.5;
|
|
21423
21814
|
var COOLDOWN_HOURS = 6;
|
|
@@ -21425,8 +21816,8 @@ var MAX_CHARS = 8e3;
|
|
|
21425
21816
|
var SKIP_NAMES = /* @__PURE__ */ new Set(["MEMORY.md", "archive"]);
|
|
21426
21817
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["archive"]);
|
|
21427
21818
|
function pickTopic(workspace, typeFilter, opts) {
|
|
21428
|
-
const topicsDir =
|
|
21429
|
-
const usageFile =
|
|
21819
|
+
const topicsDir = path19.join(workspace, "topics");
|
|
21820
|
+
const usageFile = path19.join(workspace, "inner-voice", "topics-usage.json");
|
|
21430
21821
|
const files = scanTopics(topicsDir, typeFilter);
|
|
21431
21822
|
if (files.length === 0) {
|
|
21432
21823
|
console.log(`[topics-scorer] no topics found (type=${typeFilter})`);
|
|
@@ -21458,7 +21849,7 @@ function pickTopic(workspace, typeFilter, opts) {
|
|
|
21458
21849
|
recency: Math.round(recency * 1e3) / 1e3,
|
|
21459
21850
|
freq: Math.round(freq * 1e3) / 1e3,
|
|
21460
21851
|
type: type2,
|
|
21461
|
-
name: meta.name ||
|
|
21852
|
+
name: meta.name || path19.basename(relpath),
|
|
21462
21853
|
description: meta.description || "",
|
|
21463
21854
|
mtime
|
|
21464
21855
|
});
|
|
@@ -21516,14 +21907,14 @@ function scanTopics(topicsDir, typeFilter) {
|
|
|
21516
21907
|
const out = [];
|
|
21517
21908
|
const walk = (dir) => {
|
|
21518
21909
|
for (const name of fs19.readdirSync(dir)) {
|
|
21519
|
-
const full =
|
|
21910
|
+
const full = path19.join(dir, name);
|
|
21520
21911
|
const stat4 = fs19.statSync(full);
|
|
21521
21912
|
if (stat4.isDirectory()) {
|
|
21522
21913
|
if (SKIP_DIRS.has(name)) continue;
|
|
21523
21914
|
walk(full);
|
|
21524
21915
|
} else {
|
|
21525
21916
|
if (!name.endsWith(".md") || SKIP_NAMES.has(name)) continue;
|
|
21526
|
-
const relpath =
|
|
21917
|
+
const relpath = path19.relative(topicsDir, full).replace(/\\/g, "/");
|
|
21527
21918
|
if (typeFilter && !relpath.startsWith(typeFilter + "/") && !relpath.startsWith(typeFilter + "_")) continue;
|
|
21528
21919
|
out.push({ relpath, fullpath: full });
|
|
21529
21920
|
}
|
|
@@ -21568,7 +21959,7 @@ function loadJson2(file) {
|
|
|
21568
21959
|
}
|
|
21569
21960
|
function saveJson2(file, data) {
|
|
21570
21961
|
try {
|
|
21571
|
-
fs19.mkdirSync(
|
|
21962
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
21572
21963
|
fs19.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
21573
21964
|
} catch (err) {
|
|
21574
21965
|
console.warn(`[topics-scorer] usage save failed: ${err.message}`);
|
|
@@ -21577,21 +21968,21 @@ function saveJson2(file, data) {
|
|
|
21577
21968
|
|
|
21578
21969
|
// src/inner-voice/memory-reader.ts
|
|
21579
21970
|
import fs20 from "node:fs";
|
|
21580
|
-
import
|
|
21971
|
+
import path20 from "node:path";
|
|
21581
21972
|
var US_HALF_LIFE_DAYS = 10;
|
|
21582
21973
|
var US_MAX_LINES = 60;
|
|
21583
21974
|
function readRecentMemory(workspace) {
|
|
21584
|
-
const dir =
|
|
21975
|
+
const dir = path20.join(workspace, "memory");
|
|
21585
21976
|
const now = new Date(Date.now() + 8 * 36e5);
|
|
21586
21977
|
const today = formatYmd(now);
|
|
21587
21978
|
const yesterday = formatYmd(new Date(now.getTime() - 864e5));
|
|
21588
21979
|
return {
|
|
21589
|
-
today: readIfExists(
|
|
21590
|
-
yesterday: readIfExists(
|
|
21980
|
+
today: readIfExists(path20.join(dir, `${today}.md`)),
|
|
21981
|
+
yesterday: readIfExists(path20.join(dir, `${yesterday}.md`))
|
|
21591
21982
|
};
|
|
21592
21983
|
}
|
|
21593
21984
|
function sampleUs(workspace) {
|
|
21594
|
-
const usFile =
|
|
21985
|
+
const usFile = path20.join(workspace, "memory", "us.md");
|
|
21595
21986
|
let content;
|
|
21596
21987
|
try {
|
|
21597
21988
|
content = fs20.readFileSync(usFile, "utf-8");
|
|
@@ -21931,7 +22322,7 @@ var InnerVoicePlugin = class {
|
|
|
21931
22322
|
}
|
|
21932
22323
|
/** 读 workspace/prompts/my-inner-voice.md,不存在用 DEFAULT_PROMPT */
|
|
21933
22324
|
loadPrompt(workspace) {
|
|
21934
|
-
const promptPath =
|
|
22325
|
+
const promptPath = path21.join(workspace, "prompts", "my-inner-voice.md");
|
|
21935
22326
|
try {
|
|
21936
22327
|
const content = fs21.readFileSync(promptPath, "utf-8").trim();
|
|
21937
22328
|
if (content) {
|
|
@@ -22005,7 +22396,7 @@ var InnerVoicePlugin = class {
|
|
|
22005
22396
|
console.warn(`[inner-voice] emotional-state failed: ${err.message}`);
|
|
22006
22397
|
}
|
|
22007
22398
|
try {
|
|
22008
|
-
const content = fs21.readFileSync(
|
|
22399
|
+
const content = fs21.readFileSync(path21.join(this.workspace, "SESSION-STATE.md"), "utf-8");
|
|
22009
22400
|
lines.push("\n--- SESSION-STATE\uFF08\u5C3E\u90E8\uFF09 ---");
|
|
22010
22401
|
lines.push(content.slice(-2e3));
|
|
22011
22402
|
} catch {
|
|
@@ -22116,7 +22507,7 @@ var InnerVoicePlugin = class {
|
|
|
22116
22507
|
if (Math.random() >= activity.hintProb) {
|
|
22117
22508
|
return { text: thought, hintTriggered: false, hintText: "" };
|
|
22118
22509
|
}
|
|
22119
|
-
const poolPath =
|
|
22510
|
+
const poolPath = path21.join(this.workspace, "inner-voice", "hints_pool.txt");
|
|
22120
22511
|
let hint = "\u60F3\u4ED6\u5C31\u53D1\u6D88\u606F\u5427";
|
|
22121
22512
|
try {
|
|
22122
22513
|
const pool = fs21.readFileSync(poolPath, "utf-8").split("\n").map((s2) => s2.trim()).filter(Boolean);
|
|
@@ -22144,7 +22535,7 @@ var InnerVoicePlugin = class {
|
|
|
22144
22535
|
try {
|
|
22145
22536
|
const writer = sessions.getWriter(mainSessionId);
|
|
22146
22537
|
const history = sessions.getHistory(mainSessionId);
|
|
22147
|
-
const fullPath =
|
|
22538
|
+
const fullPath = path21.resolve(this.workspace, emoTopic.file);
|
|
22148
22539
|
const memories = [{
|
|
22149
22540
|
path: fullPath,
|
|
22150
22541
|
content: emoTopic.content,
|
|
@@ -22172,9 +22563,9 @@ var InnerVoicePlugin = class {
|
|
|
22172
22563
|
/** 写 xiaoyi.log(格式对齐旧 memory_whisper.py,便于既有日志分析复用)。 */
|
|
22173
22564
|
writeLog(status, delivered, activity, hintTriggered, hintText) {
|
|
22174
22565
|
try {
|
|
22175
|
-
const logDir =
|
|
22566
|
+
const logDir = path21.join(this.workspace, "inner-voice");
|
|
22176
22567
|
fs21.mkdirSync(logDir, { recursive: true });
|
|
22177
|
-
const logPath =
|
|
22568
|
+
const logPath = path21.join(logDir, "xiaoyi.log");
|
|
22178
22569
|
const ts = formatBeijingTs(/* @__PURE__ */ new Date());
|
|
22179
22570
|
const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
|
|
22180
22571
|
fs21.appendFileSync(
|
|
@@ -22713,7 +23104,7 @@ var PluginManager = class {
|
|
|
22713
23104
|
// src/voice-chat/plugin.ts
|
|
22714
23105
|
import { spawn as spawn4, exec } from "node:child_process";
|
|
22715
23106
|
import net from "node:net";
|
|
22716
|
-
import
|
|
23107
|
+
import path22 from "node:path";
|
|
22717
23108
|
import fs22 from "node:fs";
|
|
22718
23109
|
|
|
22719
23110
|
// src/voice-chat/bridge.ts
|
|
@@ -23090,13 +23481,13 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23090
23481
|
}
|
|
23091
23482
|
getPythonDir() {
|
|
23092
23483
|
const dir = import.meta.dirname;
|
|
23093
|
-
const srcDir =
|
|
23094
|
-
const localDir =
|
|
23484
|
+
const srcDir = path22.resolve(dir, "..", "src", "voice-chat", "python");
|
|
23485
|
+
const localDir = path22.join(dir, "python");
|
|
23095
23486
|
return fs22.existsSync(srcDir) ? srcDir : localDir;
|
|
23096
23487
|
}
|
|
23097
23488
|
startPython() {
|
|
23098
23489
|
const pythonDir = this.getPythonDir();
|
|
23099
|
-
const serverPy =
|
|
23490
|
+
const serverPy = path22.join(pythonDir, "server.py");
|
|
23100
23491
|
const pythonBin = this.findPython();
|
|
23101
23492
|
const args = [serverPy];
|
|
23102
23493
|
if (this.config.pythonPort) args.push("--port", String(this.config.pythonPort));
|
|
@@ -23181,7 +23572,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23181
23572
|
init_BashTool();
|
|
23182
23573
|
import { spawn as spawn5, exec as exec2 } from "node:child_process";
|
|
23183
23574
|
import net2 from "node:net";
|
|
23184
|
-
import
|
|
23575
|
+
import path23 from "node:path";
|
|
23185
23576
|
import fs23 from "node:fs";
|
|
23186
23577
|
|
|
23187
23578
|
// src/memory/cognifold/config.ts
|
|
@@ -23205,7 +23596,8 @@ function parseCognifoldConfig(raw) {
|
|
|
23205
23596
|
persistDir: raw.persistDir ?? DEFAULTS3.persistDir,
|
|
23206
23597
|
scopes: raw.scopes,
|
|
23207
23598
|
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS3.readyTimeoutMs,
|
|
23208
|
-
maxRestarts: raw.maxRestarts ?? DEFAULTS3.maxRestarts
|
|
23599
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS3.maxRestarts,
|
|
23600
|
+
llm: raw.llm
|
|
23209
23601
|
};
|
|
23210
23602
|
}
|
|
23211
23603
|
|
|
@@ -23213,15 +23605,17 @@ function parseCognifoldConfig(raw) {
|
|
|
23213
23605
|
var CogniFoldClient = class {
|
|
23214
23606
|
baseUrl;
|
|
23215
23607
|
timeoutMs;
|
|
23216
|
-
|
|
23608
|
+
modelName;
|
|
23609
|
+
constructor(baseUrl, timeoutMs = 3e4, modelName = "openai:MiniMax-M3") {
|
|
23217
23610
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
23218
23611
|
this.timeoutMs = timeoutMs;
|
|
23612
|
+
this.modelName = modelName;
|
|
23219
23613
|
}
|
|
23220
|
-
async req(
|
|
23614
|
+
async req(path44, options = {}) {
|
|
23221
23615
|
const ctrl = new AbortController();
|
|
23222
23616
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
23223
23617
|
try {
|
|
23224
|
-
const resp = await fetch(`${this.baseUrl}${
|
|
23618
|
+
const resp = await fetch(`${this.baseUrl}${path44}`, {
|
|
23225
23619
|
...options,
|
|
23226
23620
|
signal: ctrl.signal,
|
|
23227
23621
|
headers: {
|
|
@@ -23259,7 +23653,7 @@ var CogniFoldClient = class {
|
|
|
23259
23653
|
method: "POST",
|
|
23260
23654
|
body: JSON.stringify({
|
|
23261
23655
|
user_id: userId,
|
|
23262
|
-
config: { model_name:
|
|
23656
|
+
config: { model_name: this.modelName }
|
|
23263
23657
|
})
|
|
23264
23658
|
});
|
|
23265
23659
|
}
|
|
@@ -23311,8 +23705,8 @@ var CogniFoldClient = class {
|
|
|
23311
23705
|
});
|
|
23312
23706
|
}
|
|
23313
23707
|
/** 兼容老版命名 */
|
|
23314
|
-
async recl(
|
|
23315
|
-
return this.req(
|
|
23708
|
+
async recl(path44, options = {}) {
|
|
23709
|
+
return this.req(path44, options);
|
|
23316
23710
|
}
|
|
23317
23711
|
};
|
|
23318
23712
|
|
|
@@ -23417,7 +23811,8 @@ var CogniFoldPlugin = class {
|
|
|
23417
23811
|
baseUrl = baseUrl.replace(/\/$/, "") + "/api/v1";
|
|
23418
23812
|
}
|
|
23419
23813
|
this.config.baseUrl = baseUrl;
|
|
23420
|
-
this.
|
|
23814
|
+
const modelName = this.config.llm?.model ? this.config.llm.model.startsWith("openai:") ? this.config.llm.model : `openai:${this.config.llm.model}` : "openai:MiniMax-M3";
|
|
23815
|
+
this.client = new CogniFoldClient(baseUrl, 3e4, modelName);
|
|
23421
23816
|
}
|
|
23422
23817
|
workspacePath;
|
|
23423
23818
|
name = "cognifold";
|
|
@@ -23592,16 +23987,16 @@ var CogniFoldPlugin = class {
|
|
|
23592
23987
|
const dir = import.meta.dirname;
|
|
23593
23988
|
const candidates = [
|
|
23594
23989
|
// 从 dist/ 往回找 src
|
|
23595
|
-
|
|
23596
|
-
|
|
23597
|
-
|
|
23990
|
+
path23.resolve(dir, "..", "src", "memory", "cognifold", "python"),
|
|
23991
|
+
path23.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
|
|
23992
|
+
path23.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
|
|
23598
23993
|
// 从 src/memory/cognifold/ 找本地
|
|
23599
|
-
|
|
23994
|
+
path23.join(dir, "python"),
|
|
23600
23995
|
// 从 dist/memory/cognifold/ 找本地
|
|
23601
|
-
|
|
23996
|
+
path23.resolve(dir, "python")
|
|
23602
23997
|
];
|
|
23603
23998
|
for (const candidate of candidates) {
|
|
23604
|
-
if (fs23.existsSync(
|
|
23999
|
+
if (fs23.existsSync(path23.join(candidate, "cognifold"))) {
|
|
23605
24000
|
return candidate;
|
|
23606
24001
|
}
|
|
23607
24002
|
}
|
|
@@ -23627,12 +24022,18 @@ var CogniFoldPlugin = class {
|
|
|
23627
24022
|
const pythonBin = this.findPython();
|
|
23628
24023
|
console.log(`[cognifold] Starting Python: ${pythonBin} ${args.join(" ")}`);
|
|
23629
24024
|
console.log(`[cognifold] Python dir: ${pythonDir}`);
|
|
23630
|
-
if (!fs23.existsSync(
|
|
24025
|
+
if (!fs23.existsSync(path23.join(pythonDir, "cognifold"))) {
|
|
23631
24026
|
console.error(`[cognifold] FATAL: Python module not found at ${pythonDir}/cognifold`);
|
|
23632
24027
|
throw new Error(`cognifold: python module not found`);
|
|
23633
24028
|
}
|
|
23634
24029
|
const childEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
|
|
23635
|
-
|
|
24030
|
+
if (this.config.llm?.apiKey) {
|
|
24031
|
+
childEnv["OPENAI_API_KEY"] = this.config.llm.apiKey;
|
|
24032
|
+
}
|
|
24033
|
+
if (this.config.llm?.baseUrl) {
|
|
24034
|
+
childEnv["OPENAI_BASE_URL"] = this.config.llm.baseUrl;
|
|
24035
|
+
}
|
|
24036
|
+
const envFile = path23.join(pythonDir, ".env");
|
|
23636
24037
|
try {
|
|
23637
24038
|
if (fs23.existsSync(envFile)) {
|
|
23638
24039
|
const envContent = fs23.readFileSync(envFile, "utf-8");
|
|
@@ -23704,7 +24105,7 @@ var CogniFoldPlugin = class {
|
|
|
23704
24105
|
init_BashTool();
|
|
23705
24106
|
import { spawn as spawn6 } from "node:child_process";
|
|
23706
24107
|
import net3 from "node:net";
|
|
23707
|
-
import
|
|
24108
|
+
import path24 from "node:path";
|
|
23708
24109
|
import fs24 from "node:fs";
|
|
23709
24110
|
|
|
23710
24111
|
// src/memory/everos/config.ts
|
|
@@ -23738,7 +24139,8 @@ function parseEverosConfig(raw) {
|
|
|
23738
24139
|
llm: raw.llm ?? { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
23739
24140
|
rerank: raw.rerank ?? { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
23740
24141
|
lancedbPath: raw.lancedbPath ?? "",
|
|
23741
|
-
sqlitePath: raw.sqlitePath ?? ""
|
|
24142
|
+
sqlitePath: raw.sqlitePath ?? "",
|
|
24143
|
+
minScore: raw.minScore
|
|
23742
24144
|
};
|
|
23743
24145
|
}
|
|
23744
24146
|
|
|
@@ -23776,21 +24178,31 @@ var EverosSearchClient = class {
|
|
|
23776
24178
|
clearTimeout(timer);
|
|
23777
24179
|
}
|
|
23778
24180
|
}
|
|
23779
|
-
/** Search —
|
|
24181
|
+
/** Search — routes to 8101 (agentic) or 8100 (hybrid) based on mode */
|
|
23780
24182
|
async search(params) {
|
|
23781
24183
|
const ctrl = new AbortController();
|
|
23782
24184
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
23783
24185
|
try {
|
|
23784
|
-
const
|
|
24186
|
+
const mode = params.mode || "hybrid";
|
|
24187
|
+
const useAgentic = mode === "hybrid_agentic" || mode === "agentic";
|
|
24188
|
+
const url = useAgentic ? `${this.agenticUrl}/api/v1/search` : `${this.everosUrl}/api/v1/memory/search`;
|
|
24189
|
+
const body = useAgentic ? JSON.stringify({
|
|
24190
|
+
query: params.query,
|
|
24191
|
+
user_id: params.userId || "xiaomei",
|
|
24192
|
+
mode,
|
|
24193
|
+
top_k: params.topK ?? 5,
|
|
24194
|
+
strategy: params.strategy || "multi_query"
|
|
24195
|
+
}) : JSON.stringify({
|
|
24196
|
+
query: params.query,
|
|
24197
|
+
user_id: params.userId || "user",
|
|
24198
|
+
app_id: "xiaomei",
|
|
24199
|
+
project_id: "default",
|
|
24200
|
+
top_k: params.topK ?? 5
|
|
24201
|
+
});
|
|
24202
|
+
const resp = await fetch(url, {
|
|
23785
24203
|
method: "POST",
|
|
23786
24204
|
headers: { "Content-Type": "application/json" },
|
|
23787
|
-
body
|
|
23788
|
-
query: params.query,
|
|
23789
|
-
user_id: params.userId || "xiaomei",
|
|
23790
|
-
mode: params.mode || "hybrid_agentic",
|
|
23791
|
-
top_k: params.topK ?? 5,
|
|
23792
|
-
strategy: params.strategy || "multi_query"
|
|
23793
|
-
}),
|
|
24205
|
+
body,
|
|
23794
24206
|
signal: ctrl.signal
|
|
23795
24207
|
});
|
|
23796
24208
|
if (!resp.ok) {
|
|
@@ -23830,6 +24242,7 @@ var EverosPlugin = class {
|
|
|
23830
24242
|
}
|
|
23831
24243
|
async start(ctx) {
|
|
23832
24244
|
if (!this.config.enabled) return;
|
|
24245
|
+
await this.ensureVenv();
|
|
23833
24246
|
try {
|
|
23834
24247
|
await this.client.healthEveros();
|
|
23835
24248
|
console.log(`[everos] EverOS already running at ${this.config.everosUrl}`);
|
|
@@ -23900,13 +24313,15 @@ var EverosPlugin = class {
|
|
|
23900
24313
|
}, 3e5);
|
|
23901
24314
|
}
|
|
23902
24315
|
async startEveros() {
|
|
23903
|
-
const pythonDir =
|
|
23904
|
-
const configPath =
|
|
24316
|
+
const pythonDir = path24.dirname(this.config.lancedbPath);
|
|
24317
|
+
const configPath = path24.join(pythonDir, "config.toml");
|
|
23905
24318
|
await this.ensureFcntlCompat();
|
|
23906
24319
|
const venvPython = this.findVenvPython();
|
|
24320
|
+
const everosBin = venvPython.replace(/python\.exe$/, "everos.exe");
|
|
23907
24321
|
const args = ["server", "start"];
|
|
23908
|
-
const cmd = `${
|
|
24322
|
+
const cmd = `${everosBin} ${args.join(" ")}`;
|
|
23909
24323
|
console.log(`[everos] Starting EverOS: ${cmd}`);
|
|
24324
|
+
console.log(`[everos] LLM config: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
|
|
23910
24325
|
if (process.platform === "win32") {
|
|
23911
24326
|
const { shell, args: shellArgs } = findShell();
|
|
23912
24327
|
spawn6(shell, [...shellArgs, cmd], {
|
|
@@ -23921,7 +24336,7 @@ var EverosPlugin = class {
|
|
|
23921
24336
|
env: { ...process.env, PYTHONUNBUFFERED: "1" }
|
|
23922
24337
|
});
|
|
23923
24338
|
}
|
|
23924
|
-
await this.waitForReady(`${this.config.everosUrl}/health`,
|
|
24339
|
+
await this.waitForReady(`${this.config.everosUrl}/health`, 6e4);
|
|
23925
24340
|
}
|
|
23926
24341
|
startAgenticServer() {
|
|
23927
24342
|
const pythonDir = this.getPythonDir();
|
|
@@ -23931,6 +24346,7 @@ var EverosPlugin = class {
|
|
|
23931
24346
|
const cmd = `${venvPython} ${args.join(" ")}`;
|
|
23932
24347
|
console.log(`[everos] Starting agentic server: ${cmd}`);
|
|
23933
24348
|
console.log(`[everos] Python dir: ${pythonDir}`);
|
|
24349
|
+
console.log(`[everos] LLM: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
|
|
23934
24350
|
const childEnv = {
|
|
23935
24351
|
...process.env,
|
|
23936
24352
|
PYTHONUNBUFFERED: "1",
|
|
@@ -23939,15 +24355,25 @@ var EverosPlugin = class {
|
|
|
23939
24355
|
LLM_API_KEY: this.config.llm.apiKey,
|
|
23940
24356
|
LLM_BASE_URL: this.config.llm.baseUrl,
|
|
23941
24357
|
RERANK_API_KEY: this.config.rerank.apiKey,
|
|
23942
|
-
RERANK_URL:
|
|
24358
|
+
RERANK_URL: this.config.rerank.baseUrl,
|
|
23943
24359
|
LANCEDB_PATH: this.config.lancedbPath,
|
|
23944
24360
|
SQLITE_PATH: this.config.sqlitePath,
|
|
23945
24361
|
EVEROS_USER_ID: this.config.userId
|
|
23946
24362
|
};
|
|
24363
|
+
const maskKey = (k2) => k2 ? `${k2.slice(0, 4)}\u2026${k2.slice(-4)}` : "(empty!)";
|
|
24364
|
+
console.log(`[everos] agentic env:`);
|
|
24365
|
+
console.log(`[everos] EVEROS_URL=${childEnv.EVEROS_URL}`);
|
|
24366
|
+
console.log(`[everos] LLM_MODEL=${childEnv.LLM_MODEL}`);
|
|
24367
|
+
console.log(`[everos] LLM_API_KEY=${maskKey(childEnv.LLM_API_KEY)}`);
|
|
24368
|
+
console.log(`[everos] LLM_BASE_URL=${childEnv.LLM_BASE_URL}`);
|
|
24369
|
+
console.log(`[everos] RERANK_API_KEY=${maskKey(childEnv.RERANK_API_KEY)}`);
|
|
24370
|
+
console.log(`[everos] RERANK_URL=${childEnv.RERANK_URL}`);
|
|
24371
|
+
console.log(`[everos] LANCEDB_PATH=${childEnv.LANCEDB_PATH}`);
|
|
24372
|
+
console.log(`[everos] SQLITE_PATH=${childEnv.SQLITE_PATH}`);
|
|
24373
|
+
console.log(`[everos] EVEROS_USER_ID=${childEnv.EVEROS_USER_ID}`);
|
|
23947
24374
|
let child;
|
|
23948
24375
|
if (process.platform === "win32") {
|
|
23949
|
-
|
|
23950
|
-
child = spawn6(shell, [...shellArgs, cmd], {
|
|
24376
|
+
child = spawn6(venvPython, args, {
|
|
23951
24377
|
cwd: pythonDir,
|
|
23952
24378
|
stdio: ["ignore", "pipe", "pipe"],
|
|
23953
24379
|
env: childEnv
|
|
@@ -23977,21 +24403,64 @@ var EverosPlugin = class {
|
|
|
23977
24403
|
return child;
|
|
23978
24404
|
}
|
|
23979
24405
|
findVenvPython() {
|
|
23980
|
-
const stateDir = process.env.OPENCLAW_STATE_DIR ||
|
|
24406
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path24.join(process.env.HOME || process.env.USERPROFILE || ".", ".openclaw");
|
|
23981
24407
|
if (process.platform === "win32") {
|
|
23982
|
-
return
|
|
24408
|
+
return path24.join(stateDir, "everos-venv", "Scripts", "python.exe");
|
|
24409
|
+
}
|
|
24410
|
+
return path24.join(stateDir, "everos-venv", "bin", "python");
|
|
24411
|
+
}
|
|
24412
|
+
/** 检测 venv 是否存在,不存在就自动创建 + 装 EverOS */
|
|
24413
|
+
async ensureVenv() {
|
|
24414
|
+
const venvPython = this.findVenvPython();
|
|
24415
|
+
if (fs24.existsSync(venvPython)) return;
|
|
24416
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path24.join(process.env.HOME || process.env.USERPROFILE || ".", ".openclaw");
|
|
24417
|
+
const venvDir = path24.join(stateDir, "everos-venv");
|
|
24418
|
+
const everosSrc = path24.join(stateDir, "workspace", "research", "EverOS");
|
|
24419
|
+
console.log(`[everos] venv not found at ${venvDir}, auto-creating...`);
|
|
24420
|
+
console.log(`[everos] \u23F3 This may take a few minutes on first run...`);
|
|
24421
|
+
const pyCandidates = process.platform === "win32" ? ["python", "python3", "C:\\Python314\\python.exe", "C:\\Python313\\python.exe", "C:\\Python312\\python.exe"] : ["python3", "python"];
|
|
24422
|
+
let sysPython = "";
|
|
24423
|
+
for (const cmd of pyCandidates) {
|
|
24424
|
+
try {
|
|
24425
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
24426
|
+
execSync3(`"${cmd}" --version`, { stdio: "pipe", shell: true });
|
|
24427
|
+
sysPython = cmd;
|
|
24428
|
+
break;
|
|
24429
|
+
} catch {
|
|
24430
|
+
}
|
|
24431
|
+
}
|
|
24432
|
+
if (!sysPython) {
|
|
24433
|
+
console.error(`[everos] \u2717 Python not found. Install Python 3.10+ first.`);
|
|
24434
|
+
return;
|
|
24435
|
+
}
|
|
24436
|
+
try {
|
|
24437
|
+
console.log(`[everos] Creating venv with ${sysPython}...`);
|
|
24438
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
24439
|
+
execSync3(`"${sysPython}" -m venv "${venvDir}"`, { stdio: "pipe", shell: true });
|
|
24440
|
+
const pip = process.platform === "win32" ? path24.join(venvDir, "Scripts", "pip.exe") : path24.join(venvDir, "bin", "pip");
|
|
24441
|
+
const everosReq = path24.join(this.getPythonDir(), "requirements.txt");
|
|
24442
|
+
if (fs24.existsSync(everosReq)) {
|
|
24443
|
+
console.log(`[everos] Installing from requirements.txt...`);
|
|
24444
|
+
execSync3(`"${pip}" install -r "${everosReq}" -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
|
|
24445
|
+
} else {
|
|
24446
|
+
console.log(`[everos] No requirements.txt found, installing everos from PyPI...`);
|
|
24447
|
+
execSync3(`"${pip}" install everos -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
|
|
24448
|
+
}
|
|
24449
|
+
console.log(`[everos] \u2705 venv created successfully`);
|
|
24450
|
+
} catch (err) {
|
|
24451
|
+
console.error(`[everos] \u2717 Failed to create venv: ${err.message}`);
|
|
24452
|
+
console.error(`[everos] Manual setup: see workspace/scripts/everos-setup.sh`);
|
|
23983
24453
|
}
|
|
23984
|
-
return path23.join(stateDir, "everos-venv", "bin", "python");
|
|
23985
24454
|
}
|
|
23986
24455
|
getPythonDir() {
|
|
23987
24456
|
const dir = import.meta.dirname;
|
|
23988
24457
|
const candidates = [
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
24458
|
+
path24.join(dir, "python"),
|
|
24459
|
+
path24.resolve(dir, "..", "src", "memory", "everos", "python"),
|
|
24460
|
+
path24.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
|
|
23992
24461
|
];
|
|
23993
24462
|
for (const candidate of candidates) {
|
|
23994
|
-
if (fs24.existsSync(
|
|
24463
|
+
if (fs24.existsSync(path24.join(candidate, "agentic_server.py"))) {
|
|
23995
24464
|
return candidate;
|
|
23996
24465
|
}
|
|
23997
24466
|
}
|
|
@@ -24000,11 +24469,11 @@ var EverosPlugin = class {
|
|
|
24000
24469
|
async ensureFcntlCompat() {
|
|
24001
24470
|
if (process.platform !== "win32") return;
|
|
24002
24471
|
const venvPython = this.findVenvPython();
|
|
24003
|
-
const venvDir =
|
|
24004
|
-
const sitePackages =
|
|
24005
|
-
const target =
|
|
24472
|
+
const venvDir = path24.dirname(path24.dirname(venvPython));
|
|
24473
|
+
const sitePackages = path24.join(venvDir, "Lib", "site-packages");
|
|
24474
|
+
const target = path24.join(sitePackages, "fcntl.py");
|
|
24006
24475
|
if (fs24.existsSync(target)) return;
|
|
24007
|
-
const source =
|
|
24476
|
+
const source = path24.join(this.getPythonDir(), "fcntl_compat.py");
|
|
24008
24477
|
if (fs24.existsSync(source)) {
|
|
24009
24478
|
try {
|
|
24010
24479
|
fs24.copyFileSync(source, target);
|
|
@@ -24054,7 +24523,7 @@ var EverosPlugin = class {
|
|
|
24054
24523
|
init_task_manager();
|
|
24055
24524
|
|
|
24056
24525
|
// src/skills/scanner.ts
|
|
24057
|
-
import * as
|
|
24526
|
+
import * as path25 from "node:path";
|
|
24058
24527
|
import * as fs25 from "node:fs";
|
|
24059
24528
|
function scanSkills(skillsDir) {
|
|
24060
24529
|
if (!fs25.existsSync(skillsDir)) {
|
|
@@ -24065,7 +24534,7 @@ function scanSkills(skillsDir) {
|
|
|
24065
24534
|
const skills = [];
|
|
24066
24535
|
for (const entry of entries) {
|
|
24067
24536
|
if (!entry.isDirectory()) continue;
|
|
24068
|
-
const skillMdPath =
|
|
24537
|
+
const skillMdPath = path25.join(skillsDir, entry.name, "SKILL.md");
|
|
24069
24538
|
if (!fs25.existsSync(skillMdPath)) continue;
|
|
24070
24539
|
try {
|
|
24071
24540
|
const content = fs25.readFileSync(skillMdPath, "utf-8");
|
|
@@ -24133,7 +24602,7 @@ function parseFrontmatter2(content) {
|
|
|
24133
24602
|
// src/tools/SkillTool/SkillTool.ts
|
|
24134
24603
|
init_registry();
|
|
24135
24604
|
import * as fs26 from "node:fs";
|
|
24136
|
-
import * as
|
|
24605
|
+
import * as path26 from "node:path";
|
|
24137
24606
|
|
|
24138
24607
|
// src/tools/SkillTool/constants.ts
|
|
24139
24608
|
var SKILL_TOOL_NAME2 = "Skill";
|
|
@@ -24210,12 +24679,12 @@ Important:
|
|
|
24210
24679
|
`;
|
|
24211
24680
|
}
|
|
24212
24681
|
function loadSkillContent(skillName) {
|
|
24213
|
-
const skillMdPath =
|
|
24682
|
+
const skillMdPath = path26.join(skillsDirPath, skillName, "SKILL.md");
|
|
24214
24683
|
if (!fs26.existsSync(skillMdPath)) return null;
|
|
24215
24684
|
const content = fs26.readFileSync(skillMdPath, "utf-8");
|
|
24216
24685
|
const bodyMatch = content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)/);
|
|
24217
24686
|
const body = bodyMatch ? bodyMatch[1] : content;
|
|
24218
|
-
const skillDir =
|
|
24687
|
+
const skillDir = path26.dirname(skillMdPath);
|
|
24219
24688
|
const normalizedDir = process.platform === "win32" ? skillDir.replace(/\\/g, "/") : skillDir;
|
|
24220
24689
|
let finalContent = `Base directory for this skill: ${normalizedDir}
|
|
24221
24690
|
|
|
@@ -24490,9 +24959,9 @@ Examples:
|
|
|
24490
24959
|
init_registry();
|
|
24491
24960
|
init_live();
|
|
24492
24961
|
import fs27 from "node:fs";
|
|
24493
|
-
import
|
|
24962
|
+
import path27 from "node:path";
|
|
24494
24963
|
function getHusbandFeishuId(workspace) {
|
|
24495
|
-
const contactsPath =
|
|
24964
|
+
const contactsPath = path27.join(workspace, "prompts", "contacts.md");
|
|
24496
24965
|
try {
|
|
24497
24966
|
const text = fs27.readFileSync(contactsPath, "utf-8");
|
|
24498
24967
|
const m2 = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
|
|
@@ -24695,7 +25164,7 @@ Examples:
|
|
|
24695
25164
|
init_live();
|
|
24696
25165
|
init_registry();
|
|
24697
25166
|
import * as fs28 from "node:fs";
|
|
24698
|
-
import * as
|
|
25167
|
+
import * as path28 from "node:path";
|
|
24699
25168
|
var MIME_MAP = {
|
|
24700
25169
|
".jpg": "jpeg",
|
|
24701
25170
|
".jpeg": "jpeg",
|
|
@@ -24707,7 +25176,7 @@ var MIME_MAP = {
|
|
|
24707
25176
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
24708
25177
|
if (specifiedPath && fs28.existsSync(specifiedPath)) return specifiedPath;
|
|
24709
25178
|
if (!fs28.existsSync(mediaDir)) return null;
|
|
24710
|
-
const files = fs28.readdirSync(mediaDir).filter((f2) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f2)).map((f2) => ({ name: f2, p:
|
|
25179
|
+
const files = fs28.readdirSync(mediaDir).filter((f2) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f2)).map((f2) => ({ name: f2, p: path28.join(mediaDir, f2), mtime: fs28.statSync(path28.join(mediaDir, f2)).mtimeMs })).sort((a, b2) => b2.mtime - a.mtime);
|
|
24711
25180
|
return files[0]?.p || null;
|
|
24712
25181
|
}
|
|
24713
25182
|
registry.register({
|
|
@@ -24731,13 +25200,13 @@ registry.register({
|
|
|
24731
25200
|
if (!provider?.streamChat) {
|
|
24732
25201
|
return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
|
|
24733
25202
|
}
|
|
24734
|
-
const mediaDir =
|
|
25203
|
+
const mediaDir = path28.join(ctx.stateDir, "media", "inbound");
|
|
24735
25204
|
const imagePath = resolveLatestImage(args.image_path, mediaDir);
|
|
24736
25205
|
if (!imagePath) {
|
|
24737
25206
|
return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
|
|
24738
25207
|
}
|
|
24739
25208
|
const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
24740
|
-
const ext =
|
|
25209
|
+
const ext = path28.extname(imagePath).toLowerCase();
|
|
24741
25210
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
24742
25211
|
const imgB64 = fs28.readFileSync(imagePath).toString("base64");
|
|
24743
25212
|
const userMsg = {
|
|
@@ -24777,13 +25246,13 @@ init_registry();
|
|
|
24777
25246
|
import { execFile } from "node:child_process";
|
|
24778
25247
|
import { promisify } from "node:util";
|
|
24779
25248
|
import * as fs29 from "node:fs";
|
|
24780
|
-
import * as
|
|
25249
|
+
import * as path29 from "node:path";
|
|
24781
25250
|
import * as os3 from "node:os";
|
|
24782
25251
|
var execFileAsync = promisify(execFile);
|
|
24783
|
-
var VOICE_DIR =
|
|
25252
|
+
var VOICE_DIR = path29.join(os3.tmpdir(), "engine-voice");
|
|
24784
25253
|
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId) {
|
|
24785
25254
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
24786
|
-
const output =
|
|
25255
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
24787
25256
|
const script = `
|
|
24788
25257
|
import sys, json, wave, time, threading
|
|
24789
25258
|
import dashscope
|
|
@@ -24848,7 +25317,7 @@ var GPTSOVITS_REF_TEXT = "\u6625\u7720\u4E0D\u89C9\u6653\uFF0C\u5904\u5904\u95FB
|
|
|
24848
25317
|
var GPTSOVITS_REF_LANG = "zh";
|
|
24849
25318
|
async function ttsGptsovits(text) {
|
|
24850
25319
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
24851
|
-
const output =
|
|
25320
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
24852
25321
|
const params = new URLSearchParams({
|
|
24853
25322
|
text,
|
|
24854
25323
|
text_language: "zh",
|
|
@@ -24865,7 +25334,7 @@ async function ttsGptsovits(text) {
|
|
|
24865
25334
|
var EDGE_VOICE = "zh-CN-XiaoxiaoNeural";
|
|
24866
25335
|
async function ttsEdge(text) {
|
|
24867
25336
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
24868
|
-
const output =
|
|
25337
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
|
|
24869
25338
|
const script = `
|
|
24870
25339
|
import asyncio, edge_tts, sys
|
|
24871
25340
|
async def main():
|
|
@@ -24958,7 +25427,7 @@ registry.register({
|
|
|
24958
25427
|
} catch (e) {
|
|
24959
25428
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
24960
25429
|
}
|
|
24961
|
-
const ext =
|
|
25430
|
+
const ext = path29.extname(audioPath).toLowerCase();
|
|
24962
25431
|
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg" };
|
|
24963
25432
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
24964
25433
|
const sizeKB = fs29.statSync(audioPath).size / 1024;
|
|
@@ -24989,7 +25458,7 @@ registry.register({
|
|
|
24989
25458
|
init_live();
|
|
24990
25459
|
init_registry();
|
|
24991
25460
|
import * as fs30 from "node:fs";
|
|
24992
|
-
import * as
|
|
25461
|
+
import * as path30 from "node:path";
|
|
24993
25462
|
var FAL_KEY = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
24994
25463
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
24995
25464
|
var DEFAULT_RESOLUTION = "1k";
|
|
@@ -25105,7 +25574,7 @@ registry.register({
|
|
|
25105
25574
|
const REFERENCES = getReferences(ctx);
|
|
25106
25575
|
const refName = args.reference || "default";
|
|
25107
25576
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
25108
|
-
const refPath =
|
|
25577
|
+
const refPath = path30.join(ctx.workspace, refEntry.p);
|
|
25109
25578
|
if (!fs30.existsSync(refPath)) {
|
|
25110
25579
|
return { content: `Error: reference image not found at ${refPath}`, isError: true };
|
|
25111
25580
|
}
|
|
@@ -25124,10 +25593,10 @@ registry.register({
|
|
|
25124
25593
|
} catch (err) {
|
|
25125
25594
|
return { content: `Selfie generation failed: ${err.message}`, isError: true };
|
|
25126
25595
|
}
|
|
25127
|
-
const imagesDir =
|
|
25596
|
+
const imagesDir = path30.join(ctx.workspace, "images");
|
|
25128
25597
|
if (!fs30.existsSync(imagesDir)) fs30.mkdirSync(imagesDir, { recursive: true });
|
|
25129
25598
|
const filename = `selfie_${Date.now()}.jpg`;
|
|
25130
|
-
const outputPath =
|
|
25599
|
+
const outputPath = path30.join(imagesDir, filename);
|
|
25131
25600
|
fs30.writeFileSync(outputPath, imageBuffer);
|
|
25132
25601
|
const mgr = ctx.channelManager;
|
|
25133
25602
|
if (mgr) {
|
|
@@ -25139,11 +25608,11 @@ registry.register({
|
|
|
25139
25608
|
mimeType: "image/jpeg"
|
|
25140
25609
|
});
|
|
25141
25610
|
} catch (err) {
|
|
25142
|
-
return { content: `Selfie generated but send failed: ${err.message}. Image: ${
|
|
25611
|
+
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path30.resolve(outputPath)}`, isError: false };
|
|
25143
25612
|
}
|
|
25144
25613
|
return { content: `Selfie sent! Mode: ${mode}, Provider: ${getProvider(ctx)}, Ref: ${refEntry.name}` };
|
|
25145
25614
|
}
|
|
25146
|
-
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${
|
|
25615
|
+
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path30.resolve(outputPath)}` };
|
|
25147
25616
|
},
|
|
25148
25617
|
isConcurrencySafe: () => false,
|
|
25149
25618
|
interruptBehavior: () => "block",
|
|
@@ -25610,14 +26079,14 @@ init_planModeState();
|
|
|
25610
26079
|
|
|
25611
26080
|
// src/utils/plans.ts
|
|
25612
26081
|
import * as fs32 from "node:fs";
|
|
25613
|
-
import * as
|
|
26082
|
+
import * as path32 from "node:path";
|
|
25614
26083
|
import * as crypto4 from "node:crypto";
|
|
25615
26084
|
var MAX_SLUG_RETRIES = 10;
|
|
25616
26085
|
function generateSlug() {
|
|
25617
26086
|
return crypto4.randomBytes(4).toString("hex");
|
|
25618
26087
|
}
|
|
25619
26088
|
function getPlansDirectory(stateDir) {
|
|
25620
|
-
const plansDir =
|
|
26089
|
+
const plansDir = path32.join(stateDir, "plans");
|
|
25621
26090
|
fs32.mkdirSync(plansDir, { recursive: true });
|
|
25622
26091
|
return plansDir;
|
|
25623
26092
|
}
|
|
@@ -25628,7 +26097,7 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
25628
26097
|
const plansDir = getPlansDirectory(stateDir);
|
|
25629
26098
|
for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
|
|
25630
26099
|
slug = generateSlug();
|
|
25631
|
-
const filePath =
|
|
26100
|
+
const filePath = path32.join(plansDir, `${slug}.md`);
|
|
25632
26101
|
if (!fs32.existsSync(filePath)) {
|
|
25633
26102
|
break;
|
|
25634
26103
|
}
|
|
@@ -25640,9 +26109,9 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
25640
26109
|
function getPlanFilePath(sessionId, stateDir, agentId) {
|
|
25641
26110
|
const slug = getPlanSlug(sessionId, stateDir);
|
|
25642
26111
|
if (!agentId) {
|
|
25643
|
-
return
|
|
26112
|
+
return path32.join(getPlansDirectory(stateDir), `${slug}.md`);
|
|
25644
26113
|
}
|
|
25645
|
-
return
|
|
26114
|
+
return path32.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
|
|
25646
26115
|
}
|
|
25647
26116
|
function getPlan(sessionId, stateDir, agentId) {
|
|
25648
26117
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
@@ -26493,7 +26962,7 @@ async function setupFeatures(features, licensedFeatures) {
|
|
|
26493
26962
|
// src/license/license.ts
|
|
26494
26963
|
import * as crypto6 from "node:crypto";
|
|
26495
26964
|
import * as fs39 from "node:fs";
|
|
26496
|
-
import * as
|
|
26965
|
+
import * as path40 from "node:path";
|
|
26497
26966
|
var EMBEDDED_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
26498
26967
|
MCowBQYDK2VwAyEAaKBEX+e8+D59qwtidazsu7WYDglApyvsVI3APwFoakA=
|
|
26499
26968
|
-----END PUBLIC KEY-----`;
|
|
@@ -26524,7 +26993,7 @@ function loadLicense(stateDir, devMode) {
|
|
|
26524
26993
|
_cachedLicense = allActive;
|
|
26525
26994
|
return allActive;
|
|
26526
26995
|
}
|
|
26527
|
-
const licensePath =
|
|
26996
|
+
const licensePath = path40.join(stateDir, "license.json");
|
|
26528
26997
|
if (!fs39.existsSync(licensePath)) {
|
|
26529
26998
|
console.log("[license] No license.json found, running basic engine only");
|
|
26530
26999
|
return null;
|
|
@@ -26593,14 +27062,14 @@ var EverosSearchSchema = {
|
|
|
26593
27062
|
type: "object",
|
|
26594
27063
|
properties: {
|
|
26595
27064
|
query: { type: "string", description: "\u641C\u7D22\u67E5\u8BE2" },
|
|
26596
|
-
maxResults: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u6570 (\u9ED8\
|
|
27065
|
+
maxResults: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u6570 (\u9ED8\u8BA410)" },
|
|
27066
|
+
mode: { type: "string", enum: ["hybrid", "hybrid_agentic", "agentic"], description: "\u68C0\u7D22\u6A21\u5F0F: hybrid=\u5FEB(3s\u7CBE\u786E\u5B9E\u4F53), hybrid_agentic=\u6027\u4EF7\u6BD4\u4E4B\u738B(5-14s\u9ED8\u8BA4), agentic=\u6DF1\u5EA6\u63A8\u7406(44s\u590D\u6742\u67E5\u8BE2)" }
|
|
26597
27067
|
},
|
|
26598
27068
|
required: ["query"]
|
|
26599
27069
|
};
|
|
26600
27070
|
function createEverosSearchTool(everosCfg) {
|
|
26601
27071
|
const agenticUrl = (everosCfg?.agenticUrl || "http://127.0.0.1:8101").replace(/\/$/, "");
|
|
26602
27072
|
const userId = everosCfg?.userId || "xiaomei";
|
|
26603
|
-
const defaultMode = everosCfg?.defaultMode || "hybrid_agentic";
|
|
26604
27073
|
return {
|
|
26605
27074
|
name: "memory_search",
|
|
26606
27075
|
description: "Mandatory recall step: semantically search memory before answering questions about prior work, decisions, dates, people, preferences, or todos.",
|
|
@@ -26609,6 +27078,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26609
27078
|
const query = args.query;
|
|
26610
27079
|
if (!query) return { content: "\u7F3A\u5C11 query \u53C2\u6570" };
|
|
26611
27080
|
const topK = args.maxResults ?? 10;
|
|
27081
|
+
const mode = args.mode || "hybrid_agentic";
|
|
26612
27082
|
const ctrl = new AbortController();
|
|
26613
27083
|
const timer = setTimeout(() => ctrl.abort(), 3e4);
|
|
26614
27084
|
try {
|
|
@@ -26618,7 +27088,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26618
27088
|
body: JSON.stringify({
|
|
26619
27089
|
query,
|
|
26620
27090
|
user_id: userId,
|
|
26621
|
-
mode
|
|
27091
|
+
mode,
|
|
26622
27092
|
top_k: topK
|
|
26623
27093
|
}),
|
|
26624
27094
|
signal: ctrl.signal
|
|
@@ -26626,7 +27096,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26626
27096
|
clearTimeout(timer);
|
|
26627
27097
|
if (!resp.ok) {
|
|
26628
27098
|
const text = await resp.text();
|
|
26629
|
-
console.warn(`[
|
|
27099
|
+
console.warn(`[memory_search] failed: ${resp.status} ${text.slice(0, 200)}`);
|
|
26630
27100
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26631
27101
|
}
|
|
26632
27102
|
const data = await resp.json();
|
|
@@ -26635,7 +27105,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26635
27105
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26636
27106
|
}
|
|
26637
27107
|
const formatted = episodes.map((ep, i) => {
|
|
26638
|
-
const score = ep.score != null ? ` (score: ${ep.score.toFixed(3)})` : "";
|
|
27108
|
+
const score = ep.score != null ? ` (score: ${typeof ep.score === "number" ? ep.score.toFixed(3) : ep.score})` : "";
|
|
26639
27109
|
const subject = ep.subject || "";
|
|
26640
27110
|
const ts = ep.timestamp ? ` [${ep.timestamp}]` : "";
|
|
26641
27111
|
return `### ${i + 1}. ${subject}${ts}${score}
|
|
@@ -26647,9 +27117,9 @@ ${formatted}` };
|
|
|
26647
27117
|
} catch (err) {
|
|
26648
27118
|
clearTimeout(timer);
|
|
26649
27119
|
if (err.name === "AbortError") {
|
|
26650
|
-
console.warn(`[
|
|
27120
|
+
console.warn(`[memory_search] timeout: ${query.slice(0, 50)}`);
|
|
26651
27121
|
} else {
|
|
26652
|
-
console.warn(`[
|
|
27122
|
+
console.warn(`[memory_search] error: ${err.message}`);
|
|
26653
27123
|
}
|
|
26654
27124
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26655
27125
|
}
|
|
@@ -27148,9 +27618,9 @@ async function startEngine(config, opts) {
|
|
|
27148
27618
|
process.env.ENGINE_MEDIA_DIR = config.mediaDir;
|
|
27149
27619
|
process.env.ENGINE7_WORKSPACE = config.workspace;
|
|
27150
27620
|
process.env.OPENCLAW_WORKSPACE = config.workspace;
|
|
27151
|
-
fs41.mkdirSync(
|
|
27152
|
-
fs41.mkdirSync(
|
|
27153
|
-
fs41.mkdirSync(
|
|
27621
|
+
fs41.mkdirSync(path43.join(config.stateDir, "agents", "main", "memory"), { recursive: true });
|
|
27622
|
+
fs41.mkdirSync(path43.join(config.stateDir, "agents", "main", "sessions"), { recursive: true });
|
|
27623
|
+
fs41.mkdirSync(path43.join(config.stateDir, "logs"), { recursive: true });
|
|
27154
27624
|
fs41.mkdirSync(config.workspace, { recursive: true });
|
|
27155
27625
|
fs41.mkdirSync(config.mediaDir, { recursive: true });
|
|
27156
27626
|
try {
|
|
@@ -27260,7 +27730,7 @@ async function startEngine(config, opts) {
|
|
|
27260
27730
|
const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
|
|
27261
27731
|
initSessionMemory2({
|
|
27262
27732
|
workspace: config.workspace,
|
|
27263
|
-
stateDir:
|
|
27733
|
+
stateDir: path43.join(config.stateDir, "session-memory"),
|
|
27264
27734
|
provider,
|
|
27265
27735
|
model: config.provider.modelId || config.model || "deepseek-v4-flash",
|
|
27266
27736
|
features: config.profile.features
|
|
@@ -27290,9 +27760,9 @@ async function startEngine(config, opts) {
|
|
|
27290
27760
|
if (config.hooks) {
|
|
27291
27761
|
loadHooksFromConfig({ hooks: config.hooks });
|
|
27292
27762
|
}
|
|
27293
|
-
const hooksPath =
|
|
27763
|
+
const hooksPath = path43.join(config.workspace, ".hooks.json");
|
|
27294
27764
|
loadHooksFromFile(hooksPath);
|
|
27295
|
-
const settingsHooksPath =
|
|
27765
|
+
const settingsHooksPath = path43.join(config.stateDir, "settings.json");
|
|
27296
27766
|
loadHooksFromFile(settingsHooksPath);
|
|
27297
27767
|
console.log(`[hooks] Loaded hooks configuration`);
|
|
27298
27768
|
registerCallbackHook("PreCompact", {
|
|
@@ -27306,15 +27776,15 @@ async function startEngine(config, opts) {
|
|
|
27306
27776
|
const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
27307
27777
|
const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
|
|
27308
27778
|
const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
27309
|
-
const dailyDir =
|
|
27310
|
-
const dailyPath =
|
|
27779
|
+
const dailyDir = path43.join(workspace, "memory", "daily");
|
|
27780
|
+
const dailyPath = path43.join(dailyDir, `${dateStr}.md`);
|
|
27311
27781
|
try {
|
|
27312
27782
|
const fs42 = await import("node:fs");
|
|
27313
27783
|
if (!fs42.existsSync(dailyDir)) {
|
|
27314
27784
|
fs42.mkdirSync(dailyDir, { recursive: true });
|
|
27315
27785
|
}
|
|
27316
|
-
const sessionsDir =
|
|
27317
|
-
const sessionFile =
|
|
27786
|
+
const sessionsDir = path43.join(config.stateDir, "agents", "main", "sessions");
|
|
27787
|
+
const sessionFile = path43.join(sessionsDir, `${sessionId}.jsonl`);
|
|
27318
27788
|
const recentLines = [];
|
|
27319
27789
|
if (fs42.existsSync(sessionFile)) {
|
|
27320
27790
|
const content = fs42.readFileSync(sessionFile, "utf-8");
|
|
@@ -27367,7 +27837,7 @@ ${entry}`);
|
|
|
27367
27837
|
if (!workspace) return { continue: true };
|
|
27368
27838
|
try {
|
|
27369
27839
|
const fs42 = await import("node:fs");
|
|
27370
|
-
const bufferPath =
|
|
27840
|
+
const bufferPath = path43.join(workspace, "memory", "working-buffer.md");
|
|
27371
27841
|
if (fs42.existsSync(bufferPath)) {
|
|
27372
27842
|
const stat4 = fs42.statSync(bufferPath);
|
|
27373
27843
|
const ageMs = Date.now() - stat4.mtimeMs;
|
|
@@ -27420,7 +27890,7 @@ ${content}`
|
|
|
27420
27890
|
return `${hr}h ${remMin}m`;
|
|
27421
27891
|
}
|
|
27422
27892
|
if (config.skills?.enabled !== false) {
|
|
27423
|
-
const skillsDir = config.skills?.path ?
|
|
27893
|
+
const skillsDir = config.skills?.path ? path43.isAbsolute(config.skills.path) ? config.skills.path : path43.resolve(config.workspace, config.skills.path) : path43.resolve(config.workspace, "skills");
|
|
27424
27894
|
const modelDef2 = config.provider.models.find((m2) => m2.id === config.model);
|
|
27425
27895
|
const contextWindowTokens = modelDef2?.contextWindow;
|
|
27426
27896
|
const skills = scanSkills(skillsDir);
|
|
@@ -27439,7 +27909,7 @@ ${content}`
|
|
|
27439
27909
|
workspace: config.workspace
|
|
27440
27910
|
});
|
|
27441
27911
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
27442
|
-
const promptDumpPath =
|
|
27912
|
+
const promptDumpPath = path43.join(config.workspace, ".system-prompt.txt");
|
|
27443
27913
|
fs41.writeFileSync(promptDumpPath, systemPrompt);
|
|
27444
27914
|
console.log(`System prompt: ${systemStable.length} chars stable + ${systemDynamic.length} chars dynamic \u2192 ${promptDumpPath}`);
|
|
27445
27915
|
const modelDef = config.provider.models.find((m2) => m2.id === config.model);
|
|
@@ -27550,20 +28020,23 @@ ${content}`
|
|
|
27550
28020
|
enabled: true,
|
|
27551
28021
|
url: everosCfg.everosUrl || "http://127.0.0.1:8100",
|
|
27552
28022
|
appId: everosCfg.userId || "default",
|
|
27553
|
-
userId: everosCfg.userId || "default"
|
|
28023
|
+
userId: everosCfg.userId || "default",
|
|
28024
|
+
agentName: everosCfg.agentName || everosCfg.userId || "assistant"
|
|
27554
28025
|
});
|
|
27555
28026
|
sessions.onWriterCreated = (writer, sessionId) => {
|
|
27556
28027
|
writer.onMessageWritten = (msg2) => {
|
|
28028
|
+
console.log(`[everos-sync] onMessageWritten fired: role=${msg2.role} len=${msg2.text.length}`);
|
|
27557
28029
|
everosSync.push({
|
|
27558
28030
|
sessionId: writer.engineSessionId || sessionId,
|
|
27559
28031
|
role: msg2.role === "toolResult" ? "tool" : msg2.role,
|
|
27560
28032
|
text: msg2.text,
|
|
27561
28033
|
timestamp: new Date(msg2.timestamp).getTime()
|
|
27562
|
-
}).catch(() => {
|
|
27563
|
-
});
|
|
28034
|
+
}).catch((e) => console.warn(`[everos-sync] push error: ${e}`));
|
|
27564
28035
|
};
|
|
27565
28036
|
};
|
|
27566
28037
|
console.log(`[everos-sync] hook registered (appId=${everosCfg.userId})`);
|
|
28038
|
+
} else {
|
|
28039
|
+
console.log(`[everos-sync] SKIPPED \u2014 config.everos not enabled or missing`);
|
|
27567
28040
|
}
|
|
27568
28041
|
const channelManager = new ChannelManager();
|
|
27569
28042
|
const memoryRecallProvider = createMemorySideProvider(
|
|
@@ -27594,6 +28067,7 @@ ${content}`
|
|
|
27594
28067
|
recallProvider: memoryRecallProvider || void 0,
|
|
27595
28068
|
extractProvider: memoryExtractProvider || void 0,
|
|
27596
28069
|
topics: config.topics,
|
|
28070
|
+
everosCfg: config.everos,
|
|
27597
28071
|
mcpManager
|
|
27598
28072
|
};
|
|
27599
28073
|
if (visionEngine && visionConfig) {
|
|
@@ -28514,8 +28988,8 @@ Auto-routing disabled \u2014 all messages use this model.
|
|
|
28514
28988
|
let writePath = configPath;
|
|
28515
28989
|
if (configPath && !fs41.existsSync(configPath)) {
|
|
28516
28990
|
const __pFile = fileURLToPath(import.meta.url);
|
|
28517
|
-
const __pDir =
|
|
28518
|
-
const altPath =
|
|
28991
|
+
const __pDir = path43.dirname(__pFile);
|
|
28992
|
+
const altPath = path43.join(path43.resolve(__pDir, "../configs"), path43.basename(configPath));
|
|
28519
28993
|
if (fs41.existsSync(altPath)) {
|
|
28520
28994
|
console.warn(`[primary] Config not found at ${configPath}, falling back to ${altPath}`);
|
|
28521
28995
|
writePath = altPath;
|
|
@@ -28795,7 +29269,7 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
28795
29269
|
const ext = detected.split("/")[1] || "png";
|
|
28796
29270
|
const resized = await maybeResizeAndDownsampleImageBuffer2(rawBuffer, rawBuffer.length, ext);
|
|
28797
29271
|
const imageId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
28798
|
-
const savedPath =
|
|
29272
|
+
const savedPath = path43.join(config.mediaDir, `${imageId}.${ext}`);
|
|
28799
29273
|
fs41.writeFileSync(savedPath, resized.buffer);
|
|
28800
29274
|
savedPaths.push(savedPath);
|
|
28801
29275
|
console.log(`[vision] Saved: ${savedPath} (${resized.buffer.length}B)`);
|
|
@@ -28822,7 +29296,7 @@ ${pathStr}` }];
|
|
|
28822
29296
|
}
|
|
28823
29297
|
const nonImageAttachments = inbound.attachments?.filter((a) => !a.contentType.startsWith("image/"));
|
|
28824
29298
|
if (nonImageAttachments && nonImageAttachments.length > 0) {
|
|
28825
|
-
const outDir =
|
|
29299
|
+
const outDir = path43.join(config.mediaDir, sessionId);
|
|
28826
29300
|
fs41.mkdirSync(outDir, { recursive: true });
|
|
28827
29301
|
const resolved = [];
|
|
28828
29302
|
for (const att of nonImageAttachments) {
|
|
@@ -28831,8 +29305,8 @@ ${pathStr}` }];
|
|
|
28831
29305
|
const resp = await fetch(att.url);
|
|
28832
29306
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
28833
29307
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
28834
|
-
const safeName2 =
|
|
28835
|
-
const savedPath =
|
|
29308
|
+
const safeName2 = path43.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
29309
|
+
const savedPath = path43.join(outDir, safeName2);
|
|
28836
29310
|
fs41.writeFileSync(savedPath, buffer);
|
|
28837
29311
|
resolved.push(savedPath);
|
|
28838
29312
|
console.log(`[file] Saved: ${savedPath} (${buffer.length}B)`);
|
|
@@ -29180,7 +29654,8 @@ ${pathStr}` }];
|
|
|
29180
29654
|
if (config.cognifold?.intentWatcher?.enabled) {
|
|
29181
29655
|
try {
|
|
29182
29656
|
const { registerCognifoldIntentWatcher: registerCognifoldIntentWatcher2 } = await Promise.resolve().then(() => (init_cognifold_intent_watcher(), cognifold_intent_watcher_exports));
|
|
29183
|
-
const
|
|
29657
|
+
const sm = globalThis.__cognifoldSessions;
|
|
29658
|
+
const cfSessionId = sm?.getSessionId?.("main") || config.cognifold.sessionId;
|
|
29184
29659
|
if (!cfSessionId) {
|
|
29185
29660
|
console.warn("[cognifold] intent-watcher: config.cognifold.sessionId \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 watcher");
|
|
29186
29661
|
} else {
|
|
@@ -29193,7 +29668,7 @@ ${pathStr}` }];
|
|
|
29193
29668
|
console.warn("[cognifold] watcher: config.workspace \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 proactive \u5199\u5165");
|
|
29194
29669
|
return;
|
|
29195
29670
|
}
|
|
29196
|
-
const pFile =
|
|
29671
|
+
const pFile = path43.join(wsDir, ".cognifold-proactive.json");
|
|
29197
29672
|
const cognifoldBaseUrl = config.cognifold?.baseUrl || "http://127.0.0.1:9001";
|
|
29198
29673
|
const cognifoldSessionId = cfSessionId;
|
|
29199
29674
|
const rawSuggestions = data.suggestions || data.actions || (data.intent_id ? [data] : []);
|
|
@@ -29247,7 +29722,7 @@ ${pathStr}` }];
|
|
|
29247
29722
|
console.error(`[cognifold] failed to save proactive: ${e.message}`);
|
|
29248
29723
|
}
|
|
29249
29724
|
if (enriched.length > 0) {
|
|
29250
|
-
const promptFile =
|
|
29725
|
+
const promptFile = path43.join(config.workspace, "prompts", "cognifold-proactive.md");
|
|
29251
29726
|
const promptText = fs41.existsSync(promptFile) ? fs41.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
|
|
29252
29727
|
const actionsJson = JSON.stringify(enriched, null, 2);
|
|
29253
29728
|
const sessionId = cfSessionId;
|
|
@@ -29353,9 +29828,9 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
29353
29828
|
let reloadConfigPath = savedConfigPath;
|
|
29354
29829
|
if (!fs41.existsSync(reloadConfigPath)) {
|
|
29355
29830
|
const __filename = fileURLToPath(import.meta.url);
|
|
29356
|
-
const __dirname =
|
|
29357
|
-
const engineConfigsDir =
|
|
29358
|
-
const altPath =
|
|
29831
|
+
const __dirname = path43.dirname(__filename);
|
|
29832
|
+
const engineConfigsDir = path43.resolve(__dirname, "../configs");
|
|
29833
|
+
const altPath = path43.join(engineConfigsDir, path43.basename(savedConfigPath));
|
|
29359
29834
|
if (fs41.existsSync(altPath)) {
|
|
29360
29835
|
console.warn(`[reload] Config not found at ${reloadConfigPath}, falling back to ${altPath} (dev mode)`);
|
|
29361
29836
|
reloadConfigPath = altPath;
|
|
@@ -29408,7 +29883,7 @@ async function doReloadConfig(config, deps, provider) {
|
|
|
29408
29883
|
} catch (err) {
|
|
29409
29884
|
console.error(`[reload] Failed: ${err.message}`);
|
|
29410
29885
|
try {
|
|
29411
|
-
fs41.appendFileSync(
|
|
29886
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
|
|
29412
29887
|
${err.stack}
|
|
29413
29888
|
`);
|
|
29414
29889
|
} catch {
|
|
@@ -29420,17 +29895,17 @@ function startConfigWatcher(config, deps, provider) {
|
|
|
29420
29895
|
const raw = config._configFilePath;
|
|
29421
29896
|
let configPath = raw;
|
|
29422
29897
|
if (!fs41.existsSync(configPath)) {
|
|
29423
|
-
configPath =
|
|
29898
|
+
configPath = path43.resolve(raw);
|
|
29424
29899
|
}
|
|
29425
29900
|
if (!fs41.existsSync(configPath)) {
|
|
29426
29901
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
29427
|
-
const __dirname2 =
|
|
29428
|
-
configPath =
|
|
29902
|
+
const __dirname2 = path43.dirname(__filename2);
|
|
29903
|
+
configPath = path43.resolve(__dirname2, "..", raw);
|
|
29429
29904
|
}
|
|
29430
29905
|
if (!fs41.existsSync(configPath)) {
|
|
29431
29906
|
console.warn(`[config-watch] config path invalid: ${configPath}, watcher disabled`);
|
|
29432
29907
|
try {
|
|
29433
|
-
fs41.appendFileSync(
|
|
29908
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath}
|
|
29434
29909
|
`);
|
|
29435
29910
|
} catch {
|
|
29436
29911
|
}
|
|
@@ -29442,13 +29917,13 @@ function startConfigWatcher(config, deps, provider) {
|
|
|
29442
29917
|
debounceTimer = setTimeout(async () => {
|
|
29443
29918
|
console.log(`[config-watch] file changed (${eventType}), reloading...`);
|
|
29444
29919
|
try {
|
|
29445
|
-
fs41.appendFileSync(
|
|
29920
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
|
|
29446
29921
|
`);
|
|
29447
29922
|
} catch {
|
|
29448
29923
|
}
|
|
29449
29924
|
const result = await doReloadConfig(config, deps, provider);
|
|
29450
29925
|
try {
|
|
29451
|
-
fs41.appendFileSync(
|
|
29926
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
|
|
29452
29927
|
`);
|
|
29453
29928
|
} catch {
|
|
29454
29929
|
}
|
|
@@ -29457,14 +29932,14 @@ function startConfigWatcher(config, deps, provider) {
|
|
|
29457
29932
|
watcher.on("error", (err) => {
|
|
29458
29933
|
console.error(`[config-watch] error: ${err.message}`);
|
|
29459
29934
|
try {
|
|
29460
|
-
fs41.appendFileSync(
|
|
29935
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
|
|
29461
29936
|
`);
|
|
29462
29937
|
} catch {
|
|
29463
29938
|
}
|
|
29464
29939
|
});
|
|
29465
29940
|
console.log(`[config-watch] watching ${configPath}`);
|
|
29466
29941
|
try {
|
|
29467
|
-
fs41.appendFileSync(
|
|
29942
|
+
fs41.appendFileSync(path43.join(config.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath}
|
|
29468
29943
|
`);
|
|
29469
29944
|
} catch {
|
|
29470
29945
|
}
|