engine7 7.1.22 → 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 +785 -325
- package/dist/main.mjs +786 -326
- 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/main.mjs
CHANGED
|
@@ -985,8 +985,10 @@ async function executeStopHooks(ctx, signal, lastAssistantMessage) {
|
|
|
985
985
|
cwd: ctx.cwd,
|
|
986
986
|
stop_hook_active: false,
|
|
987
987
|
last_assistant_message: lastAssistantMessage,
|
|
988
|
-
channel: ctx.channel
|
|
988
|
+
channel: ctx.channel,
|
|
989
989
|
// 让 callback hook 能判断来源
|
|
990
|
+
source: ctx.source || ""
|
|
991
|
+
// 消息来源(heartbeat/cron/system 等注入 turn 可据此跳过)
|
|
990
992
|
};
|
|
991
993
|
return executeHooks("Stop", hookInput, ctx, signal);
|
|
992
994
|
}
|
|
@@ -1338,6 +1340,7 @@ function collectSurfacedMemories(messages) {
|
|
|
1338
1340
|
return { paths };
|
|
1339
1341
|
}
|
|
1340
1342
|
function normalizePath(p2) {
|
|
1343
|
+
if (p2.startsWith("everos://")) return p2;
|
|
1341
1344
|
return pathResolve(p2);
|
|
1342
1345
|
}
|
|
1343
1346
|
function memoryHeader(filePath, mtimeMs) {
|
|
@@ -2784,10 +2787,12 @@ function breakdownMessages(messages) {
|
|
|
2784
2787
|
if (memCount === 0) console.log(`[context-analyzer] WARNING: relevant_memories attachment with 0 memories! keys=${Object.keys(m2.attachment).join(",")}`);
|
|
2785
2788
|
for (const mem of m2.attachment.memories || []) {
|
|
2786
2789
|
const fileName = mem.path.split(/[/\\]/).pop() || mem.path;
|
|
2790
|
+
const scoreMatch = mem.header?.match(/score=([\d.]+)/);
|
|
2787
2791
|
recalledTopics.push({
|
|
2788
2792
|
path: fileName,
|
|
2789
2793
|
tokens: roughTokenCountEstimation(mem.content),
|
|
2790
|
-
stable: false
|
|
2794
|
+
stable: false,
|
|
2795
|
+
score: scoreMatch ? scoreMatch[1] : void 0
|
|
2791
2796
|
});
|
|
2792
2797
|
}
|
|
2793
2798
|
}
|
|
@@ -2998,13 +3003,25 @@ function formatContextReport(report) {
|
|
|
2998
3003
|
output += `### Recalled Memories (${rt.length})
|
|
2999
3004
|
|
|
3000
3005
|
`;
|
|
3001
|
-
|
|
3006
|
+
const hasScore = rt.some((t) => t.score);
|
|
3007
|
+
if (hasScore) {
|
|
3008
|
+
output += `| File | Score | Tokens |
|
|
3002
3009
|
`;
|
|
3003
|
-
|
|
3010
|
+
output += `|------|-------|--------|
|
|
3011
|
+
`;
|
|
3012
|
+
for (const t of rt) {
|
|
3013
|
+
output += `| ${t.path} | ${t.score ?? "-"} | ${formatTokens(t.tokens)} |
|
|
3014
|
+
`;
|
|
3015
|
+
}
|
|
3016
|
+
} else {
|
|
3017
|
+
output += `| File | Tokens |
|
|
3018
|
+
`;
|
|
3019
|
+
output += `|------|--------|
|
|
3004
3020
|
`;
|
|
3005
|
-
|
|
3006
|
-
|
|
3021
|
+
for (const t of rt) {
|
|
3022
|
+
output += `| ${t.path} | ${formatTokens(t.tokens)} |
|
|
3007
3023
|
`;
|
|
3024
|
+
}
|
|
3008
3025
|
}
|
|
3009
3026
|
output += "\n";
|
|
3010
3027
|
}
|
|
@@ -3342,7 +3359,8 @@ ${perTurnSystemDynamic}` : deferredHint || perTurnSystemDynamic;
|
|
|
3342
3359
|
sessionId: context?.sessionId || "default",
|
|
3343
3360
|
workspace: context?.workspace || "",
|
|
3344
3361
|
channel: context?.channel || "",
|
|
3345
|
-
cwd: context?.workspace || ""
|
|
3362
|
+
cwd: context?.workspace || "",
|
|
3363
|
+
source: typeof context?.source === "string" ? context.source : ""
|
|
3346
3364
|
};
|
|
3347
3365
|
const stopResult = await executeStopHooks(stopCtx, ac.signal, textContent);
|
|
3348
3366
|
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(args2, searchPath, signal) {
|
|
7220
7238
|
return new Promise((resolve10) => {
|
|
7221
7239
|
const fullArgs = [...args2, 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 (args2) => {
|
|
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 (args2.task_id) {
|
|
11226
|
-
const file =
|
|
11244
|
+
const file = path44.join(resultsDir, `${args2.task_id}.json`);
|
|
11227
11245
|
if (!fs42.existsSync(file)) {
|
|
11228
11246
|
return { content: `\u4EFB\u52A1 ${args2.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 ? "..." : ""}`;
|
|
@@ -11622,7 +11640,7 @@ __export(license_exports, {
|
|
|
11622
11640
|
});
|
|
11623
11641
|
import * as crypto6 from "node:crypto";
|
|
11624
11642
|
import * as fs39 from "node:fs";
|
|
11625
|
-
import * as
|
|
11643
|
+
import * as path40 from "node:path";
|
|
11626
11644
|
function loadLicense(stateDir, devMode) {
|
|
11627
11645
|
if (_licenseChecked) return _cachedLicense;
|
|
11628
11646
|
_licenseChecked = true;
|
|
@@ -11635,7 +11653,7 @@ function loadLicense(stateDir, devMode) {
|
|
|
11635
11653
|
_cachedLicense = allActive;
|
|
11636
11654
|
return allActive;
|
|
11637
11655
|
}
|
|
11638
|
-
const licensePath =
|
|
11656
|
+
const licensePath = path40.join(stateDir, "license.json");
|
|
11639
11657
|
if (!fs39.existsSync(licensePath)) {
|
|
11640
11658
|
console.log("[license] No license.json found, running basic engine only");
|
|
11641
11659
|
return null;
|
|
@@ -11691,7 +11709,7 @@ function isFeatureLicensed(featureId) {
|
|
|
11691
11709
|
return f2?.active === true;
|
|
11692
11710
|
}
|
|
11693
11711
|
function getLicenseStatus(stateDir) {
|
|
11694
|
-
const licensePath =
|
|
11712
|
+
const licensePath = path40.join(stateDir, "license.json");
|
|
11695
11713
|
if (!fs39.existsSync(licensePath)) {
|
|
11696
11714
|
return { licensed: false, features: {} };
|
|
11697
11715
|
}
|
|
@@ -11756,7 +11774,7 @@ __export(manager_exports, {
|
|
|
11756
11774
|
McpManager: () => McpManager
|
|
11757
11775
|
});
|
|
11758
11776
|
import * as fs40 from "node:fs";
|
|
11759
|
-
import * as
|
|
11777
|
+
import * as path41 from "node:path";
|
|
11760
11778
|
import { Client as Client3 } from "@modelcontextprotocol/sdk/client/index.js";
|
|
11761
11779
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
11762
11780
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
@@ -11789,9 +11807,9 @@ function convertInputSchema(inputSchema) {
|
|
|
11789
11807
|
}
|
|
11790
11808
|
function persistBinary(base64Data, mimeType, persistId) {
|
|
11791
11809
|
const ext = mimeType?.split("/")[1] || "bin";
|
|
11792
|
-
const dir =
|
|
11810
|
+
const dir = path41.join(process.env.ENGINE_STATE_DIR || ".engine", "mcp-blobs");
|
|
11793
11811
|
fs40.mkdirSync(dir, { recursive: true });
|
|
11794
|
-
const filepath =
|
|
11812
|
+
const filepath = path41.join(dir, `${persistId}.${ext}`);
|
|
11795
11813
|
try {
|
|
11796
11814
|
const buf = Buffer.from(base64Data, "base64");
|
|
11797
11815
|
fs40.writeFileSync(filepath, buf);
|
|
@@ -12128,7 +12146,7 @@ __export(resources_exports, {
|
|
|
12128
12146
|
registerMcpResourceTools: () => registerMcpResourceTools,
|
|
12129
12147
|
unregisterMcpResourceTools: () => unregisterMcpResourceTools
|
|
12130
12148
|
});
|
|
12131
|
-
import * as
|
|
12149
|
+
import * as path42 from "node:path";
|
|
12132
12150
|
function registerMcpResourceTools(manager) {
|
|
12133
12151
|
mcpManagerRef = manager;
|
|
12134
12152
|
registry.register(listResourcesTool);
|
|
@@ -12146,7 +12164,7 @@ var init_resources = __esm({
|
|
|
12146
12164
|
"use strict";
|
|
12147
12165
|
init_registry();
|
|
12148
12166
|
MAX_RESULT_CHARS2 = 1e5;
|
|
12149
|
-
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR ||
|
|
12167
|
+
MEDIA_DIR = process.env.ENGINE_MEDIA_DIR || path42.join(process.env.ENGINE_STATE_DIR || ".engine", "media", "inbound");
|
|
12150
12168
|
MCP_LIST_RESOURCES_TOOL = "mcp__list_resources";
|
|
12151
12169
|
MCP_READ_RESOURCE_TOOL = "mcp__read_resource";
|
|
12152
12170
|
mcpManagerRef = null;
|
|
@@ -12236,11 +12254,21 @@ var everos_sync_exports = {};
|
|
|
12236
12254
|
__export(everos_sync_exports, {
|
|
12237
12255
|
createEverosSync: () => createEverosSync
|
|
12238
12256
|
});
|
|
12257
|
+
function parseMeta(text) {
|
|
12258
|
+
const m2 = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
|
|
12259
|
+
if (!m2) return null;
|
|
12260
|
+
return { senderName: m2[1].trim(), senderId: m2[2].trim(), platform: m2[3].trim() };
|
|
12261
|
+
}
|
|
12239
12262
|
function createEverosSync(cfg) {
|
|
12240
|
-
const { enabled, url, appId, userId } = cfg;
|
|
12263
|
+
const { enabled, url, appId, userId, agentName } = cfg;
|
|
12241
12264
|
async function push(event) {
|
|
12242
12265
|
if (!enabled) return;
|
|
12243
12266
|
if (!event.text.trim()) return;
|
|
12267
|
+
return;
|
|
12268
|
+
const meta = event.role === "user" ? parseMeta(event.text) : null;
|
|
12269
|
+
const senderId = appId;
|
|
12270
|
+
const senderName = meta?.senderName ?? (event.role === "assistant" ? agentName : void 0) ?? event.senderName ?? event.role;
|
|
12271
|
+
console.log(`[everos-sync] role=${event.role} sender_id=${senderId} sender_name=${senderName} metaParsed=${!!meta} textLen=${event.text.length}`);
|
|
12244
12272
|
try {
|
|
12245
12273
|
const resp = await fetch(`${url}/api/v1/memory/add`, {
|
|
12246
12274
|
method: "POST",
|
|
@@ -12250,8 +12278,8 @@ function createEverosSync(cfg) {
|
|
|
12250
12278
|
app_id: appId,
|
|
12251
12279
|
project_id: "default",
|
|
12252
12280
|
messages: [{
|
|
12253
|
-
sender_id:
|
|
12254
|
-
sender_name:
|
|
12281
|
+
sender_id: senderId,
|
|
12282
|
+
sender_name: senderName,
|
|
12255
12283
|
role: event.role,
|
|
12256
12284
|
timestamp: event.timestamp,
|
|
12257
12285
|
content: event.text
|
|
@@ -12275,13 +12303,17 @@ function createEverosSync(cfg) {
|
|
|
12275
12303
|
session_id: events[0].sessionId,
|
|
12276
12304
|
app_id: appId,
|
|
12277
12305
|
project_id: "default",
|
|
12278
|
-
messages: events.map((e) =>
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
|
|
12283
|
-
|
|
12284
|
-
|
|
12306
|
+
messages: events.map((e) => {
|
|
12307
|
+
const meta = e.role === "user" ? parseMeta(e.text) : null;
|
|
12308
|
+
const name = meta?.senderName ?? (e.role === "assistant" ? agentName : void 0) ?? e.senderName ?? e.role;
|
|
12309
|
+
return {
|
|
12310
|
+
sender_id: appId,
|
|
12311
|
+
sender_name: name,
|
|
12312
|
+
role: e.role,
|
|
12313
|
+
timestamp: e.timestamp,
|
|
12314
|
+
content: e.text
|
|
12315
|
+
};
|
|
12316
|
+
})
|
|
12285
12317
|
}),
|
|
12286
12318
|
signal: AbortSignal.timeout(3e4)
|
|
12287
12319
|
});
|
|
@@ -12383,10 +12415,10 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12383
12415
|
if (!state.blockedUserIds.includes(id)) state.blockedUserIds.push(id);
|
|
12384
12416
|
}
|
|
12385
12417
|
}
|
|
12386
|
-
const
|
|
12418
|
+
const path44 = join36(workspace, ".reply-blocklist.json");
|
|
12387
12419
|
try {
|
|
12388
|
-
if (existsSync24(
|
|
12389
|
-
const raw = readFileSync26(
|
|
12420
|
+
if (existsSync24(path44)) {
|
|
12421
|
+
const raw = readFileSync26(path44, "utf-8");
|
|
12390
12422
|
const parsed = JSON.parse(raw);
|
|
12391
12423
|
if (parsed.blockedUserIds) {
|
|
12392
12424
|
for (const id of parsed.blockedUserIds) {
|
|
@@ -12402,9 +12434,9 @@ function ensureLoaded(workspace, configIds) {
|
|
|
12402
12434
|
loaded = true;
|
|
12403
12435
|
}
|
|
12404
12436
|
function save(workspace) {
|
|
12405
|
-
const
|
|
12437
|
+
const path44 = join36(workspace, ".reply-blocklist.json");
|
|
12406
12438
|
try {
|
|
12407
|
-
writeFileSync15(
|
|
12439
|
+
writeFileSync15(path44, JSON.stringify(state, null, 2), "utf-8");
|
|
12408
12440
|
} catch (err) {
|
|
12409
12441
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
12410
12442
|
}
|
|
@@ -13004,7 +13036,7 @@ var init_cognifold_intent_watcher = __esm({
|
|
|
13004
13036
|
init_loader();
|
|
13005
13037
|
|
|
13006
13038
|
// src/engine-startup.ts
|
|
13007
|
-
import * as
|
|
13039
|
+
import * as path43 from "node:path";
|
|
13008
13040
|
import * as fs41 from "node:fs";
|
|
13009
13041
|
import { fileURLToPath } from "node:url";
|
|
13010
13042
|
|
|
@@ -14348,11 +14380,11 @@ var DiscordAdapter = class _DiscordAdapter {
|
|
|
14348
14380
|
/** 发送媒体附件(图片/文件/音频)— discord.js channel.send({ files }) */
|
|
14349
14381
|
async sendFile(target, message, attachment) {
|
|
14350
14382
|
const fs42 = await import("node:fs");
|
|
14351
|
-
const
|
|
14383
|
+
const path44 = await import("node:path");
|
|
14352
14384
|
if (!fs42.existsSync(attachment.path)) {
|
|
14353
14385
|
throw new Error(`File not found: ${attachment.path}`);
|
|
14354
14386
|
}
|
|
14355
|
-
const filename = attachment.filename ||
|
|
14387
|
+
const filename = attachment.filename || path44.basename(attachment.path);
|
|
14356
14388
|
const fileBuffer = fs42.readFileSync(attachment.path);
|
|
14357
14389
|
const filePayload = {
|
|
14358
14390
|
attachment: fileBuffer,
|
|
@@ -14794,11 +14826,11 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
14794
14826
|
/** 发送媒体附件(图片/文件) */
|
|
14795
14827
|
async sendFile(target, message, attachment) {
|
|
14796
14828
|
const fs42 = await import("node:fs");
|
|
14797
|
-
const
|
|
14829
|
+
const path44 = await import("node:path");
|
|
14798
14830
|
if (!fs42.existsSync(attachment.path)) {
|
|
14799
14831
|
throw new Error(`File not found: ${attachment.path}`);
|
|
14800
14832
|
}
|
|
14801
|
-
const filename = attachment.filename ||
|
|
14833
|
+
const filename = attachment.filename || path44.basename(attachment.path);
|
|
14802
14834
|
const fileBuffer = fs42.readFileSync(attachment.path);
|
|
14803
14835
|
const receiveIdType = target.startsWith("ou_") ? "open_id" : "chat_id";
|
|
14804
14836
|
const mimeType = attachment.mimeType || "application/octet-stream";
|
|
@@ -18410,22 +18442,8 @@ ${skillsListing}`);
|
|
|
18410
18442
|
parts.push(getEnvInfoSection(options.workspace));
|
|
18411
18443
|
const now = /* @__PURE__ */ new Date();
|
|
18412
18444
|
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
18413
|
-
const osPlatform = process.platform === "win32" ? "Windows" : process.platform === "darwin" ? "macOS" : process.platform;
|
|
18414
|
-
const contextParts = [`\u5F53\u524D\u65F6\u95F4: ${dateStr}`, `\u7CFB\u7EDF: ${osPlatform} (${process.platform})`];
|
|
18415
|
-
const meta = options.inboundMeta;
|
|
18416
|
-
if (meta) {
|
|
18417
|
-
contextParts.push(`\u6765\u6E90: ${meta.channel}`);
|
|
18418
|
-
if (meta.channelType) contextParts.push(`\u6D88\u606F\u7C7B\u578B: ${meta.channelType === "dm" ? "\u79C1\u4FE1" : "\u7FA4\u804A"}`);
|
|
18419
|
-
if (meta.channel_id) contextParts.push(`\u9891\u9053ID: ${meta.channel_id}`);
|
|
18420
|
-
contextParts.push(`\u53D1\u9001\u8005ID: ${meta.from}`);
|
|
18421
|
-
if (meta.fromName) contextParts.push(`\u53D1\u9001\u8005\u540D\u79F0: ${meta.fromName}`);
|
|
18422
|
-
if (meta.messageId) contextParts.push(`\u6D88\u606FID: ${meta.messageId}`);
|
|
18423
|
-
} else if (options.channel) {
|
|
18424
|
-
contextParts.push(`\u6765\u6E90: ${options.channel}`);
|
|
18425
|
-
}
|
|
18426
|
-
if (options.sessionId) contextParts.push(`\u4F1A\u8BDDID: ${options.sessionId}`);
|
|
18427
18445
|
parts.push(`# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
18428
|
-
${
|
|
18446
|
+
\u5F53\u524D\u65F6\u95F4: ${dateStr}`);
|
|
18429
18447
|
console.log(`[dynamic-prompt] Loaded: ${loaded2.length > 0 ? loaded2.join(", ") : "(none)"}`);
|
|
18430
18448
|
return parts.join("\n\n");
|
|
18431
18449
|
}
|
|
@@ -18727,6 +18745,169 @@ async function findRelevantMemoriesVector(query, memoryDir, alreadySurfaced = /*
|
|
|
18727
18745
|
}));
|
|
18728
18746
|
}
|
|
18729
18747
|
|
|
18748
|
+
// src/memory/memdir/findRelevantMemoriesEveros.ts
|
|
18749
|
+
var ROUND1_TOP_N = 30;
|
|
18750
|
+
var RERANK_BATCH_SIZE = 100;
|
|
18751
|
+
function formatEverosHeader(ep) {
|
|
18752
|
+
const score = ep.score.toFixed(3);
|
|
18753
|
+
const ts = ep.timestamp?.slice(0, 10) ?? "";
|
|
18754
|
+
let ageLabel = "";
|
|
18755
|
+
if (ts) {
|
|
18756
|
+
const days = Math.floor((Date.now() - new Date(ts).getTime()) / 864e5);
|
|
18757
|
+
if (days <= 1) ageLabel = "\u4ECA\u5929";
|
|
18758
|
+
else if (days <= 3) ageLabel = `${days}\u5929\u524D`;
|
|
18759
|
+
else if (days <= 14) ageLabel = `${days}\u5929\u524D`;
|
|
18760
|
+
else if (days <= 30) ageLabel = `~${Math.ceil(days / 7)}\u5468\u524D`;
|
|
18761
|
+
else ageLabel = `~${Math.ceil(days / 30)}\u4E2A\u6708\u524D`;
|
|
18762
|
+
}
|
|
18763
|
+
return `[EverOS score=${score} ${ts} (${ageLabel})]`;
|
|
18764
|
+
}
|
|
18765
|
+
async function hybridSearch(query, everosUrl, userId, topK) {
|
|
18766
|
+
const resp = await fetch(`${everosUrl}/api/v1/memory/search`, {
|
|
18767
|
+
method: "POST",
|
|
18768
|
+
headers: { "Content-Type": "application/json" },
|
|
18769
|
+
body: JSON.stringify({
|
|
18770
|
+
query: query.slice(0, 2e3),
|
|
18771
|
+
user_id: userId,
|
|
18772
|
+
app_id: userId,
|
|
18773
|
+
project_id: "default",
|
|
18774
|
+
top_k: topK,
|
|
18775
|
+
method: "hybrid"
|
|
18776
|
+
}),
|
|
18777
|
+
signal: AbortSignal.timeout(15e3)
|
|
18778
|
+
});
|
|
18779
|
+
if (!resp.ok) {
|
|
18780
|
+
console.warn(`[memdir] everos hybrid: search failed ${resp.status}`);
|
|
18781
|
+
return [];
|
|
18782
|
+
}
|
|
18783
|
+
const body = await resp.json();
|
|
18784
|
+
const episodes = body.data?.episodes ?? [];
|
|
18785
|
+
return episodes;
|
|
18786
|
+
}
|
|
18787
|
+
async function deepinfraRerank(query, episodes, rerankUrl, rerankApiKey, rerankModel, provider) {
|
|
18788
|
+
if (episodes.length === 0) return [];
|
|
18789
|
+
const documents = episodes.map(
|
|
18790
|
+
(ep) => ep.episode?.slice(0, 500) || ep.summary?.slice(0, 500) || ep.subject
|
|
18791
|
+
);
|
|
18792
|
+
const allScores = [];
|
|
18793
|
+
const isDashscope = provider === "dashscope";
|
|
18794
|
+
for (let i = 0; i < documents.length; i += RERANK_BATCH_SIZE) {
|
|
18795
|
+
const batch = documents.slice(i, i + RERANK_BATCH_SIZE);
|
|
18796
|
+
const body = isDashscope ? JSON.stringify({
|
|
18797
|
+
model: rerankModel || "qwen3-rerank",
|
|
18798
|
+
input: { query, documents: batch },
|
|
18799
|
+
parameters: { return_documents: false, top_n: batch.length }
|
|
18800
|
+
}) : JSON.stringify({ queries: [query], documents: batch });
|
|
18801
|
+
const makeRequest = () => fetch(rerankUrl, {
|
|
18802
|
+
method: "POST",
|
|
18803
|
+
headers: {
|
|
18804
|
+
"Authorization": `Bearer ${rerankApiKey}`,
|
|
18805
|
+
"Content-Type": "application/json"
|
|
18806
|
+
},
|
|
18807
|
+
body,
|
|
18808
|
+
signal: AbortSignal.timeout(3e4)
|
|
18809
|
+
});
|
|
18810
|
+
let resp = await makeRequest();
|
|
18811
|
+
if (resp.status === 429) {
|
|
18812
|
+
await new Promise((r) => setTimeout(r, 2e3));
|
|
18813
|
+
resp = await makeRequest();
|
|
18814
|
+
}
|
|
18815
|
+
if (!resp.ok) {
|
|
18816
|
+
console.warn(`[memdir] everos rerank: failed ${resp.status}`);
|
|
18817
|
+
return episodes;
|
|
18818
|
+
}
|
|
18819
|
+
if (isDashscope) {
|
|
18820
|
+
const data = await resp.json();
|
|
18821
|
+
const results = data.output?.results ?? [];
|
|
18822
|
+
const scoreMap = new Array(batch.length).fill(0);
|
|
18823
|
+
for (const r of results) {
|
|
18824
|
+
scoreMap[r.index] = r.relevance_score;
|
|
18825
|
+
}
|
|
18826
|
+
allScores.push(...scoreMap);
|
|
18827
|
+
} else {
|
|
18828
|
+
const data = await resp.json();
|
|
18829
|
+
let batchScores = data.scores ?? [];
|
|
18830
|
+
if (Array.isArray(batchScores) && batchScores.length > 0 && Array.isArray(batchScores[0])) {
|
|
18831
|
+
batchScores = batchScores[0];
|
|
18832
|
+
}
|
|
18833
|
+
allScores.push(...batchScores);
|
|
18834
|
+
}
|
|
18835
|
+
}
|
|
18836
|
+
const ranked = episodes.map((ep, idx) => ({ ep, score: allScores[idx] ?? 0 })).sort((a, b2) => b2.score - a.score);
|
|
18837
|
+
for (const { ep, score } of ranked) {
|
|
18838
|
+
ep.score = score;
|
|
18839
|
+
}
|
|
18840
|
+
return ranked.map((r) => r.ep);
|
|
18841
|
+
}
|
|
18842
|
+
var DEFAULT_MIN_SCORE2 = 0.5;
|
|
18843
|
+
async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /* @__PURE__ */ new Set(), options) {
|
|
18844
|
+
const everosUrl = options?.everosUrl ?? "http://127.0.0.1:8100";
|
|
18845
|
+
const userId = options?.userId ?? "xiaomei";
|
|
18846
|
+
const topK = options?.topK ?? 3;
|
|
18847
|
+
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
18848
|
+
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} userId=${userId} topK=${topK}`);
|
|
18849
|
+
const t0 = Date.now();
|
|
18850
|
+
try {
|
|
18851
|
+
const tH1 = Date.now();
|
|
18852
|
+
let episodes = await hybridSearch(query, everosUrl, userId, ROUND1_TOP_N);
|
|
18853
|
+
const tH2 = Date.now();
|
|
18854
|
+
console.log(`[memdir] everos recall: hybrid ${episodes.length} candidates in ${tH2 - tH1}ms`);
|
|
18855
|
+
if (episodes.length === 0) return [];
|
|
18856
|
+
const rerankUrl = options?.rerankUrl;
|
|
18857
|
+
const rerankApiKey = options?.rerankApiKey;
|
|
18858
|
+
if (rerankUrl && rerankApiKey) {
|
|
18859
|
+
const tR1 = Date.now();
|
|
18860
|
+
episodes = await deepinfraRerank(
|
|
18861
|
+
query,
|
|
18862
|
+
episodes,
|
|
18863
|
+
rerankUrl,
|
|
18864
|
+
rerankApiKey,
|
|
18865
|
+
options?.rerankModel,
|
|
18866
|
+
options?.rerankProvider
|
|
18867
|
+
);
|
|
18868
|
+
const tR2 = Date.now();
|
|
18869
|
+
console.log(`[memdir] everos recall: rerank done in ${tR2 - tR1}ms (${options?.rerankProvider || "deepinfra"})`);
|
|
18870
|
+
} else {
|
|
18871
|
+
console.log(`[memdir] everos recall: no rerank key, using hybrid scores as-is`);
|
|
18872
|
+
}
|
|
18873
|
+
const ms = Date.now() - t0;
|
|
18874
|
+
console.log(`[memdir] everos recall: ${episodes.length} episodes in ${ms}ms total (hybrid+rerank)`);
|
|
18875
|
+
const surfacedSubjects = /* @__PURE__ */ new Set();
|
|
18876
|
+
for (const p2 of alreadySurfaced) {
|
|
18877
|
+
if (p2.startsWith("everos://")) {
|
|
18878
|
+
surfacedSubjects.add(p2.slice(8));
|
|
18879
|
+
} else {
|
|
18880
|
+
}
|
|
18881
|
+
}
|
|
18882
|
+
const result = [];
|
|
18883
|
+
const seenSubjects = /* @__PURE__ */ new Set();
|
|
18884
|
+
for (const ep of episodes) {
|
|
18885
|
+
if (ep.score < minScore) continue;
|
|
18886
|
+
const subject = (ep.subject || ep.id).slice(0, 80).replace(/[\n\r]/g, " ");
|
|
18887
|
+
const virtualPath = `everos://${subject}`;
|
|
18888
|
+
if (alreadySurfaced.has(virtualPath)) continue;
|
|
18889
|
+
if (surfacedSubjects.has(subject)) continue;
|
|
18890
|
+
if (seenSubjects.has(subject)) continue;
|
|
18891
|
+
seenSubjects.add(subject);
|
|
18892
|
+
result.push({
|
|
18893
|
+
path: virtualPath,
|
|
18894
|
+
mtimeMs: ep.timestamp ? new Date(ep.timestamp).getTime() : Date.now(),
|
|
18895
|
+
content: `### ${ep.subject}
|
|
18896
|
+
|
|
18897
|
+
${ep.episode || ep.summary}`,
|
|
18898
|
+
header: formatEverosHeader(ep)
|
|
18899
|
+
});
|
|
18900
|
+
if (result.length >= topK) break;
|
|
18901
|
+
}
|
|
18902
|
+
console.log(`[memdir] everos recall: returning ${result.length} memories (after dedup)`);
|
|
18903
|
+
return result;
|
|
18904
|
+
} catch (e) {
|
|
18905
|
+
const ms = Date.now() - t0;
|
|
18906
|
+
console.warn(`[memdir] everos recall: error after ${ms}ms: ${e?.message ?? e}`);
|
|
18907
|
+
return [];
|
|
18908
|
+
}
|
|
18909
|
+
}
|
|
18910
|
+
|
|
18730
18911
|
// src/handle-query.ts
|
|
18731
18912
|
init_paths();
|
|
18732
18913
|
import { readFileSync as readFileSync15, existsSync as existsSync12 } from "node:fs";
|
|
@@ -18799,18 +18980,18 @@ function truncate(s2, maxLen) {
|
|
|
18799
18980
|
}
|
|
18800
18981
|
var externalChanRulesCache = null;
|
|
18801
18982
|
function loadExternalChanRules(workspace) {
|
|
18802
|
-
const
|
|
18803
|
-
if (externalChanRulesCache && externalChanRulesCache.path ===
|
|
18983
|
+
const path44 = join20(workspace, "prompts", "external-chan-rules.md");
|
|
18984
|
+
if (externalChanRulesCache && externalChanRulesCache.path === path44) return externalChanRulesCache;
|
|
18804
18985
|
let content = "";
|
|
18805
|
-
if (existsSync12(
|
|
18986
|
+
if (existsSync12(path44)) {
|
|
18806
18987
|
try {
|
|
18807
|
-
content = readFileSync15(
|
|
18988
|
+
content = readFileSync15(path44, "utf-8").trim();
|
|
18808
18989
|
} catch (e) {
|
|
18809
18990
|
console.warn(`[external-chan-rules] Failed to load: ${e}`);
|
|
18810
18991
|
}
|
|
18811
18992
|
}
|
|
18812
|
-
externalChanRulesCache = { path:
|
|
18813
|
-
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${
|
|
18993
|
+
externalChanRulesCache = { path: path44, content };
|
|
18994
|
+
console.log(`[external-chan-rules] Loaded ${content.length} chars from ${path44}`);
|
|
18814
18995
|
return externalChanRulesCache;
|
|
18815
18996
|
}
|
|
18816
18997
|
function getExternalChanRulesBlock(inboundMeta, workspace) {
|
|
@@ -18833,10 +19014,10 @@ function getExternalChanWhitelist(workspace, configExternalChannels) {
|
|
|
18833
19014
|
if (!externalChanWhitelist) loadContactMap(workspace);
|
|
18834
19015
|
return externalChanWhitelist;
|
|
18835
19016
|
}
|
|
18836
|
-
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall) {
|
|
18837
|
-
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall);
|
|
19017
|
+
async function handleQuery(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
19018
|
+
return handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source);
|
|
18838
19019
|
}
|
|
18839
|
-
async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall) {
|
|
19020
|
+
async function handleQueryInner(text, sessionId, channelName, cb, deps, channelTarget, inboundMeta, skipRecall, source) {
|
|
18840
19021
|
const { engine, sessions, channelManager, workspace, providerId, providerApi, model } = deps;
|
|
18841
19022
|
const features = deps.features || {};
|
|
18842
19023
|
const preQueryAbort = new AbortController();
|
|
@@ -19013,6 +19194,8 @@ ${text}` : text });
|
|
|
19013
19194
|
const toolContext = {
|
|
19014
19195
|
sessionId,
|
|
19015
19196
|
channel: channelName === "cli" ? "console" : channelName,
|
|
19197
|
+
source: source || "",
|
|
19198
|
+
// 消息来源(user/inbox/heartbeat/cron/system/inner-voice),Stop hook 用来区分注入 turn
|
|
19016
19199
|
workspace,
|
|
19017
19200
|
stateDir: deps.stateDir || workspace,
|
|
19018
19201
|
channelManager,
|
|
@@ -19187,7 +19370,23 @@ ${text}` : text });
|
|
|
19187
19370
|
const recallP = deps.recallProvider;
|
|
19188
19371
|
const recallMode = deps.topics?.recall?.mode || "llm";
|
|
19189
19372
|
let relevantMemories;
|
|
19190
|
-
if (recallMode === "
|
|
19373
|
+
if (recallMode === "everos") {
|
|
19374
|
+
const everosCfg = deps?.everosCfg;
|
|
19375
|
+
relevantMemories = await findRelevantMemoriesEveros(
|
|
19376
|
+
textForMemory,
|
|
19377
|
+
memoryDir,
|
|
19378
|
+
surfaced.paths,
|
|
19379
|
+
everosCfg ? {
|
|
19380
|
+
everosUrl: everosCfg.everosUrl || "http://127.0.0.1:8100",
|
|
19381
|
+
userId: everosCfg.userId || "xiaomei",
|
|
19382
|
+
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
19383
|
+
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
19384
|
+
rerankModel: everosCfg.rerank?.model,
|
|
19385
|
+
rerankProvider: everosCfg.rerank?.provider,
|
|
19386
|
+
minScore: deps.topics?.recall?.minScore
|
|
19387
|
+
} : void 0
|
|
19388
|
+
);
|
|
19389
|
+
} else if (recallMode === "vector") {
|
|
19191
19390
|
relevantMemories = await findRelevantMemoriesVector(
|
|
19192
19391
|
textForMemory,
|
|
19193
19392
|
memoryDir,
|
|
@@ -19210,8 +19409,8 @@ ${text}` : text });
|
|
|
19210
19409
|
const attachmentMemories = [];
|
|
19211
19410
|
for (const mem of relevantMemories) {
|
|
19212
19411
|
try {
|
|
19213
|
-
const content = readFileSync15(mem.path, "utf-8");
|
|
19214
|
-
const header = memoryHeader(mem.path, mem.mtimeMs);
|
|
19412
|
+
const content = mem.content ?? readFileSync15(mem.path, "utf-8");
|
|
19413
|
+
const header = mem.header ?? memoryHeader(mem.path, mem.mtimeMs);
|
|
19215
19414
|
attachmentMemories.push({ path: mem.path, content, mtimeMs: mem.mtimeMs, header });
|
|
19216
19415
|
} catch {
|
|
19217
19416
|
}
|
|
@@ -19759,7 +19958,9 @@ function registerCognifoldBridge(config2) {
|
|
|
19759
19958
|
messageId: ctx.inbound.messageId
|
|
19760
19959
|
}
|
|
19761
19960
|
};
|
|
19762
|
-
|
|
19961
|
+
const sm = globalThis.__cognifoldSessions;
|
|
19962
|
+
const dynamicSessionId = sm?.getSessionId?.("main") || cognifoldConfig.sessionId;
|
|
19963
|
+
void enqueueEvent(dynamicSessionId, event);
|
|
19763
19964
|
return null;
|
|
19764
19965
|
}, 80);
|
|
19765
19966
|
}
|
|
@@ -20083,7 +20284,8 @@ var MessageDispatcher = class {
|
|
|
20083
20284
|
msg2.deps,
|
|
20084
20285
|
msg2.channelTarget,
|
|
20085
20286
|
msg2.inboundMeta,
|
|
20086
|
-
msg2.skipRecall
|
|
20287
|
+
msg2.skipRecall,
|
|
20288
|
+
msg2.source
|
|
20087
20289
|
);
|
|
20088
20290
|
} catch (err) {
|
|
20089
20291
|
console.error(`[dispatcher] Query error (session=${msg2.sessionId}): ${err.message}`);
|
|
@@ -20243,13 +20445,19 @@ function startCliLoop(deps, cliConfig, channelManager, dispatcher) {
|
|
|
20243
20445
|
|
|
20244
20446
|
// src/session/session-history.ts
|
|
20245
20447
|
import fs14 from "node:fs";
|
|
20448
|
+
import path14 from "node:path";
|
|
20246
20449
|
var BEIJING_OFFSET_MS = 8 * 36e5;
|
|
20247
20450
|
var INJECTED_CONTENT_PATTERNS = [
|
|
20248
20451
|
/【定时心跳】/,
|
|
20249
20452
|
/\[内心对话测试\]/,
|
|
20250
20453
|
/\[inner-voice\]/,
|
|
20251
20454
|
/\[微信巡检\]/,
|
|
20252
|
-
/\[plugin\]
|
|
20455
|
+
/\[plugin\]/,
|
|
20456
|
+
/<nudge-notification>/,
|
|
20457
|
+
/<task-notification>/,
|
|
20458
|
+
/<calendar-notification>/,
|
|
20459
|
+
/## Actions \(\d+\s*个\)/
|
|
20460
|
+
// CogniFold proactive 注入(block[0] 固定格式,engine-startup 拼的)
|
|
20253
20461
|
];
|
|
20254
20462
|
function parseJsonlEntries(lines) {
|
|
20255
20463
|
const entries = [];
|
|
@@ -20284,6 +20492,20 @@ function resolveScopeMainJsonl(sessions) {
|
|
|
20284
20492
|
if (!sessionId) return null;
|
|
20285
20493
|
return sessions.getSessionFilePath(sessionId);
|
|
20286
20494
|
}
|
|
20495
|
+
function scopeMainJsonlPaths(sessions) {
|
|
20496
|
+
const current = resolveScopeMainJsonl(sessions);
|
|
20497
|
+
let latestArchive = null;
|
|
20498
|
+
if (current) {
|
|
20499
|
+
try {
|
|
20500
|
+
const dir = path14.dirname(current);
|
|
20501
|
+
const base = path14.basename(current);
|
|
20502
|
+
const archives = fs14.readdirSync(dir).filter((f2) => f2.startsWith(base + ".archived.")).sort();
|
|
20503
|
+
if (archives.length > 0) latestArchive = path14.join(dir, archives[archives.length - 1]);
|
|
20504
|
+
} catch {
|
|
20505
|
+
}
|
|
20506
|
+
}
|
|
20507
|
+
return { current, latestArchive };
|
|
20508
|
+
}
|
|
20287
20509
|
function cleanText(rawText) {
|
|
20288
20510
|
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]");
|
|
20289
20511
|
return clean.trim();
|
|
@@ -20374,6 +20596,7 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
20374
20596
|
time: `${p2(bj.getHours())}:${p2(bj.getMinutes())}`,
|
|
20375
20597
|
role,
|
|
20376
20598
|
text: clean.slice(0, 80),
|
|
20599
|
+
timestamp: dtMs,
|
|
20377
20600
|
_utc: dtMs
|
|
20378
20601
|
});
|
|
20379
20602
|
}
|
|
@@ -20505,6 +20728,8 @@ ${basePrompt}`;
|
|
|
20505
20728
|
channelName: "heartbeat",
|
|
20506
20729
|
source: "heartbeat",
|
|
20507
20730
|
priority: "later",
|
|
20731
|
+
skipRecall: true,
|
|
20732
|
+
// 心跳不需要记忆召回,避免重复注入心跳相关记忆
|
|
20508
20733
|
callbacks: {
|
|
20509
20734
|
onResult: () => resolveDone()
|
|
20510
20735
|
},
|
|
@@ -20522,7 +20747,7 @@ ${basePrompt}`;
|
|
|
20522
20747
|
|
|
20523
20748
|
// src/nudge/plugin.ts
|
|
20524
20749
|
import fs17 from "node:fs";
|
|
20525
|
-
import
|
|
20750
|
+
import path17 from "node:path";
|
|
20526
20751
|
|
|
20527
20752
|
// src/nudge/judge.ts
|
|
20528
20753
|
function shouldNudge(task, taskState, cfg) {
|
|
@@ -20691,10 +20916,10 @@ function formatDuration2(ms) {
|
|
|
20691
20916
|
|
|
20692
20917
|
// src/nudge/session-state-reader.ts
|
|
20693
20918
|
import fs15 from "node:fs";
|
|
20694
|
-
import
|
|
20919
|
+
import path15 from "node:path";
|
|
20695
20920
|
function parseSessionStateFull(workspace, sessionStateFile) {
|
|
20696
20921
|
const stateFile = sessionStateFile || "SESSION-STATE.md";
|
|
20697
|
-
const statePath =
|
|
20922
|
+
const statePath = path15.isAbsolute(stateFile) ? stateFile : path15.join(workspace, stateFile);
|
|
20698
20923
|
let content;
|
|
20699
20924
|
try {
|
|
20700
20925
|
content = fs15.readFileSync(statePath, "utf-8");
|
|
@@ -20749,13 +20974,13 @@ function taskIdFromTitle(title) {
|
|
|
20749
20974
|
|
|
20750
20975
|
// src/calendar/db.ts
|
|
20751
20976
|
import { DatabaseSync } from "node:sqlite";
|
|
20752
|
-
import * as
|
|
20977
|
+
import * as path16 from "node:path";
|
|
20753
20978
|
import * as fs16 from "node:fs";
|
|
20754
20979
|
var TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
20755
20980
|
function openDb(workspace) {
|
|
20756
|
-
const dir =
|
|
20981
|
+
const dir = path16.join(workspace, ".calendar");
|
|
20757
20982
|
fs16.mkdirSync(dir, { recursive: true });
|
|
20758
|
-
const dbPath =
|
|
20983
|
+
const dbPath = path16.join(dir, "calendar.db");
|
|
20759
20984
|
const db = new DatabaseSync(dbPath);
|
|
20760
20985
|
db.exec("PRAGMA journal_mode=WAL");
|
|
20761
20986
|
db.exec(`CREATE TABLE IF NOT EXISTS events (
|
|
@@ -20844,7 +21069,7 @@ var NudgePlugin = class {
|
|
|
20844
21069
|
provider;
|
|
20845
21070
|
model;
|
|
20846
21071
|
loadPrompt(workspace, promptFile) {
|
|
20847
|
-
const promptPath = promptFile ?
|
|
21072
|
+
const promptPath = promptFile ? path17.isAbsolute(promptFile) ? promptFile : path17.join(workspace, promptFile) : path17.join(workspace, "prompts", "nudge-prompt.md");
|
|
20848
21073
|
try {
|
|
20849
21074
|
const content = fs17.readFileSync(promptPath, "utf-8").trim();
|
|
20850
21075
|
if (content) {
|
|
@@ -20881,11 +21106,20 @@ var NudgePlugin = class {
|
|
|
20881
21106
|
const lastMsg = input?.last_assistant_message || "";
|
|
20882
21107
|
const sessionId = input?.session_id || "";
|
|
20883
21108
|
console.log(`[stop-hook] lastMsg len=${lastMsg.length}, text="${lastMsg.slice(0, 80)}"`);
|
|
21109
|
+
const repliedIds = this.extractWakeReplyIds(lastMsg);
|
|
21110
|
+
if (repliedIds.length > 0) {
|
|
21111
|
+
this.removeNotificationsById(repliedIds);
|
|
21112
|
+
}
|
|
20884
21113
|
const msgChannel = input?.channel || "";
|
|
20885
21114
|
if (sessionId.includes("voice-chat") || msgChannel === "voice-chat") {
|
|
20886
21115
|
console.log(`[stop-hook] skipping voice-chat (channel=${msgChannel})`);
|
|
20887
21116
|
return { outcome: { outcome: "success" } };
|
|
20888
21117
|
}
|
|
21118
|
+
const msgSource = input?.source || "";
|
|
21119
|
+
if (msgSource && msgSource !== "user" && msgSource !== "inbox") {
|
|
21120
|
+
console.log(`[stop-hook] skipping non-conversation turn (source=${msgSource})`);
|
|
21121
|
+
return { outcome: { outcome: "success" } };
|
|
21122
|
+
}
|
|
20889
21123
|
if (!lastMsg) {
|
|
20890
21124
|
return { outcome: { outcome: "success" } };
|
|
20891
21125
|
}
|
|
@@ -20958,8 +21192,8 @@ var NudgePlugin = class {
|
|
|
20958
21192
|
if (pushedDecision && waitDesc) {
|
|
20959
21193
|
console.log(`[stop-hook] DETECTED pushedDecision! Injecting corrective message to ${sessionId}`);
|
|
20960
21194
|
try {
|
|
20961
|
-
const correctiveMsg = [
|
|
20962
|
-
"
|
|
21195
|
+
const correctiveMsg = buildNudgeNotification("prompt", [
|
|
21196
|
+
"[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",
|
|
20963
21197
|
"",
|
|
20964
21198
|
`\u8BCA\u65AD\uFF1A${waitDesc}`,
|
|
20965
21199
|
"",
|
|
@@ -20972,9 +21206,10 @@ var NudgePlugin = class {
|
|
|
20972
21206
|
'3. \u6267\u884C\u5B8C\u6C47\u62A5\u7ED3\u679C\uFF08"\u5DF2\u5904\u7406" / "\u5DF2 commit" / "\u5DF2 archive"\uFF09',
|
|
20973
21207
|
"",
|
|
20974
21208
|
"\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"
|
|
20975
|
-
].join("\n");
|
|
20976
|
-
|
|
20977
|
-
|
|
21209
|
+
].join("\n"));
|
|
21210
|
+
const route = this.getRoute(sessions);
|
|
21211
|
+
if (route) {
|
|
21212
|
+
enqueueNotification(correctiveMsg, route);
|
|
20978
21213
|
}
|
|
20979
21214
|
} catch (e) {
|
|
20980
21215
|
console.warn(`[stop-hook] Failed to inject corrective message: ${e.message}`);
|
|
@@ -20983,14 +21218,21 @@ var NudgePlugin = class {
|
|
|
20983
21218
|
if (!isWaiting) {
|
|
20984
21219
|
return { outcome: { outcome: "success" } };
|
|
20985
21220
|
}
|
|
20986
|
-
const nudgeDir =
|
|
20987
|
-
const notifPath =
|
|
21221
|
+
const nudgeDir = path17.join(this.workspace, ".nudge");
|
|
21222
|
+
const notifPath = path17.join(nudgeDir, "stop-hook-notifications.json");
|
|
20988
21223
|
try {
|
|
20989
21224
|
if (!fs17.existsSync(nudgeDir)) fs17.mkdirSync(nudgeDir, { recursive: true });
|
|
20990
21225
|
let notifs = [];
|
|
20991
21226
|
if (fs17.existsSync(notifPath)) {
|
|
20992
21227
|
notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
20993
21228
|
const now = Date.now();
|
|
21229
|
+
const dup = notifs.find((n) => !n.notified && n.description === (waitDesc || lastMsg.slice(0, 200)));
|
|
21230
|
+
if (dup) {
|
|
21231
|
+
dup.wakeAt = new Date(now + 5 * 6e4).toISOString();
|
|
21232
|
+
fs17.writeFileSync(notifPath, JSON.stringify(notifs, null, 2));
|
|
21233
|
+
console.log(`[stop-hook] Duplicate wait (same desc, not fired yet), refreshed wakeAt: ${dup.id}`);
|
|
21234
|
+
return { outcome: { outcome: "success" } };
|
|
21235
|
+
}
|
|
20994
21236
|
const recentReg = notifs.find((n) => now - new Date(n.createdAt).getTime() < 3 * 6e4);
|
|
20995
21237
|
if (recentReg) {
|
|
20996
21238
|
console.log(`[stop-hook] Skip (recent registration within 3min)`);
|
|
@@ -21039,9 +21281,14 @@ var NudgePlugin = class {
|
|
|
21039
21281
|
return null;
|
|
21040
21282
|
}
|
|
21041
21283
|
}
|
|
21042
|
-
/**
|
|
21043
|
-
|
|
21044
|
-
|
|
21284
|
+
/**
|
|
21285
|
+
* 收集到期的 stop-hook notifications,批量构建一条 wake 消息。
|
|
21286
|
+
* 不标 notified——投递成功后由 tick 调 markNotified 标(route 拿不到时保留原样下个 tick 重试,
|
|
21287
|
+
* 避免"消息没投出去但已标 notified"的死账)。
|
|
21288
|
+
* 已 notified 的不会再触发,等 agent 回复 "<id> 过期了" 由 cleanup 删。
|
|
21289
|
+
*/
|
|
21290
|
+
collectDueStopHookNotifications() {
|
|
21291
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21045
21292
|
try {
|
|
21046
21293
|
if (!fs17.existsSync(notifPath)) return null;
|
|
21047
21294
|
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
@@ -21049,54 +21296,161 @@ var NudgePlugin = class {
|
|
|
21049
21296
|
const now = Date.now();
|
|
21050
21297
|
const due = notifs.filter((n) => new Date(n.wakeAt).getTime() <= now && !n.notified);
|
|
21051
21298
|
if (due.length === 0) return null;
|
|
21052
|
-
|
|
21053
|
-
|
|
21054
|
-
|
|
21055
|
-
|
|
21056
|
-
return buildNudgeNotification("wake", `\u4F60\u4E4B\u524D\u5728\u7B49\u5F85\u67D0\u4E2A\u5916\u90E8\u6761\u4EF6\uFF0C\u65F6\u95F4\u5230\u4E86\uFF0C\u56DE\u53BB\u68C0\u67E5\uFF01
|
|
21299
|
+
console.log(`[nudge] ${due.length} stop-hook notification(s) due: ${due.map((n) => n.id).join(", ")}`);
|
|
21300
|
+
const items = due.map((n) => `[\u901A\u77E5ID: ${n.id}]
|
|
21301
|
+
\u4E0A\u6B21\u8BF4\uFF1A${n.description}`).join("\n\n");
|
|
21302
|
+
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
|
|
21057
21303
|
|
|
21058
|
-
|
|
21059
|
-
\u4E0A\u6B21\u8BF4\uFF1A${latest.description}
|
|
21304
|
+
${items}
|
|
21060
21305
|
|
|
21061
|
-
\u68C0\u67E5\u6761\u4EF6\u662F\u5426\u6EE1\u8DB3\uFF0C\u6EE1\u8DB3\u5C31\u7EE7\u7EED\u5E72\u6D3B\uFF0C\u4E0D\u6EE1\u8DB3\u5C31\u56DE\u590D"${
|
|
21306
|
+
\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
|
|
21307
|
+
|
|
21308
|
+
${items}
|
|
21309
|
+
|
|
21310
|
+
\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`;
|
|
21311
|
+
return { message: buildNudgeNotification("wake", desc), ids: due.map((n) => n.id) };
|
|
21062
21312
|
} catch (e) {
|
|
21063
|
-
console.warn(`[nudge]
|
|
21313
|
+
console.warn(`[nudge] collectDueStopHookNotifications error: ${e.message}`);
|
|
21064
21314
|
return null;
|
|
21065
21315
|
}
|
|
21066
21316
|
}
|
|
21067
|
-
/**
|
|
21317
|
+
/** 从回复文本里提取 "<id> 过期了" 的 wake id(一条回复可能处置多个) */
|
|
21318
|
+
extractWakeReplyIds(text) {
|
|
21319
|
+
if (!text) return [];
|
|
21320
|
+
const ids = [];
|
|
21321
|
+
const re = /(wake-\d+-[a-z0-9]+)\s*过期了/g;
|
|
21322
|
+
let m2;
|
|
21323
|
+
while ((m2 = re.exec(text)) !== null) {
|
|
21324
|
+
if (!ids.includes(m2[1])) ids.push(m2[1]);
|
|
21325
|
+
}
|
|
21326
|
+
return ids;
|
|
21327
|
+
}
|
|
21328
|
+
/** 按 id 删除条目(stop-hook 实时清理用;正常删除路径,agent 回复即删) */
|
|
21329
|
+
removeNotificationsById(ids) {
|
|
21330
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21331
|
+
try {
|
|
21332
|
+
if (!fs17.existsSync(notifPath)) return;
|
|
21333
|
+
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
21334
|
+
const idSet = new Set(ids);
|
|
21335
|
+
const remaining = notifs.filter((n) => !idSet.has(n.id));
|
|
21336
|
+
const removed = notifs.length - remaining.length;
|
|
21337
|
+
if (removed === 0) return;
|
|
21338
|
+
if (remaining.length > 0) {
|
|
21339
|
+
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
21340
|
+
} else {
|
|
21341
|
+
fs17.unlinkSync(notifPath);
|
|
21342
|
+
}
|
|
21343
|
+
console.log(`[stop-hook] Cleaned ${removed} notification(s) from reply: ${ids.join(", ")}`);
|
|
21344
|
+
} catch (e) {
|
|
21345
|
+
console.warn(`[stop-hook] removeNotificationsById error: ${e.message}`);
|
|
21346
|
+
}
|
|
21347
|
+
}
|
|
21348
|
+
/** 投递成功后标记 notified(防重复触发);不删除——删除只走 agent 回复 "<id> 过期了" */
|
|
21349
|
+
markNotified(ids) {
|
|
21350
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21351
|
+
try {
|
|
21352
|
+
if (!fs17.existsSync(notifPath)) return;
|
|
21353
|
+
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
21354
|
+
const idSet = new Set(ids);
|
|
21355
|
+
const updated = notifs.map((n) => idSet.has(n.id) ? { ...n, notified: true } : n);
|
|
21356
|
+
fs17.writeFileSync(notifPath, JSON.stringify(updated, null, 2));
|
|
21357
|
+
} catch (e) {
|
|
21358
|
+
console.warn(`[nudge] markNotified error: ${e.message}`);
|
|
21359
|
+
}
|
|
21360
|
+
}
|
|
21361
|
+
/**
|
|
21362
|
+
* tick 兜底清理。正常删除走 stop-hook 实时路径(agent 回复 "<id> 过期了" 当 turn 就删,
|
|
21363
|
+
* 见 registerStopHook 第 0 步),这里只接两种漏网:
|
|
21364
|
+
* ① 回复已落盘但 stop-hook 没来得及执行(进程中途崩等边缘情况)→ 扫 jsonl 补删;
|
|
21365
|
+
* ② TTL 清道夫:wakeAt 超过 cleanupTtlHours(默认 24h)仍无回复 → 回复永远来不了,删。
|
|
21366
|
+
*
|
|
21367
|
+
* 扫描不走 recentMessages()——它截断 80 字符、会过滤"对注入消息的回复"(wake 回复恰好
|
|
21368
|
+
* 被过滤掉)、限 20 条窗口。直接读 jsonl 原始条目:倒序扫、扫过最老 pending 的 wakeAt
|
|
21369
|
+
* 即停、全命中提前退、archive 只在 current 被 2MB 轮转切断时才读。
|
|
21370
|
+
*/
|
|
21068
21371
|
cleanupStaleNotificationsFromMessages(sessions) {
|
|
21069
21372
|
try {
|
|
21070
|
-
const
|
|
21071
|
-
const fiveMinAgo = Date.now() - 5 * 60 * 1e3;
|
|
21072
|
-
const recentTexts = recent.filter((r) => new Date(r.timestamp || r.createdAt || Date.now()).getTime() > fiveMinAgo).map((r) => r.text);
|
|
21073
|
-
const notifPath = path16.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21373
|
+
const notifPath = path17.join(this.workspace, ".nudge", "stop-hook-notifications.json");
|
|
21074
21374
|
if (!fs17.existsSync(notifPath)) return;
|
|
21075
21375
|
const notifs = JSON.parse(fs17.readFileSync(notifPath, "utf-8"));
|
|
21076
21376
|
if (notifs.length === 0) return;
|
|
21077
|
-
const expiredIds =
|
|
21078
|
-
|
|
21079
|
-
|
|
21080
|
-
|
|
21081
|
-
|
|
21082
|
-
|
|
21083
|
-
|
|
21377
|
+
const expiredIds = this.findExpiredReplyIds(sessions, notifs);
|
|
21378
|
+
const ttlMs = (this.cfg.cleanupTtlHours || 24) * 36e5;
|
|
21379
|
+
const now = Date.now();
|
|
21380
|
+
const ttlIds = new Set(
|
|
21381
|
+
notifs.filter((n) => now - new Date(n.wakeAt).getTime() > ttlMs && !expiredIds.has(n.id)).map((n) => n.id)
|
|
21382
|
+
);
|
|
21383
|
+
const removeIds = /* @__PURE__ */ new Set([...expiredIds, ...ttlIds]);
|
|
21384
|
+
if (removeIds.size === 0) return;
|
|
21385
|
+
const remaining = notifs.filter((n) => !removeIds.has(n.id));
|
|
21386
|
+
if (remaining.length > 0) {
|
|
21387
|
+
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
21388
|
+
} else {
|
|
21389
|
+
fs17.unlinkSync(notifPath);
|
|
21084
21390
|
}
|
|
21085
|
-
if (expiredIds.size
|
|
21086
|
-
|
|
21087
|
-
|
|
21088
|
-
if (
|
|
21089
|
-
|
|
21090
|
-
fs17.writeFileSync(notifPath, JSON.stringify(remaining, null, 2));
|
|
21091
|
-
} else {
|
|
21092
|
-
fs17.unlinkSync(notifPath);
|
|
21093
|
-
}
|
|
21094
|
-
console.log(`[nudge] Cleaned ${cleaned} stale notification(s) by explicit id: ${[...expiredIds].join(", ")}`);
|
|
21391
|
+
if (expiredIds.size > 0) {
|
|
21392
|
+
console.log(`[nudge] Cleaned ${expiredIds.size} notification(s) by reply: ${[...expiredIds].join(", ")}`);
|
|
21393
|
+
}
|
|
21394
|
+
if (ttlIds.size > 0) {
|
|
21395
|
+
console.log(`[nudge] Cleaned ${ttlIds.size} zombie notification(s) by TTL (>${this.cfg.cleanupTtlHours || 24}h no reply): ${[...ttlIds].join(", ")}`);
|
|
21095
21396
|
}
|
|
21096
21397
|
} catch (e) {
|
|
21097
21398
|
console.warn(`[nudge] cleanupStaleNotificationsFromMessages error: ${e.message}`);
|
|
21098
21399
|
}
|
|
21099
21400
|
}
|
|
21401
|
+
/**
|
|
21402
|
+
* 扫 "<id> 过期了" 回复,返回匹配到的 id 集合。
|
|
21403
|
+
* 不做全文扫描:倒序扫(回复紧跟在 fire 之后,通常就在尾部几条);
|
|
21404
|
+
* 扫过最老 pending 条目的 wakeAt 就停(回复不可能早于触发时间);
|
|
21405
|
+
* 全部命中提前退出;archive 只在 current 没覆盖时间范围(被 2MB 轮转切断)时才读。
|
|
21406
|
+
* 典型开销:解析几十条而不是上千条。
|
|
21407
|
+
*/
|
|
21408
|
+
findExpiredReplyIds(sessions, notifs) {
|
|
21409
|
+
const found = /* @__PURE__ */ new Set();
|
|
21410
|
+
if (notifs.length === 0) return found;
|
|
21411
|
+
const oldestMs = Math.min(...notifs.map((n) => new Date(n.wakeAt).getTime()));
|
|
21412
|
+
const { current, latestArchive } = scopeMainJsonlPaths(sessions);
|
|
21413
|
+
for (const file of [current, latestArchive]) {
|
|
21414
|
+
if (!file || !fs17.existsSync(file)) continue;
|
|
21415
|
+
let lines;
|
|
21416
|
+
try {
|
|
21417
|
+
lines = fs17.readFileSync(file, "utf-8").split("\n");
|
|
21418
|
+
} catch (e) {
|
|
21419
|
+
console.warn(`[nudge] findExpiredReplyIds read error on ${file}: ${e.message}`);
|
|
21420
|
+
continue;
|
|
21421
|
+
}
|
|
21422
|
+
let coveredOldest = false;
|
|
21423
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
21424
|
+
const trimmed = lines[i].trim();
|
|
21425
|
+
if (!trimmed) continue;
|
|
21426
|
+
let entry;
|
|
21427
|
+
try {
|
|
21428
|
+
entry = JSON.parse(trimmed);
|
|
21429
|
+
} catch {
|
|
21430
|
+
continue;
|
|
21431
|
+
}
|
|
21432
|
+
const tsMs = entry?.timestamp ? new Date(entry.timestamp).getTime() : 0;
|
|
21433
|
+
if (tsMs > 0 && tsMs < oldestMs) {
|
|
21434
|
+
coveredOldest = true;
|
|
21435
|
+
break;
|
|
21436
|
+
}
|
|
21437
|
+
if (entry?.type !== "message") continue;
|
|
21438
|
+
const msg2 = entry.message;
|
|
21439
|
+
if (!msg2 || msg2.role !== "assistant") continue;
|
|
21440
|
+
const content = msg2.content;
|
|
21441
|
+
const text = typeof content === "string" ? content : Array.isArray(content) && content[0] && typeof content[0].text === "string" ? content[0].text : "";
|
|
21442
|
+
if (!text) continue;
|
|
21443
|
+
for (const n of notifs) {
|
|
21444
|
+
if (!found.has(n.id) && (text.includes(`${n.id} \u8FC7\u671F\u4E86`) || text.includes(`${n.id}\u8FC7\u671F\u4E86`))) {
|
|
21445
|
+
found.add(n.id);
|
|
21446
|
+
}
|
|
21447
|
+
}
|
|
21448
|
+
if (found.size === notifs.length) return found;
|
|
21449
|
+
}
|
|
21450
|
+
if (coveredOldest) break;
|
|
21451
|
+
}
|
|
21452
|
+
return found;
|
|
21453
|
+
}
|
|
21100
21454
|
async tick(sessions, deps) {
|
|
21101
21455
|
if (this.running) {
|
|
21102
21456
|
console.log("[nudge] Previous tick still running, skipping");
|
|
@@ -21111,7 +21465,7 @@ var NudgePlugin = class {
|
|
|
21111
21465
|
const recent = recentMessages(sessions, 0.5, 6);
|
|
21112
21466
|
const lastUserMsg2 = recent.filter((r) => r.role === "user").slice(-1)[0];
|
|
21113
21467
|
if (lastUserMsg2) {
|
|
21114
|
-
const elapsed = Date.now() -
|
|
21468
|
+
const elapsed = lastUserMsg2.timestamp ? Date.now() - lastUserMsg2.timestamp : 0;
|
|
21115
21469
|
if (elapsed < activeThresholdMs) {
|
|
21116
21470
|
console.log(`[nudge] User active ${Math.round(elapsed / 1e3)}s ago (<${activeThresholdMs / 1e3}s), skipping tick`);
|
|
21117
21471
|
return;
|
|
@@ -21123,10 +21477,15 @@ var NudgePlugin = class {
|
|
|
21123
21477
|
this.running = true;
|
|
21124
21478
|
try {
|
|
21125
21479
|
this.cleanupStaleNotificationsFromMessages(sessions);
|
|
21126
|
-
const
|
|
21127
|
-
if (
|
|
21480
|
+
const dueNotifs = this.collectDueStopHookNotifications();
|
|
21481
|
+
if (dueNotifs) {
|
|
21128
21482
|
const route2 = this.getRoute(sessions);
|
|
21129
|
-
if (route2)
|
|
21483
|
+
if (route2) {
|
|
21484
|
+
enqueueNotification(dueNotifs.message, route2);
|
|
21485
|
+
this.markNotified(dueNotifs.ids);
|
|
21486
|
+
} else {
|
|
21487
|
+
console.warn(`[nudge] No route for ${dueNotifs.ids.length} stop-hook notification(s), keeping for retry next tick`);
|
|
21488
|
+
}
|
|
21130
21489
|
const state0 = this.loadState();
|
|
21131
21490
|
state0.lastAnyNudgeAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21132
21491
|
this.saveState(state0);
|
|
@@ -21312,7 +21671,7 @@ var NudgePlugin = class {
|
|
|
21312
21671
|
// === state 持久化 ===
|
|
21313
21672
|
loadState() {
|
|
21314
21673
|
const stateFile = this.cfg.stateFile || "nudge-state.json";
|
|
21315
|
-
const statePath =
|
|
21674
|
+
const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(this.workspace, stateFile);
|
|
21316
21675
|
try {
|
|
21317
21676
|
const content = fs17.readFileSync(statePath, "utf-8");
|
|
21318
21677
|
return JSON.parse(content);
|
|
@@ -21322,7 +21681,7 @@ var NudgePlugin = class {
|
|
|
21322
21681
|
}
|
|
21323
21682
|
saveState(state2) {
|
|
21324
21683
|
const stateFile = this.cfg.stateFile || "nudge-state.json";
|
|
21325
|
-
const statePath =
|
|
21684
|
+
const statePath = path17.isAbsolute(stateFile) ? stateFile : path17.join(this.workspace, stateFile);
|
|
21326
21685
|
fs17.writeFileSync(statePath, JSON.stringify(state2, null, 2), "utf-8");
|
|
21327
21686
|
}
|
|
21328
21687
|
newTaskState() {
|
|
@@ -21408,30 +21767,47 @@ var NudgePlugin = class {
|
|
|
21408
21767
|
const now = /* @__PURE__ */ new Date();
|
|
21409
21768
|
const bjOffset = (8 * 60 + now.getTimezoneOffset()) * 6e4;
|
|
21410
21769
|
const bj = new Date(now.getTime() + bjOffset);
|
|
21411
|
-
const month = bj.getMonth() + 1;
|
|
21412
|
-
const day = bj.getDate();
|
|
21413
21770
|
const bjHour = bj.getHours();
|
|
21414
21771
|
const bjMinute = bj.getMinutes();
|
|
21772
|
+
const todayStart = new Date(bj.getFullYear(), bj.getMonth(), bj.getDate()).getTime();
|
|
21415
21773
|
const rows = db.prepare(
|
|
21416
|
-
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND
|
|
21417
|
-
).all(
|
|
21774
|
+
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
21775
|
+
).all();
|
|
21418
21776
|
db.close();
|
|
21419
|
-
|
|
21420
|
-
const
|
|
21421
|
-
|
|
21422
|
-
|
|
21423
|
-
|
|
21424
|
-
if (
|
|
21425
|
-
|
|
21777
|
+
const due = [];
|
|
21778
|
+
for (const r2 of rows) {
|
|
21779
|
+
const dayMs = this.parseCalendarDateStr(r2.date_str, bj);
|
|
21780
|
+
if (dayMs === null || dayMs > todayStart) continue;
|
|
21781
|
+
const isToday2 = dayMs === todayStart;
|
|
21782
|
+
if (isToday2 && r2.time_exact) {
|
|
21783
|
+
const [h, m2] = String(r2.time_exact).split(":").map(Number);
|
|
21784
|
+
if (h > bjHour || h === bjHour && m2 > bjMinute) continue;
|
|
21785
|
+
}
|
|
21786
|
+
due.push({ id: r2.id, event: r2.event, date_str: r2.date_str, time_exact: r2.time_exact, dayMs, isToday: isToday2 });
|
|
21787
|
+
}
|
|
21788
|
+
if (due.length === 0) return null;
|
|
21789
|
+
due.sort((a, b2) => {
|
|
21790
|
+
if (a.isToday !== b2.isToday) return a.isToday ? -1 : 1;
|
|
21791
|
+
return b2.dayMs - a.dayMs;
|
|
21426
21792
|
});
|
|
21427
|
-
|
|
21428
|
-
|
|
21429
|
-
return `#${r.id} ${r.event} (${r.date_str} ${r.time_exact || ""})`.trim();
|
|
21793
|
+
const r = due[0];
|
|
21794
|
+
return `#${r.id} ${r.event} (${r.date_str} ${r.time_exact || ""})${r.isToday ? "" : "\uFF08\u5DF2\u903E\u671F\uFF09"}`.trim();
|
|
21430
21795
|
} catch (e) {
|
|
21431
21796
|
console.warn(`[nudge] checkCalendarDue error: ${e.message}`);
|
|
21432
21797
|
return null;
|
|
21433
21798
|
}
|
|
21434
21799
|
}
|
|
21800
|
+
/** 解析 date_str 为当日 0 点 epoch ms(北京时间);"M/D" 按当前年,"YYYY-M-D" 按字面年 */
|
|
21801
|
+
parseCalendarDateStr(ds, bj) {
|
|
21802
|
+
if (!ds) return null;
|
|
21803
|
+
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(ds)) {
|
|
21804
|
+
const t = (/* @__PURE__ */ new Date(ds + "T00:00:00+08:00")).getTime();
|
|
21805
|
+
return Number.isNaN(t) ? null : t;
|
|
21806
|
+
}
|
|
21807
|
+
const m2 = String(ds).match(/^(\d{1,2})\/(\d{1,2})$/);
|
|
21808
|
+
if (!m2) return null;
|
|
21809
|
+
return new Date(bj.getFullYear(), Number(m2[1]) - 1, Number(m2[2])).getTime();
|
|
21810
|
+
}
|
|
21435
21811
|
/** 检查 carry-over:如果 in_progress task 24h+ 没推进,自动 calendar add-task 排明天 */
|
|
21436
21812
|
checkCarryOver(task, _sessions) {
|
|
21437
21813
|
try {
|
|
@@ -21485,7 +21861,7 @@ var NudgePlugin = class {
|
|
|
21485
21861
|
|
|
21486
21862
|
// src/inner-voice/plugin.ts
|
|
21487
21863
|
import fs21 from "node:fs";
|
|
21488
|
-
import
|
|
21864
|
+
import path21 from "node:path";
|
|
21489
21865
|
|
|
21490
21866
|
// src/inner-voice/activity.ts
|
|
21491
21867
|
function checkActivity(sessions, activeThresholdMs) {
|
|
@@ -21525,7 +21901,7 @@ function calcHintProb(min) {
|
|
|
21525
21901
|
|
|
21526
21902
|
// src/inner-voice/emotional-state.ts
|
|
21527
21903
|
import fs18 from "node:fs";
|
|
21528
|
-
import
|
|
21904
|
+
import path18 from "node:path";
|
|
21529
21905
|
var NEUTRAL = 0.5;
|
|
21530
21906
|
var DECAY_RATE = 0.17;
|
|
21531
21907
|
var MAX_EVENTS = 20;
|
|
@@ -21576,7 +21952,7 @@ function initialState() {
|
|
|
21576
21952
|
return { version: 1, mood: NEUTRAL, trend: "stable", updatedAt: nowIsoBj(), events: [] };
|
|
21577
21953
|
}
|
|
21578
21954
|
async function updateEmotionalState(workspace, sessions) {
|
|
21579
|
-
const stateFile =
|
|
21955
|
+
const stateFile = path18.join(workspace, "inner-voice", "emotional-state.json");
|
|
21580
21956
|
const messages = readRecentMessages(sessions, RECENT_N);
|
|
21581
21957
|
if (messages.length === 0) {
|
|
21582
21958
|
console.log("[emotional-state] no messages");
|
|
@@ -21609,7 +21985,7 @@ async function updateEmotionalState(workspace, sessions) {
|
|
|
21609
21985
|
function readRecentMessages(sessions, n) {
|
|
21610
21986
|
const mainId = sessions.getSessionId("scope:main");
|
|
21611
21987
|
if (!mainId) return [];
|
|
21612
|
-
const file =
|
|
21988
|
+
const file = path18.join(sessions.sessionsDir, `${mainId}.jsonl`);
|
|
21613
21989
|
if (!fs18.existsSync(file)) return [];
|
|
21614
21990
|
const lines = readLastNLines(file, n * 4 + 20);
|
|
21615
21991
|
const entries = [];
|
|
@@ -21727,7 +22103,7 @@ function refreshHoursAgo(events) {
|
|
|
21727
22103
|
}
|
|
21728
22104
|
function appendMoodLog(workspace, state2, summary) {
|
|
21729
22105
|
try {
|
|
21730
|
-
const logPath =
|
|
22106
|
+
const logPath = path18.join(workspace, "mood-history.log");
|
|
21731
22107
|
const ts = formatBj(/* @__PURE__ */ new Date(), false);
|
|
21732
22108
|
fs18.appendFileSync(logPath, `${ts} mood=${state2.mood.toFixed(2)} trend=${state2.trend} ${summary}
|
|
21733
22109
|
`);
|
|
@@ -21744,7 +22120,7 @@ function loadJson(file) {
|
|
|
21744
22120
|
}
|
|
21745
22121
|
function saveJson(file, data) {
|
|
21746
22122
|
try {
|
|
21747
|
-
fs18.mkdirSync(
|
|
22123
|
+
fs18.mkdirSync(path18.dirname(file), { recursive: true });
|
|
21748
22124
|
fs18.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
21749
22125
|
} catch (err) {
|
|
21750
22126
|
console.warn(`[emotional-state] save failed: ${err.message}`);
|
|
@@ -21790,7 +22166,7 @@ function formatBj(d, withSec) {
|
|
|
21790
22166
|
|
|
21791
22167
|
// src/inner-voice/topics-scorer.ts
|
|
21792
22168
|
import fs19 from "node:fs";
|
|
21793
|
-
import
|
|
22169
|
+
import path19 from "node:path";
|
|
21794
22170
|
var HALF_LIFE_DAYS = 3;
|
|
21795
22171
|
var PROJECT_HALF_LIFE_DAYS = 1.5;
|
|
21796
22172
|
var COOLDOWN_HOURS = 6;
|
|
@@ -21798,8 +22174,8 @@ var MAX_CHARS = 8e3;
|
|
|
21798
22174
|
var SKIP_NAMES = /* @__PURE__ */ new Set(["MEMORY.md", "archive"]);
|
|
21799
22175
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["archive"]);
|
|
21800
22176
|
function pickTopic(workspace, typeFilter, opts) {
|
|
21801
|
-
const topicsDir =
|
|
21802
|
-
const usageFile =
|
|
22177
|
+
const topicsDir = path19.join(workspace, "topics");
|
|
22178
|
+
const usageFile = path19.join(workspace, "inner-voice", "topics-usage.json");
|
|
21803
22179
|
const files = scanTopics(topicsDir, typeFilter);
|
|
21804
22180
|
if (files.length === 0) {
|
|
21805
22181
|
console.log(`[topics-scorer] no topics found (type=${typeFilter})`);
|
|
@@ -21831,7 +22207,7 @@ function pickTopic(workspace, typeFilter, opts) {
|
|
|
21831
22207
|
recency: Math.round(recency * 1e3) / 1e3,
|
|
21832
22208
|
freq: Math.round(freq * 1e3) / 1e3,
|
|
21833
22209
|
type: type2,
|
|
21834
|
-
name: meta.name ||
|
|
22210
|
+
name: meta.name || path19.basename(relpath),
|
|
21835
22211
|
description: meta.description || "",
|
|
21836
22212
|
mtime
|
|
21837
22213
|
});
|
|
@@ -21889,14 +22265,14 @@ function scanTopics(topicsDir, typeFilter) {
|
|
|
21889
22265
|
const out = [];
|
|
21890
22266
|
const walk = (dir) => {
|
|
21891
22267
|
for (const name of fs19.readdirSync(dir)) {
|
|
21892
|
-
const full =
|
|
22268
|
+
const full = path19.join(dir, name);
|
|
21893
22269
|
const stat4 = fs19.statSync(full);
|
|
21894
22270
|
if (stat4.isDirectory()) {
|
|
21895
22271
|
if (SKIP_DIRS.has(name)) continue;
|
|
21896
22272
|
walk(full);
|
|
21897
22273
|
} else {
|
|
21898
22274
|
if (!name.endsWith(".md") || SKIP_NAMES.has(name)) continue;
|
|
21899
|
-
const relpath =
|
|
22275
|
+
const relpath = path19.relative(topicsDir, full).replace(/\\/g, "/");
|
|
21900
22276
|
if (typeFilter && !relpath.startsWith(typeFilter + "/") && !relpath.startsWith(typeFilter + "_")) continue;
|
|
21901
22277
|
out.push({ relpath, fullpath: full });
|
|
21902
22278
|
}
|
|
@@ -21941,7 +22317,7 @@ function loadJson2(file) {
|
|
|
21941
22317
|
}
|
|
21942
22318
|
function saveJson2(file, data) {
|
|
21943
22319
|
try {
|
|
21944
|
-
fs19.mkdirSync(
|
|
22320
|
+
fs19.mkdirSync(path19.dirname(file), { recursive: true });
|
|
21945
22321
|
fs19.writeFileSync(file, JSON.stringify(data, null, 2));
|
|
21946
22322
|
} catch (err) {
|
|
21947
22323
|
console.warn(`[topics-scorer] usage save failed: ${err.message}`);
|
|
@@ -21950,21 +22326,21 @@ function saveJson2(file, data) {
|
|
|
21950
22326
|
|
|
21951
22327
|
// src/inner-voice/memory-reader.ts
|
|
21952
22328
|
import fs20 from "node:fs";
|
|
21953
|
-
import
|
|
22329
|
+
import path20 from "node:path";
|
|
21954
22330
|
var US_HALF_LIFE_DAYS = 10;
|
|
21955
22331
|
var US_MAX_LINES = 60;
|
|
21956
22332
|
function readRecentMemory(workspace) {
|
|
21957
|
-
const dir =
|
|
22333
|
+
const dir = path20.join(workspace, "memory");
|
|
21958
22334
|
const now = new Date(Date.now() + 8 * 36e5);
|
|
21959
22335
|
const today = formatYmd(now);
|
|
21960
22336
|
const yesterday = formatYmd(new Date(now.getTime() - 864e5));
|
|
21961
22337
|
return {
|
|
21962
|
-
today: readIfExists(
|
|
21963
|
-
yesterday: readIfExists(
|
|
22338
|
+
today: readIfExists(path20.join(dir, `${today}.md`)),
|
|
22339
|
+
yesterday: readIfExists(path20.join(dir, `${yesterday}.md`))
|
|
21964
22340
|
};
|
|
21965
22341
|
}
|
|
21966
22342
|
function sampleUs(workspace) {
|
|
21967
|
-
const usFile =
|
|
22343
|
+
const usFile = path20.join(workspace, "memory", "us.md");
|
|
21968
22344
|
let content;
|
|
21969
22345
|
try {
|
|
21970
22346
|
content = fs20.readFileSync(usFile, "utf-8");
|
|
@@ -22304,7 +22680,7 @@ var InnerVoicePlugin = class {
|
|
|
22304
22680
|
}
|
|
22305
22681
|
/** 读 workspace/prompts/my-inner-voice.md,不存在用 DEFAULT_PROMPT */
|
|
22306
22682
|
loadPrompt(workspace) {
|
|
22307
|
-
const promptPath =
|
|
22683
|
+
const promptPath = path21.join(workspace, "prompts", "my-inner-voice.md");
|
|
22308
22684
|
try {
|
|
22309
22685
|
const content = fs21.readFileSync(promptPath, "utf-8").trim();
|
|
22310
22686
|
if (content) {
|
|
@@ -22378,7 +22754,7 @@ var InnerVoicePlugin = class {
|
|
|
22378
22754
|
console.warn(`[inner-voice] emotional-state failed: ${err.message}`);
|
|
22379
22755
|
}
|
|
22380
22756
|
try {
|
|
22381
|
-
const content = fs21.readFileSync(
|
|
22757
|
+
const content = fs21.readFileSync(path21.join(this.workspace, "SESSION-STATE.md"), "utf-8");
|
|
22382
22758
|
lines.push("\n--- SESSION-STATE\uFF08\u5C3E\u90E8\uFF09 ---");
|
|
22383
22759
|
lines.push(content.slice(-2e3));
|
|
22384
22760
|
} catch {
|
|
@@ -22489,7 +22865,7 @@ var InnerVoicePlugin = class {
|
|
|
22489
22865
|
if (Math.random() >= activity.hintProb) {
|
|
22490
22866
|
return { text: thought, hintTriggered: false, hintText: "" };
|
|
22491
22867
|
}
|
|
22492
|
-
const poolPath =
|
|
22868
|
+
const poolPath = path21.join(this.workspace, "inner-voice", "hints_pool.txt");
|
|
22493
22869
|
let hint = "\u60F3\u4ED6\u5C31\u53D1\u6D88\u606F\u5427";
|
|
22494
22870
|
try {
|
|
22495
22871
|
const pool = fs21.readFileSync(poolPath, "utf-8").split("\n").map((s2) => s2.trim()).filter(Boolean);
|
|
@@ -22517,7 +22893,7 @@ var InnerVoicePlugin = class {
|
|
|
22517
22893
|
try {
|
|
22518
22894
|
const writer = sessions.getWriter(mainSessionId);
|
|
22519
22895
|
const history = sessions.getHistory(mainSessionId);
|
|
22520
|
-
const fullPath =
|
|
22896
|
+
const fullPath = path21.resolve(this.workspace, emoTopic.file);
|
|
22521
22897
|
const memories = [{
|
|
22522
22898
|
path: fullPath,
|
|
22523
22899
|
content: emoTopic.content,
|
|
@@ -22545,9 +22921,9 @@ var InnerVoicePlugin = class {
|
|
|
22545
22921
|
/** 写 xiaoyi.log(格式对齐旧 memory_whisper.py,便于既有日志分析复用)。 */
|
|
22546
22922
|
writeLog(status, delivered, activity, hintTriggered, hintText) {
|
|
22547
22923
|
try {
|
|
22548
|
-
const logDir =
|
|
22924
|
+
const logDir = path21.join(this.workspace, "inner-voice");
|
|
22549
22925
|
fs21.mkdirSync(logDir, { recursive: true });
|
|
22550
|
-
const logPath =
|
|
22926
|
+
const logPath = path21.join(logDir, "xiaoyi.log");
|
|
22551
22927
|
const ts = formatBeijingTs(/* @__PURE__ */ new Date());
|
|
22552
22928
|
const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
|
|
22553
22929
|
fs21.appendFileSync(
|
|
@@ -23086,7 +23462,7 @@ var PluginManager = class {
|
|
|
23086
23462
|
// src/voice-chat/plugin.ts
|
|
23087
23463
|
import { spawn as spawn4, exec } from "node:child_process";
|
|
23088
23464
|
import net from "node:net";
|
|
23089
|
-
import
|
|
23465
|
+
import path22 from "node:path";
|
|
23090
23466
|
import fs22 from "node:fs";
|
|
23091
23467
|
|
|
23092
23468
|
// src/voice-chat/bridge.ts
|
|
@@ -23463,13 +23839,13 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23463
23839
|
}
|
|
23464
23840
|
getPythonDir() {
|
|
23465
23841
|
const dir = import.meta.dirname;
|
|
23466
|
-
const srcDir =
|
|
23467
|
-
const localDir =
|
|
23842
|
+
const srcDir = path22.resolve(dir, "..", "src", "voice-chat", "python");
|
|
23843
|
+
const localDir = path22.join(dir, "python");
|
|
23468
23844
|
return fs22.existsSync(srcDir) ? srcDir : localDir;
|
|
23469
23845
|
}
|
|
23470
23846
|
startPython() {
|
|
23471
23847
|
const pythonDir = this.getPythonDir();
|
|
23472
|
-
const serverPy =
|
|
23848
|
+
const serverPy = path22.join(pythonDir, "server.py");
|
|
23473
23849
|
const pythonBin = this.findPython();
|
|
23474
23850
|
const args2 = [serverPy];
|
|
23475
23851
|
if (this.config.pythonPort) args2.push("--port", String(this.config.pythonPort));
|
|
@@ -23554,7 +23930,7 @@ var VoiceChatPlugin = class _VoiceChatPlugin {
|
|
|
23554
23930
|
init_BashTool();
|
|
23555
23931
|
import { spawn as spawn5, exec as exec2 } from "node:child_process";
|
|
23556
23932
|
import net2 from "node:net";
|
|
23557
|
-
import
|
|
23933
|
+
import path23 from "node:path";
|
|
23558
23934
|
import fs23 from "node:fs";
|
|
23559
23935
|
|
|
23560
23936
|
// src/memory/cognifold/config.ts
|
|
@@ -23578,7 +23954,8 @@ function parseCognifoldConfig(raw) {
|
|
|
23578
23954
|
persistDir: raw.persistDir ?? DEFAULTS3.persistDir,
|
|
23579
23955
|
scopes: raw.scopes,
|
|
23580
23956
|
readyTimeoutMs: raw.readyTimeoutMs ?? DEFAULTS3.readyTimeoutMs,
|
|
23581
|
-
maxRestarts: raw.maxRestarts ?? DEFAULTS3.maxRestarts
|
|
23957
|
+
maxRestarts: raw.maxRestarts ?? DEFAULTS3.maxRestarts,
|
|
23958
|
+
llm: raw.llm
|
|
23582
23959
|
};
|
|
23583
23960
|
}
|
|
23584
23961
|
|
|
@@ -23586,15 +23963,17 @@ function parseCognifoldConfig(raw) {
|
|
|
23586
23963
|
var CogniFoldClient = class {
|
|
23587
23964
|
baseUrl;
|
|
23588
23965
|
timeoutMs;
|
|
23589
|
-
|
|
23966
|
+
modelName;
|
|
23967
|
+
constructor(baseUrl, timeoutMs = 3e4, modelName = "openai:MiniMax-M3") {
|
|
23590
23968
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
23591
23969
|
this.timeoutMs = timeoutMs;
|
|
23970
|
+
this.modelName = modelName;
|
|
23592
23971
|
}
|
|
23593
|
-
async req(
|
|
23972
|
+
async req(path44, options = {}) {
|
|
23594
23973
|
const ctrl = new AbortController();
|
|
23595
23974
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
23596
23975
|
try {
|
|
23597
|
-
const resp = await fetch(`${this.baseUrl}${
|
|
23976
|
+
const resp = await fetch(`${this.baseUrl}${path44}`, {
|
|
23598
23977
|
...options,
|
|
23599
23978
|
signal: ctrl.signal,
|
|
23600
23979
|
headers: {
|
|
@@ -23632,7 +24011,7 @@ var CogniFoldClient = class {
|
|
|
23632
24011
|
method: "POST",
|
|
23633
24012
|
body: JSON.stringify({
|
|
23634
24013
|
user_id: userId,
|
|
23635
|
-
config: { model_name:
|
|
24014
|
+
config: { model_name: this.modelName }
|
|
23636
24015
|
})
|
|
23637
24016
|
});
|
|
23638
24017
|
}
|
|
@@ -23684,8 +24063,8 @@ var CogniFoldClient = class {
|
|
|
23684
24063
|
});
|
|
23685
24064
|
}
|
|
23686
24065
|
/** 兼容老版命名 */
|
|
23687
|
-
async recl(
|
|
23688
|
-
return this.req(
|
|
24066
|
+
async recl(path44, options = {}) {
|
|
24067
|
+
return this.req(path44, options);
|
|
23689
24068
|
}
|
|
23690
24069
|
};
|
|
23691
24070
|
|
|
@@ -23790,7 +24169,8 @@ var CogniFoldPlugin = class {
|
|
|
23790
24169
|
baseUrl = baseUrl.replace(/\/$/, "") + "/api/v1";
|
|
23791
24170
|
}
|
|
23792
24171
|
this.config.baseUrl = baseUrl;
|
|
23793
|
-
this.
|
|
24172
|
+
const modelName = this.config.llm?.model ? this.config.llm.model.startsWith("openai:") ? this.config.llm.model : `openai:${this.config.llm.model}` : "openai:MiniMax-M3";
|
|
24173
|
+
this.client = new CogniFoldClient(baseUrl, 3e4, modelName);
|
|
23794
24174
|
}
|
|
23795
24175
|
workspacePath;
|
|
23796
24176
|
name = "cognifold";
|
|
@@ -23965,16 +24345,16 @@ var CogniFoldPlugin = class {
|
|
|
23965
24345
|
const dir = import.meta.dirname;
|
|
23966
24346
|
const candidates = [
|
|
23967
24347
|
// 从 dist/ 往回找 src
|
|
23968
|
-
|
|
23969
|
-
|
|
23970
|
-
|
|
24348
|
+
path23.resolve(dir, "..", "src", "memory", "cognifold", "python"),
|
|
24349
|
+
path23.resolve(dir, "..", "..", "src", "memory", "cognifold", "python"),
|
|
24350
|
+
path23.resolve(dir, "..", "..", "..", "src", "memory", "cognifold", "python"),
|
|
23971
24351
|
// 从 src/memory/cognifold/ 找本地
|
|
23972
|
-
|
|
24352
|
+
path23.join(dir, "python"),
|
|
23973
24353
|
// 从 dist/memory/cognifold/ 找本地
|
|
23974
|
-
|
|
24354
|
+
path23.resolve(dir, "python")
|
|
23975
24355
|
];
|
|
23976
24356
|
for (const candidate of candidates) {
|
|
23977
|
-
if (fs23.existsSync(
|
|
24357
|
+
if (fs23.existsSync(path23.join(candidate, "cognifold"))) {
|
|
23978
24358
|
return candidate;
|
|
23979
24359
|
}
|
|
23980
24360
|
}
|
|
@@ -24000,12 +24380,18 @@ var CogniFoldPlugin = class {
|
|
|
24000
24380
|
const pythonBin = this.findPython();
|
|
24001
24381
|
console.log(`[cognifold] Starting Python: ${pythonBin} ${args2.join(" ")}`);
|
|
24002
24382
|
console.log(`[cognifold] Python dir: ${pythonDir}`);
|
|
24003
|
-
if (!fs23.existsSync(
|
|
24383
|
+
if (!fs23.existsSync(path23.join(pythonDir, "cognifold"))) {
|
|
24004
24384
|
console.error(`[cognifold] FATAL: Python module not found at ${pythonDir}/cognifold`);
|
|
24005
24385
|
throw new Error(`cognifold: python module not found`);
|
|
24006
24386
|
}
|
|
24007
24387
|
const childEnv = { ...process.env, PYTHONUNBUFFERED: "1" };
|
|
24008
|
-
|
|
24388
|
+
if (this.config.llm?.apiKey) {
|
|
24389
|
+
childEnv["OPENAI_API_KEY"] = this.config.llm.apiKey;
|
|
24390
|
+
}
|
|
24391
|
+
if (this.config.llm?.baseUrl) {
|
|
24392
|
+
childEnv["OPENAI_BASE_URL"] = this.config.llm.baseUrl;
|
|
24393
|
+
}
|
|
24394
|
+
const envFile = path23.join(pythonDir, ".env");
|
|
24009
24395
|
try {
|
|
24010
24396
|
if (fs23.existsSync(envFile)) {
|
|
24011
24397
|
const envContent = fs23.readFileSync(envFile, "utf-8");
|
|
@@ -24077,7 +24463,7 @@ var CogniFoldPlugin = class {
|
|
|
24077
24463
|
init_BashTool();
|
|
24078
24464
|
import { spawn as spawn6 } from "node:child_process";
|
|
24079
24465
|
import net3 from "node:net";
|
|
24080
|
-
import
|
|
24466
|
+
import path24 from "node:path";
|
|
24081
24467
|
import fs24 from "node:fs";
|
|
24082
24468
|
|
|
24083
24469
|
// src/memory/everos/config.ts
|
|
@@ -24111,7 +24497,8 @@ function parseEverosConfig(raw) {
|
|
|
24111
24497
|
llm: raw.llm ?? { model: "glm-5.2", apiKey: "", baseUrl: "https://open.bigmodel.cn/api/coding/paas/v4" },
|
|
24112
24498
|
rerank: raw.rerank ?? { model: "Qwen/Qwen3-Reranker-4B", apiKey: "", baseUrl: "https://api.deepinfra.com/v1/inference" },
|
|
24113
24499
|
lancedbPath: raw.lancedbPath ?? "",
|
|
24114
|
-
sqlitePath: raw.sqlitePath ?? ""
|
|
24500
|
+
sqlitePath: raw.sqlitePath ?? "",
|
|
24501
|
+
minScore: raw.minScore
|
|
24115
24502
|
};
|
|
24116
24503
|
}
|
|
24117
24504
|
|
|
@@ -24149,21 +24536,31 @@ var EverosSearchClient = class {
|
|
|
24149
24536
|
clearTimeout(timer);
|
|
24150
24537
|
}
|
|
24151
24538
|
}
|
|
24152
|
-
/** Search —
|
|
24539
|
+
/** Search — routes to 8101 (agentic) or 8100 (hybrid) based on mode */
|
|
24153
24540
|
async search(params) {
|
|
24154
24541
|
const ctrl = new AbortController();
|
|
24155
24542
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
24156
24543
|
try {
|
|
24157
|
-
const
|
|
24544
|
+
const mode = params.mode || "hybrid";
|
|
24545
|
+
const useAgentic = mode === "hybrid_agentic" || mode === "agentic";
|
|
24546
|
+
const url = useAgentic ? `${this.agenticUrl}/api/v1/search` : `${this.everosUrl}/api/v1/memory/search`;
|
|
24547
|
+
const body = useAgentic ? JSON.stringify({
|
|
24548
|
+
query: params.query,
|
|
24549
|
+
user_id: params.userId || "xiaomei",
|
|
24550
|
+
mode,
|
|
24551
|
+
top_k: params.topK ?? 5,
|
|
24552
|
+
strategy: params.strategy || "multi_query"
|
|
24553
|
+
}) : JSON.stringify({
|
|
24554
|
+
query: params.query,
|
|
24555
|
+
user_id: params.userId || "user",
|
|
24556
|
+
app_id: "xiaomei",
|
|
24557
|
+
project_id: "default",
|
|
24558
|
+
top_k: params.topK ?? 5
|
|
24559
|
+
});
|
|
24560
|
+
const resp = await fetch(url, {
|
|
24158
24561
|
method: "POST",
|
|
24159
24562
|
headers: { "Content-Type": "application/json" },
|
|
24160
|
-
body
|
|
24161
|
-
query: params.query,
|
|
24162
|
-
user_id: params.userId || "xiaomei",
|
|
24163
|
-
mode: params.mode || "hybrid_agentic",
|
|
24164
|
-
top_k: params.topK ?? 5,
|
|
24165
|
-
strategy: params.strategy || "multi_query"
|
|
24166
|
-
}),
|
|
24563
|
+
body,
|
|
24167
24564
|
signal: ctrl.signal
|
|
24168
24565
|
});
|
|
24169
24566
|
if (!resp.ok) {
|
|
@@ -24203,6 +24600,7 @@ var EverosPlugin = class {
|
|
|
24203
24600
|
}
|
|
24204
24601
|
async start(ctx) {
|
|
24205
24602
|
if (!this.config.enabled) return;
|
|
24603
|
+
await this.ensureVenv();
|
|
24206
24604
|
try {
|
|
24207
24605
|
await this.client.healthEveros();
|
|
24208
24606
|
console.log(`[everos] EverOS already running at ${this.config.everosUrl}`);
|
|
@@ -24273,13 +24671,15 @@ var EverosPlugin = class {
|
|
|
24273
24671
|
}, 3e5);
|
|
24274
24672
|
}
|
|
24275
24673
|
async startEveros() {
|
|
24276
|
-
const pythonDir =
|
|
24277
|
-
const configPath2 =
|
|
24674
|
+
const pythonDir = path24.dirname(this.config.lancedbPath);
|
|
24675
|
+
const configPath2 = path24.join(pythonDir, "config.toml");
|
|
24278
24676
|
await this.ensureFcntlCompat();
|
|
24279
24677
|
const venvPython = this.findVenvPython();
|
|
24678
|
+
const everosBin = venvPython.replace(/python\.exe$/, "everos.exe");
|
|
24280
24679
|
const args2 = ["server", "start"];
|
|
24281
|
-
const cmd = `${
|
|
24680
|
+
const cmd = `${everosBin} ${args2.join(" ")}`;
|
|
24282
24681
|
console.log(`[everos] Starting EverOS: ${cmd}`);
|
|
24682
|
+
console.log(`[everos] LLM config: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
|
|
24283
24683
|
if (process.platform === "win32") {
|
|
24284
24684
|
const { shell, args: shellArgs } = findShell();
|
|
24285
24685
|
spawn6(shell, [...shellArgs, cmd], {
|
|
@@ -24294,7 +24694,7 @@ var EverosPlugin = class {
|
|
|
24294
24694
|
env: { ...process.env, PYTHONUNBUFFERED: "1" }
|
|
24295
24695
|
});
|
|
24296
24696
|
}
|
|
24297
|
-
await this.waitForReady(`${this.config.everosUrl}/health`,
|
|
24697
|
+
await this.waitForReady(`${this.config.everosUrl}/health`, 6e4);
|
|
24298
24698
|
}
|
|
24299
24699
|
startAgenticServer() {
|
|
24300
24700
|
const pythonDir = this.getPythonDir();
|
|
@@ -24304,6 +24704,7 @@ var EverosPlugin = class {
|
|
|
24304
24704
|
const cmd = `${venvPython} ${args2.join(" ")}`;
|
|
24305
24705
|
console.log(`[everos] Starting agentic server: ${cmd}`);
|
|
24306
24706
|
console.log(`[everos] Python dir: ${pythonDir}`);
|
|
24707
|
+
console.log(`[everos] LLM: ${this.config.llm.model} @ ${this.config.llm.baseUrl}`);
|
|
24307
24708
|
const childEnv = {
|
|
24308
24709
|
...process.env,
|
|
24309
24710
|
PYTHONUNBUFFERED: "1",
|
|
@@ -24312,15 +24713,25 @@ var EverosPlugin = class {
|
|
|
24312
24713
|
LLM_API_KEY: this.config.llm.apiKey,
|
|
24313
24714
|
LLM_BASE_URL: this.config.llm.baseUrl,
|
|
24314
24715
|
RERANK_API_KEY: this.config.rerank.apiKey,
|
|
24315
|
-
RERANK_URL:
|
|
24716
|
+
RERANK_URL: this.config.rerank.baseUrl,
|
|
24316
24717
|
LANCEDB_PATH: this.config.lancedbPath,
|
|
24317
24718
|
SQLITE_PATH: this.config.sqlitePath,
|
|
24318
24719
|
EVEROS_USER_ID: this.config.userId
|
|
24319
24720
|
};
|
|
24721
|
+
const maskKey = (k2) => k2 ? `${k2.slice(0, 4)}\u2026${k2.slice(-4)}` : "(empty!)";
|
|
24722
|
+
console.log(`[everos] agentic env:`);
|
|
24723
|
+
console.log(`[everos] EVEROS_URL=${childEnv.EVEROS_URL}`);
|
|
24724
|
+
console.log(`[everos] LLM_MODEL=${childEnv.LLM_MODEL}`);
|
|
24725
|
+
console.log(`[everos] LLM_API_KEY=${maskKey(childEnv.LLM_API_KEY)}`);
|
|
24726
|
+
console.log(`[everos] LLM_BASE_URL=${childEnv.LLM_BASE_URL}`);
|
|
24727
|
+
console.log(`[everos] RERANK_API_KEY=${maskKey(childEnv.RERANK_API_KEY)}`);
|
|
24728
|
+
console.log(`[everos] RERANK_URL=${childEnv.RERANK_URL}`);
|
|
24729
|
+
console.log(`[everos] LANCEDB_PATH=${childEnv.LANCEDB_PATH}`);
|
|
24730
|
+
console.log(`[everos] SQLITE_PATH=${childEnv.SQLITE_PATH}`);
|
|
24731
|
+
console.log(`[everos] EVEROS_USER_ID=${childEnv.EVEROS_USER_ID}`);
|
|
24320
24732
|
let child;
|
|
24321
24733
|
if (process.platform === "win32") {
|
|
24322
|
-
|
|
24323
|
-
child = spawn6(shell, [...shellArgs, cmd], {
|
|
24734
|
+
child = spawn6(venvPython, args2, {
|
|
24324
24735
|
cwd: pythonDir,
|
|
24325
24736
|
stdio: ["ignore", "pipe", "pipe"],
|
|
24326
24737
|
env: childEnv
|
|
@@ -24350,21 +24761,64 @@ var EverosPlugin = class {
|
|
|
24350
24761
|
return child;
|
|
24351
24762
|
}
|
|
24352
24763
|
findVenvPython() {
|
|
24353
|
-
const stateDir = process.env.OPENCLAW_STATE_DIR ||
|
|
24764
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path24.join(process.env.HOME || process.env.USERPROFILE || ".", ".openclaw");
|
|
24354
24765
|
if (process.platform === "win32") {
|
|
24355
|
-
return
|
|
24766
|
+
return path24.join(stateDir, "everos-venv", "Scripts", "python.exe");
|
|
24767
|
+
}
|
|
24768
|
+
return path24.join(stateDir, "everos-venv", "bin", "python");
|
|
24769
|
+
}
|
|
24770
|
+
/** 检测 venv 是否存在,不存在就自动创建 + 装 EverOS */
|
|
24771
|
+
async ensureVenv() {
|
|
24772
|
+
const venvPython = this.findVenvPython();
|
|
24773
|
+
if (fs24.existsSync(venvPython)) return;
|
|
24774
|
+
const stateDir = process.env.OPENCLAW_STATE_DIR || path24.join(process.env.HOME || process.env.USERPROFILE || ".", ".openclaw");
|
|
24775
|
+
const venvDir = path24.join(stateDir, "everos-venv");
|
|
24776
|
+
const everosSrc = path24.join(stateDir, "workspace", "research", "EverOS");
|
|
24777
|
+
console.log(`[everos] venv not found at ${venvDir}, auto-creating...`);
|
|
24778
|
+
console.log(`[everos] \u23F3 This may take a few minutes on first run...`);
|
|
24779
|
+
const pyCandidates = process.platform === "win32" ? ["python", "python3", "C:\\Python314\\python.exe", "C:\\Python313\\python.exe", "C:\\Python312\\python.exe"] : ["python3", "python"];
|
|
24780
|
+
let sysPython = "";
|
|
24781
|
+
for (const cmd of pyCandidates) {
|
|
24782
|
+
try {
|
|
24783
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
24784
|
+
execSync3(`"${cmd}" --version`, { stdio: "pipe", shell: true });
|
|
24785
|
+
sysPython = cmd;
|
|
24786
|
+
break;
|
|
24787
|
+
} catch {
|
|
24788
|
+
}
|
|
24789
|
+
}
|
|
24790
|
+
if (!sysPython) {
|
|
24791
|
+
console.error(`[everos] \u2717 Python not found. Install Python 3.10+ first.`);
|
|
24792
|
+
return;
|
|
24793
|
+
}
|
|
24794
|
+
try {
|
|
24795
|
+
console.log(`[everos] Creating venv with ${sysPython}...`);
|
|
24796
|
+
const { execSync: execSync3 } = await import("node:child_process");
|
|
24797
|
+
execSync3(`"${sysPython}" -m venv "${venvDir}"`, { stdio: "pipe", shell: true });
|
|
24798
|
+
const pip = process.platform === "win32" ? path24.join(venvDir, "Scripts", "pip.exe") : path24.join(venvDir, "bin", "pip");
|
|
24799
|
+
const everosReq = path24.join(this.getPythonDir(), "requirements.txt");
|
|
24800
|
+
if (fs24.existsSync(everosReq)) {
|
|
24801
|
+
console.log(`[everos] Installing from requirements.txt...`);
|
|
24802
|
+
execSync3(`"${pip}" install -r "${everosReq}" -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
|
|
24803
|
+
} else {
|
|
24804
|
+
console.log(`[everos] No requirements.txt found, installing everos from PyPI...`);
|
|
24805
|
+
execSync3(`"${pip}" install everos -q`, { stdio: "pipe", shell: true, timeout: 3e5 });
|
|
24806
|
+
}
|
|
24807
|
+
console.log(`[everos] \u2705 venv created successfully`);
|
|
24808
|
+
} catch (err) {
|
|
24809
|
+
console.error(`[everos] \u2717 Failed to create venv: ${err.message}`);
|
|
24810
|
+
console.error(`[everos] Manual setup: see workspace/scripts/everos-setup.sh`);
|
|
24356
24811
|
}
|
|
24357
|
-
return path23.join(stateDir, "everos-venv", "bin", "python");
|
|
24358
24812
|
}
|
|
24359
24813
|
getPythonDir() {
|
|
24360
24814
|
const dir = import.meta.dirname;
|
|
24361
24815
|
const candidates = [
|
|
24362
|
-
|
|
24363
|
-
|
|
24364
|
-
|
|
24816
|
+
path24.join(dir, "python"),
|
|
24817
|
+
path24.resolve(dir, "..", "src", "memory", "everos", "python"),
|
|
24818
|
+
path24.resolve(dir, "..", "..", "..", "src", "memory", "everos", "python")
|
|
24365
24819
|
];
|
|
24366
24820
|
for (const candidate of candidates) {
|
|
24367
|
-
if (fs24.existsSync(
|
|
24821
|
+
if (fs24.existsSync(path24.join(candidate, "agentic_server.py"))) {
|
|
24368
24822
|
return candidate;
|
|
24369
24823
|
}
|
|
24370
24824
|
}
|
|
@@ -24373,11 +24827,11 @@ var EverosPlugin = class {
|
|
|
24373
24827
|
async ensureFcntlCompat() {
|
|
24374
24828
|
if (process.platform !== "win32") return;
|
|
24375
24829
|
const venvPython = this.findVenvPython();
|
|
24376
|
-
const venvDir =
|
|
24377
|
-
const sitePackages =
|
|
24378
|
-
const target =
|
|
24830
|
+
const venvDir = path24.dirname(path24.dirname(venvPython));
|
|
24831
|
+
const sitePackages = path24.join(venvDir, "Lib", "site-packages");
|
|
24832
|
+
const target = path24.join(sitePackages, "fcntl.py");
|
|
24379
24833
|
if (fs24.existsSync(target)) return;
|
|
24380
|
-
const source =
|
|
24834
|
+
const source = path24.join(this.getPythonDir(), "fcntl_compat.py");
|
|
24381
24835
|
if (fs24.existsSync(source)) {
|
|
24382
24836
|
try {
|
|
24383
24837
|
fs24.copyFileSync(source, target);
|
|
@@ -24427,7 +24881,7 @@ var EverosPlugin = class {
|
|
|
24427
24881
|
init_task_manager();
|
|
24428
24882
|
|
|
24429
24883
|
// src/skills/scanner.ts
|
|
24430
|
-
import * as
|
|
24884
|
+
import * as path25 from "node:path";
|
|
24431
24885
|
import * as fs25 from "node:fs";
|
|
24432
24886
|
function scanSkills(skillsDir) {
|
|
24433
24887
|
if (!fs25.existsSync(skillsDir)) {
|
|
@@ -24438,7 +24892,7 @@ function scanSkills(skillsDir) {
|
|
|
24438
24892
|
const skills = [];
|
|
24439
24893
|
for (const entry of entries) {
|
|
24440
24894
|
if (!entry.isDirectory()) continue;
|
|
24441
|
-
const skillMdPath =
|
|
24895
|
+
const skillMdPath = path25.join(skillsDir, entry.name, "SKILL.md");
|
|
24442
24896
|
if (!fs25.existsSync(skillMdPath)) continue;
|
|
24443
24897
|
try {
|
|
24444
24898
|
const content = fs25.readFileSync(skillMdPath, "utf-8");
|
|
@@ -24506,7 +24960,7 @@ function parseFrontmatter2(content) {
|
|
|
24506
24960
|
// src/tools/SkillTool/SkillTool.ts
|
|
24507
24961
|
init_registry();
|
|
24508
24962
|
import * as fs26 from "node:fs";
|
|
24509
|
-
import * as
|
|
24963
|
+
import * as path26 from "node:path";
|
|
24510
24964
|
|
|
24511
24965
|
// src/tools/SkillTool/constants.ts
|
|
24512
24966
|
var SKILL_TOOL_NAME2 = "Skill";
|
|
@@ -24583,12 +25037,12 @@ Important:
|
|
|
24583
25037
|
`;
|
|
24584
25038
|
}
|
|
24585
25039
|
function loadSkillContent(skillName) {
|
|
24586
|
-
const skillMdPath =
|
|
25040
|
+
const skillMdPath = path26.join(skillsDirPath, skillName, "SKILL.md");
|
|
24587
25041
|
if (!fs26.existsSync(skillMdPath)) return null;
|
|
24588
25042
|
const content = fs26.readFileSync(skillMdPath, "utf-8");
|
|
24589
25043
|
const bodyMatch = content.match(/^---\s*\n[\s\S]*?\n---\s*\n([\s\S]*)/);
|
|
24590
25044
|
const body = bodyMatch ? bodyMatch[1] : content;
|
|
24591
|
-
const skillDir =
|
|
25045
|
+
const skillDir = path26.dirname(skillMdPath);
|
|
24592
25046
|
const normalizedDir = process.platform === "win32" ? skillDir.replace(/\\/g, "/") : skillDir;
|
|
24593
25047
|
let finalContent = `Base directory for this skill: ${normalizedDir}
|
|
24594
25048
|
|
|
@@ -24863,9 +25317,9 @@ Examples:
|
|
|
24863
25317
|
init_registry();
|
|
24864
25318
|
init_live();
|
|
24865
25319
|
import fs27 from "node:fs";
|
|
24866
|
-
import
|
|
25320
|
+
import path27 from "node:path";
|
|
24867
25321
|
function getHusbandFeishuId(workspace) {
|
|
24868
|
-
const contactsPath =
|
|
25322
|
+
const contactsPath = path27.join(workspace, "prompts", "contacts.md");
|
|
24869
25323
|
try {
|
|
24870
25324
|
const text = fs27.readFileSync(contactsPath, "utf-8");
|
|
24871
25325
|
const m2 = text.match(/\|\s*翀哥\s*\|\s*(ou_[a-f0-9]+)\s*\|/);
|
|
@@ -25068,7 +25522,7 @@ Examples:
|
|
|
25068
25522
|
init_live();
|
|
25069
25523
|
init_registry();
|
|
25070
25524
|
import * as fs28 from "node:fs";
|
|
25071
|
-
import * as
|
|
25525
|
+
import * as path28 from "node:path";
|
|
25072
25526
|
var MIME_MAP = {
|
|
25073
25527
|
".jpg": "jpeg",
|
|
25074
25528
|
".jpeg": "jpeg",
|
|
@@ -25080,7 +25534,7 @@ var MIME_MAP = {
|
|
|
25080
25534
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
25081
25535
|
if (specifiedPath && fs28.existsSync(specifiedPath)) return specifiedPath;
|
|
25082
25536
|
if (!fs28.existsSync(mediaDir)) return null;
|
|
25083
|
-
const files = fs28.readdirSync(mediaDir).filter((f2) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f2)).map((f2) => ({ name: f2, p:
|
|
25537
|
+
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);
|
|
25084
25538
|
return files[0]?.p || null;
|
|
25085
25539
|
}
|
|
25086
25540
|
registry.register({
|
|
@@ -25104,13 +25558,13 @@ registry.register({
|
|
|
25104
25558
|
if (!provider?.streamChat) {
|
|
25105
25559
|
return { content: "Error: provider \u4E0D\u53EF\u7528\u3002", isError: true };
|
|
25106
25560
|
}
|
|
25107
|
-
const mediaDir =
|
|
25561
|
+
const mediaDir = path28.join(ctx.stateDir, "media", "inbound");
|
|
25108
25562
|
const imagePath = resolveLatestImage(args2.image_path, mediaDir);
|
|
25109
25563
|
if (!imagePath) {
|
|
25110
25564
|
return { content: "Error: no image found. Provide image_path or ensure media/inbound has images.", isError: true };
|
|
25111
25565
|
}
|
|
25112
25566
|
const rawPrompt = args2.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
25113
|
-
const ext =
|
|
25567
|
+
const ext = path28.extname(imagePath).toLowerCase();
|
|
25114
25568
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
25115
25569
|
const imgB64 = fs28.readFileSync(imagePath).toString("base64");
|
|
25116
25570
|
const userMsg = {
|
|
@@ -25150,13 +25604,13 @@ init_registry();
|
|
|
25150
25604
|
import { execFile } from "node:child_process";
|
|
25151
25605
|
import { promisify } from "node:util";
|
|
25152
25606
|
import * as fs29 from "node:fs";
|
|
25153
|
-
import * as
|
|
25607
|
+
import * as path29 from "node:path";
|
|
25154
25608
|
import * as os3 from "node:os";
|
|
25155
25609
|
var execFileAsync = promisify(execFile);
|
|
25156
|
-
var VOICE_DIR =
|
|
25610
|
+
var VOICE_DIR = path29.join(os3.tmpdir(), "engine-voice");
|
|
25157
25611
|
async function ttsCosyvoice(text, apiKey, model, voice, workspaceId) {
|
|
25158
25612
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
25159
|
-
const output =
|
|
25613
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
25160
25614
|
const script = `
|
|
25161
25615
|
import sys, json, wave, time, threading
|
|
25162
25616
|
import dashscope
|
|
@@ -25221,7 +25675,7 @@ var GPTSOVITS_REF_TEXT = "\u6625\u7720\u4E0D\u89C9\u6653\uFF0C\u5904\u5904\u95FB
|
|
|
25221
25675
|
var GPTSOVITS_REF_LANG = "zh";
|
|
25222
25676
|
async function ttsGptsovits(text) {
|
|
25223
25677
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
25224
|
-
const output =
|
|
25678
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.wav`);
|
|
25225
25679
|
const params = new URLSearchParams({
|
|
25226
25680
|
text,
|
|
25227
25681
|
text_language: "zh",
|
|
@@ -25238,7 +25692,7 @@ async function ttsGptsovits(text) {
|
|
|
25238
25692
|
var EDGE_VOICE = "zh-CN-XiaoxiaoNeural";
|
|
25239
25693
|
async function ttsEdge(text) {
|
|
25240
25694
|
fs29.mkdirSync(VOICE_DIR, { recursive: true });
|
|
25241
|
-
const output =
|
|
25695
|
+
const output = path29.join(VOICE_DIR, `tts_${Date.now()}.mp3`);
|
|
25242
25696
|
const script = `
|
|
25243
25697
|
import asyncio, edge_tts, sys
|
|
25244
25698
|
async def main():
|
|
@@ -25331,7 +25785,7 @@ registry.register({
|
|
|
25331
25785
|
} catch (e) {
|
|
25332
25786
|
return { content: `TTS failed: ${e.message}`, isError: true };
|
|
25333
25787
|
}
|
|
25334
|
-
const ext =
|
|
25788
|
+
const ext = path29.extname(audioPath).toLowerCase();
|
|
25335
25789
|
const mimeMap = { ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".ogg": "audio/ogg" };
|
|
25336
25790
|
const mimeType = mimeMap[ext] || "audio/mpeg";
|
|
25337
25791
|
const sizeKB = fs29.statSync(audioPath).size / 1024;
|
|
@@ -25362,7 +25816,7 @@ registry.register({
|
|
|
25362
25816
|
init_live();
|
|
25363
25817
|
init_registry();
|
|
25364
25818
|
import * as fs30 from "node:fs";
|
|
25365
|
-
import * as
|
|
25819
|
+
import * as path30 from "node:path";
|
|
25366
25820
|
var FAL_KEY = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
25367
25821
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
25368
25822
|
var DEFAULT_RESOLUTION = "1k";
|
|
@@ -25478,7 +25932,7 @@ registry.register({
|
|
|
25478
25932
|
const REFERENCES = getReferences(ctx);
|
|
25479
25933
|
const refName = args2.reference || "default";
|
|
25480
25934
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
25481
|
-
const refPath =
|
|
25935
|
+
const refPath = path30.join(ctx.workspace, refEntry.p);
|
|
25482
25936
|
if (!fs30.existsSync(refPath)) {
|
|
25483
25937
|
return { content: `Error: reference image not found at ${refPath}`, isError: true };
|
|
25484
25938
|
}
|
|
@@ -25497,10 +25951,10 @@ registry.register({
|
|
|
25497
25951
|
} catch (err) {
|
|
25498
25952
|
return { content: `Selfie generation failed: ${err.message}`, isError: true };
|
|
25499
25953
|
}
|
|
25500
|
-
const imagesDir =
|
|
25954
|
+
const imagesDir = path30.join(ctx.workspace, "images");
|
|
25501
25955
|
if (!fs30.existsSync(imagesDir)) fs30.mkdirSync(imagesDir, { recursive: true });
|
|
25502
25956
|
const filename = `selfie_${Date.now()}.jpg`;
|
|
25503
|
-
const outputPath =
|
|
25957
|
+
const outputPath = path30.join(imagesDir, filename);
|
|
25504
25958
|
fs30.writeFileSync(outputPath, imageBuffer);
|
|
25505
25959
|
const mgr = ctx.channelManager;
|
|
25506
25960
|
if (mgr) {
|
|
@@ -25512,11 +25966,11 @@ registry.register({
|
|
|
25512
25966
|
mimeType: "image/jpeg"
|
|
25513
25967
|
});
|
|
25514
25968
|
} catch (err) {
|
|
25515
|
-
return { content: `Selfie generated but send failed: ${err.message}. Image: ${
|
|
25969
|
+
return { content: `Selfie generated but send failed: ${err.message}. Image: ${path30.resolve(outputPath)}`, isError: false };
|
|
25516
25970
|
}
|
|
25517
25971
|
return { content: `Selfie sent! Mode: ${mode}, Provider: ${getProvider(ctx)}, Ref: ${refEntry.name}` };
|
|
25518
25972
|
}
|
|
25519
|
-
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${
|
|
25973
|
+
return { content: `Selfie generated! Mode: ${mode}, Ref: ${refEntry.name}. Image: ${path30.resolve(outputPath)}` };
|
|
25520
25974
|
},
|
|
25521
25975
|
isConcurrencySafe: () => false,
|
|
25522
25976
|
interruptBehavior: () => "block",
|
|
@@ -25983,14 +26437,14 @@ init_planModeState();
|
|
|
25983
26437
|
|
|
25984
26438
|
// src/utils/plans.ts
|
|
25985
26439
|
import * as fs32 from "node:fs";
|
|
25986
|
-
import * as
|
|
26440
|
+
import * as path32 from "node:path";
|
|
25987
26441
|
import * as crypto4 from "node:crypto";
|
|
25988
26442
|
var MAX_SLUG_RETRIES = 10;
|
|
25989
26443
|
function generateSlug() {
|
|
25990
26444
|
return crypto4.randomBytes(4).toString("hex");
|
|
25991
26445
|
}
|
|
25992
26446
|
function getPlansDirectory(stateDir) {
|
|
25993
|
-
const plansDir =
|
|
26447
|
+
const plansDir = path32.join(stateDir, "plans");
|
|
25994
26448
|
fs32.mkdirSync(plansDir, { recursive: true });
|
|
25995
26449
|
return plansDir;
|
|
25996
26450
|
}
|
|
@@ -26001,7 +26455,7 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
26001
26455
|
const plansDir = getPlansDirectory(stateDir);
|
|
26002
26456
|
for (let i = 0; i < MAX_SLUG_RETRIES; i++) {
|
|
26003
26457
|
slug = generateSlug();
|
|
26004
|
-
const filePath =
|
|
26458
|
+
const filePath = path32.join(plansDir, `${slug}.md`);
|
|
26005
26459
|
if (!fs32.existsSync(filePath)) {
|
|
26006
26460
|
break;
|
|
26007
26461
|
}
|
|
@@ -26013,9 +26467,9 @@ function getPlanSlug(sessionId, stateDir) {
|
|
|
26013
26467
|
function getPlanFilePath(sessionId, stateDir, agentId) {
|
|
26014
26468
|
const slug = getPlanSlug(sessionId, stateDir);
|
|
26015
26469
|
if (!agentId) {
|
|
26016
|
-
return
|
|
26470
|
+
return path32.join(getPlansDirectory(stateDir), `${slug}.md`);
|
|
26017
26471
|
}
|
|
26018
|
-
return
|
|
26472
|
+
return path32.join(getPlansDirectory(stateDir), `${slug}-agent-${agentId}.md`);
|
|
26019
26473
|
}
|
|
26020
26474
|
function getPlan(sessionId, stateDir, agentId) {
|
|
26021
26475
|
const filePath = getPlanFilePath(sessionId, stateDir, agentId);
|
|
@@ -26674,14 +27128,14 @@ var EverosSearchSchema = {
|
|
|
26674
27128
|
type: "object",
|
|
26675
27129
|
properties: {
|
|
26676
27130
|
query: { type: "string", description: "\u641C\u7D22\u67E5\u8BE2" },
|
|
26677
|
-
maxResults: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u6570 (\u9ED8\
|
|
27131
|
+
maxResults: { type: "number", description: "\u6700\u5927\u8FD4\u56DE\u6570 (\u9ED8\u8BA410)" },
|
|
27132
|
+
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)" }
|
|
26678
27133
|
},
|
|
26679
27134
|
required: ["query"]
|
|
26680
27135
|
};
|
|
26681
27136
|
function createEverosSearchTool(everosCfg) {
|
|
26682
27137
|
const agenticUrl = (everosCfg?.agenticUrl || "http://127.0.0.1:8101").replace(/\/$/, "");
|
|
26683
27138
|
const userId = everosCfg?.userId || "xiaomei";
|
|
26684
|
-
const defaultMode = everosCfg?.defaultMode || "hybrid_agentic";
|
|
26685
27139
|
return {
|
|
26686
27140
|
name: "memory_search",
|
|
26687
27141
|
description: "Mandatory recall step: semantically search memory before answering questions about prior work, decisions, dates, people, preferences, or todos.",
|
|
@@ -26690,6 +27144,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26690
27144
|
const query = args2.query;
|
|
26691
27145
|
if (!query) return { content: "\u7F3A\u5C11 query \u53C2\u6570" };
|
|
26692
27146
|
const topK = args2.maxResults ?? 10;
|
|
27147
|
+
const mode = args2.mode || "hybrid_agentic";
|
|
26693
27148
|
const ctrl = new AbortController();
|
|
26694
27149
|
const timer = setTimeout(() => ctrl.abort(), 3e4);
|
|
26695
27150
|
try {
|
|
@@ -26699,7 +27154,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26699
27154
|
body: JSON.stringify({
|
|
26700
27155
|
query,
|
|
26701
27156
|
user_id: userId,
|
|
26702
|
-
mode
|
|
27157
|
+
mode,
|
|
26703
27158
|
top_k: topK
|
|
26704
27159
|
}),
|
|
26705
27160
|
signal: ctrl.signal
|
|
@@ -26707,7 +27162,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26707
27162
|
clearTimeout(timer);
|
|
26708
27163
|
if (!resp.ok) {
|
|
26709
27164
|
const text = await resp.text();
|
|
26710
|
-
console.warn(`[
|
|
27165
|
+
console.warn(`[memory_search] failed: ${resp.status} ${text.slice(0, 200)}`);
|
|
26711
27166
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26712
27167
|
}
|
|
26713
27168
|
const data = await resp.json();
|
|
@@ -26716,7 +27171,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
26716
27171
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26717
27172
|
}
|
|
26718
27173
|
const formatted = episodes.map((ep, i) => {
|
|
26719
|
-
const score = ep.score != null ? ` (score: ${ep.score.toFixed(3)})` : "";
|
|
27174
|
+
const score = ep.score != null ? ` (score: ${typeof ep.score === "number" ? ep.score.toFixed(3) : ep.score})` : "";
|
|
26720
27175
|
const subject = ep.subject || "";
|
|
26721
27176
|
const ts = ep.timestamp ? ` [${ep.timestamp}]` : "";
|
|
26722
27177
|
return `### ${i + 1}. ${subject}${ts}${score}
|
|
@@ -26728,9 +27183,9 @@ ${formatted}` };
|
|
|
26728
27183
|
} catch (err) {
|
|
26729
27184
|
clearTimeout(timer);
|
|
26730
27185
|
if (err.name === "AbortError") {
|
|
26731
|
-
console.warn(`[
|
|
27186
|
+
console.warn(`[memory_search] timeout: ${query.slice(0, 50)}`);
|
|
26732
27187
|
} else {
|
|
26733
|
-
console.warn(`[
|
|
27188
|
+
console.warn(`[memory_search] error: ${err.message}`);
|
|
26734
27189
|
}
|
|
26735
27190
|
return { content: "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u8BB0\u5FC6\u3002" };
|
|
26736
27191
|
}
|
|
@@ -27229,9 +27684,9 @@ async function startEngine(config2, opts) {
|
|
|
27229
27684
|
process.env.ENGINE_MEDIA_DIR = config2.mediaDir;
|
|
27230
27685
|
process.env.ENGINE7_WORKSPACE = config2.workspace;
|
|
27231
27686
|
process.env.OPENCLAW_WORKSPACE = config2.workspace;
|
|
27232
|
-
fs41.mkdirSync(
|
|
27233
|
-
fs41.mkdirSync(
|
|
27234
|
-
fs41.mkdirSync(
|
|
27687
|
+
fs41.mkdirSync(path43.join(config2.stateDir, "agents", "main", "memory"), { recursive: true });
|
|
27688
|
+
fs41.mkdirSync(path43.join(config2.stateDir, "agents", "main", "sessions"), { recursive: true });
|
|
27689
|
+
fs41.mkdirSync(path43.join(config2.stateDir, "logs"), { recursive: true });
|
|
27235
27690
|
fs41.mkdirSync(config2.workspace, { recursive: true });
|
|
27236
27691
|
fs41.mkdirSync(config2.mediaDir, { recursive: true });
|
|
27237
27692
|
try {
|
|
@@ -27341,7 +27796,7 @@ async function startEngine(config2, opts) {
|
|
|
27341
27796
|
const { initSessionMemory: initSessionMemory2 } = await Promise.resolve().then(() => (init_sessionMemory(), sessionMemory_exports));
|
|
27342
27797
|
initSessionMemory2({
|
|
27343
27798
|
workspace: config2.workspace,
|
|
27344
|
-
stateDir:
|
|
27799
|
+
stateDir: path43.join(config2.stateDir, "session-memory"),
|
|
27345
27800
|
provider,
|
|
27346
27801
|
model: config2.provider.modelId || config2.model || "deepseek-v4-flash",
|
|
27347
27802
|
features: config2.profile.features
|
|
@@ -27371,9 +27826,9 @@ async function startEngine(config2, opts) {
|
|
|
27371
27826
|
if (config2.hooks) {
|
|
27372
27827
|
loadHooksFromConfig({ hooks: config2.hooks });
|
|
27373
27828
|
}
|
|
27374
|
-
const hooksPath =
|
|
27829
|
+
const hooksPath = path43.join(config2.workspace, ".hooks.json");
|
|
27375
27830
|
loadHooksFromFile(hooksPath);
|
|
27376
|
-
const settingsHooksPath =
|
|
27831
|
+
const settingsHooksPath = path43.join(config2.stateDir, "settings.json");
|
|
27377
27832
|
loadHooksFromFile(settingsHooksPath);
|
|
27378
27833
|
console.log(`[hooks] Loaded hooks configuration`);
|
|
27379
27834
|
registerCallbackHook("PreCompact", {
|
|
@@ -27387,15 +27842,15 @@ async function startEngine(config2, opts) {
|
|
|
27387
27842
|
const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
27388
27843
|
const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
|
|
27389
27844
|
const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
27390
|
-
const dailyDir =
|
|
27391
|
-
const dailyPath =
|
|
27845
|
+
const dailyDir = path43.join(workspace, "memory", "daily");
|
|
27846
|
+
const dailyPath = path43.join(dailyDir, `${dateStr}.md`);
|
|
27392
27847
|
try {
|
|
27393
27848
|
const fs42 = await import("node:fs");
|
|
27394
27849
|
if (!fs42.existsSync(dailyDir)) {
|
|
27395
27850
|
fs42.mkdirSync(dailyDir, { recursive: true });
|
|
27396
27851
|
}
|
|
27397
|
-
const sessionsDir =
|
|
27398
|
-
const sessionFile =
|
|
27852
|
+
const sessionsDir = path43.join(config2.stateDir, "agents", "main", "sessions");
|
|
27853
|
+
const sessionFile = path43.join(sessionsDir, `${sessionId}.jsonl`);
|
|
27399
27854
|
const recentLines = [];
|
|
27400
27855
|
if (fs42.existsSync(sessionFile)) {
|
|
27401
27856
|
const content = fs42.readFileSync(sessionFile, "utf-8");
|
|
@@ -27448,7 +27903,7 @@ ${entry}`);
|
|
|
27448
27903
|
if (!workspace) return { continue: true };
|
|
27449
27904
|
try {
|
|
27450
27905
|
const fs42 = await import("node:fs");
|
|
27451
|
-
const bufferPath =
|
|
27906
|
+
const bufferPath = path43.join(workspace, "memory", "working-buffer.md");
|
|
27452
27907
|
if (fs42.existsSync(bufferPath)) {
|
|
27453
27908
|
const stat4 = fs42.statSync(bufferPath);
|
|
27454
27909
|
const ageMs = Date.now() - stat4.mtimeMs;
|
|
@@ -27501,7 +27956,7 @@ ${content}`
|
|
|
27501
27956
|
return `${hr}h ${remMin}m`;
|
|
27502
27957
|
}
|
|
27503
27958
|
if (config2.skills?.enabled !== false) {
|
|
27504
|
-
const skillsDir = config2.skills?.path ?
|
|
27959
|
+
const skillsDir = config2.skills?.path ? path43.isAbsolute(config2.skills.path) ? config2.skills.path : path43.resolve(config2.workspace, config2.skills.path) : path43.resolve(config2.workspace, "skills");
|
|
27505
27960
|
const modelDef2 = config2.provider.models.find((m2) => m2.id === config2.model);
|
|
27506
27961
|
const contextWindowTokens = modelDef2?.contextWindow;
|
|
27507
27962
|
const skills = scanSkills(skillsDir);
|
|
@@ -27520,7 +27975,7 @@ ${content}`
|
|
|
27520
27975
|
workspace: config2.workspace
|
|
27521
27976
|
});
|
|
27522
27977
|
const systemPrompt = [systemStable, systemDynamic].join("\n\n");
|
|
27523
|
-
const promptDumpPath =
|
|
27978
|
+
const promptDumpPath = path43.join(config2.workspace, ".system-prompt.txt");
|
|
27524
27979
|
fs41.writeFileSync(promptDumpPath, systemPrompt);
|
|
27525
27980
|
console.log(`System prompt: ${systemStable.length} chars stable + ${systemDynamic.length} chars dynamic \u2192 ${promptDumpPath}`);
|
|
27526
27981
|
const modelDef = config2.provider.models.find((m2) => m2.id === config2.model);
|
|
@@ -27631,20 +28086,23 @@ ${content}`
|
|
|
27631
28086
|
enabled: true,
|
|
27632
28087
|
url: everosCfg.everosUrl || "http://127.0.0.1:8100",
|
|
27633
28088
|
appId: everosCfg.userId || "default",
|
|
27634
|
-
userId: everosCfg.userId || "default"
|
|
28089
|
+
userId: everosCfg.userId || "default",
|
|
28090
|
+
agentName: everosCfg.agentName || everosCfg.userId || "assistant"
|
|
27635
28091
|
});
|
|
27636
28092
|
sessions.onWriterCreated = (writer, sessionId) => {
|
|
27637
28093
|
writer.onMessageWritten = (msg2) => {
|
|
28094
|
+
console.log(`[everos-sync] onMessageWritten fired: role=${msg2.role} len=${msg2.text.length}`);
|
|
27638
28095
|
everosSync.push({
|
|
27639
28096
|
sessionId: writer.engineSessionId || sessionId,
|
|
27640
28097
|
role: msg2.role === "toolResult" ? "tool" : msg2.role,
|
|
27641
28098
|
text: msg2.text,
|
|
27642
28099
|
timestamp: new Date(msg2.timestamp).getTime()
|
|
27643
|
-
}).catch(() => {
|
|
27644
|
-
});
|
|
28100
|
+
}).catch((e) => console.warn(`[everos-sync] push error: ${e}`));
|
|
27645
28101
|
};
|
|
27646
28102
|
};
|
|
27647
28103
|
console.log(`[everos-sync] hook registered (appId=${everosCfg.userId})`);
|
|
28104
|
+
} else {
|
|
28105
|
+
console.log(`[everos-sync] SKIPPED \u2014 config.everos not enabled or missing`);
|
|
27648
28106
|
}
|
|
27649
28107
|
const channelManager = new ChannelManager();
|
|
27650
28108
|
const memoryRecallProvider = createMemorySideProvider(
|
|
@@ -27675,6 +28133,7 @@ ${content}`
|
|
|
27675
28133
|
recallProvider: memoryRecallProvider || void 0,
|
|
27676
28134
|
extractProvider: memoryExtractProvider || void 0,
|
|
27677
28135
|
topics: config2.topics,
|
|
28136
|
+
everosCfg: config2.everos,
|
|
27678
28137
|
mcpManager
|
|
27679
28138
|
};
|
|
27680
28139
|
if (visionEngine && visionConfig) {
|
|
@@ -28595,8 +29054,8 @@ Auto-routing disabled \u2014 all messages use this model.
|
|
|
28595
29054
|
let writePath = configPath2;
|
|
28596
29055
|
if (configPath2 && !fs41.existsSync(configPath2)) {
|
|
28597
29056
|
const __pFile = fileURLToPath(import.meta.url);
|
|
28598
|
-
const __pDir =
|
|
28599
|
-
const altPath =
|
|
29057
|
+
const __pDir = path43.dirname(__pFile);
|
|
29058
|
+
const altPath = path43.join(path43.resolve(__pDir, "../configs"), path43.basename(configPath2));
|
|
28600
29059
|
if (fs41.existsSync(altPath)) {
|
|
28601
29060
|
console.warn(`[primary] Config not found at ${configPath2}, falling back to ${altPath}`);
|
|
28602
29061
|
writePath = altPath;
|
|
@@ -28876,7 +29335,7 @@ Use full ref like \`/vision-model ${candidates[0].ref}\``);
|
|
|
28876
29335
|
const ext = detected.split("/")[1] || "png";
|
|
28877
29336
|
const resized = await maybeResizeAndDownsampleImageBuffer2(rawBuffer, rawBuffer.length, ext);
|
|
28878
29337
|
const imageId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
28879
|
-
const savedPath =
|
|
29338
|
+
const savedPath = path43.join(config2.mediaDir, `${imageId}.${ext}`);
|
|
28880
29339
|
fs41.writeFileSync(savedPath, resized.buffer);
|
|
28881
29340
|
savedPaths.push(savedPath);
|
|
28882
29341
|
console.log(`[vision] Saved: ${savedPath} (${resized.buffer.length}B)`);
|
|
@@ -28903,7 +29362,7 @@ ${pathStr}` }];
|
|
|
28903
29362
|
}
|
|
28904
29363
|
const nonImageAttachments = inbound.attachments?.filter((a) => !a.contentType.startsWith("image/"));
|
|
28905
29364
|
if (nonImageAttachments && nonImageAttachments.length > 0) {
|
|
28906
|
-
const outDir =
|
|
29365
|
+
const outDir = path43.join(config2.mediaDir, sessionId);
|
|
28907
29366
|
fs41.mkdirSync(outDir, { recursive: true });
|
|
28908
29367
|
const resolved = [];
|
|
28909
29368
|
for (const att of nonImageAttachments) {
|
|
@@ -28912,8 +29371,8 @@ ${pathStr}` }];
|
|
|
28912
29371
|
const resp = await fetch(att.url);
|
|
28913
29372
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
|
28914
29373
|
const buffer = Buffer.from(await resp.arrayBuffer());
|
|
28915
|
-
const safeName2 =
|
|
28916
|
-
const savedPath =
|
|
29374
|
+
const safeName2 = path43.basename(att.filename).replace(/[<>:"/\\|?*\x00-\x1f]/g, "_") || "attachment";
|
|
29375
|
+
const savedPath = path43.join(outDir, safeName2);
|
|
28917
29376
|
fs41.writeFileSync(savedPath, buffer);
|
|
28918
29377
|
resolved.push(savedPath);
|
|
28919
29378
|
console.log(`[file] Saved: ${savedPath} (${buffer.length}B)`);
|
|
@@ -29261,7 +29720,8 @@ ${pathStr}` }];
|
|
|
29261
29720
|
if (config2.cognifold?.intentWatcher?.enabled) {
|
|
29262
29721
|
try {
|
|
29263
29722
|
const { registerCognifoldIntentWatcher: registerCognifoldIntentWatcher2 } = await Promise.resolve().then(() => (init_cognifold_intent_watcher(), cognifold_intent_watcher_exports));
|
|
29264
|
-
const
|
|
29723
|
+
const sm = globalThis.__cognifoldSessions;
|
|
29724
|
+
const cfSessionId = sm?.getSessionId?.("main") || config2.cognifold.sessionId;
|
|
29265
29725
|
if (!cfSessionId) {
|
|
29266
29726
|
console.warn("[cognifold] intent-watcher: config.cognifold.sessionId \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 watcher");
|
|
29267
29727
|
} else {
|
|
@@ -29274,7 +29734,7 @@ ${pathStr}` }];
|
|
|
29274
29734
|
console.warn("[cognifold] watcher: config.workspace \u672A\u914D\u7F6E\uFF0C\u8DF3\u8FC7 proactive \u5199\u5165");
|
|
29275
29735
|
return;
|
|
29276
29736
|
}
|
|
29277
|
-
const pFile =
|
|
29737
|
+
const pFile = path43.join(wsDir, ".cognifold-proactive.json");
|
|
29278
29738
|
const cognifoldBaseUrl = config2.cognifold?.baseUrl || "http://127.0.0.1:9001";
|
|
29279
29739
|
const cognifoldSessionId = cfSessionId;
|
|
29280
29740
|
const rawSuggestions = data.suggestions || data.actions || (data.intent_id ? [data] : []);
|
|
@@ -29328,7 +29788,7 @@ ${pathStr}` }];
|
|
|
29328
29788
|
console.error(`[cognifold] failed to save proactive: ${e.message}`);
|
|
29329
29789
|
}
|
|
29330
29790
|
if (enriched.length > 0) {
|
|
29331
|
-
const promptFile =
|
|
29791
|
+
const promptFile = path43.join(config2.workspace, "prompts", "cognifold-proactive.md");
|
|
29332
29792
|
const promptText = fs41.existsSync(promptFile) ? fs41.readFileSync(promptFile, "utf-8") : "[CogniFold proactive] \u6709 " + enriched.length + " \u4E2A action \u5230\u671F\u4E86";
|
|
29333
29793
|
const actionsJson = JSON.stringify(enriched, null, 2);
|
|
29334
29794
|
const sessionId = cfSessionId;
|
|
@@ -29434,9 +29894,9 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
29434
29894
|
let reloadConfigPath = savedConfigPath;
|
|
29435
29895
|
if (!fs41.existsSync(reloadConfigPath)) {
|
|
29436
29896
|
const __filename = fileURLToPath(import.meta.url);
|
|
29437
|
-
const __dirname =
|
|
29438
|
-
const engineConfigsDir =
|
|
29439
|
-
const altPath =
|
|
29897
|
+
const __dirname = path43.dirname(__filename);
|
|
29898
|
+
const engineConfigsDir = path43.resolve(__dirname, "../configs");
|
|
29899
|
+
const altPath = path43.join(engineConfigsDir, path43.basename(savedConfigPath));
|
|
29440
29900
|
if (fs41.existsSync(altPath)) {
|
|
29441
29901
|
console.warn(`[reload] Config not found at ${reloadConfigPath}, falling back to ${altPath} (dev mode)`);
|
|
29442
29902
|
reloadConfigPath = altPath;
|
|
@@ -29489,7 +29949,7 @@ async function doReloadConfig(config2, deps, provider) {
|
|
|
29489
29949
|
} catch (err) {
|
|
29490
29950
|
console.error(`[reload] Failed: ${err.message}`);
|
|
29491
29951
|
try {
|
|
29492
|
-
fs41.appendFileSync(
|
|
29952
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD FAILED: ${err.message}
|
|
29493
29953
|
${err.stack}
|
|
29494
29954
|
`);
|
|
29495
29955
|
} catch {
|
|
@@ -29501,17 +29961,17 @@ function startConfigWatcher(config2, deps, provider) {
|
|
|
29501
29961
|
const raw = config2._configFilePath;
|
|
29502
29962
|
let configPath2 = raw;
|
|
29503
29963
|
if (!fs41.existsSync(configPath2)) {
|
|
29504
|
-
configPath2 =
|
|
29964
|
+
configPath2 = path43.resolve(raw);
|
|
29505
29965
|
}
|
|
29506
29966
|
if (!fs41.existsSync(configPath2)) {
|
|
29507
29967
|
const __filename2 = fileURLToPath(import.meta.url);
|
|
29508
|
-
const __dirname22 =
|
|
29509
|
-
configPath2 =
|
|
29968
|
+
const __dirname22 = path43.dirname(__filename2);
|
|
29969
|
+
configPath2 = path43.resolve(__dirname22, "..", raw);
|
|
29510
29970
|
}
|
|
29511
29971
|
if (!fs41.existsSync(configPath2)) {
|
|
29512
29972
|
console.warn(`[config-watch] config path invalid: ${configPath2}, watcher disabled`);
|
|
29513
29973
|
try {
|
|
29514
|
-
fs41.appendFileSync(
|
|
29974
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] DISABLED: configPath=${configPath2}
|
|
29515
29975
|
`);
|
|
29516
29976
|
} catch {
|
|
29517
29977
|
}
|
|
@@ -29523,13 +29983,13 @@ function startConfigWatcher(config2, deps, provider) {
|
|
|
29523
29983
|
debounceTimer = setTimeout(async () => {
|
|
29524
29984
|
console.log(`[config-watch] file changed (${eventType}), reloading...`);
|
|
29525
29985
|
try {
|
|
29526
|
-
fs41.appendFileSync(
|
|
29986
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] CHANGE eventType=${eventType}, calling doReloadConfig
|
|
29527
29987
|
`);
|
|
29528
29988
|
} catch {
|
|
29529
29989
|
}
|
|
29530
29990
|
const result = await doReloadConfig(config2, deps, provider);
|
|
29531
29991
|
try {
|
|
29532
|
-
fs41.appendFileSync(
|
|
29992
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] RELOAD DONE: ok=${result.ok} changes=${result.changes.join(",")}
|
|
29533
29993
|
`);
|
|
29534
29994
|
} catch {
|
|
29535
29995
|
}
|
|
@@ -29538,14 +29998,14 @@ function startConfigWatcher(config2, deps, provider) {
|
|
|
29538
29998
|
watcher.on("error", (err) => {
|
|
29539
29999
|
console.error(`[config-watch] error: ${err.message}`);
|
|
29540
30000
|
try {
|
|
29541
|
-
fs41.appendFileSync(
|
|
30001
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${err.message}
|
|
29542
30002
|
`);
|
|
29543
30003
|
} catch {
|
|
29544
30004
|
}
|
|
29545
30005
|
});
|
|
29546
30006
|
console.log(`[config-watch] watching ${configPath2}`);
|
|
29547
30007
|
try {
|
|
29548
|
-
fs41.appendFileSync(
|
|
30008
|
+
fs41.appendFileSync(path43.join(config2.stateDir, "logs", "engine-config-watch.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] STARTED watching=${configPath2}
|
|
29549
30009
|
`);
|
|
29550
30010
|
} catch {
|
|
29551
30011
|
}
|