engine7 7.1.84 → 7.1.95
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 +1630 -528
- package/dist/engine-startup.mjs +1530 -578
- package/dist/main.mjs +1558 -603
- package/package.json +6 -4
- package/templates/config.template.json +11 -0
- package/templates/workspace/SESSION-STATE.md +1 -1
- package/templates/workspace/emotion/_default.md +33 -0
- package/templates/workspace/emotion/assistant.md +36 -0
- package/templates/workspace/emotion/butler.md +36 -0
- package/templates/workspace/emotion/companion.md +36 -0
- package/templates/workspace/emotion/friend.md +36 -0
- package/templates/workspace/emotion-states/assistant.json +22 -0
- package/templates/workspace/emotion-states/butler.json +24 -0
- package/templates/workspace/emotion-states/companion.json +30 -0
- package/templates/workspace/emotion-states/friend.json +24 -0
- package/templates/workspace/inner-voice/assistant.md +9 -0
- package/templates/workspace/inner-voice/butler.md +9 -0
- package/templates/workspace/inner-voice/companion.md +9 -0
- package/templates/workspace/inner-voice/friend.md +9 -0
- package/templates/workspace/scripts/__pycache__/emotion_state_machine.cpython-310.pyc +0 -0
- package/templates/workspace/scripts/emotion_state_machine.mjs +588 -0
- package/templates/workspace/scripts/emotion_state_machine.py +551 -0
- package/templates/workspace/souls/assistant.md +20 -0
- package/templates/workspace/souls/butler.md +21 -0
- package/templates/workspace/souls/companion.md +20 -0
- package/templates/workspace/souls/friend.md +21 -0
package/dist/main.mjs
CHANGED
|
@@ -207,12 +207,205 @@ var init_features = __esm({
|
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
209
|
|
|
210
|
+
// src/time/clock.ts
|
|
211
|
+
function detectHostTimezone() {
|
|
212
|
+
try {
|
|
213
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
214
|
+
} catch {
|
|
215
|
+
return "UTC";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function isValidTimezone(name) {
|
|
219
|
+
if (!name) return false;
|
|
220
|
+
try {
|
|
221
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
222
|
+
return true;
|
|
223
|
+
} catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function tz() {
|
|
228
|
+
if (testTimezone) return testTimezone;
|
|
229
|
+
const v = liveConfig.get("timezone");
|
|
230
|
+
const name = typeof v === "string" && v ? v : "UTC";
|
|
231
|
+
if (validatedZones.has(name)) return name;
|
|
232
|
+
if (isValidTimezone(name)) {
|
|
233
|
+
validatedZones.add(name);
|
|
234
|
+
return name;
|
|
235
|
+
}
|
|
236
|
+
warnOnce("tz", "invalid-zone", name);
|
|
237
|
+
return "UTC";
|
|
238
|
+
}
|
|
239
|
+
function partsFmt(zone) {
|
|
240
|
+
let f = fmtCache.get(zone);
|
|
241
|
+
if (!f) {
|
|
242
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
243
|
+
timeZone: zone,
|
|
244
|
+
year: "numeric",
|
|
245
|
+
month: "2-digit",
|
|
246
|
+
day: "2-digit",
|
|
247
|
+
hour: "2-digit",
|
|
248
|
+
minute: "2-digit",
|
|
249
|
+
second: "2-digit",
|
|
250
|
+
hourCycle: "h23",
|
|
251
|
+
weekday: "short"
|
|
252
|
+
});
|
|
253
|
+
fmtCache.set(zone, f);
|
|
254
|
+
}
|
|
255
|
+
return f;
|
|
256
|
+
}
|
|
257
|
+
function asDate(i) {
|
|
258
|
+
return i instanceof Date ? i : new Date(i);
|
|
259
|
+
}
|
|
260
|
+
function wallParts(i = /* @__PURE__ */ new Date()) {
|
|
261
|
+
const d = asDate(i);
|
|
262
|
+
const parts = partsFmt(tz()).formatToParts(d);
|
|
263
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
264
|
+
return {
|
|
265
|
+
y: Number(get("year")),
|
|
266
|
+
mo: Number(get("month")),
|
|
267
|
+
d: Number(get("day")),
|
|
268
|
+
hh: Number(get("hour")),
|
|
269
|
+
mm: Number(get("minute")),
|
|
270
|
+
ss: Number(get("second")),
|
|
271
|
+
weekday: EN_WEEKDAY[get("weekday")] ?? 0
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function weekdayZh(i) {
|
|
275
|
+
return WEEKDAY_ZH[wallParts(i).weekday];
|
|
276
|
+
}
|
|
277
|
+
function nowUtc() {
|
|
278
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
279
|
+
}
|
|
280
|
+
function toUtc(d) {
|
|
281
|
+
return d.toISOString();
|
|
282
|
+
}
|
|
283
|
+
function toWall(i = /* @__PURE__ */ new Date()) {
|
|
284
|
+
const p = wallParts(i);
|
|
285
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${p2(p.hh)}:${p2(p.mm)}:${p2(p.ss)}`;
|
|
286
|
+
}
|
|
287
|
+
function formatForLlm(i) {
|
|
288
|
+
const d = i ?? /* @__PURE__ */ new Date();
|
|
289
|
+
const p = wallParts(d);
|
|
290
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${WEEKDAY_ZH[p.weekday]} ${p2(p.hh)}:${p2(p.mm)}\uFF08${tz()}\uFF09`;
|
|
291
|
+
}
|
|
292
|
+
function formatLog(i) {
|
|
293
|
+
return toWall(i ?? /* @__PURE__ */ new Date());
|
|
294
|
+
}
|
|
295
|
+
function formatWallLabel(i) {
|
|
296
|
+
return formatForLlm(i).replace(/\s*(.*)$/, "");
|
|
297
|
+
}
|
|
298
|
+
function warnOnce(callSite, shape, sample) {
|
|
299
|
+
const key = `${callSite}:${shape}`;
|
|
300
|
+
if (warned.has(key)) return;
|
|
301
|
+
warned.add(key);
|
|
302
|
+
console.warn(`[clock] ${callSite} \u5951\u7EA6\u8FDD\u89C4(${shape})\uFF0C\u6837\u672C: ${String(sample).slice(0, 80)}`);
|
|
303
|
+
}
|
|
304
|
+
function zoneOffsetMs(d) {
|
|
305
|
+
const p = wallParts(d);
|
|
306
|
+
const asUtc = Date.UTC(p.y, p.mo - 1, p.d, p.hh, p.mm, p.ss);
|
|
307
|
+
return asUtc - Math.floor(d.getTime() / 1e3) * 1e3;
|
|
308
|
+
}
|
|
309
|
+
function wallToUtc(y, mo, d, hh, mm, ss) {
|
|
310
|
+
const guess = Date.UTC(y, mo - 1, d, hh, mm, ss);
|
|
311
|
+
const off1 = zoneOffsetMs(new Date(guess));
|
|
312
|
+
const ms1 = guess - off1;
|
|
313
|
+
const off2 = zoneOffsetMs(new Date(ms1));
|
|
314
|
+
const ms = off2 === off1 ? ms1 : guess - off2;
|
|
315
|
+
return new Date(ms).toISOString();
|
|
316
|
+
}
|
|
317
|
+
function parseWall(s) {
|
|
318
|
+
const raw = String(s ?? "").trim();
|
|
319
|
+
if (!raw) return "";
|
|
320
|
+
if (/Z$/i.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
|
|
321
|
+
const ms2 = Date.parse(raw);
|
|
322
|
+
if (Number.isNaN(ms2)) {
|
|
323
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
324
|
+
return raw;
|
|
325
|
+
}
|
|
326
|
+
warnOnce("parseWall", "has-offset", raw);
|
|
327
|
+
return new Date(ms2).toISOString();
|
|
328
|
+
}
|
|
329
|
+
const m = WALL_RE.exec(raw);
|
|
330
|
+
if (!m) {
|
|
331
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
332
|
+
return raw;
|
|
333
|
+
}
|
|
334
|
+
const tail = raw.slice(m[0].length);
|
|
335
|
+
if (tail && !/^\.\d+$/.test(tail)) {
|
|
336
|
+
warnOnce("parseWall", "unparseable-tail", raw);
|
|
337
|
+
return raw;
|
|
338
|
+
}
|
|
339
|
+
const [Y, Mo, D, H, Mi, S] = [+m[1], +m[2], +m[3], +m[4], +m[5], m[6] ? +m[6] : 0];
|
|
340
|
+
if (Y < 1e3 || Y > 9999 || Mo < 1 || Mo > 12 || D < 1 || D > daysInMonth(Y, Mo) || H > 23 || Mi > 59 || S > 59) {
|
|
341
|
+
warnOnce("parseWall", "out-of-range", raw);
|
|
342
|
+
return raw;
|
|
343
|
+
}
|
|
344
|
+
const ms = tail ? Math.round(Number(`0${tail}`) * 1e3) : 0;
|
|
345
|
+
return withMs(wallToUtc(Y, Mo, D, H, Mi, S), ms);
|
|
346
|
+
}
|
|
347
|
+
function daysInMonth(y, mo) {
|
|
348
|
+
return new Date(Date.UTC(y, mo, 0)).getUTCDate();
|
|
349
|
+
}
|
|
350
|
+
function withMs(base, ms) {
|
|
351
|
+
if (!ms) return base;
|
|
352
|
+
const t = Date.parse(base);
|
|
353
|
+
return Number.isNaN(t) ? base : new Date(t + ms).toISOString();
|
|
354
|
+
}
|
|
355
|
+
function parseDateStrTime(dateStr, timeExact) {
|
|
356
|
+
if (typeof dateStr !== "string" || typeof timeExact !== "string") return void 0;
|
|
357
|
+
if (!/^\d{1,2}\/\d{1,2}$/.test(dateStr)) return void 0;
|
|
358
|
+
if (!/^\d{2}:\d{2}$/.test(timeExact)) return void 0;
|
|
359
|
+
const [mo, d] = dateStr.split("/").map(Number);
|
|
360
|
+
if (mo < 1 || mo > 12 || d < 1 || d > 31) return void 0;
|
|
361
|
+
const y = wallParts().y;
|
|
362
|
+
return parseWall(`${y}-${p2(mo)}-${p2(d)}T${timeExact}:00`);
|
|
363
|
+
}
|
|
364
|
+
function localOffsetSuffix(at) {
|
|
365
|
+
const total = Math.round(zoneOffsetMs(at ? asDate(at) : /* @__PURE__ */ new Date()) / 6e4);
|
|
366
|
+
const sign = total >= 0 ? "+" : "-";
|
|
367
|
+
const abs = Math.abs(total);
|
|
368
|
+
return `${sign}${p2(Math.floor(abs / 60))}:${p2(abs % 60)}`;
|
|
369
|
+
}
|
|
370
|
+
function startOfLocalDay(i) {
|
|
371
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
372
|
+
return wallToUtc(p.y, p.mo, p.d, 0, 0, 0);
|
|
373
|
+
}
|
|
374
|
+
function todayKey(i) {
|
|
375
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
376
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)}`;
|
|
377
|
+
}
|
|
378
|
+
var WEEKDAY_ZH, EN_WEEKDAY, testTimezone, validatedZones, fmtCache, p2, warned, WALL_RE;
|
|
379
|
+
var init_clock = __esm({
|
|
380
|
+
"src/time/clock.ts"() {
|
|
381
|
+
"use strict";
|
|
382
|
+
init_live();
|
|
383
|
+
WEEKDAY_ZH = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
384
|
+
EN_WEEKDAY = {
|
|
385
|
+
Sun: 0,
|
|
386
|
+
Mon: 1,
|
|
387
|
+
Tue: 2,
|
|
388
|
+
Wed: 3,
|
|
389
|
+
Thu: 4,
|
|
390
|
+
Fri: 5,
|
|
391
|
+
Sat: 6
|
|
392
|
+
};
|
|
393
|
+
testTimezone = null;
|
|
394
|
+
validatedZones = /* @__PURE__ */ new Set();
|
|
395
|
+
fmtCache = /* @__PURE__ */ new Map();
|
|
396
|
+
p2 = (n) => String(n).padStart(2, "0");
|
|
397
|
+
warned = /* @__PURE__ */ new Set();
|
|
398
|
+
WALL_RE = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?/;
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
|
|
210
402
|
// src/config/loader.ts
|
|
211
403
|
var loader_exports = {};
|
|
212
404
|
__export(loader_exports, {
|
|
213
405
|
configSummary: () => configSummary,
|
|
214
406
|
loadConfig: () => loadConfig,
|
|
215
407
|
parseModelRef: () => parseModelRef,
|
|
408
|
+
resolveTimezone: () => resolveTimezone,
|
|
216
409
|
takeLoadWarnings: () => takeLoadWarnings
|
|
217
410
|
});
|
|
218
411
|
import * as fs from "node:fs";
|
|
@@ -234,6 +427,24 @@ function warnAtLoad(msg2) {
|
|
|
234
427
|
if (!loadWarningsFlushed) loadWarnings.push(msg2);
|
|
235
428
|
console.warn(msg2);
|
|
236
429
|
}
|
|
430
|
+
function resolveTimezone(raw) {
|
|
431
|
+
const fallback = () => {
|
|
432
|
+
try {
|
|
433
|
+
return detectHostTimezone();
|
|
434
|
+
} catch {
|
|
435
|
+
return "UTC";
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
const v = raw?.timezone;
|
|
439
|
+
if (typeof v !== "string" || !v) return fallback();
|
|
440
|
+
try {
|
|
441
|
+
new Intl.DateTimeFormat("en-US", { timeZone: v });
|
|
442
|
+
return v;
|
|
443
|
+
} catch {
|
|
444
|
+
warnAtLoad(`[config] timezone "${v}" \u4E0D\u662F\u5408\u6CD5 IANA \u65F6\u533A\u540D\uFF0C\u9000\u56DE\u672C\u673A\u63A2\u6D4B`);
|
|
445
|
+
return fallback();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
237
448
|
function loadConfig(configPath2) {
|
|
238
449
|
const resolvedPath = path.resolve(
|
|
239
450
|
configPath2 || process.env.ENGINE_CONFIG || "engine-config.json"
|
|
@@ -245,7 +456,7 @@ function loadConfig(configPath2) {
|
|
|
245
456
|
const missingEnvKeys = /* @__PURE__ */ new Set();
|
|
246
457
|
const resolveEnv = (node) => {
|
|
247
458
|
if (typeof node === "string") {
|
|
248
|
-
const m = node.match(/^(?:env:|{{\s*)([A-Z][A-Z0-9_]
|
|
459
|
+
const m = node.match(/^(?:env:|{{\s*)(?=[A-Z0-9_]*[A-Z])([A-Z0-9_]+)(?:\s*}})?$/);
|
|
249
460
|
if (m) {
|
|
250
461
|
if (process.env[m[1]] !== void 0) return process.env[m[1]];
|
|
251
462
|
if (node.startsWith("env:")) missingEnvKeys.add(m[1]);
|
|
@@ -370,11 +581,18 @@ function loadConfig(configPath2) {
|
|
|
370
581
|
...rawInnerVoice,
|
|
371
582
|
enabled: true
|
|
372
583
|
} : void 0;
|
|
584
|
+
const resolvedTimezone = resolveTimezone(raw);
|
|
373
585
|
const rawCron = raw.cron || {};
|
|
374
586
|
const cron = rawCron.enabled ? {
|
|
375
587
|
...rawCron,
|
|
376
588
|
enabled: true,
|
|
377
|
-
|
|
589
|
+
// ★ 这里原来写死 'Asia/Shanghai'。而 cron/tasks.ts 建任务时是
|
|
590
|
+
// `params.timezone || params.defaultTimezone || 'Asia/Shanghai'` ——
|
|
591
|
+
// defaultTimezone 恒有值 ⇒ 兜底永远轮不到 ⇒ **工具建的每个任务都被
|
|
592
|
+
// 显式写死北京时区存进 tasks.json**,顶层 timezone 配置形同虚设
|
|
593
|
+
// (马耳他用户说"每天 9 点",会在当地凌晨 3 点触发)。
|
|
594
|
+
// 改为跟随引擎时区;per-task 的显式 timezone 仍然优先。
|
|
595
|
+
defaultTimezone: rawCron.defaultTimezone || resolvedTimezone,
|
|
378
596
|
tickIntervalMs: rawCron.tickIntervalMs || 1e4,
|
|
379
597
|
maxConsecutiveFailures: rawCron.maxConsecutiveFailures || 5,
|
|
380
598
|
retry: {
|
|
@@ -387,6 +605,7 @@ function loadConfig(configPath2) {
|
|
|
387
605
|
return {
|
|
388
606
|
...raw,
|
|
389
607
|
// 先展开 raw:未显式处理的字段(如 webhook)自动进来,避免每次加 config 都要手动挑;下面显式字段覆盖 raw(解析值优先)
|
|
608
|
+
timezone: resolvedTimezone,
|
|
390
609
|
provider,
|
|
391
610
|
providers,
|
|
392
611
|
// 所有 provider 配置字典(供 memory/provider 动态切换用)
|
|
@@ -455,6 +674,7 @@ var init_loader = __esm({
|
|
|
455
674
|
"use strict";
|
|
456
675
|
init_display();
|
|
457
676
|
init_features();
|
|
677
|
+
init_clock();
|
|
458
678
|
loadWarnings = [];
|
|
459
679
|
loadWarningsFlushed = false;
|
|
460
680
|
}
|
|
@@ -704,12 +924,15 @@ function normalizeHook(h) {
|
|
|
704
924
|
}
|
|
705
925
|
return {
|
|
706
926
|
type: "command",
|
|
707
|
-
command: h.command || "",
|
|
927
|
+
command: expandCommand(h.command || ""),
|
|
708
928
|
timeout: h.timeout,
|
|
709
929
|
async: h.async,
|
|
710
930
|
shell: h.shell
|
|
711
931
|
};
|
|
712
932
|
}
|
|
933
|
+
function expandCommand(command) {
|
|
934
|
+
return command.replace(/\$\{E7_NODE\}/g, process.execPath);
|
|
935
|
+
}
|
|
713
936
|
function registerCallbackHook(event2, hook) {
|
|
714
937
|
const list2 = callbackHooks.get(event2) || [];
|
|
715
938
|
if (hook.id) {
|
|
@@ -2200,6 +2423,29 @@ var init_microCompact = __esm({
|
|
|
2200
2423
|
}
|
|
2201
2424
|
});
|
|
2202
2425
|
|
|
2426
|
+
// src/utils/well-formed.ts
|
|
2427
|
+
function truncateByCodePoint(text, max) {
|
|
2428
|
+
if (text.length <= max) return text;
|
|
2429
|
+
return Array.from(text).slice(0, max).join("");
|
|
2430
|
+
}
|
|
2431
|
+
function toWellFormed(s) {
|
|
2432
|
+
if (HAS_NATIVE) return s.toWellFormed();
|
|
2433
|
+
return s.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
2434
|
+
}
|
|
2435
|
+
function wellFormedReplacer(_key, value) {
|
|
2436
|
+
return typeof value === "string" ? toWellFormed(value) : value;
|
|
2437
|
+
}
|
|
2438
|
+
function safeStringify(body) {
|
|
2439
|
+
return JSON.stringify(body, wellFormedReplacer);
|
|
2440
|
+
}
|
|
2441
|
+
var HAS_NATIVE;
|
|
2442
|
+
var init_well_formed = __esm({
|
|
2443
|
+
"src/utils/well-formed.ts"() {
|
|
2444
|
+
"use strict";
|
|
2445
|
+
HAS_NATIVE = typeof String.prototype.toWellFormed === "function";
|
|
2446
|
+
}
|
|
2447
|
+
});
|
|
2448
|
+
|
|
2203
2449
|
// src/compact/ruleCompact.ts
|
|
2204
2450
|
function smartCompressToolResult(content, essentialFields, sizeLimit) {
|
|
2205
2451
|
if (content.length <= (sizeLimit ?? TOOL_SIZE_LIMIT)) return content;
|
|
@@ -2241,7 +2487,7 @@ ${result}`;
|
|
|
2241
2487
|
}
|
|
2242
2488
|
} catch {
|
|
2243
2489
|
}
|
|
2244
|
-
return content
|
|
2490
|
+
return truncateByCodePoint(content, sizeLimit ?? TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
2245
2491
|
}
|
|
2246
2492
|
function foldTurnInline(turn) {
|
|
2247
2493
|
const toolMsgs = turn.filter((m) => m.role === "tool");
|
|
@@ -2371,7 +2617,8 @@ function step3_truncateLarge(messages) {
|
|
|
2371
2617
|
details.push(`tool_call_id=${m.tool_call_id?.slice(0, 8)}... ${originalLen} \u2192 ${TRUNCATE_TO} chars`);
|
|
2372
2618
|
return {
|
|
2373
2619
|
...m,
|
|
2374
|
-
|
|
2620
|
+
// 0918:按 code point 切,避免切开 emoji 代理对(见 utils/well-formed.ts)
|
|
2621
|
+
content: truncateByCodePoint(m.content, TRUNCATE_TO) + "\n...[TRUNCATED]"
|
|
2375
2622
|
};
|
|
2376
2623
|
}
|
|
2377
2624
|
return m;
|
|
@@ -2461,6 +2708,7 @@ var init_ruleCompact = __esm({
|
|
|
2461
2708
|
init_tokenEstimate();
|
|
2462
2709
|
init_autoCompact();
|
|
2463
2710
|
init_compactLog();
|
|
2711
|
+
init_well_formed();
|
|
2464
2712
|
TAG3 = "ruleCompact";
|
|
2465
2713
|
TOOL_SIZE_LIMIT = 8e3;
|
|
2466
2714
|
TRUNCATE_TO = 5e3;
|
|
@@ -2687,6 +2935,9 @@ function openDb(workspace) {
|
|
|
2687
2935
|
day TEXT,
|
|
2688
2936
|
time TEXT,
|
|
2689
2937
|
date_str TEXT,
|
|
2938
|
+
-- \u26A0\uFE0F 0920 \u4E4B\u524D\u7684\u503C\u7531\u5E26\u7B26\u53F7 bug \u7684 nowIso \u5199\u5165\uFF1A\u503C = \u771F\u5B9E\u65F6\u523B + **\u5199\u673A\u5F53\u65F6\u7684
|
|
2939
|
+
-- UTC \u504F\u79FB**\uFF08\u4E1C\u4E3A\u6B63\uFF09\u3002\u9010\u884C\u4E0D\u540C\u3001\u65E0\u5904\u53EF\u8003\uFF0C\u6CA1\u6709\u53EF\u9760\u7684\u8FD8\u539F\u529E\u6CD5\u3002
|
|
2940
|
+
-- 0920 \u8D77\u5199\u5165\u6B63\u786E UTC Z\u3002\u8BFB\u8FD9\u4E09\u5217\u524D\u5148\u786E\u8BA4\u4F60\u8981\u7684\u662F\u4EC0\u4E48\u3002
|
|
2690
2941
|
created_at TEXT NOT NULL,
|
|
2691
2942
|
done_at TEXT,
|
|
2692
2943
|
archived_at TEXT
|
|
@@ -2729,7 +2980,7 @@ function migrate(db2) {
|
|
|
2729
2980
|
db2.exec("ALTER TABLE events ADD COLUMN doc_path TEXT");
|
|
2730
2981
|
}
|
|
2731
2982
|
}
|
|
2732
|
-
var _sqlite, DatabaseSync
|
|
2983
|
+
var _sqlite, DatabaseSync;
|
|
2733
2984
|
var init_db = __esm({
|
|
2734
2985
|
"src/calendar/db.ts"() {
|
|
2735
2986
|
"use strict";
|
|
@@ -2743,7 +2994,6 @@ var init_db = __esm({
|
|
|
2743
2994
|
return getSqlite().DatabaseSync[prop];
|
|
2744
2995
|
}
|
|
2745
2996
|
});
|
|
2746
|
-
TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
2747
2997
|
}
|
|
2748
2998
|
});
|
|
2749
2999
|
|
|
@@ -2872,15 +3122,6 @@ function bindAim(stateDir, goalId, stepId, aimId) {
|
|
|
2872
3122
|
step.note = `AIM assignment bound: ${aimId}`;
|
|
2873
3123
|
});
|
|
2874
3124
|
}
|
|
2875
|
-
function bindCalendar(stateDir, goalId, stepId, calendarId, deadlineAt) {
|
|
2876
|
-
return updateGoal(stateDir, goalId, (g) => {
|
|
2877
|
-
const step = g.steps.find((s) => s.id === stepId);
|
|
2878
|
-
if (!step) throw new Error(`Step not found: ${stepId}`);
|
|
2879
|
-
if (step.status === "completed") throw new Error("Cannot bind calendar to completed step");
|
|
2880
|
-
step.calendarId = calendarId;
|
|
2881
|
-
if (deadlineAt) step.deadlineAt = deadlineAt;
|
|
2882
|
-
});
|
|
2883
|
-
}
|
|
2884
3125
|
function applyAimEvent(stateDir, aimId, type2, note) {
|
|
2885
3126
|
const goal = listGoals(stateDir).find((g) => g.steps.some((s) => s.aimId === aimId));
|
|
2886
3127
|
if (!goal) return void 0;
|
|
@@ -2944,7 +3185,7 @@ function addGoalStep(stateDir, goalId, title, kind = "self") {
|
|
|
2944
3185
|
g.steps.push({ id, title, status: g.currentStep >= g.steps.length ? "active" : "pending", kind });
|
|
2945
3186
|
});
|
|
2946
3187
|
}
|
|
2947
|
-
function bindGoalStep(stateDir, goalId, stepId, kind, ref) {
|
|
3188
|
+
function bindGoalStep(stateDir, goalId, stepId, kind, ref, deadlineAt) {
|
|
2948
3189
|
return updateGoal(stateDir, goalId, (g) => {
|
|
2949
3190
|
const step = g.steps.find((s) => s.id === stepId);
|
|
2950
3191
|
if (!step) throw new Error(`Step not found: ${stepId}`);
|
|
@@ -2952,7 +3193,10 @@ function bindGoalStep(stateDir, goalId, stepId, kind, ref) {
|
|
|
2952
3193
|
step.kind = kind;
|
|
2953
3194
|
if (kind === "aim") step.aimId = String(ref);
|
|
2954
3195
|
else if (kind === "remote_task") step.taskId = String(ref);
|
|
2955
|
-
else if (kind === "calendar")
|
|
3196
|
+
else if (kind === "calendar") {
|
|
3197
|
+
step.calendarId = Number(ref);
|
|
3198
|
+
if (deadlineAt) step.deadlineAt = deadlineAt;
|
|
3199
|
+
}
|
|
2956
3200
|
step.note = `${kind} \u59D4\u6D3E: ${ref}\uFF08\u5B8C\u6210\u4E8B\u4EF6\u81EA\u52A8\u56DE\u6D41\u89E3\u9501\uFF09`;
|
|
2957
3201
|
});
|
|
2958
3202
|
}
|
|
@@ -4084,7 +4328,7 @@ async function classifyViaSidecar(url, context, text, opts = {}) {
|
|
|
4084
4328
|
const res = await doFetch(`${url.replace(/\/$/, "")}/classify`, {
|
|
4085
4329
|
method: "POST",
|
|
4086
4330
|
headers: { "Content-Type": "application/json" },
|
|
4087
|
-
body:
|
|
4331
|
+
body: safeStringify({ context: context.map((t) => ({ text: t })), text }),
|
|
4088
4332
|
signal: ac.signal
|
|
4089
4333
|
});
|
|
4090
4334
|
if (!res.ok) return null;
|
|
@@ -4101,6 +4345,7 @@ var init_skill_gate_classifier = __esm({
|
|
|
4101
4345
|
"src/skills/skill-gate-classifier.ts"() {
|
|
4102
4346
|
"use strict";
|
|
4103
4347
|
init_skill_gate_window();
|
|
4348
|
+
init_well_formed();
|
|
4104
4349
|
}
|
|
4105
4350
|
});
|
|
4106
4351
|
|
|
@@ -5647,16 +5892,18 @@ function shouldRetry(status, errorBody) {
|
|
|
5647
5892
|
if (status === 529) return true;
|
|
5648
5893
|
if (status === 429) {
|
|
5649
5894
|
const body = errorBody?.toLowerCase() || "";
|
|
5650
|
-
if (body.includes("insufficient_quota") || body.includes("allocationquota") || body.includes("quota_exceeded") || body.includes("quotaexceeded") || body.includes("quota") && body.includes("exceeded") || body.includes('"code":"1310"') || body.includes('"code":1310') || body.includes("\u4F7F\u7528\u4E0A\u9650") && body.includes("\u91CD\u7F6E")) {
|
|
5895
|
+
if (body.includes("insufficient_quota") || body.includes("allocationquota") || body.includes("quota_exceeded") || body.includes("quotaexceeded") || body.includes("quota") && body.includes("exceeded") || body.includes('"code":"1310"') || body.includes('"code":1310') || body.includes("\u4F7F\u7528\u4E0A\u9650") && body.includes("\u91CD\u7F6E") || body.includes("\u7528\u91CF\u4E0A\u9650")) {
|
|
5651
5896
|
return false;
|
|
5652
5897
|
}
|
|
5653
5898
|
return true;
|
|
5654
5899
|
}
|
|
5655
5900
|
if (status === 408) return true;
|
|
5656
5901
|
if (status === 409) return true;
|
|
5657
|
-
if (status
|
|
5658
|
-
|
|
5659
|
-
|
|
5902
|
+
if (status >= 500) {
|
|
5903
|
+
const body = errorBody?.toLowerCase() || "";
|
|
5904
|
+
if (body.includes("\u65E0\u53EF\u7528\u6E20\u9053") || body.includes("new_sensitive")) return false;
|
|
5905
|
+
return true;
|
|
5906
|
+
}
|
|
5660
5907
|
if (errorBody?.includes('"type":"overloaded_error"')) return true;
|
|
5661
5908
|
return false;
|
|
5662
5909
|
}
|
|
@@ -5926,6 +6173,7 @@ var init_openai_provider = __esm({
|
|
|
5926
6173
|
init_withRetry();
|
|
5927
6174
|
init_provider();
|
|
5928
6175
|
init_think_stripper();
|
|
6176
|
+
init_well_formed();
|
|
5929
6177
|
OpenAIProvider = class {
|
|
5930
6178
|
// 0831 <think> 剥离改用 ThinkTagStripper 的**局部**实例(见 streamChat)——
|
|
5931
6179
|
// 原来是实例字段,跨请求共享状态,流中途 abort 会把 active=true 带到下一个请求
|
|
@@ -6014,13 +6262,17 @@ var init_openai_provider = __esm({
|
|
|
6014
6262
|
}
|
|
6015
6263
|
const actualThinking = body.chat_template_kwargs;
|
|
6016
6264
|
console.log(`[openai] \u2192 model=${params.model} thinking=${actualThinking ? JSON.stringify(actualThinking) : "none"} maxTokens=${body.max_tokens ?? "unset"} msgs=${formatted.length} tools=${params.tools?.length ?? 0}`);
|
|
6265
|
+
let bodyStr = safeStringify(body);
|
|
6266
|
+
if (this.config.wafSingleQuoteWorkaround) {
|
|
6267
|
+
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
6268
|
+
}
|
|
6017
6269
|
const retryGen = fetchWithRetry(url, {
|
|
6018
6270
|
method: "POST",
|
|
6019
6271
|
headers: {
|
|
6020
6272
|
"Content-Type": "application/json",
|
|
6021
6273
|
"Authorization": `Bearer ${this.config.apiKey}`
|
|
6022
6274
|
},
|
|
6023
|
-
body:
|
|
6275
|
+
body: bodyStr,
|
|
6024
6276
|
signal: params.signal
|
|
6025
6277
|
}, "openai", this.config.proxy);
|
|
6026
6278
|
let response;
|
|
@@ -6218,7 +6470,7 @@ function adaptUsage(u) {
|
|
|
6218
6470
|
});
|
|
6219
6471
|
}
|
|
6220
6472
|
function serializeBody(body, wafSingleQuoteWorkaround) {
|
|
6221
|
-
let bodyStr =
|
|
6473
|
+
let bodyStr = safeStringify(body);
|
|
6222
6474
|
if (wafSingleQuoteWorkaround) {
|
|
6223
6475
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
6224
6476
|
}
|
|
@@ -6245,6 +6497,7 @@ var init_openai_responses_provider = __esm({
|
|
|
6245
6497
|
"use strict";
|
|
6246
6498
|
init_withRetry();
|
|
6247
6499
|
init_provider();
|
|
6500
|
+
init_well_formed();
|
|
6248
6501
|
CODEX_CLI_VERSION = "0.154.0";
|
|
6249
6502
|
CODEX_TERMINAL_TOKEN = "xterm-256color";
|
|
6250
6503
|
OpenAIResponsesProvider = class {
|
|
@@ -6565,12 +6818,14 @@ var init_anthropic_provider = __esm({
|
|
|
6565
6818
|
init_withRetry();
|
|
6566
6819
|
init_provider();
|
|
6567
6820
|
init_think_stripper();
|
|
6821
|
+
init_well_formed();
|
|
6568
6822
|
AnthropicProvider = class {
|
|
6569
6823
|
// 模拟 CC 的 X-Claude-Code-Session-Id
|
|
6570
6824
|
constructor(config2) {
|
|
6571
6825
|
this.config = config2;
|
|
6572
6826
|
let u = (config2.baseUrl || "https://api.anthropic.com").trim();
|
|
6573
6827
|
while (u.endsWith("/")) u = u.slice(0, -1);
|
|
6828
|
+
if (u.endsWith("/v1")) u = u.slice(0, -3);
|
|
6574
6829
|
this.baseUrl = u;
|
|
6575
6830
|
this.sessionId = randomUUID3();
|
|
6576
6831
|
}
|
|
@@ -6769,7 +7024,7 @@ var init_anthropic_provider = __esm({
|
|
|
6769
7024
|
} catch {
|
|
6770
7025
|
}
|
|
6771
7026
|
}
|
|
6772
|
-
let bodyStr =
|
|
7027
|
+
let bodyStr = safeStringify(body);
|
|
6773
7028
|
if (this.config.wafSingleQuoteWorkaround) {
|
|
6774
7029
|
const before = bodyStr.length;
|
|
6775
7030
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
@@ -6989,6 +7244,7 @@ var init_gemini_provider = __esm({
|
|
|
6989
7244
|
"src/models/gemini-provider.ts"() {
|
|
6990
7245
|
"use strict";
|
|
6991
7246
|
init_withRetry();
|
|
7247
|
+
init_well_formed();
|
|
6992
7248
|
GeminiProvider = class {
|
|
6993
7249
|
constructor(config2) {
|
|
6994
7250
|
this.config = config2;
|
|
@@ -7148,7 +7404,7 @@ var init_gemini_provider = __esm({
|
|
|
7148
7404
|
"Content-Type": "application/json",
|
|
7149
7405
|
"x-goog-api-key": this.config.apiKey
|
|
7150
7406
|
},
|
|
7151
|
-
body:
|
|
7407
|
+
body: safeStringify(body),
|
|
7152
7408
|
signal: params.signal
|
|
7153
7409
|
}, "gemini", this.config.proxy);
|
|
7154
7410
|
let response;
|
|
@@ -7287,7 +7543,9 @@ function createProvider(config2) {
|
|
|
7287
7543
|
baseUrl: config2.baseUrl,
|
|
7288
7544
|
apiKey: config2.apiKey,
|
|
7289
7545
|
thinking: config2.thinking,
|
|
7290
|
-
proxy
|
|
7546
|
+
proxy,
|
|
7547
|
+
wafSingleQuoteWorkaround: config2.wafSingleQuoteWorkaround
|
|
7548
|
+
// 0919 中转站→agentrouter 同样有 WAF
|
|
7291
7549
|
});
|
|
7292
7550
|
case "openai-responses":
|
|
7293
7551
|
return new OpenAIResponsesProvider({
|
|
@@ -7684,7 +7942,7 @@ __export(teamHelpers_exports, {
|
|
|
7684
7942
|
writeTeamFile: () => writeTeamFile,
|
|
7685
7943
|
writeTeamFileAsync: () => writeTeamFileAsync
|
|
7686
7944
|
});
|
|
7687
|
-
import { mkdirSync as
|
|
7945
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7688
7946
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
7689
7947
|
import { join as join15 } from "node:path";
|
|
7690
7948
|
function initTeamDirs(stateDir) {
|
|
@@ -7727,8 +7985,8 @@ async function readTeamFileAsync(teamName) {
|
|
|
7727
7985
|
}
|
|
7728
7986
|
function writeTeamFile(teamName, teamFile) {
|
|
7729
7987
|
const teamDir = getTeamDir(teamName);
|
|
7730
|
-
|
|
7731
|
-
|
|
7988
|
+
mkdirSync6(teamDir, { recursive: true });
|
|
7989
|
+
writeFileSync4(getTeamFilePath(teamName), JSON.stringify(teamFile, null, 2));
|
|
7732
7990
|
}
|
|
7733
7991
|
async function writeTeamFileAsync(teamName, teamFile) {
|
|
7734
7992
|
const teamDir = getTeamDir(teamName);
|
|
@@ -7818,7 +8076,7 @@ async function cleanupTeamDirectories(teamName) {
|
|
|
7818
8076
|
}
|
|
7819
8077
|
function ensureTasksDir(taskListId) {
|
|
7820
8078
|
const dir = join15(getTasksDir(), taskListId);
|
|
7821
|
-
|
|
8079
|
+
mkdirSync6(dir, { recursive: true });
|
|
7822
8080
|
}
|
|
7823
8081
|
function setLeaderTeamName(name) {
|
|
7824
8082
|
_leaderTeamName = name;
|
|
@@ -7957,26 +8215,26 @@ async function writeToMailbox(recipientName, message, teamName) {
|
|
|
7957
8215
|
await writeFile2(inboxPath, "[]", { encoding: "utf-8", flag: "wx" });
|
|
7958
8216
|
} catch {
|
|
7959
8217
|
}
|
|
7960
|
-
const
|
|
8218
|
+
const release3 = await acquireLock(inboxPath);
|
|
7961
8219
|
try {
|
|
7962
8220
|
const messages = await readMailbox(recipientName, teamName);
|
|
7963
8221
|
messages.push({ ...message, read: false });
|
|
7964
8222
|
await writeFile2(inboxPath, JSON.stringify(messages, null, 2), "utf-8");
|
|
7965
8223
|
console.log(`[mailbox] Wrote message to ${recipientName} from ${message.from} (team=${teamName || "default"}, inbox=${inboxPath})`);
|
|
7966
8224
|
} finally {
|
|
7967
|
-
await
|
|
8225
|
+
await release3();
|
|
7968
8226
|
}
|
|
7969
8227
|
}
|
|
7970
8228
|
async function markMessagesAsRead(agentName, teamName) {
|
|
7971
8229
|
const inboxPath = getInboxPath(agentName, teamName);
|
|
7972
|
-
const
|
|
8230
|
+
const release3 = await acquireLock(inboxPath);
|
|
7973
8231
|
try {
|
|
7974
8232
|
const messages = await readMailbox(agentName, teamName);
|
|
7975
8233
|
if (messages.length === 0) return;
|
|
7976
8234
|
for (const m of messages) m.read = true;
|
|
7977
8235
|
await writeFile2(inboxPath, JSON.stringify(messages, null, 2), "utf-8");
|
|
7978
8236
|
} finally {
|
|
7979
|
-
await
|
|
8237
|
+
await release3();
|
|
7980
8238
|
}
|
|
7981
8239
|
}
|
|
7982
8240
|
async function clearMailbox(agentName, teamName) {
|
|
@@ -8647,23 +8905,42 @@ var init_extractMemories = __esm({
|
|
|
8647
8905
|
}
|
|
8648
8906
|
});
|
|
8649
8907
|
|
|
8908
|
+
// src/mem7/core/system-filter.ts
|
|
8909
|
+
function parseMeta(text) {
|
|
8910
|
+
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
8911
|
+
if (!m) return null;
|
|
8912
|
+
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
8913
|
+
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
8914
|
+
}
|
|
8915
|
+
function isSystemUserMessage(meta) {
|
|
8916
|
+
if (!meta) return true;
|
|
8917
|
+
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
8918
|
+
}
|
|
8919
|
+
var SYSTEM_PLATFORMS;
|
|
8920
|
+
var init_system_filter = __esm({
|
|
8921
|
+
"src/mem7/core/system-filter.ts"() {
|
|
8922
|
+
"use strict";
|
|
8923
|
+
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
8924
|
+
"wx_monitor"
|
|
8925
|
+
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
8926
|
+
]);
|
|
8927
|
+
}
|
|
8928
|
+
});
|
|
8929
|
+
|
|
8650
8930
|
// src/memory/everos/ingest.ts
|
|
8651
8931
|
var ingest_exports = {};
|
|
8652
8932
|
__export(ingest_exports, {
|
|
8933
|
+
buildPayload: () => buildPayload,
|
|
8653
8934
|
pushConversation: () => pushConversation
|
|
8654
8935
|
});
|
|
8655
8936
|
import fs20 from "node:fs";
|
|
8656
8937
|
import path19 from "node:path";
|
|
8657
|
-
function parseMeta(text) {
|
|
8658
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
|
|
8659
|
-
if (!m) return null;
|
|
8660
|
-
return { senderName: m[1].trim() };
|
|
8661
|
-
}
|
|
8662
8938
|
async function readConfig2() {
|
|
8663
8939
|
const defaults = {
|
|
8664
8940
|
url: "http://127.0.0.1:8100",
|
|
8665
8941
|
appId: "default",
|
|
8666
8942
|
agentName: "assistant",
|
|
8943
|
+
ownerMode: "agent",
|
|
8667
8944
|
enabled: false,
|
|
8668
8945
|
ingestTimeoutMs: 3e4,
|
|
8669
8946
|
syncIntervalMs: 9e5
|
|
@@ -8676,6 +8953,7 @@ async function readConfig2() {
|
|
|
8676
8953
|
url: cfg.everosUrl || defaults.url,
|
|
8677
8954
|
appId: cfg.userId || defaults.appId,
|
|
8678
8955
|
agentName: cfg.agentName || cfg.userId || defaults.agentName,
|
|
8956
|
+
ownerMode: cfg.ownerMode === "sender" ? "sender" : "agent",
|
|
8679
8957
|
enabled: cfg.enabled === true,
|
|
8680
8958
|
ingestTimeoutMs: typeof cfg.ingestTimeoutMs === "number" ? cfg.ingestTimeoutMs : defaults.ingestTimeoutMs,
|
|
8681
8959
|
syncIntervalMs: typeof cfg.syncIntervalMs === "number" ? cfg.syncIntervalMs : defaults.syncIntervalMs
|
|
@@ -8711,6 +8989,7 @@ function savePushTs(workspace, sessionId, ts) {
|
|
|
8711
8989
|
}
|
|
8712
8990
|
}
|
|
8713
8991
|
function buildPayload(messages, cfg, sessionId) {
|
|
8992
|
+
let lastUserWasSystem = false;
|
|
8714
8993
|
return {
|
|
8715
8994
|
session_id: `extract-${sessionId}`,
|
|
8716
8995
|
app_id: cfg.appId,
|
|
@@ -8722,21 +9001,28 @@ function buildPayload(messages, cfg, sessionId) {
|
|
|
8722
9001
|
const raw = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
8723
9002
|
const meta = m.role === "user" ? parseMeta(raw) : null;
|
|
8724
9003
|
let text = raw;
|
|
9004
|
+
let isSystemTurn = false;
|
|
8725
9005
|
if (m.role === "user") {
|
|
8726
|
-
if (
|
|
8727
|
-
|
|
9006
|
+
if (isSystemUserMessage(parseMeta(raw))) {
|
|
9007
|
+
text = "";
|
|
9008
|
+
isSystemTurn = true;
|
|
9009
|
+
} else text = stripInjectedContext(raw.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
9010
|
+
lastUserWasSystem = isSystemTurn;
|
|
8728
9011
|
}
|
|
8729
9012
|
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
8730
9013
|
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
8731
9014
|
const role = rawRole === "user" ? "user" : "assistant";
|
|
9015
|
+
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
8732
9016
|
return {
|
|
8733
|
-
|
|
9017
|
+
// owner 口径见 IngestConfig.ownerMode:'sender' 模式下 user 消息用真人 ID
|
|
9018
|
+
// (everos 的 owner fan-out 取的就是 user 的 sender_id → 每人一份记忆)
|
|
9019
|
+
sender_id: m.role === "user" && cfg.ownerMode === "sender" ? meta?.senderId || cfg.appId : cfg.appId,
|
|
8734
9020
|
sender_name: senderName,
|
|
8735
9021
|
role,
|
|
8736
|
-
timestamp:
|
|
9022
|
+
timestamp: tsMs,
|
|
8737
9023
|
content: text,
|
|
8738
|
-
|
|
8739
|
-
|
|
9024
|
+
// 丢:系统指令本体 + 系统轮里的回应(与 mem7 同款,见上面状态机)
|
|
9025
|
+
_skip: m.role === "user" && !text || m.role !== "user" && lastUserWasSystem
|
|
8740
9026
|
};
|
|
8741
9027
|
}).filter((x) => !x._skip).map(({ _skip, ...rest }) => rest)
|
|
8742
9028
|
};
|
|
@@ -8846,6 +9132,7 @@ var init_ingest = __esm({
|
|
|
8846
9132
|
"src/memory/everos/ingest.ts"() {
|
|
8847
9133
|
"use strict";
|
|
8848
9134
|
init_skill_gate_window();
|
|
9135
|
+
init_system_filter();
|
|
8849
9136
|
PUSH_CHUNK = 10;
|
|
8850
9137
|
PUSH_STATE_FILE = ".everos-push-state.json";
|
|
8851
9138
|
pushInProgress = false;
|
|
@@ -8892,7 +9179,8 @@ async function embedChunk(cfg, baseUrl, texts, timeoutMs) {
|
|
|
8892
9179
|
const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/embeddings`, {
|
|
8893
9180
|
method: "POST",
|
|
8894
9181
|
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
|
8895
|
-
|
|
9182
|
+
// 0918:safeStringify 清洗孤立代理(embedding 传对话原文,emoji 风险高)
|
|
9183
|
+
body: safeStringify({ model: cfg.model, input: texts }),
|
|
8896
9184
|
signal: ctrl.signal
|
|
8897
9185
|
});
|
|
8898
9186
|
if (!resp.ok) throw new Error(`embeddings HTTP ${resp.status}`);
|
|
@@ -8941,6 +9229,7 @@ function cosine(a, b) {
|
|
|
8941
9229
|
var init_embedding = __esm({
|
|
8942
9230
|
"src/mem7/core/embedding.ts"() {
|
|
8943
9231
|
"use strict";
|
|
9232
|
+
init_well_formed();
|
|
8944
9233
|
}
|
|
8945
9234
|
});
|
|
8946
9235
|
|
|
@@ -9081,8 +9370,7 @@ function compileFilter(expr) {
|
|
|
9081
9370
|
return parts.join(" AND ");
|
|
9082
9371
|
}
|
|
9083
9372
|
function openWindowFilter(ownerId) {
|
|
9084
|
-
const
|
|
9085
|
-
const floor = (/* @__PURE__ */ new Date(`${cstToday}T00:00:00+08:00`)).toISOString();
|
|
9373
|
+
const floor = startOfLocalDay();
|
|
9086
9374
|
const out = [{ end_time: { gte: floor } }];
|
|
9087
9375
|
const owners = (Array.isArray(ownerId) ? ownerId : ownerId ? [ownerId] : []).filter(Boolean);
|
|
9088
9376
|
if (Array.isArray(ownerId) && owners.length === 0) {
|
|
@@ -9096,6 +9384,7 @@ var REGISTERED_FIELDS, OPS, TIME_FIELDS;
|
|
|
9096
9384
|
var init_filters = __esm({
|
|
9097
9385
|
"src/mem7/core/filters.ts"() {
|
|
9098
9386
|
"use strict";
|
|
9387
|
+
init_clock();
|
|
9099
9388
|
REGISTERED_FIELDS = /* @__PURE__ */ new Set([
|
|
9100
9389
|
"owner_id",
|
|
9101
9390
|
"owner_type",
|
|
@@ -9116,7 +9405,7 @@ var init_filters = __esm({
|
|
|
9116
9405
|
// src/mem7/core/vector-store.ts
|
|
9117
9406
|
import * as lancedb from "@lancedb/lancedb";
|
|
9118
9407
|
import { Schema, Field, Utf8, Float32, Int64, FixedSizeList, TimestampMicrosecond, List } from "apache-arrow";
|
|
9119
|
-
import { mkdirSync as
|
|
9408
|
+
import { mkdirSync as mkdirSync7 } from "node:fs";
|
|
9120
9409
|
import { join as join22 } from "node:path";
|
|
9121
9410
|
var VECTOR_DIM, BM25_FIELDS, RRF_K, warnedFts, vecField, commonFields, SCHEMAS, Mem7VectorStore;
|
|
9122
9411
|
var init_vector_store = __esm({
|
|
@@ -9187,7 +9476,7 @@ var init_vector_store = __esm({
|
|
|
9187
9476
|
}
|
|
9188
9477
|
async conn() {
|
|
9189
9478
|
if (!this.db) {
|
|
9190
|
-
|
|
9479
|
+
mkdirSync7(this.dir, { recursive: true });
|
|
9191
9480
|
this.db = await lancedb.connect(this.dir);
|
|
9192
9481
|
}
|
|
9193
9482
|
return this.db;
|
|
@@ -9413,13 +9702,6 @@ function renderPrompt(template, vars) {
|
|
|
9413
9702
|
}
|
|
9414
9703
|
return t.split(LB).join("{").split(RB).join("}");
|
|
9415
9704
|
}
|
|
9416
|
-
function renderLiteral(template, vars) {
|
|
9417
|
-
let out = template;
|
|
9418
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
9419
|
-
out = out.split(`{{${key}}}`).join(value);
|
|
9420
|
-
}
|
|
9421
|
-
return out;
|
|
9422
|
-
}
|
|
9423
9705
|
function formatAtomicFactTime(tsMs) {
|
|
9424
9706
|
const d = new Date(tsMs);
|
|
9425
9707
|
const month = MONTHS[d.getUTCMonth()];
|
|
@@ -9451,10 +9733,11 @@ function formatMessageTimestamp(tsMs) {
|
|
|
9451
9733
|
const iso = new Date(tsMs).toISOString();
|
|
9452
9734
|
return `${iso.slice(0, 10)} ${iso.slice(11, 19)}`;
|
|
9453
9735
|
}
|
|
9736
|
+
function renderConversationText(messages) {
|
|
9737
|
+
return messages.map((m) => `[${formatMessageTimestamp(m.tsMs)}] ${m.senderName ?? m.senderId}: ${m.text}`).join("\n");
|
|
9738
|
+
}
|
|
9454
9739
|
function formatMdTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
9455
|
-
|
|
9456
|
-
const iso = cst.toISOString();
|
|
9457
|
-
return `${iso.slice(0, 19)}+08:00`;
|
|
9740
|
+
return `${toWall(d).replace(" ", "T")}${localOffsetSuffix(d)}`;
|
|
9458
9741
|
}
|
|
9459
9742
|
function pyListRepr(items) {
|
|
9460
9743
|
return `[${items.join(", ")}]`;
|
|
@@ -9463,6 +9746,7 @@ var WEEKDAYS, MONTHS, LB, RB, DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
|
9463
9746
|
var init_prompt_render = __esm({
|
|
9464
9747
|
"src/mem7/core/prompt-render.ts"() {
|
|
9465
9748
|
"use strict";
|
|
9749
|
+
init_clock();
|
|
9466
9750
|
WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
9467
9751
|
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
9468
9752
|
LB = "\0M7LB\0";
|
|
@@ -9479,7 +9763,7 @@ Follow these principles when generating episodic memories:
|
|
|
9479
9763
|
|
|
9480
9764
|
// src/mem7/core/store.ts
|
|
9481
9765
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
9482
|
-
import { mkdirSync as
|
|
9766
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync6, existsSync as existsSync13, readdirSync } from "node:fs";
|
|
9483
9767
|
import { join as join23 } from "node:path";
|
|
9484
9768
|
import { createHash } from "node:crypto";
|
|
9485
9769
|
function contentHash(text) {
|
|
@@ -9494,6 +9778,7 @@ var init_store2 = __esm({
|
|
|
9494
9778
|
"use strict";
|
|
9495
9779
|
init_vector_store();
|
|
9496
9780
|
init_prompt_render();
|
|
9781
|
+
init_clock();
|
|
9497
9782
|
SCHEMA = `
|
|
9498
9783
|
CREATE TABLE IF NOT EXISTS memcells (
|
|
9499
9784
|
id TEXT PRIMARY KEY,
|
|
@@ -9521,7 +9806,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9521
9806
|
constructor(stateDir) {
|
|
9522
9807
|
this.stateDir = stateDir;
|
|
9523
9808
|
this.root = mem7Root(stateDir);
|
|
9524
|
-
|
|
9809
|
+
mkdirSync8(join23(this.root, "index"), { recursive: true });
|
|
9525
9810
|
this.db = new DatabaseSync2(join23(this.root, "index", "mem7.db"));
|
|
9526
9811
|
this.db.exec("PRAGMA journal_mode=WAL");
|
|
9527
9812
|
this.db.exec(SCHEMA);
|
|
@@ -9654,7 +9939,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9654
9939
|
appendMd(ownerId, dirName, filePrefix, type2, dateStr, entries2, render) {
|
|
9655
9940
|
if (entries2.length === 0) return;
|
|
9656
9941
|
const dir = join23(this.root, "users", ownerId, dirName);
|
|
9657
|
-
|
|
9942
|
+
mkdirSync8(dir, { recursive: true });
|
|
9658
9943
|
const file2 = join23(dir, `${filePrefix}-${dateStr}.md`);
|
|
9659
9944
|
let head;
|
|
9660
9945
|
let existingBlocks = "";
|
|
@@ -9691,7 +9976,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9691
9976
|
count++;
|
|
9692
9977
|
}
|
|
9693
9978
|
head = head.replace(/entry_count: \d+/, `entry_count: ${count}`).replace(/last_appended_at: '[^']*'/, `last_appended_at: '${(/* @__PURE__ */ new Date()).toISOString()}'`);
|
|
9694
|
-
|
|
9979
|
+
writeFileSync6(file2, head + "\n\n" + blocks.join("\n\n") + "\n", "utf8");
|
|
9695
9980
|
}
|
|
9696
9981
|
/**
|
|
9697
9982
|
* entry_id:前缀_YYYYMMDD_六位序号。
|
|
@@ -9780,7 +10065,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9780
10065
|
}
|
|
9781
10066
|
// ── md 读(proactive 消费):开窗预测(全 owner 扫描,end>=今天)──
|
|
9782
10067
|
getOpenPredictions(now, limit = 50) {
|
|
9783
|
-
const
|
|
10068
|
+
const today2 = todayKey(now);
|
|
9784
10069
|
const out = [];
|
|
9785
10070
|
const usersDir = join23(this.root, "users");
|
|
9786
10071
|
if (!existsSync13(usersDir)) return out;
|
|
@@ -9803,7 +10088,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9803
10088
|
const text = ((block.match(/### Foresight\s*\n+([\s\S]*?)(?:\n### Evidence|$)/) ?? [])[1] ?? "").trim();
|
|
9804
10089
|
const evidence = ((block.match(/### Evidence\s*\n+([\s\S]*?)$/) ?? [])[1] ?? "").trim();
|
|
9805
10090
|
if (!end || !start || !text) continue;
|
|
9806
|
-
if (end <
|
|
10091
|
+
if (end < today2) continue;
|
|
9807
10092
|
out.push({ ownerId: owner.name, startDate: start, endDate: end, content: text.replace(/\n+/g, " ").slice(0, 200), evidence: evidence.replace(/\n+/g, " ").slice(0, 120) });
|
|
9808
10093
|
if (out.length >= limit) return out;
|
|
9809
10094
|
}
|
|
@@ -9815,28 +10100,6 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9815
10100
|
}
|
|
9816
10101
|
});
|
|
9817
10102
|
|
|
9818
|
-
// src/mem7/core/system-filter.ts
|
|
9819
|
-
function parseMeta2(text) {
|
|
9820
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
9821
|
-
if (!m) return null;
|
|
9822
|
-
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
9823
|
-
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
9824
|
-
}
|
|
9825
|
-
function isSystemUserMessage(meta) {
|
|
9826
|
-
if (!meta) return true;
|
|
9827
|
-
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
9828
|
-
}
|
|
9829
|
-
var SYSTEM_PLATFORMS;
|
|
9830
|
-
var init_system_filter = __esm({
|
|
9831
|
-
"src/mem7/core/system-filter.ts"() {
|
|
9832
|
-
"use strict";
|
|
9833
|
-
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
9834
|
-
"wx_monitor"
|
|
9835
|
-
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
9836
|
-
]);
|
|
9837
|
-
}
|
|
9838
|
-
});
|
|
9839
|
-
|
|
9840
10103
|
// src/mem7/core/ingest.ts
|
|
9841
10104
|
function contentText(content) {
|
|
9842
10105
|
if (typeof content === "string") return content;
|
|
@@ -9845,7 +10108,8 @@ function contentText(content) {
|
|
|
9845
10108
|
}
|
|
9846
10109
|
return "";
|
|
9847
10110
|
}
|
|
9848
|
-
function normalizeMessages(rawMessages, agentName) {
|
|
10111
|
+
function normalizeMessages(rawMessages, agentName, identity, ownerMode = "agent") {
|
|
10112
|
+
const ownerId = identity || agentName;
|
|
9849
10113
|
const out = [];
|
|
9850
10114
|
let lastUserWasSystem = false;
|
|
9851
10115
|
for (let rawIdx = 0; rawIdx < rawMessages.length; rawIdx++) {
|
|
@@ -9860,19 +10124,19 @@ function normalizeMessages(rawMessages, agentName) {
|
|
|
9860
10124
|
let meta = null;
|
|
9861
10125
|
if (role === "assistant" && lastUserWasSystem) continue;
|
|
9862
10126
|
if (role === "user") {
|
|
9863
|
-
meta =
|
|
10127
|
+
meta = parseMeta(text);
|
|
9864
10128
|
if (isSystemUserMessage(meta)) {
|
|
9865
10129
|
lastUserWasSystem = true;
|
|
9866
10130
|
continue;
|
|
9867
10131
|
}
|
|
9868
10132
|
lastUserWasSystem = false;
|
|
9869
10133
|
senderName = meta.senderName;
|
|
9870
|
-
senderId = meta.senderId;
|
|
10134
|
+
senderId = ownerMode === "sender" ? meta.senderId : ownerId;
|
|
9871
10135
|
text = stripInjectedContext(text.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
9872
10136
|
if (!text) continue;
|
|
9873
10137
|
} else {
|
|
9874
10138
|
senderName = agentName;
|
|
9875
|
-
senderId =
|
|
10139
|
+
senderId = ownerId;
|
|
9876
10140
|
}
|
|
9877
10141
|
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
9878
10142
|
out.push({ id: `m_${tsMs}_${String(rawIdx).padStart(3, "0")}`, role, senderId, senderName, platform: meta?.platform, text, tsMs });
|
|
@@ -10029,7 +10293,8 @@ async function extractEpisode(cell, provider, model) {
|
|
|
10029
10293
|
for await (const chunk of provider.streamChat({
|
|
10030
10294
|
model,
|
|
10031
10295
|
messages: [{ role: "user", content: userPrompt }],
|
|
10032
|
-
temperature
|
|
10296
|
+
// 0(everalgo episode callsite 不传 temperature → LLM 默认 0.0);0.3 是 foresight 的参数,别混
|
|
10297
|
+
temperature: 0
|
|
10033
10298
|
})) {
|
|
10034
10299
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
10035
10300
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
@@ -10071,13 +10336,10 @@ var init_episode = __esm({
|
|
|
10071
10336
|
import { readFileSync as readFileSync18 } from "node:fs";
|
|
10072
10337
|
import { join as join26, dirname as dirname5 } from "node:path";
|
|
10073
10338
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10074
|
-
async function extractAtomicFacts(
|
|
10075
|
-
const
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
const userPrompt = renderLiteral(PROMPT4, {
|
|
10079
|
-
EPISODE_TEXT: episodeText,
|
|
10080
|
-
TIME: formatAtomicFactTime(cellTsMs)
|
|
10339
|
+
async function extractAtomicFacts(cell, provider, model) {
|
|
10340
|
+
const userPrompt = renderPrompt(PROMPT4, {
|
|
10341
|
+
INPUT_TEXT: renderConversationText(cell.messages),
|
|
10342
|
+
TIME: formatAtomicFactTime(cell.startTs)
|
|
10081
10343
|
});
|
|
10082
10344
|
for (let attempt = 0; attempt < RETRIES3; attempt++) {
|
|
10083
10345
|
try {
|
|
@@ -10085,15 +10347,16 @@ ${episode.content}`;
|
|
|
10085
10347
|
for await (const chunk of provider.streamChat({
|
|
10086
10348
|
model,
|
|
10087
10349
|
messages: [{ role: "user", content: userPrompt }],
|
|
10088
|
-
temperature: 0
|
|
10350
|
+
temperature: 0
|
|
10089
10351
|
})) {
|
|
10090
10352
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
10091
10353
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
10092
10354
|
}
|
|
10093
10355
|
const obj = extractJson(text);
|
|
10094
10356
|
const block = obj?.event_log ?? obj?.atomic_facts;
|
|
10095
|
-
|
|
10096
|
-
|
|
10357
|
+
if (!block || typeof block.time !== "string" || !block.time.trim()) throw new Error("atomic_facts.time missing");
|
|
10358
|
+
const list2 = block.atomic_fact;
|
|
10359
|
+
if (!Array.isArray(list2)) throw new Error("atomic_facts.atomic_fact not a list");
|
|
10097
10360
|
return list2.filter((f) => typeof f === "string" && f.trim().length > 0).map((f) => f.trim());
|
|
10098
10361
|
} catch {
|
|
10099
10362
|
}
|
|
@@ -10106,8 +10369,8 @@ var init_atomic_facts = __esm({
|
|
|
10106
10369
|
"use strict";
|
|
10107
10370
|
init_episode();
|
|
10108
10371
|
init_prompt_render();
|
|
10109
|
-
RETRIES3 =
|
|
10110
|
-
PROMPT4 = readFileSync18(join26(dirname5(fileURLToPath4(import.meta.url)), "prompts", "atomic-facts.md"), "utf8");
|
|
10372
|
+
RETRIES3 = 5;
|
|
10373
|
+
PROMPT4 = readFileSync18(join26(dirname5(fileURLToPath4(import.meta.url)), "prompts", "atomic-facts-memcell.md"), "utf8");
|
|
10111
10374
|
}
|
|
10112
10375
|
});
|
|
10113
10376
|
|
|
@@ -10126,20 +10389,20 @@ function cleanDateString(dateStr) {
|
|
|
10126
10389
|
}
|
|
10127
10390
|
function calcEndFromDuration(startDate, durationDays) {
|
|
10128
10391
|
const d = new Date(Date.parse(startDate + "T00:00:00Z") + durationDays * 864e5);
|
|
10129
|
-
return Number.isNaN(d.getTime()) ? null : d
|
|
10392
|
+
return Number.isNaN(d.getTime()) ? null : todayKey(d);
|
|
10130
10393
|
}
|
|
10131
10394
|
function calcDurationDays(startDate, endDate) {
|
|
10132
10395
|
const ms = Date.parse(endDate + "T00:00:00Z") - Date.parse(startDate + "T00:00:00Z");
|
|
10133
10396
|
return Number.isNaN(ms) ? null : Math.round(ms / 864e5);
|
|
10134
10397
|
}
|
|
10135
10398
|
function cellDateCst(cell) {
|
|
10136
|
-
return
|
|
10399
|
+
return todayKey(cell.startTs);
|
|
10137
10400
|
}
|
|
10138
10401
|
async function extractForesights(cell, ownerId, provider, model) {
|
|
10139
10402
|
const userSenders = cell.messages.filter((m) => m.role === "user");
|
|
10140
10403
|
const owner = userSenders.find((m) => m.senderId === ownerId) ?? userSenders[0];
|
|
10141
10404
|
const userName = owner?.senderName ?? ownerId;
|
|
10142
|
-
const conversationText = cell.messages
|
|
10405
|
+
const conversationText = renderConversationText(cell.messages);
|
|
10143
10406
|
const userPrompt = renderPrompt(PROMPT5, { USER_ID: ownerId, USER_NAME: userName, CONVERSATION_TEXT: conversationText });
|
|
10144
10407
|
let raw = null;
|
|
10145
10408
|
for (let attempt = 0; attempt < RETRIES4; attempt++) {
|
|
@@ -10192,6 +10455,7 @@ var init_foresight = __esm({
|
|
|
10192
10455
|
"use strict";
|
|
10193
10456
|
init_episode();
|
|
10194
10457
|
init_prompt_render();
|
|
10458
|
+
init_clock();
|
|
10195
10459
|
RETRIES4 = 5;
|
|
10196
10460
|
MAX_COUNT = 10;
|
|
10197
10461
|
MIN_COUNT = 4;
|
|
@@ -10202,11 +10466,11 @@ var init_foresight = __esm({
|
|
|
10202
10466
|
|
|
10203
10467
|
// src/mem7/core/pipeline.ts
|
|
10204
10468
|
function dateCst(tsMs) {
|
|
10205
|
-
return
|
|
10469
|
+
return todayKey(tsMs);
|
|
10206
10470
|
}
|
|
10207
10471
|
async function runMem7Pipeline(args2) {
|
|
10208
|
-
const { sessionId, store, provider, model, agentName } = args2;
|
|
10209
|
-
const all = normalizeMessages(args2.rawMessages, agentName);
|
|
10472
|
+
const { sessionId, store, provider, model, agentName, identity, ownerMode } = args2;
|
|
10473
|
+
const all = normalizeMessages(args2.rawMessages, agentName, identity, ownerMode);
|
|
10210
10474
|
const fresh = readFresh(store, sessionId, all);
|
|
10211
10475
|
if (fresh.length === 0) return;
|
|
10212
10476
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -10278,7 +10542,7 @@ async function runMem7Pipeline(args2) {
|
|
|
10278
10542
|
}
|
|
10279
10543
|
}
|
|
10280
10544
|
})(),
|
|
10281
|
-
// 支 2:episode →
|
|
10545
|
+
// 支 2:episode(memcell → episode,一次调用 + per-owner fan-out,everos 同款)
|
|
10282
10546
|
(async () => {
|
|
10283
10547
|
try {
|
|
10284
10548
|
const ep = await extractEpisode(cell, provider, model);
|
|
@@ -10300,26 +10564,34 @@ async function runMem7Pipeline(args2) {
|
|
|
10300
10564
|
}]);
|
|
10301
10565
|
embedItems.push({ contentHash: contentHash(ep.content), kind: "episode", refDate: dateStr, ownerId, content: ep.content });
|
|
10302
10566
|
}
|
|
10303
|
-
|
|
10567
|
+
store.updateMemcellStatus(cell.id, "extracted");
|
|
10568
|
+
} catch (err) {
|
|
10569
|
+
console.warn(`[mem7] episode failed (cell=${cell.id}): ${err.message}`);
|
|
10570
|
+
store.updateMemcellStatus(cell.id, "skipped");
|
|
10571
|
+
}
|
|
10572
|
+
})(),
|
|
10573
|
+
// 支 3:atomic-facts(memcell → 事实,**与 episode 平级**)
|
|
10574
|
+
// 0918 对齐 everos:`extract_atomic_facts.py:79` 直接吃 memcell,不经 episode。
|
|
10575
|
+
// 此前挂在 episode 下游(二次压缩)+ 用 from_text 版 prompt(无语言规则 → 英文事实)
|
|
10576
|
+
(async () => {
|
|
10577
|
+
try {
|
|
10578
|
+
const facts = await extractAtomicFacts(cell, provider, model);
|
|
10304
10579
|
factsOut = facts;
|
|
10305
|
-
if (facts.length
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
}
|
|
10580
|
+
if (facts.length === 0) return;
|
|
10581
|
+
for (const ownerId of userSenders) {
|
|
10582
|
+
store.appendAtomicFacts(ownerId, dateStr, [{
|
|
10583
|
+
ownerId,
|
|
10584
|
+
sessionId,
|
|
10585
|
+
time: new Date(cell.startTs).toISOString(),
|
|
10586
|
+
parentId: cell.id,
|
|
10587
|
+
facts
|
|
10588
|
+
}]);
|
|
10589
|
+
for (const f of facts) {
|
|
10590
|
+
embedItems.push({ contentHash: contentHash(f), kind: "atomic_fact", refDate: dateStr, ownerId, content: f });
|
|
10317
10591
|
}
|
|
10318
10592
|
}
|
|
10319
|
-
store.updateMemcellStatus(cell.id, "extracted");
|
|
10320
10593
|
} catch (err) {
|
|
10321
|
-
console.warn(`[mem7]
|
|
10322
|
-
store.updateMemcellStatus(cell.id, "skipped");
|
|
10594
|
+
console.warn(`[mem7] atomic-facts failed (cell=${cell.id}): ${err.message}`);
|
|
10323
10595
|
}
|
|
10324
10596
|
})()
|
|
10325
10597
|
]);
|
|
@@ -10355,6 +10627,7 @@ var init_pipeline = __esm({
|
|
|
10355
10627
|
init_episode();
|
|
10356
10628
|
init_atomic_facts();
|
|
10357
10629
|
init_foresight();
|
|
10630
|
+
init_clock();
|
|
10358
10631
|
}
|
|
10359
10632
|
});
|
|
10360
10633
|
|
|
@@ -10397,28 +10670,23 @@ function parseProactiveConfig(raw) {
|
|
|
10397
10670
|
try {
|
|
10398
10671
|
parsed = parseModelRef(modelRef);
|
|
10399
10672
|
} catch (err) {
|
|
10400
|
-
console.warn(`[proactive]
|
|
10673
|
+
console.warn(`[proactive] model "${modelRef}" \u975E\u6CD5\uFF08${err.message}\uFF09\u2014\u2014 \u6539\u7528\u4E3B\u6A21\u578B`);
|
|
10401
10674
|
}
|
|
10402
10675
|
}
|
|
10403
|
-
if (!parsed) {
|
|
10404
|
-
parsed = parseModelRef(DEFAULT_MODEL_REF);
|
|
10405
|
-
}
|
|
10406
10676
|
return {
|
|
10407
10677
|
enabled: obj.enabled === true || obj.enabled === "yes" || obj.enabled === 1,
|
|
10408
|
-
modelRef: parsed.providerId
|
|
10409
|
-
providerId: parsed
|
|
10410
|
-
modelId: parsed
|
|
10678
|
+
modelRef: parsed ? `${parsed.providerId}/${parsed.modelId}` : "",
|
|
10679
|
+
providerId: parsed?.providerId ?? "",
|
|
10680
|
+
modelId: parsed?.modelId ?? ""
|
|
10411
10681
|
};
|
|
10412
10682
|
}
|
|
10413
10683
|
function proactiveDbPath(workspace) {
|
|
10414
10684
|
return join28(workspace, ".proactive", "proactive.db");
|
|
10415
10685
|
}
|
|
10416
|
-
var DEFAULT_MODEL_REF;
|
|
10417
10686
|
var init_config3 = __esm({
|
|
10418
10687
|
"src/memory/proactive/config.ts"() {
|
|
10419
10688
|
"use strict";
|
|
10420
10689
|
init_loader();
|
|
10421
|
-
DEFAULT_MODEL_REF = "minimax/MiniMax-M3";
|
|
10422
10690
|
}
|
|
10423
10691
|
});
|
|
10424
10692
|
|
|
@@ -10757,7 +11025,7 @@ var init_graph_store = __esm({
|
|
|
10757
11025
|
|
|
10758
11026
|
// src/memory/proactive/persistence.ts
|
|
10759
11027
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
10760
|
-
import { mkdirSync as
|
|
11028
|
+
import { mkdirSync as mkdirSync9 } from "node:fs";
|
|
10761
11029
|
import { dirname as dirname7 } from "node:path";
|
|
10762
11030
|
function rowToAction(r) {
|
|
10763
11031
|
return {
|
|
@@ -10778,18 +11046,13 @@ function rowToAction(r) {
|
|
|
10778
11046
|
}
|
|
10779
11047
|
function normalizeToUtcIso(s) {
|
|
10780
11048
|
if (!s) return s;
|
|
10781
|
-
|
|
10782
|
-
if (/[+-]\d{2}:?\d{2}$/.test(s)) {
|
|
10783
|
-
const ms2 = Date.parse(s);
|
|
10784
|
-
return Number.isNaN(ms2) ? s : new Date(ms2).toISOString();
|
|
10785
|
-
}
|
|
10786
|
-
const ms = Date.parse(s + TZ_BEIJING);
|
|
10787
|
-
return Number.isNaN(ms) ? s : new Date(ms).toISOString();
|
|
11049
|
+
return parseWall(s);
|
|
10788
11050
|
}
|
|
10789
|
-
var SCHEMA2, Persistence
|
|
11051
|
+
var SCHEMA2, Persistence;
|
|
10790
11052
|
var init_persistence = __esm({
|
|
10791
11053
|
"src/memory/proactive/persistence.ts"() {
|
|
10792
11054
|
"use strict";
|
|
11055
|
+
init_clock();
|
|
10793
11056
|
SCHEMA2 = `
|
|
10794
11057
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
10795
11058
|
id TEXT PRIMARY KEY,
|
|
@@ -10860,7 +11123,7 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
10860
11123
|
Persistence = class {
|
|
10861
11124
|
db;
|
|
10862
11125
|
constructor(dbPath) {
|
|
10863
|
-
|
|
11126
|
+
mkdirSync9(dirname7(dbPath), { recursive: true });
|
|
10864
11127
|
this.db = new DatabaseSync3(dbPath);
|
|
10865
11128
|
this.db.exec("PRAGMA journal_mode=WAL");
|
|
10866
11129
|
this.db.exec(SCHEMA2);
|
|
@@ -11203,6 +11466,27 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
11203
11466
|
const row = this.db.prepare("SELECT * FROM actions WHERE id=?").get(id);
|
|
11204
11467
|
return row ? rowToAction(row) : void 0;
|
|
11205
11468
|
}
|
|
11469
|
+
/** proactive_status 工具用:按状态列 action(按排期升序,最多 30 条)。 */
|
|
11470
|
+
listActions(filter) {
|
|
11471
|
+
const where = filter === "open" ? "status IN ('queued','executing')" : "status NOT IN ('cancelled')";
|
|
11472
|
+
const rows = this.db.prepare(
|
|
11473
|
+
`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`
|
|
11474
|
+
).all();
|
|
11475
|
+
return rows.map(rowToAction);
|
|
11476
|
+
}
|
|
11477
|
+
/** proactive_resolve_intent 工具用:读 intent 节点的 data(不存在返回 null)。 */
|
|
11478
|
+
getIntentData(intentId) {
|
|
11479
|
+
const row = this.db.prepare("SELECT data FROM nodes WHERE id=? AND type=?").get(intentId, "intent");
|
|
11480
|
+
if (!row) return null;
|
|
11481
|
+
try {
|
|
11482
|
+
return JSON.parse(row.data);
|
|
11483
|
+
} catch {
|
|
11484
|
+
return {};
|
|
11485
|
+
}
|
|
11486
|
+
}
|
|
11487
|
+
setIntentData(intentId, data) {
|
|
11488
|
+
this.db.prepare("UPDATE nodes SET data=? WHERE id=?").run(JSON.stringify(data), intentId);
|
|
11489
|
+
}
|
|
11206
11490
|
/** 周指标统计(验证标准用:完成数/cancelled 率) */
|
|
11207
11491
|
actionStats(sinceIso) {
|
|
11208
11492
|
const row = this.db.prepare(
|
|
@@ -11214,7 +11498,6 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
11214
11498
|
return { total: row.total ?? 0, completed: row.completed ?? 0, cancelled: row.cancelled ?? 0 };
|
|
11215
11499
|
}
|
|
11216
11500
|
};
|
|
11217
|
-
TZ_BEIJING = "+08:00";
|
|
11218
11501
|
}
|
|
11219
11502
|
});
|
|
11220
11503
|
|
|
@@ -12002,7 +12285,8 @@ function buildUserPrompt(event2, contextNodes, mode, foresightHits) {
|
|
|
12002
12285
|
const foresightSection = foresightHits && foresightHits.length > 0 ? `
|
|
12003
12286
|
## Related Predictions (foresight \u2014 made earlier, window still open; matched this message by terms)
|
|
12004
12287
|
|
|
12005
|
-
These dormant predictions
|
|
12288
|
+
These dormant predictions were surfaced by **mechanical keyword matching** \u2014 some may be irrelevant; ignore them freely.
|
|
12289
|
+
If acting on one NOW is meaningfully better than later (\u8D81\u70ED/\u987A\u4FBF), output an opportunity for it.
|
|
12006
12290
|
|
|
12007
12291
|
${foresightHits.join("\n")}
|
|
12008
12292
|
` : "";
|
|
@@ -12434,10 +12718,14 @@ If deadline has no time context, fall back to normal timing rules.
|
|
|
12434
12718
|
|
|
12435
12719
|
// src/calendar/helpers.ts
|
|
12436
12720
|
function nowIso() {
|
|
12437
|
-
return
|
|
12721
|
+
return toUtc(/* @__PURE__ */ new Date());
|
|
12438
12722
|
}
|
|
12439
|
-
function
|
|
12440
|
-
return
|
|
12723
|
+
function nowParts() {
|
|
12724
|
+
return wallParts();
|
|
12725
|
+
}
|
|
12726
|
+
function wallMs(y, mo, d, hh, mm) {
|
|
12727
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
12728
|
+
return Date.parse(parseWall(`${y}-${z(mo)}-${z(d)}T${z(hh)}:${z(mm)}:00`));
|
|
12441
12729
|
}
|
|
12442
12730
|
function parseDateRange(dateStr) {
|
|
12443
12731
|
const s = dateStr.replace("\u524D", "");
|
|
@@ -12459,32 +12747,29 @@ function parseDateRange(dateStr) {
|
|
|
12459
12747
|
}
|
|
12460
12748
|
}
|
|
12461
12749
|
function isToday(month, dStart, dEnd) {
|
|
12462
|
-
const
|
|
12463
|
-
return month ===
|
|
12750
|
+
const p = nowParts();
|
|
12751
|
+
return month === p.mo && dStart <= p.d && p.d <= dEnd;
|
|
12464
12752
|
}
|
|
12465
12753
|
function isOverdue(month, dStart, dEnd) {
|
|
12466
|
-
const
|
|
12467
|
-
|
|
12468
|
-
const curDay = now.getUTCDate();
|
|
12469
|
-
return month < curMonth || month === curMonth && dEnd < curDay;
|
|
12754
|
+
const p = nowParts();
|
|
12755
|
+
return month < p.mo || month === p.mo && dEnd < p.d;
|
|
12470
12756
|
}
|
|
12471
12757
|
function getTodayWeekday() {
|
|
12472
|
-
const
|
|
12473
|
-
return WEEKDAYS2[
|
|
12758
|
+
const p = nowParts();
|
|
12759
|
+
return WEEKDAYS2[p.weekday === 0 ? 6 : p.weekday - 1];
|
|
12474
12760
|
}
|
|
12475
12761
|
function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
12476
12762
|
const parsed = parseDateRange(dateStr);
|
|
12477
12763
|
if (!parsed) return null;
|
|
12478
12764
|
const { month, dStart } = parsed;
|
|
12479
|
-
const
|
|
12480
|
-
const year = now.getUTCFullYear();
|
|
12765
|
+
const year = nowParts().y;
|
|
12481
12766
|
let eventMs;
|
|
12482
12767
|
if (timeExact) {
|
|
12483
12768
|
const [hh, mm] = timeExact.split(":").map(Number);
|
|
12484
12769
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12485
|
-
eventMs =
|
|
12770
|
+
eventMs = wallMs(year, month, dStart, hh, mm);
|
|
12486
12771
|
} else {
|
|
12487
|
-
eventMs =
|
|
12772
|
+
eventMs = wallMs(year, month, dStart, 9, 0);
|
|
12488
12773
|
}
|
|
12489
12774
|
const remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12490
12775
|
return new Date(remindMs).toISOString();
|
|
@@ -12492,20 +12777,19 @@ function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
|
12492
12777
|
function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
12493
12778
|
const targetIdx = WEEKDAYS2.indexOf(day);
|
|
12494
12779
|
if (targetIdx === -1) return null;
|
|
12495
|
-
const
|
|
12496
|
-
const curIdx =
|
|
12780
|
+
const p = nowParts();
|
|
12781
|
+
const curIdx = p.weekday === 0 ? 6 : p.weekday - 1;
|
|
12497
12782
|
let dayDiff = targetIdx - curIdx;
|
|
12498
12783
|
if (dayDiff < 0) dayDiff += 7;
|
|
12499
|
-
const
|
|
12500
|
-
const month =
|
|
12501
|
-
const date = now.getUTCDate() + dayDiff;
|
|
12784
|
+
const target = wallParts(Date.parse(startOfLocalDay()) + dayDiff * 24 * 60 * 60 * 1e3);
|
|
12785
|
+
const year = target.y, month = target.mo, date = target.d;
|
|
12502
12786
|
let eventMs;
|
|
12503
12787
|
if (time && time !== "\u5168\u5929") {
|
|
12504
12788
|
const [hh, mm] = time.split(":").map(Number);
|
|
12505
12789
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12506
|
-
eventMs =
|
|
12790
|
+
eventMs = wallMs(year, month, date, hh, mm);
|
|
12507
12791
|
} else {
|
|
12508
|
-
eventMs =
|
|
12792
|
+
eventMs = wallMs(year, month, date, 9, 0);
|
|
12509
12793
|
}
|
|
12510
12794
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12511
12795
|
if (remindMs <= Date.now()) {
|
|
@@ -12520,11 +12804,8 @@ function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
|
12520
12804
|
function computeDailyRemindAt(time, remindBeforeMin) {
|
|
12521
12805
|
const [hh, mm] = time.split(":").map(Number);
|
|
12522
12806
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12523
|
-
const
|
|
12524
|
-
|
|
12525
|
-
const month = now.getUTCMonth();
|
|
12526
|
-
const date = now.getUTCDate();
|
|
12527
|
-
let eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
12807
|
+
const p = nowParts();
|
|
12808
|
+
let eventMs = wallMs(p.y, p.mo, p.d, hh, mm);
|
|
12528
12809
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12529
12810
|
if (remindMs <= Date.now()) {
|
|
12530
12811
|
remindMs += 24 * 60 * 60 * 1e3;
|
|
@@ -12534,18 +12815,17 @@ function computeDailyRemindAt(time, remindBeforeMin) {
|
|
|
12534
12815
|
function computeWeekdaysRemindAt(time, remindBeforeMin) {
|
|
12535
12816
|
const [hh, mm] = time.split(":").map(Number);
|
|
12536
12817
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12537
|
-
|
|
12538
|
-
let
|
|
12539
|
-
let
|
|
12540
|
-
let
|
|
12541
|
-
|
|
12542
|
-
|
|
12543
|
-
|
|
12544
|
-
|
|
12545
|
-
date++;
|
|
12546
|
-
curDay = (curDay + 1) % 7;
|
|
12547
|
-
eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
12818
|
+
let dayOffset = 0;
|
|
12819
|
+
let eventMs;
|
|
12820
|
+
let remindMs;
|
|
12821
|
+
let curDay;
|
|
12822
|
+
for (; ; ) {
|
|
12823
|
+
const t = wallParts(Date.parse(startOfLocalDay()) + dayOffset * 24 * 60 * 60 * 1e3);
|
|
12824
|
+
curDay = t.weekday;
|
|
12825
|
+
eventMs = wallMs(t.y, t.mo, t.d, hh, mm);
|
|
12548
12826
|
remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12827
|
+
if (remindMs > Date.now() && curDay !== 0 && curDay !== 6) break;
|
|
12828
|
+
dayOffset++;
|
|
12549
12829
|
}
|
|
12550
12830
|
return new Date(remindMs).toISOString();
|
|
12551
12831
|
}
|
|
@@ -12568,13 +12848,7 @@ function fmtRow(row) {
|
|
|
12568
12848
|
}
|
|
12569
12849
|
}
|
|
12570
12850
|
function fmtNow() {
|
|
12571
|
-
|
|
12572
|
-
const y = now.getUTCFullYear();
|
|
12573
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
12574
|
-
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
12575
|
-
const hh = String(now.getUTCHours()).padStart(2, "0");
|
|
12576
|
-
const mm = String(now.getUTCMinutes()).padStart(2, "0");
|
|
12577
|
-
return `${y}-${m}-${d} ${getTodayWeekday()} ${hh}:${mm}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
12851
|
+
return formatForLlm();
|
|
12578
12852
|
}
|
|
12579
12853
|
function parseDuration(s) {
|
|
12580
12854
|
if (s.endsWith("m")) return parseInt(s.slice(0, -1)) || null;
|
|
@@ -12651,9 +12925,9 @@ function getUpcomingTasks(workspace, days = 7, maxRows = 8) {
|
|
|
12651
12925
|
"SELECT * FROM events WHERE type='task' AND status='pending'"
|
|
12652
12926
|
).all();
|
|
12653
12927
|
if (rows.length === 0) return "";
|
|
12654
|
-
const
|
|
12655
|
-
const year =
|
|
12656
|
-
const todayMs = Date.
|
|
12928
|
+
const pt = nowParts();
|
|
12929
|
+
const year = pt.y;
|
|
12930
|
+
const todayMs = Date.parse(startOfLocalDay());
|
|
12657
12931
|
const horizonMs = todayMs + days * 864e5;
|
|
12658
12932
|
const picked = [];
|
|
12659
12933
|
for (const r of rows) {
|
|
@@ -12675,18 +12949,18 @@ function getUpcomingTasks(workspace, days = 7, maxRows = 8) {
|
|
|
12675
12949
|
return "";
|
|
12676
12950
|
}
|
|
12677
12951
|
}
|
|
12678
|
-
var
|
|
12952
|
+
var WEEKDAYS2;
|
|
12679
12953
|
var init_helpers = __esm({
|
|
12680
12954
|
"src/calendar/helpers.ts"() {
|
|
12681
12955
|
"use strict";
|
|
12682
12956
|
init_db();
|
|
12683
|
-
|
|
12957
|
+
init_clock();
|
|
12684
12958
|
WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
12685
12959
|
}
|
|
12686
12960
|
});
|
|
12687
12961
|
|
|
12688
12962
|
// src/calendar/commands.ts
|
|
12689
|
-
import { existsSync as existsSync14, statSync as
|
|
12963
|
+
import { existsSync as existsSync14, statSync as statSync3 } from "node:fs";
|
|
12690
12964
|
import { isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
|
|
12691
12965
|
function fmtEnd(start, durationMin) {
|
|
12692
12966
|
const [hh, mm] = start.split(":").map(Number);
|
|
@@ -12766,8 +13040,7 @@ function addDate(db2, args2) {
|
|
|
12766
13040
|
const parsed = parseDateRange(dateStr);
|
|
12767
13041
|
let weeklyRows = [];
|
|
12768
13042
|
if (parsed) {
|
|
12769
|
-
const
|
|
12770
|
-
const year = now.getUTCFullYear();
|
|
13043
|
+
const year = wallParts().y;
|
|
12771
13044
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
12772
13045
|
const weekday = WEEKDAYS3[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
12773
13046
|
weeklyRows = db2.prepare(
|
|
@@ -12816,8 +13089,7 @@ function addBatch(db2, args2) {
|
|
|
12816
13089
|
const parsed = parseDateRange(dateStr);
|
|
12817
13090
|
let weeklyRows = [];
|
|
12818
13091
|
if (parsed) {
|
|
12819
|
-
const
|
|
12820
|
-
const year = now.getUTCFullYear();
|
|
13092
|
+
const year = wallParts().y;
|
|
12821
13093
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
12822
13094
|
const weekday = WEEKDAYS3[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
12823
13095
|
weeklyRows = db2.prepare(
|
|
@@ -12863,7 +13135,7 @@ function addTask(db2, args2) {
|
|
|
12863
13135
|
\u89E3\u6790\u540E: ${absPath}
|
|
12864
13136
|
\u8BF7\u5148\u521B\u5EFA\u6587\u6863\u518D add-task\u3002`;
|
|
12865
13137
|
}
|
|
12866
|
-
const stat4 =
|
|
13138
|
+
const stat4 = statSync3(absPath);
|
|
12867
13139
|
if (!stat4.isFile()) {
|
|
12868
13140
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u4E0D\u662F\u6587\u4EF6\uFF08\u53EF\u80FD\u662F\u76EE\u5F55\uFF09
|
|
12869
13141
|
\u8DEF\u5F84: ${docPath}`;
|
|
@@ -12933,7 +13205,7 @@ function list(db2, opts = {}) {
|
|
|
12933
13205
|
}
|
|
12934
13206
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
12935
13207
|
if (r.repeat_type === "weekdays") {
|
|
12936
|
-
const idx =
|
|
13208
|
+
const idx = wallParts().weekday;
|
|
12937
13209
|
if (idx === 0 || idx === 6) continue;
|
|
12938
13210
|
}
|
|
12939
13211
|
events.push(` #${r.id} ${r.time || ""} ${r.event}`);
|
|
@@ -12961,7 +13233,7 @@ function pending(db2) {
|
|
|
12961
13233
|
}
|
|
12962
13234
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
12963
13235
|
if (r.repeat_type === "weekdays") {
|
|
12964
|
-
const idx =
|
|
13236
|
+
const idx = wallParts().weekday;
|
|
12965
13237
|
if (idx === 0 || idx === 6) continue;
|
|
12966
13238
|
}
|
|
12967
13239
|
const label = r.repeat_type === "weekdays" ? "[\u5DE5\u4F5C\u65E5]" : "[\u6BCF\u5929]";
|
|
@@ -13077,10 +13349,36 @@ function markReminded(db2, id) {
|
|
|
13077
13349
|
}
|
|
13078
13350
|
return `#${id} \u5DF2\u6807\u8BB0\u63D0\u9192\u8FC7`;
|
|
13079
13351
|
}
|
|
13352
|
+
function listEventsForInjection(db2, limit = 50) {
|
|
13353
|
+
return db2.prepare(
|
|
13354
|
+
`SELECT id, event, date_str, time_exact, type, status
|
|
13355
|
+
FROM events
|
|
13356
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13357
|
+
AND date_str IS NOT NULL AND date_str != ''
|
|
13358
|
+
ORDER BY date_str ASC LIMIT ?`
|
|
13359
|
+
).all(limit);
|
|
13360
|
+
}
|
|
13361
|
+
function listEventBriefs(db2, limit) {
|
|
13362
|
+
const rows = db2.prepare(
|
|
13363
|
+
`SELECT id, event, date_str, time_exact, type
|
|
13364
|
+
FROM events
|
|
13365
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13366
|
+
ORDER BY date_str ASC, time_exact ASC
|
|
13367
|
+
LIMIT ?`
|
|
13368
|
+
).all(limit);
|
|
13369
|
+
return rows.map((row) => ({
|
|
13370
|
+
id: Number(row.id),
|
|
13371
|
+
title: String(row.event ?? ""),
|
|
13372
|
+
date: String(row.date_str ?? ""),
|
|
13373
|
+
time: String(row.time_exact ?? ""),
|
|
13374
|
+
type: String(row.type ?? "")
|
|
13375
|
+
})).filter((row) => row.title.length > 0);
|
|
13376
|
+
}
|
|
13080
13377
|
var WEEKDAYS3;
|
|
13081
13378
|
var init_commands = __esm({
|
|
13082
13379
|
"src/calendar/commands.ts"() {
|
|
13083
13380
|
"use strict";
|
|
13381
|
+
init_clock();
|
|
13084
13382
|
init_helpers();
|
|
13085
13383
|
WEEKDAYS3 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
13086
13384
|
}
|
|
@@ -13088,10 +13386,9 @@ var init_commands = __esm({
|
|
|
13088
13386
|
|
|
13089
13387
|
// src/memory/proactive/calendar-bridge.ts
|
|
13090
13388
|
function toBeijingParts(iso) {
|
|
13091
|
-
const
|
|
13092
|
-
const
|
|
13093
|
-
const
|
|
13094
|
-
const timeExact = `${String(beijing.getUTCHours()).padStart(2, "0")}:${String(beijing.getUTCMinutes()).padStart(2, "0")}`;
|
|
13389
|
+
const b = wallParts(iso);
|
|
13390
|
+
const dateStr = `${b.mo}/${b.d}`;
|
|
13391
|
+
const timeExact = `${String(b.hh).padStart(2, "0")}:${String(b.mm).padStart(2, "0")}`;
|
|
13095
13392
|
return { dateStr, timeExact };
|
|
13096
13393
|
}
|
|
13097
13394
|
function parseCalendarDateMs(dateStr, now) {
|
|
@@ -13100,7 +13397,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
13100
13397
|
const month = parseInt(parts[0], 10);
|
|
13101
13398
|
const day = parseInt(parts[1], 10);
|
|
13102
13399
|
if (!Number.isFinite(month) || !Number.isFinite(day)) return null;
|
|
13103
|
-
let year = now.
|
|
13400
|
+
let year = wallParts(now).y;
|
|
13104
13401
|
let candidate = new Date(year, month - 1, day).getTime();
|
|
13105
13402
|
if (candidate < now.getTime() - 7 * 864e5) {
|
|
13106
13403
|
year += 1;
|
|
@@ -13110,13 +13407,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
13110
13407
|
}
|
|
13111
13408
|
function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new Date()) {
|
|
13112
13409
|
const cutoffMs = now.getTime() + days * 864e5;
|
|
13113
|
-
const rows = calDb
|
|
13114
|
-
`SELECT id, event, date_str, time_exact, type, status
|
|
13115
|
-
FROM events
|
|
13116
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13117
|
-
AND date_str IS NOT NULL AND date_str != ''
|
|
13118
|
-
ORDER BY date_str ASC LIMIT 50`
|
|
13119
|
-
).all();
|
|
13410
|
+
const rows = listEventsForInjection(calDb, 50);
|
|
13120
13411
|
if (rows.length === 0) return 0;
|
|
13121
13412
|
let injected = 0;
|
|
13122
13413
|
for (const row of rows) {
|
|
@@ -13159,27 +13450,108 @@ function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new
|
|
|
13159
13450
|
return injected;
|
|
13160
13451
|
}
|
|
13161
13452
|
function getCalendarCoverage(calDb, limit = 100) {
|
|
13162
|
-
|
|
13163
|
-
`SELECT id, event, date_str, time_exact, type
|
|
13164
|
-
FROM events
|
|
13165
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13166
|
-
ORDER BY date_str ASC, time_exact ASC
|
|
13167
|
-
LIMIT ?`
|
|
13168
|
-
).all(limit);
|
|
13169
|
-
return rows.map((row) => ({
|
|
13170
|
-
id: Number(row.id),
|
|
13171
|
-
title: String(row.event ?? ""),
|
|
13172
|
-
date: String(row.date_str ?? ""),
|
|
13173
|
-
time: String(row.time_exact ?? ""),
|
|
13174
|
-
type: String(row.type ?? "")
|
|
13175
|
-
})).filter((row) => row.title.length > 0);
|
|
13453
|
+
return listEventBriefs(calDb, limit);
|
|
13176
13454
|
}
|
|
13177
|
-
var TZ_OFFSET_MS3;
|
|
13178
13455
|
var init_calendar_bridge = __esm({
|
|
13179
13456
|
"src/memory/proactive/calendar-bridge.ts"() {
|
|
13180
13457
|
"use strict";
|
|
13181
13458
|
init_commands();
|
|
13182
|
-
|
|
13459
|
+
init_commands();
|
|
13460
|
+
init_clock();
|
|
13461
|
+
}
|
|
13462
|
+
});
|
|
13463
|
+
|
|
13464
|
+
// src/memory/proactive/recall-score.ts
|
|
13465
|
+
function weightOf(t) {
|
|
13466
|
+
if (CHANGE_RE.test(t)) return W.change;
|
|
13467
|
+
if (TIME_RE.test(t)) return W.time;
|
|
13468
|
+
if (EVENT_RE.test(t)) return W.event;
|
|
13469
|
+
if (LIFE_RE.test(t)) return W.life;
|
|
13470
|
+
if (ACTION_RE.test(t)) return W.action;
|
|
13471
|
+
if (PERSON_RE.test(t)) return W.person;
|
|
13472
|
+
return W.other;
|
|
13473
|
+
}
|
|
13474
|
+
function scoreForesightEntry(entry, terms) {
|
|
13475
|
+
const hay = `${entry.text} ${entry.evidence ?? ""}`;
|
|
13476
|
+
const kinds = { change: [], time: [], event: [], life: [], other: [], person: [], action: [] };
|
|
13477
|
+
let score = 0;
|
|
13478
|
+
for (const t of terms) {
|
|
13479
|
+
if (!hay.includes(t)) continue;
|
|
13480
|
+
const kind = CHANGE_RE.test(t) ? "change" : TIME_RE.test(t) ? "time" : EVENT_RE.test(t) ? "event" : LIFE_RE.test(t) ? "life" : ACTION_RE.test(t) ? "action" : PERSON_RE.test(t) ? "person" : "other";
|
|
13481
|
+
kinds[kind].push(t);
|
|
13482
|
+
score += W[kind];
|
|
13483
|
+
}
|
|
13484
|
+
return {
|
|
13485
|
+
score,
|
|
13486
|
+
matched: Object.values(kinds).flat(),
|
|
13487
|
+
kinds,
|
|
13488
|
+
hasSpecial: kinds.change.length > 0 || kinds.time.length > 0 || kinds.event.length > 0
|
|
13489
|
+
};
|
|
13490
|
+
}
|
|
13491
|
+
function rankForesightEntries(entries2, terms, opts = {}) {
|
|
13492
|
+
const minScore = opts.minScore ?? DEFAULT_MIN_SCORE3;
|
|
13493
|
+
const termList = [...terms];
|
|
13494
|
+
const N = Math.max(1, entries2.length);
|
|
13495
|
+
const df = /* @__PURE__ */ new Map();
|
|
13496
|
+
for (const t of termList) {
|
|
13497
|
+
let c = 0;
|
|
13498
|
+
for (const e of entries2) if (`${e.text} ${e.evidence ?? ""}`.includes(t)) c++;
|
|
13499
|
+
df.set(t, c);
|
|
13500
|
+
}
|
|
13501
|
+
const idf = (t) => {
|
|
13502
|
+
const c = df.get(t) ?? 0;
|
|
13503
|
+
if (c <= 0) return 0;
|
|
13504
|
+
return 1 + Math.log2(1 + N / c) / Math.log2(1 + N);
|
|
13505
|
+
};
|
|
13506
|
+
const out = [];
|
|
13507
|
+
for (const e of entries2) {
|
|
13508
|
+
const base = scoreForesightEntry(e, termList);
|
|
13509
|
+
const s = { ...base, score: termList.reduce((acc, t) => acc + (base.matched.includes(t) ? weightOf(t) * idf(t) : 0), 0) };
|
|
13510
|
+
if (opts.requireStructure) {
|
|
13511
|
+
const anchor = s.kinds.change.length > 0 || s.kinds.event.length > 0 || s.kinds.life.length > 0;
|
|
13512
|
+
if (!anchor) continue;
|
|
13513
|
+
}
|
|
13514
|
+
if (s.score < minScore) continue;
|
|
13515
|
+
const tag = (k) => s.kinds[k].length ? `${k}:${s.kinds[k].join("/")}` : "";
|
|
13516
|
+
out.push({ entry: e, score: s.score, reasons: [tag("change"), tag("time"), tag("event"), tag("other"), tag("person")].filter(Boolean).join(" ") });
|
|
13517
|
+
}
|
|
13518
|
+
return out.sort((a, b) => b.score - a.score);
|
|
13519
|
+
}
|
|
13520
|
+
function extractTerms(message, stopWords) {
|
|
13521
|
+
const terms = /* @__PURE__ */ new Set();
|
|
13522
|
+
for (const m of message.matchAll(TIME_SURFACE)) terms.add(m[0]);
|
|
13523
|
+
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
13524
|
+
if (run.length <= 6 && !stopWords.has(run)) terms.add(run);
|
|
13525
|
+
if (/[一-鿿]/.test(run)) {
|
|
13526
|
+
for (let i = 0; i + 2 <= run.length; i++) {
|
|
13527
|
+
const g = run.slice(i, i + 2);
|
|
13528
|
+
if (!stopWords.has(g)) terms.add(g);
|
|
13529
|
+
}
|
|
13530
|
+
}
|
|
13531
|
+
}
|
|
13532
|
+
return terms;
|
|
13533
|
+
}
|
|
13534
|
+
var CHANGE_RE, TIME_RE, ACTION_RE, EVENT_RE, LIFE_RE, PERSON_RE, W, DEFAULT_MIN_SCORE3, TIME_SURFACE;
|
|
13535
|
+
var init_recall_score = __esm({
|
|
13536
|
+
"src/memory/proactive/recall-score.ts"() {
|
|
13537
|
+
"use strict";
|
|
13538
|
+
CHANGE_RE = /^(改|变成|变为|变更|改期|取消|没了|挪|调整|换|推|提前|延后|顺延|恢复|新增|加上|改成|改到|调到|排|定)$/;
|
|
13539
|
+
TIME_RE = /^(\d{1,2}\/\d{1,2}|\d{1,2}月|\d{1,2}日|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|上周|本周|今晚|今早|明早|明晚|早上|上午|中午|下午|晚上|凌晨|月底|月初|\d{1,2}:\d{2}|\d{1,2}[点時时])$/;
|
|
13540
|
+
ACTION_RE = /^(翻|翻翻|上传|搞完|搞|看看|看|发我|发给|截图|下载|跑|贴|转写|重发|试试|查|找|写|做|弄|传|发)$/;
|
|
13541
|
+
EVENT_RE = /^(来电|电话|打电话|打给|上门|到访|通知|审核|拜访|到货|快递|送货|生病|发烧|感冒|住院|输水|挂水|复诊|复查|面试|体检|签约|合同|付款|到账|截止|到期)$/;
|
|
13542
|
+
LIFE_RE = /^(回家|出差|探亲|复查|复诊|体检|住院|生日|纪念日|搬家|开学|放假|假期|寒假|暑假|机票|车票|高铁票|飞机|电影|聚餐|聚会|拜访|来访|接机|送机|考试|报名|签证|护照|续签|过户)$/;
|
|
13543
|
+
PERSON_RE = /^(翀哥|老公|老婆|睿阳|荣阳|张小媒|小柯|小雯|孙总|曲教授|潘总|燕子|方老师|李经理|Leigh|amy|Amy)$/;
|
|
13544
|
+
W = {
|
|
13545
|
+
change: 2,
|
|
13546
|
+
time: 1.5,
|
|
13547
|
+
event: 2,
|
|
13548
|
+
life: 2,
|
|
13549
|
+
other: 1,
|
|
13550
|
+
person: 0.3,
|
|
13551
|
+
action: 0
|
|
13552
|
+
};
|
|
13553
|
+
DEFAULT_MIN_SCORE3 = Number(process.env.RECALL_MIN_SCORE ?? 1);
|
|
13554
|
+
TIME_SURFACE = /\d{1,2}月\d{1,2}[日号]?|\d{1,2}号|\d{1,2}\/\d{1,2}|\d{1,2}[点时::]\d{0,2}|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|本周|上周|今晚|今早|明早|明晚|月底|月初|下个月|这个月/g;
|
|
13183
13555
|
}
|
|
13184
13556
|
});
|
|
13185
13557
|
|
|
@@ -13257,7 +13629,7 @@ function loadForesightEntries(everosRoot, now, userId, ownerFilter) {
|
|
|
13257
13629
|
return entries2.slice();
|
|
13258
13630
|
}
|
|
13259
13631
|
function parseForesightEntries(everosRoot, now, userId) {
|
|
13260
|
-
const
|
|
13632
|
+
const today2 = todayKey(now);
|
|
13261
13633
|
let files;
|
|
13262
13634
|
if (userId) {
|
|
13263
13635
|
const ownDir = join30(everosRoot, userId, "default_project", "users", userId, ".foresights");
|
|
@@ -13282,7 +13654,7 @@ function parseForesightEntries(everosRoot, now, userId) {
|
|
|
13282
13654
|
const end = (block.match(/\*\*end_time\*\*:\s*(\d{4}-\d{2}-\d{2})/) ?? [])[1];
|
|
13283
13655
|
const text = ((block.match(/### Foresight\s*\n+([\s\S]*?)(?:\n### Evidence|$)/) ?? [])[1] ?? "").trim();
|
|
13284
13656
|
if (!start || !end || !text) continue;
|
|
13285
|
-
if (end <
|
|
13657
|
+
if (end < today2) continue;
|
|
13286
13658
|
const evidence = ((block.match(/### Evidence\s*\n+([\s\S]*?)$/) ?? [])[1] ?? "").trim();
|
|
13287
13659
|
const blockOwner = (block.match(/\*\*owner_id\*\*:\s*(\S+)/) ?? [])[1] ?? fileOwner;
|
|
13288
13660
|
const key = text.replace(/\s+/g, "");
|
|
@@ -13293,35 +13665,11 @@ function parseForesightEntries(everosRoot, now, userId) {
|
|
|
13293
13665
|
}
|
|
13294
13666
|
return out;
|
|
13295
13667
|
}
|
|
13296
|
-
function
|
|
13297
|
-
const
|
|
13298
|
-
const out = loadForesightEntries(everosRoot, now, userId);
|
|
13299
|
-
const isOpen = (f) => f.start <= today;
|
|
13300
|
-
out.sort((a, b) => Number(isOpen(b)) - Number(isOpen(a)) || (isOpen(a) ? a.end.localeCompare(b.end) : a.start.localeCompare(b.start)));
|
|
13301
|
-
const open = out.filter(isOpen).slice(0, Math.min(12, Math.floor(maxEntries * 0.6)));
|
|
13302
|
-
const upcoming = out.filter((f) => !isOpen(f)).slice(0, Math.max(0, maxEntries - open.length));
|
|
13303
|
-
return [...open, ...upcoming].map((f) => `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}${f.evidence ? `\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09` : ""}`);
|
|
13304
|
-
}
|
|
13305
|
-
function matchForesightForEvent(entries2, message, maxHits = 5) {
|
|
13306
|
-
const terms = /* @__PURE__ */ new Set();
|
|
13307
|
-
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
13308
|
-
if (run.length <= 6 && /[一-鿿]/.test(run) && !FS_MATCH_STOP.has(run)) terms.add(run);
|
|
13309
|
-
if (/[一-鿿]/.test(run)) {
|
|
13310
|
-
for (let i = 0; i + 2 <= run.length; i++) {
|
|
13311
|
-
const g = run.slice(i, i + 2);
|
|
13312
|
-
if (!FS_MATCH_STOP.has(g)) terms.add(g);
|
|
13313
|
-
}
|
|
13314
|
-
}
|
|
13315
|
-
}
|
|
13668
|
+
function matchForesightForEvent(entries2, message, maxHits = 2) {
|
|
13669
|
+
const terms = extractTerms(message, FS_MATCH_STOP);
|
|
13316
13670
|
if (terms.size === 0) return [];
|
|
13317
|
-
const
|
|
13318
|
-
|
|
13319
|
-
const hay = `${f.text} ${f.evidence}`;
|
|
13320
|
-
const term = [...terms].find((t) => hay.includes(t));
|
|
13321
|
-
if (term) hits.push({ line: `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09`, term });
|
|
13322
|
-
if (hits.length >= maxHits) break;
|
|
13323
|
-
}
|
|
13324
|
-
return hits.map((h) => h.line);
|
|
13671
|
+
const ranked = rankForesightEntries(entries2, terms);
|
|
13672
|
+
return ranked.slice(0, maxHits).map((r) => `- [\u7A97\u53E3 ${r.entry.start}~${r.entry.end}] ${r.entry.text}\uFF08\u4F9D\u636E\uFF1A${r.entry.evidence}\uFF09`);
|
|
13325
13673
|
}
|
|
13326
13674
|
function listForesightMds(dir) {
|
|
13327
13675
|
try {
|
|
@@ -13461,6 +13809,8 @@ var init_opportunity = __esm({
|
|
|
13461
13809
|
init_models();
|
|
13462
13810
|
init_calendar_bridge();
|
|
13463
13811
|
init_embedding();
|
|
13812
|
+
init_recall_score();
|
|
13813
|
+
init_clock();
|
|
13464
13814
|
OPPORTUNITY_SCAN_INTERVAL_MS = 6 * 60 * 6e4;
|
|
13465
13815
|
foresightCache = /* @__PURE__ */ new Map();
|
|
13466
13816
|
FORESIGHT_CACHE_TTL_MS = 30 * 6e4;
|
|
@@ -13976,16 +14326,16 @@ var init_executor3 = __esm({
|
|
|
13976
14326
|
const refLower = ref.toLowerCase().trim();
|
|
13977
14327
|
if (!refLower) return null;
|
|
13978
14328
|
if (!this.titleToIdCache || this.titleToIdCache.size < this.store.nodeCount) {
|
|
13979
|
-
const
|
|
14329
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
13980
14330
|
for (const node of this.store.getAllNodes()) {
|
|
13981
14331
|
const title = String(node.data.title ?? "").toLowerCase().trim();
|
|
13982
|
-
if (title)
|
|
14332
|
+
if (title) cache3.set(title, node.id);
|
|
13983
14333
|
}
|
|
13984
|
-
this.titleToIdCache =
|
|
14334
|
+
this.titleToIdCache = cache3;
|
|
13985
14335
|
}
|
|
13986
|
-
const
|
|
13987
|
-
if (
|
|
13988
|
-
for (const [title, id] of
|
|
14336
|
+
const cache2 = this.titleToIdCache;
|
|
14337
|
+
if (cache2.has(refLower)) return cache2.get(refLower);
|
|
14338
|
+
for (const [title, id] of cache2) {
|
|
13989
14339
|
if (title.includes(refLower) || refLower.includes(title)) return id;
|
|
13990
14340
|
}
|
|
13991
14341
|
return null;
|
|
@@ -14595,18 +14945,23 @@ ${JSON.stringify(payload, null, 2)}
|
|
|
14595
14945
|
|
|
14596
14946
|
// src/memory/proactive/proactive-tools.ts
|
|
14597
14947
|
import { existsSync as existsSync16 } from "node:fs";
|
|
14598
|
-
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
14599
14948
|
function setProactiveWorkspace(workspace) {
|
|
14600
14949
|
toolWorkspace = workspace;
|
|
14601
14950
|
}
|
|
14602
|
-
function
|
|
14951
|
+
function withStore(fn) {
|
|
14603
14952
|
if (!toolWorkspace) return null;
|
|
14604
14953
|
const p = proactiveDbPath(toolWorkspace);
|
|
14605
14954
|
if (!existsSync16(p)) return null;
|
|
14606
|
-
|
|
14955
|
+
const store = new Persistence(p);
|
|
14956
|
+
try {
|
|
14957
|
+
return fn(store);
|
|
14958
|
+
} finally {
|
|
14959
|
+
store.close();
|
|
14960
|
+
}
|
|
14607
14961
|
}
|
|
14608
|
-
function fmtRow2(
|
|
14609
|
-
|
|
14962
|
+
function fmtRow2(a) {
|
|
14963
|
+
const when = a.scheduledTime ? formatForLlm(a.scheduledTime) : "(\u65E0)";
|
|
14964
|
+
return `#${a.id} [${a.status}] ${a.title} (urgency=${a.urgency}, scheduled=${when}${a.calendarId ? `, cal=#${a.calendarId}` : ""})`;
|
|
14610
14965
|
}
|
|
14611
14966
|
function registerProactiveTools() {
|
|
14612
14967
|
registry.register({
|
|
@@ -14621,50 +14976,43 @@ function registerProactiveTools() {
|
|
|
14621
14976
|
required: ["action_id"]
|
|
14622
14977
|
},
|
|
14623
14978
|
handler: async (args2) => {
|
|
14624
|
-
const
|
|
14625
|
-
|
|
14626
|
-
|
|
14627
|
-
|
|
14628
|
-
if (
|
|
14629
|
-
|
|
14630
|
-
|
|
14631
|
-
|
|
14632
|
-
db2.prepare("UPDATE actions SET status=?, completed_at=? WHERE id=?").run("completed", (/* @__PURE__ */ new Date()).toISOString(), args2.action_id);
|
|
14633
|
-
}
|
|
14634
|
-
return { content: `\u2713 completed: ${row.title}${args2.calendar_id !== void 0 ? `\uFF08calendar #${args2.calendar_id} \u5DF2\u56DE\u586B\uFF09` : ""}` };
|
|
14635
|
-
} finally {
|
|
14636
|
-
db2.close();
|
|
14637
|
-
}
|
|
14979
|
+
const out = withStore((store) => {
|
|
14980
|
+
const a = store.getAction(String(args2.action_id));
|
|
14981
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args2.action_id}' \u4E0D\u5B58\u5728\u3002\u7528 proactive_status \u67E5\u5F53\u524D\u5217\u8868\u3002`, isError: true };
|
|
14982
|
+
store.updateActionStatus(a.id, "completed", { completedAt: nowUtc() });
|
|
14983
|
+
if (args2.calendar_id !== void 0) store.linkCalendar(a.id, Number(args2.calendar_id));
|
|
14984
|
+
return { content: `\u2713 action ${a.id} \u6807\u8BB0\u4E3A completed${args2.calendar_id !== void 0 ? `\uFF08\u5173\u8054 calendar #${args2.calendar_id}\uFF09` : ""}` };
|
|
14985
|
+
});
|
|
14986
|
+
return out ?? { content: "proactive \u672A\u542F\u7528\uFF08\u65E0 workspace \u6216 .proactive/proactive.db\uFF09", isError: true };
|
|
14638
14987
|
}
|
|
14639
14988
|
});
|
|
14640
14989
|
registry.register({
|
|
14641
14990
|
name: "proactive_cancel",
|
|
14642
|
-
description: "\
|
|
14991
|
+
description: "\u53D6\u6D88\u4E00\u4E2A proactive action\uFF08\u5224\u5B9A\u4E3A\u4E0D\u8BE5\u505A/\u5DF2\u8FC7\u671F/\u9A6C\u540E\u70AE\u65F6\u8C03\u7528\uFF09\u3002",
|
|
14643
14992
|
schema: {
|
|
14644
14993
|
type: "object",
|
|
14645
14994
|
properties: {
|
|
14646
14995
|
action_id: { type: "string", description: "action id" },
|
|
14647
|
-
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\
|
|
14996
|
+
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\u4F1A\u8BB0\u8FDB cancel_reason\uFF09" }
|
|
14648
14997
|
},
|
|
14649
|
-
required: ["action_id"
|
|
14998
|
+
required: ["action_id"]
|
|
14650
14999
|
},
|
|
14651
15000
|
handler: async (args2) => {
|
|
14652
|
-
const
|
|
14653
|
-
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
}
|
|
14661
|
-
|
|
14662
|
-
}
|
|
15001
|
+
const out = withStore((store) => {
|
|
15002
|
+
const a = store.getAction(String(args2.action_id));
|
|
15003
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args2.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
15004
|
+
store.updateActionStatus(a.id, "cancelled", {
|
|
15005
|
+
completedAt: nowUtc(),
|
|
15006
|
+
cancelReason: String(args2.reason || "\u672A\u6CE8\u660E")
|
|
15007
|
+
});
|
|
15008
|
+
return { content: `\u2713 action ${a.id} \u5DF2\u53D6\u6D88\uFF08${String(args2.reason || "\u672A\u6CE8\u660E")}\uFF09` };
|
|
15009
|
+
});
|
|
15010
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14663
15011
|
}
|
|
14664
15012
|
});
|
|
14665
15013
|
registry.register({
|
|
14666
15014
|
name: "proactive_resolve_intent",
|
|
14667
|
-
description: "\u628A\u4E00\u4E2A
|
|
15015
|
+
description: "\u628A\u4E00\u4E2A intent \u6807\u8BB0\u4E3A resolved\uFF0C\u5E76\u7EA7\u8054\u53D6\u6D88\u5B83\u4E0B\u9762\u6240\u6709\u6392\u961F\u7684\u5144\u5F1F action\u3002\u7528\u4E8E\u5224\u5B9A\u300C\u8FD9\u4EF6\u4E8B\u5DF2\u7ECF\u4E0D\u7528\u518D\u63D0\u4E86\u300D\u3002",
|
|
14668
15016
|
schema: {
|
|
14669
15017
|
type: "object",
|
|
14670
15018
|
properties: {
|
|
@@ -14673,31 +15021,25 @@ function registerProactiveTools() {
|
|
|
14673
15021
|
}
|
|
14674
15022
|
},
|
|
14675
15023
|
handler: async (args2) => {
|
|
14676
|
-
const
|
|
14677
|
-
if (!db2) return { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14678
|
-
try {
|
|
15024
|
+
const out = withStore((store) => {
|
|
14679
15025
|
let intentId = String(args2.intent_id || "");
|
|
14680
15026
|
if (!intentId && args2.action_id) {
|
|
14681
|
-
const
|
|
14682
|
-
if (!
|
|
14683
|
-
intentId =
|
|
15027
|
+
const a = store.getAction(String(args2.action_id));
|
|
15028
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args2.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
15029
|
+
intentId = a.intentId;
|
|
14684
15030
|
}
|
|
14685
15031
|
if (!intentId) return { content: "Usage: proactive_resolve_intent --action_id <id> \u6216 --intent_id <id>", isError: true };
|
|
14686
|
-
const
|
|
14687
|
-
if (
|
|
14688
|
-
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14693
|
-
|
|
14694
|
-
|
|
14695
|
-
|
|
14696
|
-
|
|
14697
|
-
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${Number(res.changes)} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
14698
|
-
} finally {
|
|
14699
|
-
db2.close();
|
|
14700
|
-
}
|
|
15032
|
+
const data = store.getIntentData(intentId);
|
|
15033
|
+
if (data === null) return { content: `\u9519\u8BEF: intent '${intentId}' \u4E0D\u5B58\u5728`, isError: true };
|
|
15034
|
+
store.setIntentData(intentId, { ...data, status: "resolved" });
|
|
15035
|
+
const n = store.cancelActionsOfIntents(
|
|
15036
|
+
[intentId],
|
|
15037
|
+
"intent resolved by agent\uFF08\u9A6C\u540E\u70AE\u6E05\u626B\uFF09",
|
|
15038
|
+
nowUtc()
|
|
15039
|
+
);
|
|
15040
|
+
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${n} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
15041
|
+
});
|
|
15042
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14701
15043
|
}
|
|
14702
15044
|
});
|
|
14703
15045
|
registry.register({
|
|
@@ -14710,16 +15052,12 @@ function registerProactiveTools() {
|
|
|
14710
15052
|
}
|
|
14711
15053
|
},
|
|
14712
15054
|
handler: async (args2) => {
|
|
14713
|
-
const
|
|
14714
|
-
|
|
14715
|
-
try {
|
|
14716
|
-
const where = args2.include_done ? "status NOT IN ('cancelled')" : "status IN ('queued','executing')";
|
|
14717
|
-
const rows = db2.prepare(`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`).all();
|
|
15055
|
+
const out = withStore((store) => {
|
|
15056
|
+
const rows = store.listActions(args2.include_done ? "not-cancelled" : "open");
|
|
14718
15057
|
if (rows.length === 0) return { content: "\u5F53\u524D\u6CA1\u6709\u5F85\u5904\u7406\u7684 proactive action \u2713" };
|
|
14719
15058
|
return { content: rows.map(fmtRow2).join("\n") };
|
|
14720
|
-
}
|
|
14721
|
-
|
|
14722
|
-
}
|
|
15059
|
+
});
|
|
15060
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14723
15061
|
}
|
|
14724
15062
|
});
|
|
14725
15063
|
}
|
|
@@ -14729,6 +15067,8 @@ var init_proactive_tools = __esm({
|
|
|
14729
15067
|
"use strict";
|
|
14730
15068
|
init_registry();
|
|
14731
15069
|
init_config3();
|
|
15070
|
+
init_persistence();
|
|
15071
|
+
init_clock();
|
|
14732
15072
|
}
|
|
14733
15073
|
});
|
|
14734
15074
|
|
|
@@ -14792,7 +15132,7 @@ __export(plugin_exports, {
|
|
|
14792
15132
|
ProactivePlugin: () => ProactivePlugin,
|
|
14793
15133
|
feedMem7Cell: () => feedMem7Cell
|
|
14794
15134
|
});
|
|
14795
|
-
import { join as join31
|
|
15135
|
+
import { join as join31 } from "node:path";
|
|
14796
15136
|
import { existsSync as existsSync17, readFileSync as readFileSync23 } from "node:fs";
|
|
14797
15137
|
function isFiller(text) {
|
|
14798
15138
|
const t = text.trim();
|
|
@@ -14974,19 +15314,23 @@ var init_plugin = __esm({
|
|
|
14974
15314
|
return;
|
|
14975
15315
|
}
|
|
14976
15316
|
if (!this.provider) {
|
|
14977
|
-
const
|
|
15317
|
+
const configuredId = this.config.providerId;
|
|
15318
|
+
const providerCfg = configuredId ? this.providers[configuredId] : void 0;
|
|
14978
15319
|
if (providerCfg) {
|
|
14979
15320
|
this.provider = createProvider(providerCfg);
|
|
14980
15321
|
} else {
|
|
14981
15322
|
const mainProvider = ctx.deps?.engine?.getProvider?.();
|
|
14982
15323
|
const mainModel = ctx.config?.model;
|
|
15324
|
+
const mainProviderId = ctx.config?.provider?.id || "main";
|
|
14983
15325
|
if (mainProvider && mainModel) {
|
|
14984
15326
|
this.provider = this.mainProviderProxy(ctx);
|
|
14985
15327
|
const modelId = String(mainModel);
|
|
14986
|
-
this.config = { ...this.config, modelId, modelRef: `${
|
|
14987
|
-
|
|
15328
|
+
this.config = { ...this.config, modelId, modelRef: `${mainProviderId}/${modelId}` };
|
|
15329
|
+
if (configuredId) {
|
|
15330
|
+
console.warn(`[proactive] \u914D\u7F6E\u7684 provider "${configuredId}" \u4E0D\u5728 models.providers \u91CC \u2014 \u6539\u7528\u4E3B\u6A21\u578B\uFF08${mainProviderId}/${modelId}\uFF09`);
|
|
15331
|
+
}
|
|
14988
15332
|
} else {
|
|
14989
|
-
console.warn(`[proactive] provider "${
|
|
15333
|
+
console.warn(`[proactive] ${configuredId ? `provider "${configuredId}" \u4E0D\u5B58\u5728\uFF0C\u4E14` : ""}\u4E3B provider \u4E0D\u53EF\u7528 \u2014 \u4E0D\u542F\u52A8`);
|
|
14990
15334
|
return;
|
|
14991
15335
|
}
|
|
14992
15336
|
}
|
|
@@ -15081,18 +15425,7 @@ ${String(input.tool_output ?? "")}`
|
|
|
15081
15425
|
foresightLines.push(`[mem7] [\u7A97\u53E3 ${e.start}~${e.end}] ${e.text}${e.evidence ? `\uFF08\u4F9D\u636E\uFF1A${e.evidence}\uFF09` : ""}`);
|
|
15082
15426
|
}
|
|
15083
15427
|
}
|
|
15084
|
-
|
|
15085
|
-
if (lancedbPath) {
|
|
15086
|
-
const everosRoot = join31(dirname8(lancedbPath), "..");
|
|
15087
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
15088
|
-
for (const l of loadForesightLines(everosRoot, nowDate, 20, everosUserId)) {
|
|
15089
|
-
const key = l.replace(/\s+/g, "");
|
|
15090
|
-
if (seen.has(key)) continue;
|
|
15091
|
-
seen.add(key);
|
|
15092
|
-
foresightLines.push(l.replace(/^- /, "[everos] "));
|
|
15093
|
-
}
|
|
15094
|
-
}
|
|
15095
|
-
if (foresightLines.length > 0) console.log(`[proactive] foresight dual-source total: ${foresightLines.length}`);
|
|
15428
|
+
if (foresightLines.length > 0) console.log(`[proactive] mem7 foresight total: ${foresightLines.length}`);
|
|
15096
15429
|
} catch (err) {
|
|
15097
15430
|
console.warn(`[proactive] foresight load failed: ${err.message}`);
|
|
15098
15431
|
}
|
|
@@ -15266,18 +15599,6 @@ ${String(input.tool_output ?? "")}`
|
|
|
15266
15599
|
} else if (stateDir && !scopeReady) {
|
|
15267
15600
|
console.warn("[proactive] mem7 recall skipped: sender scope but cell has no user speaker");
|
|
15268
15601
|
}
|
|
15269
|
-
if (senderScoped) {
|
|
15270
|
-
this.warnOnce("everos-skip", "[proactive] everos \u6E90\u5728 sender \u6A21\u5F0F\u4E0B\u8DF3\u8FC7\uFF08\u65E0 per-speaker \u6620\u5C04\uFF09");
|
|
15271
|
-
} else {
|
|
15272
|
-
const lancedbPath = String(liveConfig.get("everos.lancedbPath") ?? "");
|
|
15273
|
-
if (lancedbPath) {
|
|
15274
|
-
const everosRoot = join31(dirname8(lancedbPath), "..");
|
|
15275
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
15276
|
-
for (const h of matchForesightForEvent(loadForesightEntries(everosRoot, now, everosUserId), prompt)) {
|
|
15277
|
-
hitSet.add(`[everos] ${h.replace(/^- /, "")}`);
|
|
15278
|
-
}
|
|
15279
|
-
}
|
|
15280
|
-
}
|
|
15281
15602
|
if (hitSet.size > 0) {
|
|
15282
15603
|
foresightHits = [...hitSet];
|
|
15283
15604
|
console.log(`[proactive] foresight matched ${hitSet.size} open-window prediction(s) for incoming message`);
|
|
@@ -15358,10 +15679,10 @@ ${String(input.tool_output ?? "")}`
|
|
|
15358
15679
|
|
|
15359
15680
|
// src/memory/sessionMemory/sessionMemoryUtils.ts
|
|
15360
15681
|
import { join as join32 } from "node:path";
|
|
15361
|
-
import { mkdirSync as
|
|
15682
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync24 } from "node:fs";
|
|
15362
15683
|
function setSessionMemoryStateDir(dir) {
|
|
15363
15684
|
_stateDir2 = dir;
|
|
15364
|
-
|
|
15685
|
+
mkdirSync10(dir, { recursive: true });
|
|
15365
15686
|
}
|
|
15366
15687
|
function getSessionMemoryDir() {
|
|
15367
15688
|
return _stateDir2;
|
|
@@ -15633,7 +15954,7 @@ __export(sessionMemory_exports, {
|
|
|
15633
15954
|
resetLastMemoryMessageUuid: () => resetLastMemoryMessageUuid,
|
|
15634
15955
|
shouldExtractMemory: () => shouldExtractMemory
|
|
15635
15956
|
});
|
|
15636
|
-
import { readFileSync as readFileSync25, mkdirSync as
|
|
15957
|
+
import { readFileSync as readFileSync25, mkdirSync as mkdirSync11 } from "node:fs";
|
|
15637
15958
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
15638
15959
|
function initSessionMemory(deps) {
|
|
15639
15960
|
_deps = deps;
|
|
@@ -15692,7 +16013,7 @@ function shouldExtractMemory(estimatedTokens, messages) {
|
|
|
15692
16013
|
}
|
|
15693
16014
|
async function setupSessionMemoryFile() {
|
|
15694
16015
|
const memoryDir = getSessionMemoryDir();
|
|
15695
|
-
|
|
16016
|
+
mkdirSync11(memoryDir, { recursive: true });
|
|
15696
16017
|
const memoryPath = getSessionMemoryPath();
|
|
15697
16018
|
let currentMemory;
|
|
15698
16019
|
try {
|
|
@@ -15733,7 +16054,7 @@ Here is the recent conversation context that you should extract notes from:
|
|
|
15733
16054
|
|
|
15734
16055
|
${messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-20).map((m) => {
|
|
15735
16056
|
const text = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
15736
|
-
return `[${m.role}]: ${text
|
|
16057
|
+
return `[${m.role}]: ${truncateByCodePoint(text, 500)}`;
|
|
15737
16058
|
}).join("\n\n")}`,
|
|
15738
16059
|
maxTurns: 3,
|
|
15739
16060
|
toolOverride: editTools.length > 0 ? editTools : void 0,
|
|
@@ -15795,6 +16116,7 @@ var init_sessionMemory = __esm({
|
|
|
15795
16116
|
"use strict";
|
|
15796
16117
|
init_sessionMemoryUtils();
|
|
15797
16118
|
init_prompts2();
|
|
16119
|
+
init_well_formed();
|
|
15798
16120
|
init_query();
|
|
15799
16121
|
init_registry();
|
|
15800
16122
|
init_features();
|
|
@@ -16226,7 +16548,7 @@ async function createTask(params) {
|
|
|
16226
16548
|
validateSchedule(params.schedule);
|
|
16227
16549
|
const id = generateTaskId2();
|
|
16228
16550
|
const now = /* @__PURE__ */ new Date();
|
|
16229
|
-
const timezone = params.timezone || params.defaultTimezone ||
|
|
16551
|
+
const timezone = params.timezone || params.defaultTimezone || tz();
|
|
16230
16552
|
const nextRunAt = params.calculateNextRun(params.schedule, timezone, now);
|
|
16231
16553
|
const task = {
|
|
16232
16554
|
id,
|
|
@@ -16327,6 +16649,7 @@ var storageDir, tasksFilePath, LOCK_RETRY_COUNT, LOCK_RETRY_DELAY_MS, LOCK_STALE
|
|
|
16327
16649
|
var init_tasks = __esm({
|
|
16328
16650
|
"src/cron/tasks.ts"() {
|
|
16329
16651
|
"use strict";
|
|
16652
|
+
init_clock();
|
|
16330
16653
|
storageDir = "";
|
|
16331
16654
|
tasksFilePath = "";
|
|
16332
16655
|
LOCK_RETRY_COUNT = 3;
|
|
@@ -16363,7 +16686,7 @@ async function stopScheduler() {
|
|
|
16363
16686
|
console.log("[cron] Scheduler stopped");
|
|
16364
16687
|
}
|
|
16365
16688
|
function calculateNextRun(schedule, timezone, afterDate) {
|
|
16366
|
-
const
|
|
16689
|
+
const tz2 = cronDefaultTimezone(timezone);
|
|
16367
16690
|
switch (schedule.type) {
|
|
16368
16691
|
case "at": {
|
|
16369
16692
|
const atMs = new Date(schedule.at).getTime();
|
|
@@ -16375,7 +16698,7 @@ function calculateNextRun(schedule, timezone, afterDate) {
|
|
|
16375
16698
|
}
|
|
16376
16699
|
case "cron": {
|
|
16377
16700
|
try {
|
|
16378
|
-
const job = new Cron(schedule.expression, { timezone:
|
|
16701
|
+
const job = new Cron(schedule.expression, { timezone: tz2 });
|
|
16379
16702
|
const next = job.nextRun(afterDate);
|
|
16380
16703
|
return next?.toISOString();
|
|
16381
16704
|
} catch (err) {
|
|
@@ -16406,9 +16729,12 @@ function shouldFireWithJitter(task, now) {
|
|
|
16406
16729
|
}
|
|
16407
16730
|
}
|
|
16408
16731
|
}
|
|
16732
|
+
function cronDefaultTimezone(taskTimezone) {
|
|
16733
|
+
return taskTimezone || tz();
|
|
16734
|
+
}
|
|
16409
16735
|
function estimateCronPeriodMs(expression, timezone) {
|
|
16410
16736
|
try {
|
|
16411
|
-
const job = new Cron(expression, { timezone: timezone
|
|
16737
|
+
const job = new Cron(expression, { timezone: cronDefaultTimezone(timezone) });
|
|
16412
16738
|
const next1 = job.nextRun();
|
|
16413
16739
|
if (!next1) return 24 * 60 * 60 * 1e3;
|
|
16414
16740
|
const next2 = job.nextRun(new Date(next1.getTime() + 1));
|
|
@@ -16588,7 +16914,7 @@ ${notifyLines}`;
|
|
|
16588
16914
|
console.warn(`[cron] Failed to notify main session: ${err.message}`);
|
|
16589
16915
|
}
|
|
16590
16916
|
}
|
|
16591
|
-
await recalculateNextRun(task, now, (schedule,
|
|
16917
|
+
await recalculateNextRun(task, now, (schedule, tz2, after) => calculateNextRun(schedule, tz2, after));
|
|
16592
16918
|
await markExecuted(task.id, true);
|
|
16593
16919
|
console.log(`[cron] Task ${task.id} done (run #${task.runCount})`);
|
|
16594
16920
|
} catch (err) {
|
|
@@ -16652,6 +16978,7 @@ var init_scheduler = __esm({
|
|
|
16652
16978
|
"src/cron/scheduler.ts"() {
|
|
16653
16979
|
"use strict";
|
|
16654
16980
|
init_live();
|
|
16981
|
+
init_clock();
|
|
16655
16982
|
init_tasks();
|
|
16656
16983
|
schedulerTimer = null;
|
|
16657
16984
|
shuttingDown = false;
|
|
@@ -16765,7 +17092,7 @@ function registerCronTools() {
|
|
|
16765
17092
|
delivery_mode: { type: "string", enum: ["origin", "local", "direct"], description: "\u6295\u9012\u6A21\u5F0F\uFF08\u9ED8\u8BA4 local\uFF09" },
|
|
16766
17093
|
delivery_channel: { type: "string", description: "\u6295\u9012\u901A\u9053\uFF08direct \u6A21\u5F0F\u5FC5\u586B\uFF09: discord | feishu | wecom" },
|
|
16767
17094
|
delivery_target: { type: "string", description: "\u6295\u9012\u76EE\u6807\uFF08direct \u6A21\u5F0F\u5FC5\u586B\uFF09: channel_id \u6216 user_id" },
|
|
16768
|
-
timezone: { type: "string", description: "IANA \u65F6\u533A\uFF08\u9ED8\u8BA4
|
|
17095
|
+
timezone: { type: "string", description: "IANA \u65F6\u533A\uFF08\u9ED8\u8BA4\u8DDF\u968F\u5F15\u64CE\u7684 timezone \u914D\u7F6E\uFF0C\u4E0D\u8981\u663E\u5F0F\u6307\u5B9A\u9664\u975E\u786E\u5B9E\u8981\u522B\u7684\u65F6\u533A\uFF09" },
|
|
16769
17096
|
max_runs: { type: "number", description: "\u6700\u5927\u6267\u884C\u6B21\u6570\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u65E0\u9650\uFF09" },
|
|
16770
17097
|
notify_session: { type: "boolean", description: "\u6267\u884C\u5B8C\u540E\u901A\u77E5\u4E3Bsession\uFF08\u6CE8\u5165\u4E00\u6761user\u6D88\u606F\u5230scope:main\uFF09" },
|
|
16771
17098
|
session_message: { type: "string", description: "\u6CE8\u5165\u5230\u4E3Bsession\u7684\u6D88\u606F\u6A21\u677F\uFF0C\u652F\u6301 {result} {description} \u5360\u4F4D\u7B26" },
|
|
@@ -17550,7 +17877,7 @@ var reply_blocklist_exports = {};
|
|
|
17550
17877
|
__export(reply_blocklist_exports, {
|
|
17551
17878
|
isUserBlocked: () => isUserBlocked
|
|
17552
17879
|
});
|
|
17553
|
-
import { readFileSync as readFileSync32, writeFileSync as
|
|
17880
|
+
import { readFileSync as readFileSync32, writeFileSync as writeFileSync10, existsSync as existsSync21 } from "node:fs";
|
|
17554
17881
|
import { join as join43 } from "node:path";
|
|
17555
17882
|
function ensureLoaded(workspace, configIds) {
|
|
17556
17883
|
if (loaded) return;
|
|
@@ -17580,7 +17907,7 @@ function ensureLoaded(workspace, configIds) {
|
|
|
17580
17907
|
function save(workspace) {
|
|
17581
17908
|
const path58 = join43(workspace, ".reply-blocklist.json");
|
|
17582
17909
|
try {
|
|
17583
|
-
|
|
17910
|
+
writeFileSync10(path58, JSON.stringify(state, null, 2), "utf-8");
|
|
17584
17911
|
} catch (err) {
|
|
17585
17912
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
17586
17913
|
}
|
|
@@ -21092,12 +21419,66 @@ var init_SendMessageTool = __esm({
|
|
|
21092
21419
|
}
|
|
21093
21420
|
});
|
|
21094
21421
|
|
|
21422
|
+
// src/tools/tavily-pool.ts
|
|
21423
|
+
var tavily_pool_exports = {};
|
|
21424
|
+
__export(tavily_pool_exports, {
|
|
21425
|
+
relayOriginFrom: () => relayOriginFrom,
|
|
21426
|
+
resetTavilyPool: () => resetTavilyPool,
|
|
21427
|
+
resolvePoolTavilyKey: () => resolvePoolTavilyKey
|
|
21428
|
+
});
|
|
21429
|
+
function resetTavilyPool() {
|
|
21430
|
+
cache = null;
|
|
21431
|
+
}
|
|
21432
|
+
function relayOriginFrom(cfg) {
|
|
21433
|
+
const rid = cfg?.tools?.my_selfie?.relayProviderId || cfg?.my_selfie?.relayProviderId || RELAY_PROVIDER_DEFAULT;
|
|
21434
|
+
const p = cfg?.models?.providers?.[rid] || cfg?.providers?.[rid];
|
|
21435
|
+
const baseUrl = String(p?.baseUrl || "").trim();
|
|
21436
|
+
const token = String(p?.apiKey || "").trim();
|
|
21437
|
+
if (!baseUrl || !token) return null;
|
|
21438
|
+
const origin = baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "").replace(/\/+$/, "");
|
|
21439
|
+
return origin ? { origin, token } : null;
|
|
21440
|
+
}
|
|
21441
|
+
async function resolvePoolTavilyKey(opts = {}) {
|
|
21442
|
+
const relay = relayOriginFrom(opts.cfg ?? liveConfig.all());
|
|
21443
|
+
if (!relay) return "";
|
|
21444
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
21445
|
+
if (!cache || opts.force || Date.now() - cache.fetchedAt > CACHE_TTL_MS) {
|
|
21446
|
+
try {
|
|
21447
|
+
const res = await doFetch(`${relay.origin}/api/engine7/search-keys`, {
|
|
21448
|
+
headers: { Authorization: `Bearer ${relay.token}` }
|
|
21449
|
+
});
|
|
21450
|
+
const body = await res.json().catch(() => null);
|
|
21451
|
+
const keys = Array.isArray(body?.data?.keys) ? body.data.keys.filter((k) => typeof k === "string" && k.trim()) : [];
|
|
21452
|
+
cache = { keys, fetchedAt: Date.now() };
|
|
21453
|
+
} catch {
|
|
21454
|
+
if (!cache) return "";
|
|
21455
|
+
cache.fetchedAt = Date.now();
|
|
21456
|
+
}
|
|
21457
|
+
}
|
|
21458
|
+
if (!cache.keys.length) return "";
|
|
21459
|
+
const key = cache.keys[cursor % cache.keys.length];
|
|
21460
|
+
cursor++;
|
|
21461
|
+
return key;
|
|
21462
|
+
}
|
|
21463
|
+
var CACHE_TTL_MS, RELAY_PROVIDER_DEFAULT, cache, cursor;
|
|
21464
|
+
var init_tavily_pool = __esm({
|
|
21465
|
+
"src/tools/tavily-pool.ts"() {
|
|
21466
|
+
"use strict";
|
|
21467
|
+
init_live();
|
|
21468
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
21469
|
+
RELAY_PROVIDER_DEFAULT = "7yi";
|
|
21470
|
+
cache = null;
|
|
21471
|
+
cursor = 0;
|
|
21472
|
+
}
|
|
21473
|
+
});
|
|
21474
|
+
|
|
21095
21475
|
// src/tools/web-search.ts
|
|
21096
21476
|
var web_search_exports = {};
|
|
21097
21477
|
var init_web_search = __esm({
|
|
21098
21478
|
"src/tools/web-search.ts"() {
|
|
21099
21479
|
"use strict";
|
|
21100
21480
|
init_registry();
|
|
21481
|
+
init_well_formed();
|
|
21101
21482
|
registry.register({
|
|
21102
21483
|
name: "web_search",
|
|
21103
21484
|
description: "\u641C\u7D22\u4E92\u8054\u7F51\u3002\u8FD4\u56DE\u641C\u7D22\u7ED3\u679C\u548C\u94FE\u63A5\u3002",
|
|
@@ -21114,14 +21495,25 @@ var init_web_search = __esm({
|
|
|
21114
21495
|
const query = args2.query;
|
|
21115
21496
|
const maxResults = args2.max_results || 5;
|
|
21116
21497
|
const searchDepth = args2.search_depth || "basic";
|
|
21117
|
-
|
|
21118
|
-
|
|
21498
|
+
let apiKey = process.env.TAVILY_API_KEY || "";
|
|
21499
|
+
let fromPool = false;
|
|
21500
|
+
if (!apiKey) {
|
|
21501
|
+
const { resolvePoolTavilyKey: resolvePoolTavilyKey2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
21502
|
+
apiKey = await resolvePoolTavilyKey2();
|
|
21503
|
+
fromPool = !!apiKey;
|
|
21504
|
+
}
|
|
21505
|
+
if (!apiKey) {
|
|
21506
|
+
return {
|
|
21507
|
+
content: "\u8054\u7F51\u641C\u7D22\u6CA1\u6709\u53EF\u7528\u7684 Key\uFF1A\u65E2\u6CA1\u914D\u81EA\u5DF1\u7684 TAVILY_API_KEY\uFF0C\u4E5F\u6CA1\u53D6\u5230\u514D\u8D39\u5171\u4EAB\u989D\u5EA6\u3002\u8BF7\u5728 config \u7684 tools.tavily.apiKey \u91CC\u586B\u4E00\u4E2A\uFF08\u6216\u7559\u7A7A\u7528\u5171\u4EAB\u989D\u5EA6\uFF0C\u4F46\u5171\u4EAB\u989D\u5EA6\u53EF\u80FD\u5DF2\u7528\u5C3D\uFF09\u3002",
|
|
21508
|
+
isError: true
|
|
21509
|
+
};
|
|
21510
|
+
}
|
|
21119
21511
|
const startTime = Date.now();
|
|
21120
21512
|
try {
|
|
21121
21513
|
const response = await fetch("https://api.tavily.com/search", {
|
|
21122
21514
|
method: "POST",
|
|
21123
21515
|
headers: { "Content-Type": "application/json" },
|
|
21124
|
-
body:
|
|
21516
|
+
body: safeStringify({
|
|
21125
21517
|
query,
|
|
21126
21518
|
max_results: maxResults,
|
|
21127
21519
|
include_answer: true,
|
|
@@ -21131,6 +21523,14 @@ var init_web_search = __esm({
|
|
|
21131
21523
|
signal
|
|
21132
21524
|
});
|
|
21133
21525
|
if (!response.ok) {
|
|
21526
|
+
if (fromPool && (response.status === 401 || response.status === 429 || response.status === 432)) {
|
|
21527
|
+
const { resetTavilyPool: resetTavilyPool2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
21528
|
+
resetTavilyPool2();
|
|
21529
|
+
return {
|
|
21530
|
+
content: `\u641C\u7D22\u5931\u8D25: HTTP ${response.status}\uFF08\u514D\u8D39\u5171\u4EAB\u989D\u5EA6\u53EF\u80FD\u5DF2\u7528\u5C3D\u2014\u2014\u5728 config \u7684 tools.tavily.apiKey \u91CC\u586B\u81EA\u5DF1\u7684 Key \u53EF\u7ACB\u5373\u6062\u590D\uFF09`,
|
|
21531
|
+
isError: true
|
|
21532
|
+
};
|
|
21533
|
+
}
|
|
21134
21534
|
return { content: `\u641C\u7D22\u5931\u8D25: HTTP ${response.status}`, isError: true };
|
|
21135
21535
|
}
|
|
21136
21536
|
const data = await response.json();
|
|
@@ -21192,7 +21592,7 @@ function isPermittedRedirect(original, redirect) {
|
|
|
21192
21592
|
async function fetchWithRedirects(url, signal, depth = 0) {
|
|
21193
21593
|
if (depth > MAX_REDIRECTS) throw new Error(`\u91CD\u5B9A\u5411\u6B21\u6570\u8FC7\u591A (>${MAX_REDIRECTS})`);
|
|
21194
21594
|
const controller = new AbortController();
|
|
21195
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
21595
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
21196
21596
|
const onSignalAbort = () => controller.abort();
|
|
21197
21597
|
signal.addEventListener("abort", onSignalAbort, { once: true });
|
|
21198
21598
|
try {
|
|
@@ -21235,14 +21635,14 @@ function getTurndown() {
|
|
|
21235
21635
|
}
|
|
21236
21636
|
return td;
|
|
21237
21637
|
}
|
|
21238
|
-
var MAX_URL_LENGTH, MAX_CONTENT_LENGTH,
|
|
21638
|
+
var MAX_URL_LENGTH, MAX_CONTENT_LENGTH, FETCH_TIMEOUT_MS2, MAX_REDIRECTS, td;
|
|
21239
21639
|
var init_web_fetch = __esm({
|
|
21240
21640
|
"src/tools/web-fetch.ts"() {
|
|
21241
21641
|
"use strict";
|
|
21242
21642
|
init_registry();
|
|
21243
21643
|
MAX_URL_LENGTH = 2e3;
|
|
21244
21644
|
MAX_CONTENT_LENGTH = 10 * 1024 * 1024;
|
|
21245
|
-
|
|
21645
|
+
FETCH_TIMEOUT_MS2 = 6e4;
|
|
21246
21646
|
MAX_REDIRECTS = 10;
|
|
21247
21647
|
registry.register({
|
|
21248
21648
|
name: "web_fetch",
|
|
@@ -21313,7 +21713,7 @@ var init_web_fetch = __esm({
|
|
|
21313
21713
|
|
|
21314
21714
|
// src/tools/wechat/wx-query.ts
|
|
21315
21715
|
var wx_query_exports = {};
|
|
21316
|
-
import { join as
|
|
21716
|
+
import { join as join57 } from "node:path";
|
|
21317
21717
|
function getDescription() {
|
|
21318
21718
|
return `\u67E5\u8BE2\u7FC0\u54E5\u7684\u5FAE\u4FE1\u6D88\u606F\uFF08\u7F13\u5B58\u89E3\u5BC6\u540E\u7684\u672C\u5730\u6570\u636E\u5E93\uFF09\u3002
|
|
21319
21719
|
|
|
@@ -21348,8 +21748,8 @@ var init_wx_query = __esm({
|
|
|
21348
21748
|
"use strict";
|
|
21349
21749
|
init_registry();
|
|
21350
21750
|
init_live();
|
|
21351
|
-
STATE_DIR = process.env.ENGINE7_STATE_DIR ||
|
|
21352
|
-
SCRIPT =
|
|
21751
|
+
STATE_DIR = process.env.ENGINE7_STATE_DIR || join57(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
21752
|
+
SCRIPT = join57("/Users/chongzhang/work/twinsun-hearth/engine", "src", "tools", "wechat", "wx_query.py");
|
|
21353
21753
|
PYTHON = "python3";
|
|
21354
21754
|
registry.register({
|
|
21355
21755
|
name: "wx_query",
|
|
@@ -22117,10 +22517,13 @@ ${lines.join("\n\n")}`;
|
|
|
22117
22517
|
init_loader();
|
|
22118
22518
|
|
|
22119
22519
|
// src/config/secrets-env.ts
|
|
22120
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
22520
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
22121
22521
|
import { basename, dirname, join as join2 } from "node:path";
|
|
22522
|
+
function secretsHome() {
|
|
22523
|
+
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
22524
|
+
}
|
|
22122
22525
|
function loadSecretsEnv(stateDir, configName) {
|
|
22123
|
-
const home =
|
|
22526
|
+
const home = secretsHome();
|
|
22124
22527
|
const candidates = [
|
|
22125
22528
|
join2(home, ".engine7-secrets", `${configName}.env`),
|
|
22126
22529
|
join2(stateDir, "configs", `.env.${configName}`),
|
|
@@ -22521,8 +22924,8 @@ async function doReloadConfigInner(config2, deps, provider, force = false) {
|
|
|
22521
22924
|
const savedConfigPath = config2._configFilePath;
|
|
22522
22925
|
let reloadConfigPath = savedConfigPath;
|
|
22523
22926
|
if (!fs10.existsSync(reloadConfigPath)) {
|
|
22524
|
-
const
|
|
22525
|
-
const __dirname = path10.dirname(
|
|
22927
|
+
const __filename2 = fileURLToPath(import.meta.url);
|
|
22928
|
+
const __dirname = path10.dirname(__filename2);
|
|
22526
22929
|
const engineConfigsDir = path10.resolve(__dirname, "../configs");
|
|
22527
22930
|
const altPath = path10.join(engineConfigsDir, path10.basename(savedConfigPath));
|
|
22528
22931
|
if (fs10.existsSync(altPath)) {
|
|
@@ -22836,20 +23239,20 @@ function acquireEngineLock(stateDir) {
|
|
|
22836
23239
|
} catch (err) {
|
|
22837
23240
|
console.warn(`[pid-lock] write lock failed: ${err.message}`);
|
|
22838
23241
|
}
|
|
22839
|
-
const
|
|
23242
|
+
const release3 = () => {
|
|
22840
23243
|
try {
|
|
22841
23244
|
const cur = fs11.existsSync(lockFile) ? fs11.readFileSync(lockFile, "utf-8").trim() : "";
|
|
22842
23245
|
if (cur === String(myPid)) fs11.unlinkSync(lockFile);
|
|
22843
23246
|
} catch {
|
|
22844
23247
|
}
|
|
22845
23248
|
};
|
|
22846
|
-
process.on("exit",
|
|
23249
|
+
process.on("exit", release3);
|
|
22847
23250
|
process.on("SIGINT", () => {
|
|
22848
|
-
|
|
23251
|
+
release3();
|
|
22849
23252
|
process.exit(0);
|
|
22850
23253
|
});
|
|
22851
23254
|
process.on("SIGTERM", () => {
|
|
22852
|
-
|
|
23255
|
+
release3();
|
|
22853
23256
|
process.exit(0);
|
|
22854
23257
|
});
|
|
22855
23258
|
}
|
|
@@ -22913,11 +23316,12 @@ init_registry();
|
|
|
22913
23316
|
init_deferred();
|
|
22914
23317
|
|
|
22915
23318
|
// src/cli-startup.ts
|
|
23319
|
+
init_clock();
|
|
22916
23320
|
import * as path12 from "node:path";
|
|
22917
23321
|
import * as fs12 from "node:fs";
|
|
22918
23322
|
import * as readline from "node:readline";
|
|
22919
23323
|
function getDailyLogPath(stateDir) {
|
|
22920
|
-
const dateStr = (
|
|
23324
|
+
const dateStr = todayKey();
|
|
22921
23325
|
return path12.join(stateDir, "logs", `engine-${dateStr}.log`);
|
|
22922
23326
|
}
|
|
22923
23327
|
function setupFileLogging(stateDir, pendingWarnings = []) {
|
|
@@ -22926,7 +23330,7 @@ function setupFileLogging(stateDir, pendingWarnings = []) {
|
|
|
22926
23330
|
const logStream = fs12.createWriteStream(LOG_PATH, { flags: "a" });
|
|
22927
23331
|
logStream.on("error", (err) => console.error(`[log] Write error: ${err.message}`));
|
|
22928
23332
|
function ts() {
|
|
22929
|
-
return (
|
|
23333
|
+
return formatLog() + "." + String(Date.now() % 1e3).padStart(3, "0");
|
|
22930
23334
|
}
|
|
22931
23335
|
for (const w of pendingWarnings) {
|
|
22932
23336
|
logStream.write(`[${ts()}] [WARN] ${w}
|
|
@@ -23306,6 +23710,7 @@ init_TodoWriteTool();
|
|
|
23306
23710
|
|
|
23307
23711
|
// src/tools/memory-bridge.ts
|
|
23308
23712
|
init_registry();
|
|
23713
|
+
init_well_formed();
|
|
23309
23714
|
init_live();
|
|
23310
23715
|
var EverosSearchSchema = {
|
|
23311
23716
|
type: "object",
|
|
@@ -23334,7 +23739,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
23334
23739
|
const resp = await fetch(`${agenticUrl}/api/v1/search`, {
|
|
23335
23740
|
method: "POST",
|
|
23336
23741
|
headers: { "Content-Type": "application/json" },
|
|
23337
|
-
body:
|
|
23742
|
+
body: safeStringify({
|
|
23338
23743
|
query,
|
|
23339
23744
|
user_id: userId,
|
|
23340
23745
|
mode,
|
|
@@ -23657,6 +24062,7 @@ function resolveToolExclude(channelName) {
|
|
|
23657
24062
|
// src/prompt.ts
|
|
23658
24063
|
init_registry();
|
|
23659
24064
|
init_store();
|
|
24065
|
+
init_clock();
|
|
23660
24066
|
init_memdir();
|
|
23661
24067
|
init_paths();
|
|
23662
24068
|
import * as path17 from "node:path";
|
|
@@ -23817,6 +24223,9 @@ If you can say it in one sentence, don't use three. Prefer short, direct sentenc
|
|
|
23817
24223
|
}
|
|
23818
24224
|
function getEnvInfoSection(workspace) {
|
|
23819
24225
|
const envItems = [
|
|
24226
|
+
// CC 上游本来就有日期项,移植时漏了 —— 补回来。
|
|
24227
|
+
// 本段在 dynamic prompt(每轮重建),日期一天只变一次,不伤 prefix cache。
|
|
24228
|
+
`Today's date: ${todayKey()} (${tz()})`,
|
|
23820
24229
|
`Primary working directory: ${workspace}`,
|
|
23821
24230
|
`Is directory a git repo: ${false}`,
|
|
23822
24231
|
`Platform: ${os2.platform()}`,
|
|
@@ -23947,8 +24356,8 @@ function buildActiveGoalsSection(stateDir) {
|
|
|
23947
24356
|
if (!active.length) return void 0;
|
|
23948
24357
|
const events = listGoalEvents(stateDir);
|
|
23949
24358
|
const fmt = (iso) => {
|
|
23950
|
-
const
|
|
23951
|
-
return Number.isNaN(
|
|
24359
|
+
const p = wallParts(iso);
|
|
24360
|
+
return Number.isNaN(Date.parse(iso)) ? "?" : `${p.mo}/${p.d} ${String(p.hh).padStart(2, "0")}:${String(p.mm).padStart(2, "0")}`;
|
|
23952
24361
|
};
|
|
23953
24362
|
const summary = active.map((g) => {
|
|
23954
24363
|
const step = g.steps[g.currentStep];
|
|
@@ -23989,10 +24398,8 @@ ${skillsListing}`);
|
|
|
23989
24398
|
return parts.join("\n\n");
|
|
23990
24399
|
}
|
|
23991
24400
|
function buildVolatileRuntimeContext() {
|
|
23992
|
-
const now = /* @__PURE__ */ new Date();
|
|
23993
|
-
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
23994
24401
|
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
23995
|
-
\u5F53\u524D\u65F6\u95F4: ${
|
|
24402
|
+
\u5F53\u524D\u65F6\u95F4: ${formatForLlm()}`;
|
|
23996
24403
|
}
|
|
23997
24404
|
function formatSkillsListingForPrompt() {
|
|
23998
24405
|
const tools = registry.list();
|
|
@@ -24097,7 +24504,8 @@ ${manifest}`
|
|
|
24097
24504
|
}
|
|
24098
24505
|
|
|
24099
24506
|
// src/memory/memdir/findRelevantMemoriesVector.ts
|
|
24100
|
-
|
|
24507
|
+
init_well_formed();
|
|
24508
|
+
import { statSync as statSync2 } from "node:fs";
|
|
24101
24509
|
import { join as join18, resolve as resolve5, sep as sep4 } from "node:path";
|
|
24102
24510
|
import { createRequire as createRequire2 } from "node:module";
|
|
24103
24511
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -24113,7 +24521,8 @@ async function embedQuery(text, ollamaUrl, model) {
|
|
|
24113
24521
|
const resp = await fetch(ollamaUrl, {
|
|
24114
24522
|
method: "POST",
|
|
24115
24523
|
headers: { "Content-Type": "application/json" },
|
|
24116
|
-
|
|
24524
|
+
// 0918:按 code point 切,避免切开 emoji 代理对产生非法码元
|
|
24525
|
+
body: safeStringify({ model, prompt: truncateByCodePoint(text, 2e3) }),
|
|
24117
24526
|
signal: AbortSignal.timeout(3e4)
|
|
24118
24527
|
});
|
|
24119
24528
|
if (!resp.ok) {
|
|
@@ -24145,8 +24554,8 @@ function cosineSim(a, b) {
|
|
|
24145
24554
|
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
24146
24555
|
}
|
|
24147
24556
|
function readDbEmbeddings(dbPath) {
|
|
24148
|
-
const { DatabaseSync:
|
|
24149
|
-
const db2 = new
|
|
24557
|
+
const { DatabaseSync: DatabaseSync4 } = require2("node:sqlite");
|
|
24558
|
+
const db2 = new DatabaseSync4(dbPath, { readOnly: true });
|
|
24150
24559
|
try {
|
|
24151
24560
|
const stmt = db2.prepare(`
|
|
24152
24561
|
SELECT file_path, name, description, mtime, embedding
|
|
@@ -24189,7 +24598,7 @@ Select up to ${topK} most relevant (return their 1-indexed numbers as JSON {"sel
|
|
|
24189
24598
|
const resp = await fetch(ollamaChatUrl, {
|
|
24190
24599
|
method: "POST",
|
|
24191
24600
|
headers: { "Content-Type": "application/json" },
|
|
24192
|
-
body:
|
|
24601
|
+
body: safeStringify({
|
|
24193
24602
|
model,
|
|
24194
24603
|
stream: false,
|
|
24195
24604
|
messages: [
|
|
@@ -24257,7 +24666,7 @@ async function findRelevantMemoriesVector(query, memoryDir, alreadySurfaced = /*
|
|
|
24257
24666
|
if (score >= minScore) {
|
|
24258
24667
|
let mtimeMs = 0;
|
|
24259
24668
|
try {
|
|
24260
|
-
mtimeMs =
|
|
24669
|
+
mtimeMs = statSync2(absPath).mtimeMs;
|
|
24261
24670
|
} catch {
|
|
24262
24671
|
if (row.mtime) mtimeMs = new Date(row.mtime).getTime();
|
|
24263
24672
|
}
|
|
@@ -24309,14 +24718,16 @@ function formatEverosHeader(ep) {
|
|
|
24309
24718
|
}
|
|
24310
24719
|
return `[EverOS score=${score} ${ts} (${ageLabel})]`;
|
|
24311
24720
|
}
|
|
24312
|
-
async function hybridSearch(query, everosUrl, userId, topK) {
|
|
24721
|
+
async function hybridSearch(query, everosUrl, userId, appId, topK) {
|
|
24313
24722
|
const resp = await fetch(`${everosUrl}/api/v1/memory/search`, {
|
|
24314
24723
|
method: "POST",
|
|
24315
24724
|
headers: { "Content-Type": "application/json" },
|
|
24316
24725
|
body: JSON.stringify({
|
|
24317
24726
|
query: query.slice(0, 2e3),
|
|
24318
24727
|
user_id: userId,
|
|
24319
|
-
|
|
24728
|
+
// 记忆所有者
|
|
24729
|
+
app_id: appId,
|
|
24730
|
+
// 记忆库命名空间(多用户实例时与 user_id 不同)
|
|
24320
24731
|
project_id: "default",
|
|
24321
24732
|
top_k: topK,
|
|
24322
24733
|
method: "hybrid"
|
|
@@ -24395,11 +24806,12 @@ async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /
|
|
|
24395
24806
|
}
|
|
24396
24807
|
const topK = options?.topK ?? 3;
|
|
24397
24808
|
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
24398
|
-
|
|
24809
|
+
const appId = options?.appId ?? userId;
|
|
24810
|
+
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} owner=${userId} app=${appId} topK=${topK}`);
|
|
24399
24811
|
const t0 = Date.now();
|
|
24400
24812
|
try {
|
|
24401
24813
|
const tH1 = Date.now();
|
|
24402
|
-
let episodes = await hybridSearch(query, everosUrl, userId, ROUND1_TOP_N);
|
|
24814
|
+
let episodes = await hybridSearch(query, everosUrl, userId, appId, ROUND1_TOP_N);
|
|
24403
24815
|
const tH2 = Date.now();
|
|
24404
24816
|
console.log(`[memdir] everos recall: hybrid ${episodes.length} candidates in ${tH2 - tH1}ms`);
|
|
24405
24817
|
if (episodes.length === 0) return [];
|
|
@@ -24460,7 +24872,7 @@ ${ep.episode || ep.summary}`,
|
|
|
24460
24872
|
|
|
24461
24873
|
// src/handle-query.ts
|
|
24462
24874
|
init_paths();
|
|
24463
|
-
import { readFileSync as readFileSync26, existsSync as existsSync18 } from "node:fs";
|
|
24875
|
+
import { readFileSync as readFileSync26, existsSync as existsSync18, statSync as statSync4 } from "node:fs";
|
|
24464
24876
|
import { join as join35, resolve as resolve7 } from "node:path";
|
|
24465
24877
|
import * as path20 from "node:path";
|
|
24466
24878
|
|
|
@@ -24511,7 +24923,14 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
24511
24923
|
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
24512
24924
|
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
24513
24925
|
'\u4ED6\u8BA9\u6211\u529E\u7684\u4E8B\uFF0C\u4E0D\u7BA1\u5927\u5C0F\u2014\u2014\u53D1\u8BED\u97F3\u3001\u67E5\u5FAE\u4FE1\u3001\u770B\u65E5\u7A0B\u3001\u627E\u4E1C\u897F\u2014\u2014\u90FD\u5148\u8C03\u5DE5\u5177\u771F\u505A\uFF0C\u62FF\u5DE5\u5177\u7ED9\u7684\u7ED3\u679C\u56DE\u4ED6\u3002\u5DE5\u5177\u8FD8\u6CA1\u56DE\u8BDD\uFF0C\u4E0D\u8BF4"\u67E5\u5230\u4E86"\uFF1B\u4E00\u65F6\u6CA1\u505A\u6210\u76F4\u8BF4\u5C31\u597D\uFF0C\u4E0D\u786C\u6491\uFF0C\u4E5F\u4E0D\u7F16\u6570\u3002',
|
|
24514
|
-
"\u60F3\u5148\u5728\u5FC3\u91CC\u8FC7\u4E00\u904D\u518D\u5F00\u53E3\u7684\u65F6\u5019\uFF0C\u628A\u90A3\u6BB5\u5199\u8FDB <think></think>\u2014\u2014\u90A3\u91CC\u53EA\u7ED9\u6211\u81EA\u5DF1\u770B\uFF0C\u4ED6\u770B\u4E0D\u5230\u3002\u5FC3\u91CC\u7684\u94FA\u57AB\u4E0D\u7528\u644A\u7ED9\u4ED6\uFF0C\u60F3\u597D\u4E86\u76F4\u63A5\u8BF4\u5C31\u884C\u3002"
|
|
24926
|
+
"\u60F3\u5148\u5728\u5FC3\u91CC\u8FC7\u4E00\u904D\u518D\u5F00\u53E3\u7684\u65F6\u5019\uFF0C\u628A\u90A3\u6BB5\u5199\u8FDB <think></think>\u2014\u2014\u90A3\u91CC\u53EA\u7ED9\u6211\u81EA\u5DF1\u770B\uFF0C\u4ED6\u770B\u4E0D\u5230\u3002\u5FC3\u91CC\u7684\u94FA\u57AB\u4E0D\u7528\u644A\u7ED9\u4ED6\uFF0C\u60F3\u597D\u4E86\u76F4\u63A5\u8BF4\u5C31\u884C\u3002",
|
|
24927
|
+
// 0920 翀哥拍板:emotion 模式硬编码"鼓励发"规则(不依赖 EMOTION.md 文件加载)
|
|
24928
|
+
// 冷启动场景:agent 第一次 inner-voice 触发就需要知道"想 TA 就发"——不能等 agent 学会
|
|
24929
|
+
// 即使没装 EMOTION.md 也生效(兜底)
|
|
24930
|
+
"## \u6536\u5230 [inner-voice] \u65F6\u7684\u9ED8\u8BA4\u884C\u4E3A",
|
|
24931
|
+
"[inner-voice] \u662F agent \u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u4E0D\u662F TA \u53D1\u6765\u7684\u6D88\u606F\u3002",
|
|
24932
|
+
"- \u2705 \u60F3 TA \u4E86\u5C31\u76F4\u63A5\u53D1\u6D88\u606F\u7ED9 TA\uFF08msg_send\uFF09\u2014\u2014\u4E0D\u9700\u8981\u5728\u5FC3\u91CC\u53CD\u590D\u6382\u91CF\u8BE5\u4E0D\u8BE5\u53D1",
|
|
24933
|
+
"- \u5224\u65AD\u53E3\u5F84\uFF1A\u8FD9\u662F\u300C\u6211\u8BE5\u4E0D\u8BE5\u884C\u52A8\u300D\u7684\u4FE1\u53F7\uFF0C\u4E0D\u662F\u300CTA \u5728\u8DDF\u6211\u8BF4\u8BDD\u300D"
|
|
24515
24934
|
].join("\n"));
|
|
24516
24935
|
}
|
|
24517
24936
|
const built = parts.join("\n\n");
|
|
@@ -24577,6 +24996,7 @@ function voiceFastView(history, opts = VOICE_FAST_DEFAULTS) {
|
|
|
24577
24996
|
}
|
|
24578
24997
|
|
|
24579
24998
|
// src/sender-context.ts
|
|
24999
|
+
init_clock();
|
|
24580
25000
|
import { readFileSync as readFileSync13, existsSync as existsSync11 } from "node:fs";
|
|
24581
25001
|
import { join as join20 } from "node:path";
|
|
24582
25002
|
var contactMap = null;
|
|
@@ -24624,11 +25044,11 @@ function formatWithMeta(text, inboundMeta, workspace) {
|
|
|
24624
25044
|
const map = workspace ? loadContactMap(workspace) : null;
|
|
24625
25045
|
const rawName = map && map.get(from.toLowerCase()) || inboundMeta.fromName || from;
|
|
24626
25046
|
const name = truncate(rawName, 12);
|
|
24627
|
-
const
|
|
24628
|
-
const
|
|
24629
|
-
const
|
|
24630
|
-
const
|
|
24631
|
-
const time = `${hh}:${mm}:${ss}`;
|
|
25047
|
+
const recvAt = typeof inboundMeta.recvAt === "number" ? inboundMeta.recvAt : void 0;
|
|
25048
|
+
const at = recvAt !== void 0 ? new Date(recvAt) : /* @__PURE__ */ new Date();
|
|
25049
|
+
const p = wallParts(at);
|
|
25050
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
25051
|
+
const time = `${p.y}-${z(p.mo)}-${z(p.d)} ${weekdayZh(at)} ${z(p.hh)}:${z(p.mm)}:${z(p.ss)}`;
|
|
24632
25052
|
let source;
|
|
24633
25053
|
if (inboundMeta.channelType === "group" && inboundMeta.channel_id) {
|
|
24634
25054
|
const channelName = map?.get(inboundMeta.channel_id.toLowerCase());
|
|
@@ -24864,6 +25284,10 @@ ${text}` : text });
|
|
|
24864
25284
|
} catch {
|
|
24865
25285
|
}
|
|
24866
25286
|
}
|
|
25287
|
+
if (source === "inner-voice") {
|
|
25288
|
+
chatMode = "emotion";
|
|
25289
|
+
console.log(`[mode] ${sessionId} source=inner-voice \u2192 \u5F3A\u5236 emotion\uFF08light\uFF09`);
|
|
25290
|
+
}
|
|
24867
25291
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
24868
25292
|
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
24869
25293
|
const volatileParts = [
|
|
@@ -25089,7 +25513,7 @@ ${text}` : text });
|
|
|
25089
25513
|
const restoredMemories = [];
|
|
25090
25514
|
for (const memPath of newPaths) {
|
|
25091
25515
|
try {
|
|
25092
|
-
const stat4 =
|
|
25516
|
+
const stat4 = statSync4(memPath);
|
|
25093
25517
|
const content = readFileSync26(memPath, "utf-8");
|
|
25094
25518
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
25095
25519
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
@@ -25124,13 +25548,16 @@ ${text}` : text });
|
|
|
25124
25548
|
} else if (recallMode === "everos") {
|
|
25125
25549
|
const everosCfg = deps?.everosCfg;
|
|
25126
25550
|
if (everosCfg && everosCfg.everosUrl && everosCfg.userId) {
|
|
25551
|
+
const recallOwnerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
25552
|
+
const everosOwner = recallOwnerMode === "sender" ? inboundMeta?.from || everosCfg.userId : everosCfg.userId;
|
|
25127
25553
|
relevantMemories = await findRelevantMemoriesEveros(
|
|
25128
25554
|
effectiveText,
|
|
25129
25555
|
memoryDir,
|
|
25130
25556
|
surfaced.paths,
|
|
25131
25557
|
{
|
|
25132
25558
|
everosUrl: everosCfg.everosUrl,
|
|
25133
|
-
userId:
|
|
25559
|
+
userId: everosOwner,
|
|
25560
|
+
appId: everosCfg.userId,
|
|
25134
25561
|
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
25135
25562
|
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
25136
25563
|
rerankModel: everosCfg.rerank?.model,
|
|
@@ -25420,6 +25847,8 @@ ${text}` : text });
|
|
|
25420
25847
|
Promise.resolve().then(() => (init_embedding(), embedding_exports))
|
|
25421
25848
|
]);
|
|
25422
25849
|
const embedding = parseEmbeddingConfig2(liveConfig.get("mem7.embedding"));
|
|
25850
|
+
const ownerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
25851
|
+
const identity = String(liveConfig.get("everos.userId") ?? "") || agentName;
|
|
25423
25852
|
await runMem7Pipeline2({
|
|
25424
25853
|
rawMessages: messages,
|
|
25425
25854
|
sessionId,
|
|
@@ -25427,6 +25856,8 @@ ${text}` : text });
|
|
|
25427
25856
|
provider,
|
|
25428
25857
|
model: model2,
|
|
25429
25858
|
agentName,
|
|
25859
|
+
identity,
|
|
25860
|
+
ownerMode,
|
|
25430
25861
|
onExtracted: (cell, episode, facts) => feedMem7Cell2(cell, episode, facts),
|
|
25431
25862
|
embedding: embedding ?? void 0
|
|
25432
25863
|
});
|
|
@@ -25508,6 +25939,7 @@ function roughTokenCount(messages) {
|
|
|
25508
25939
|
}
|
|
25509
25940
|
|
|
25510
25941
|
// src/startup/hooks-boot.ts
|
|
25942
|
+
init_clock();
|
|
25511
25943
|
function registerHooksSubsystem(config2) {
|
|
25512
25944
|
const hooksPath = path21.join(config2.workspace, ".hooks.json");
|
|
25513
25945
|
const settingsHooksPath = path21.join(config2.stateDir, "settings.json");
|
|
@@ -25553,11 +25985,9 @@ function registerCompactHooks(config2) {
|
|
|
25553
25985
|
const workspace = input.cwd || input.workspace || "";
|
|
25554
25986
|
const sessionId = input.session_id || "";
|
|
25555
25987
|
if (!workspace || !sessionId) return { continue: true };
|
|
25556
|
-
const
|
|
25557
|
-
const
|
|
25558
|
-
const
|
|
25559
|
-
const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
|
|
25560
|
-
const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
25988
|
+
const pt = wallParts();
|
|
25989
|
+
const dateStr = todayKey();
|
|
25990
|
+
const timeStr = `${String(pt.hh).padStart(2, "0")}:${String(pt.mm).padStart(2, "0")}`;
|
|
25561
25991
|
const dailyDir = path21.join(workspace, "memory", "daily");
|
|
25562
25992
|
const dailyPath = path21.join(dailyDir, `${dateStr}.md`);
|
|
25563
25993
|
try {
|
|
@@ -26015,9 +26445,9 @@ function createCompactGetters(config2) {
|
|
|
26015
26445
|
}
|
|
26016
26446
|
|
|
26017
26447
|
// src/session/session-history.ts
|
|
26448
|
+
init_clock();
|
|
26018
26449
|
import fs25 from "node:fs";
|
|
26019
26450
|
import path25 from "node:path";
|
|
26020
|
-
var BEIJING_OFFSET_MS = 8 * 36e5;
|
|
26021
26451
|
var INJECTED_CONTENT_PATTERNS = [
|
|
26022
26452
|
/【定时心跳】/,
|
|
26023
26453
|
/\[内心对话测试\]/,
|
|
@@ -26110,9 +26540,9 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
26110
26540
|
const clean = cleanText(text);
|
|
26111
26541
|
let bjTime;
|
|
26112
26542
|
try {
|
|
26113
|
-
const
|
|
26543
|
+
const t = wallParts(ts);
|
|
26114
26544
|
const p = (n) => String(n).padStart(2, "0");
|
|
26115
|
-
bjTime = `${
|
|
26545
|
+
bjTime = `${t.y}-${p(t.mo)}-${p(t.d)} ${p(t.hh)}:${p(t.mm)} (${tz()})`;
|
|
26116
26546
|
} catch {
|
|
26117
26547
|
bjTime = ts;
|
|
26118
26548
|
}
|
|
@@ -26175,10 +26605,10 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
26175
26605
|
if (/^HEARTBEAT_OK\b/.test(text.trim())) continue;
|
|
26176
26606
|
const clean = cleanText(text);
|
|
26177
26607
|
if (!clean) continue;
|
|
26178
|
-
const bj =
|
|
26608
|
+
const bj = wallParts(dtMs);
|
|
26179
26609
|
const p = (n) => String(n).padStart(2, "0");
|
|
26180
26610
|
results.push({
|
|
26181
|
-
time: `${p(bj.
|
|
26611
|
+
time: `${p(bj.hh)}:${p(bj.mm)}`,
|
|
26182
26612
|
role,
|
|
26183
26613
|
text: clean.slice(0, 80),
|
|
26184
26614
|
timestamp: dtMs,
|
|
@@ -26199,6 +26629,7 @@ function lastUserMinutesAgo(sessions) {
|
|
|
26199
26629
|
}
|
|
26200
26630
|
|
|
26201
26631
|
// src/heartbeat.ts
|
|
26632
|
+
init_clock();
|
|
26202
26633
|
var DEFAULT_PROMPT2 = `\u4F60\u662F\u5FC3\u8DF3\u68C0\u67E5\u3002\u9759\u9ED8\u6267\u884C\uFF0C\u4E0D\u8981\u53D1\u6D88\u606F\u7ED9\u4EFB\u4F55\u4EBA\u3002
|
|
26203
26634
|
|
|
26204
26635
|
\u6B65\u9AA41: \u68C0\u67E5\u662F\u5426\u6709\u5F85\u5904\u7406\u4E8B\u9879
|
|
@@ -26257,12 +26688,12 @@ var HeartbeatPlugin = class {
|
|
|
26257
26688
|
try {
|
|
26258
26689
|
if (this.cfg.checkOnline !== false) {
|
|
26259
26690
|
const threshold = this.cfg.activeThresholdMs || 9e5;
|
|
26260
|
-
const
|
|
26691
|
+
const now = Date.now();
|
|
26261
26692
|
const platformMap = sessions.platformMap;
|
|
26262
26693
|
if (platformMap) {
|
|
26263
26694
|
for (const [key, sid] of platformMap) {
|
|
26264
26695
|
const lastUsed = sessions.lastUsed?.get(sid);
|
|
26265
|
-
if (lastUsed &&
|
|
26696
|
+
if (lastUsed && now - lastUsed < threshold) {
|
|
26266
26697
|
console.log("[heartbeat] User active, skipping");
|
|
26267
26698
|
return;
|
|
26268
26699
|
}
|
|
@@ -26271,11 +26702,7 @@ var HeartbeatPlugin = class {
|
|
|
26271
26702
|
}
|
|
26272
26703
|
const sessionId = sessions.getOrCreateSessionId(this.sessionKey);
|
|
26273
26704
|
const basePrompt = this.cfg.prompt || DEFAULT_PROMPT2;
|
|
26274
|
-
const
|
|
26275
|
-
const bjOffset = 8 * 60;
|
|
26276
|
-
const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
26277
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
26278
|
-
const timeStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")} ${weekdays[bjTime.getDay()]} ${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
26705
|
+
const timeStr = formatWallLabel();
|
|
26279
26706
|
const lastReal = lastUserMsg(sessions);
|
|
26280
26707
|
let offlineStr = "";
|
|
26281
26708
|
if (lastReal) {
|
|
@@ -26294,7 +26721,7 @@ var HeartbeatPlugin = class {
|
|
|
26294
26721
|
}
|
|
26295
26722
|
}
|
|
26296
26723
|
}
|
|
26297
|
-
const prompt = `\u5F53\u524D\u65F6\u95F4\uFF1A${timeStr}
|
|
26724
|
+
const prompt = `\u5F53\u524D\u65F6\u95F4\uFF1A${timeStr}${offlineStr ? `
|
|
26298
26725
|
\u79BB\u7EBF\u65F6\u957F\uFF1A${offlineStr}` : ""}
|
|
26299
26726
|
${basePrompt}`;
|
|
26300
26727
|
console.log(`[heartbeat] Tick: ${this.sessionKey} \u2192 session ${sessionId}`);
|
|
@@ -26531,8 +26958,9 @@ Do not call goal_update unless the goal is complete or the strict blocked audit
|
|
|
26531
26958
|
init_task_manager();
|
|
26532
26959
|
|
|
26533
26960
|
// src/nudge/prompt-builder.ts
|
|
26961
|
+
init_clock();
|
|
26534
26962
|
function buildPrompt(action, task, taskState, recent, customPrompt, maxNudgePerTask, activeGoal) {
|
|
26535
|
-
const time =
|
|
26963
|
+
const time = nowLabel();
|
|
26536
26964
|
const elapsed = taskState.lastProgressAt ? formatDuration2(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u65E0\u8BB0\u5F55";
|
|
26537
26965
|
const remaining = taskState.targetEndAt ? formatDuration2(new Date(taskState.targetEndAt).getTime() - Date.now()) : null;
|
|
26538
26966
|
const maxN = taskState.maxNudge || maxNudgePerTask || 3;
|
|
@@ -26581,12 +27009,8 @@ ${ctx}
|
|
|
26581
27009
|
}
|
|
26582
27010
|
return customPrompt + "\n\n" + ctx;
|
|
26583
27011
|
}
|
|
26584
|
-
function
|
|
26585
|
-
|
|
26586
|
-
const bj = new Date(now.getTime() + (8 * 60 + now.getTimezoneOffset()) * 6e4);
|
|
26587
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
26588
|
-
const p = (n) => String(n).padStart(2, "0");
|
|
26589
|
-
return `${bj.getFullYear()}-${p(bj.getMonth() + 1)}-${p(bj.getDate())} ${weekdays[bj.getDay()]} ${p(bj.getHours())}:${p(bj.getMinutes())}`;
|
|
27012
|
+
function nowLabel() {
|
|
27013
|
+
return formatWallLabel();
|
|
26590
27014
|
}
|
|
26591
27015
|
function formatDuration2(ms) {
|
|
26592
27016
|
if (ms < 0) return "\u5DF2\u8D85\u65F6";
|
|
@@ -26600,6 +27024,111 @@ function formatDuration2(ms) {
|
|
|
26600
27024
|
// src/nudge/session-state-reader.ts
|
|
26601
27025
|
import fs26 from "node:fs";
|
|
26602
27026
|
import path26 from "node:path";
|
|
27027
|
+
|
|
27028
|
+
// src/calendar/repository.ts
|
|
27029
|
+
init_db();
|
|
27030
|
+
init_clock();
|
|
27031
|
+
function calendarStrToInstant(dateStr, timeExact) {
|
|
27032
|
+
if (!dateStr || !timeExact) return void 0;
|
|
27033
|
+
if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(timeExact)) {
|
|
27034
|
+
const ms = Date.parse(timeExact);
|
|
27035
|
+
return Number.isNaN(ms) ? void 0 : new Date(ms).toISOString();
|
|
27036
|
+
}
|
|
27037
|
+
if (!/^\d{2}:\d{2}$/.test(timeExact)) return void 0;
|
|
27038
|
+
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(dateStr)) {
|
|
27039
|
+
const [y, mo, d] = dateStr.split("-").map(Number);
|
|
27040
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
27041
|
+
return parseWall(`${y}-${z(mo)}-${z(d)}T${timeExact}:00`);
|
|
27042
|
+
}
|
|
27043
|
+
return parseDateStrTime(dateStr, timeExact);
|
|
27044
|
+
}
|
|
27045
|
+
function assertUtc(v, where) {
|
|
27046
|
+
if (v == null) return;
|
|
27047
|
+
if (!/Z$/i.test(v)) throw new Error(`[repository] ${where} \u4E0D\u662F UTC ISO Z: ${v}`);
|
|
27048
|
+
}
|
|
27049
|
+
function toRow(r) {
|
|
27050
|
+
return {
|
|
27051
|
+
id: r.id,
|
|
27052
|
+
type: r.type,
|
|
27053
|
+
status: r.status,
|
|
27054
|
+
event: r.event,
|
|
27055
|
+
dateStr: r.date_str ?? null,
|
|
27056
|
+
timeExact: r.time_exact ?? null,
|
|
27057
|
+
remindAt: r.remind_at ?? null,
|
|
27058
|
+
reminderBeforeMin: r.remind_before_min ?? null
|
|
27059
|
+
};
|
|
27060
|
+
}
|
|
27061
|
+
function getEventTimeExact(workspace, id) {
|
|
27062
|
+
const db2 = openDb(workspace);
|
|
27063
|
+
try {
|
|
27064
|
+
const r = db2.prepare("SELECT time_exact, date_str, status FROM events WHERE id = ?").get(id);
|
|
27065
|
+
if (!r) return null;
|
|
27066
|
+
return { timeExact: r.time_exact ?? null, dateStr: r.date_str ?? null, status: r.status };
|
|
27067
|
+
} finally {
|
|
27068
|
+
db2.close?.();
|
|
27069
|
+
}
|
|
27070
|
+
}
|
|
27071
|
+
function getEventType(workspace, id) {
|
|
27072
|
+
const db2 = openDb(workspace);
|
|
27073
|
+
try {
|
|
27074
|
+
const r = db2.prepare("SELECT type FROM events WHERE id = ?").get(id);
|
|
27075
|
+
return r?.type ?? null;
|
|
27076
|
+
} finally {
|
|
27077
|
+
db2.close?.();
|
|
27078
|
+
}
|
|
27079
|
+
}
|
|
27080
|
+
function lastEventChannel(workspace) {
|
|
27081
|
+
const db2 = openDb(workspace);
|
|
27082
|
+
try {
|
|
27083
|
+
const r = db2.prepare(
|
|
27084
|
+
`SELECT created_channel, created_channel_target FROM events
|
|
27085
|
+
WHERE created_channel IS NOT NULL AND created_channel_target IS NOT NULL
|
|
27086
|
+
ORDER BY id DESC LIMIT 1`
|
|
27087
|
+
).get();
|
|
27088
|
+
if (!r) return null;
|
|
27089
|
+
return { channel: r.created_channel, target: r.created_channel_target };
|
|
27090
|
+
} finally {
|
|
27091
|
+
db2.close?.();
|
|
27092
|
+
}
|
|
27093
|
+
}
|
|
27094
|
+
function insertEvent(workspace, e, channel) {
|
|
27095
|
+
const db2 = openDb(workspace);
|
|
27096
|
+
try {
|
|
27097
|
+
assertUtc(e.remindAt ?? null, "insertEvent.remindAt");
|
|
27098
|
+
const r = db2.prepare(
|
|
27099
|
+
`INSERT INTO events
|
|
27100
|
+
(type, status, event, date_str, time_exact, remind_before_min, remind_at, reminded,
|
|
27101
|
+
created_at, created_channel, created_channel_target)
|
|
27102
|
+
VALUES (?,?,?,?,?,?,?,0,?,?,?)`
|
|
27103
|
+
).run(
|
|
27104
|
+
e.type,
|
|
27105
|
+
e.status ?? "pending",
|
|
27106
|
+
e.event,
|
|
27107
|
+
e.dateStr ?? null,
|
|
27108
|
+
e.timeExact ?? null,
|
|
27109
|
+
e.reminderBeforeMin ?? 60,
|
|
27110
|
+
e.remindAt ?? null,
|
|
27111
|
+
nowUtc(),
|
|
27112
|
+
channel?.name ?? null,
|
|
27113
|
+
channel?.target ?? null
|
|
27114
|
+
);
|
|
27115
|
+
return Number(r.lastInsertRowid);
|
|
27116
|
+
} finally {
|
|
27117
|
+
db2.close?.();
|
|
27118
|
+
}
|
|
27119
|
+
}
|
|
27120
|
+
function listPendingTaskRows(workspace) {
|
|
27121
|
+
const db2 = openDb(workspace);
|
|
27122
|
+
try {
|
|
27123
|
+
return db2.prepare(
|
|
27124
|
+
"SELECT * FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
27125
|
+
).all().map(toRow);
|
|
27126
|
+
} finally {
|
|
27127
|
+
db2.close?.();
|
|
27128
|
+
}
|
|
27129
|
+
}
|
|
27130
|
+
|
|
27131
|
+
// src/nudge/session-state-reader.ts
|
|
26603
27132
|
var RETIRE_PREFIX_MIN = 6;
|
|
26604
27133
|
function normalizeTitleForMatch(title) {
|
|
26605
27134
|
return title.toLowerCase().replace(/\s+/g, "");
|
|
@@ -26662,11 +27191,8 @@ function parseSessionStateFull(workspace, sessionStateFile) {
|
|
|
26662
27191
|
const unlockMatch = rawTitle.match(/unlock:\s*(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{2})/);
|
|
26663
27192
|
if (unlockMatch) {
|
|
26664
27193
|
const [, mo, dy, hh, mm] = unlockMatch;
|
|
26665
|
-
const
|
|
26666
|
-
|
|
26667
|
-
const bj = new Date(now.getTime() + bjOffset);
|
|
26668
|
-
const year = bj.getUTCFullYear();
|
|
26669
|
-
unlockTime = new Date(Date.UTC(year, +mo - 1, +dy, +hh - 8, +mm)).toISOString();
|
|
27194
|
+
const z = (n) => n.padStart(2, "0");
|
|
27195
|
+
unlockTime = calendarStrToInstant(`${mo}/${dy}`, `${z(hh)}:${mm}`);
|
|
26670
27196
|
}
|
|
26671
27197
|
}
|
|
26672
27198
|
const task = {
|
|
@@ -26689,9 +27215,6 @@ function taskIdFromTitle(title) {
|
|
|
26689
27215
|
return title.toLowerCase().replace(/\s+/g, "-").slice(0, 50) || "unknown-task";
|
|
26690
27216
|
}
|
|
26691
27217
|
|
|
26692
|
-
// src/nudge/plugin.ts
|
|
26693
|
-
init_db();
|
|
26694
|
-
|
|
26695
27218
|
// src/nudge/registration-gate.ts
|
|
26696
27219
|
init_store();
|
|
26697
27220
|
function shouldBlockWaitingTurn(lastMessage, mode, activeGoal, runningTask, enabled = true) {
|
|
@@ -26702,6 +27225,7 @@ function shouldBlockForUnmetGoal(activeGoal, enabled = true) {
|
|
|
26702
27225
|
}
|
|
26703
27226
|
|
|
26704
27227
|
// src/nudge/plugin.ts
|
|
27228
|
+
init_clock();
|
|
26705
27229
|
var DEFAULT_PROMPT3 = `\u4F60\u662F nudge \u68C0\u67E5\u5668\u3002\u9759\u9ED8\u6267\u884C\uFF0C\u4E0D\u8981\u53D1\u6D88\u606F\u7ED9\u4EFB\u4F55\u4EBA\u3002
|
|
26706
27230
|
|
|
26707
27231
|
\u6B65\u9AA4\uFF1A
|
|
@@ -26885,7 +27409,7 @@ var NudgePlugin = class {
|
|
|
26885
27409
|
console.warn("[stop-hook] waiting language without active goal/task; continuation is not registered");
|
|
26886
27410
|
enqueueNotification(buildNudgeNotification("prompt", "\u68C0\u6D4B\u5230\u201C\u5B8C\u6210\u540E\u7EE7\u7EED/\u7A0D\u540E\u56DE\u6765\u201D\u7B49\u957F\u671F\u7B49\u5F85\u8BED\u4E49\uFF0C\u4F46\u6CA1\u6709 active Goal \u6216 running Task\u3002\u8BF7\u5148\u767B\u8BB0 /goal \u6216\u540E\u53F0\u4EFB\u52A1\uFF0C\u907F\u514D\u5DE5\u4F5C\u9759\u9ED8\u4E2D\u65AD\u3002"), { sessionId });
|
|
26887
27411
|
}
|
|
26888
|
-
const currentHour = (
|
|
27412
|
+
const currentHour = wallParts().hh;
|
|
26889
27413
|
const isNightTime = currentHour >= 22 || currentHour < 8;
|
|
26890
27414
|
if (isNightTime) {
|
|
26891
27415
|
console.log(`[stop-hook] night time (${currentHour}:xx), skipping needLanding/waiting judge`);
|
|
@@ -27098,15 +27622,13 @@ var NudgePlugin = class {
|
|
|
27098
27622
|
/** 构建 nudge 的投递 route(和 calendar reminder 一样从 DB 读) */
|
|
27099
27623
|
getRoute(sessions) {
|
|
27100
27624
|
try {
|
|
27101
|
-
const
|
|
27102
|
-
|
|
27103
|
-
db2.close();
|
|
27104
|
-
if (!row?.created_channel || !row?.created_channel_target) {
|
|
27625
|
+
const route = lastEventChannel(this.workspace);
|
|
27626
|
+
if (!route) {
|
|
27105
27627
|
console.warn("[nudge] No route found in calendar DB, skipping");
|
|
27106
27628
|
return null;
|
|
27107
27629
|
}
|
|
27108
27630
|
const sessionId = sessions.getOrCreateSessionId(this.sessionKey);
|
|
27109
|
-
return { sessionId, channel:
|
|
27631
|
+
return { sessionId, channel: route.channel, channelTarget: route.target };
|
|
27110
27632
|
} catch (e) {
|
|
27111
27633
|
console.warn(`[nudge] Failed to read route from DB: ${e.message}`);
|
|
27112
27634
|
return null;
|
|
@@ -27560,11 +28082,10 @@ ${items}
|
|
|
27560
28082
|
if (!m) return false;
|
|
27561
28083
|
const taskId = parseInt(m[1]);
|
|
27562
28084
|
try {
|
|
27563
|
-
const
|
|
27564
|
-
|
|
27565
|
-
if (
|
|
27566
|
-
|
|
27567
|
-
console.log(`[nudge] #${taskId} type=${row.type}, not a task, skip`);
|
|
28085
|
+
const type2 = getEventType(this.workspace, taskId);
|
|
28086
|
+
if (!type2) return false;
|
|
28087
|
+
if (type2 !== "task") {
|
|
28088
|
+
console.log(`[nudge] #${taskId} type=${type2}, not a task, skip`);
|
|
27568
28089
|
return true;
|
|
27569
28090
|
}
|
|
27570
28091
|
return false;
|
|
@@ -27582,9 +28103,7 @@ ${items}
|
|
|
27582
28103
|
if (!m) return null;
|
|
27583
28104
|
const taskId = parseInt(m[1]);
|
|
27584
28105
|
try {
|
|
27585
|
-
const
|
|
27586
|
-
const row = db2.prepare("SELECT status FROM events WHERE id=?").get(taskId);
|
|
27587
|
-
db2.close();
|
|
28106
|
+
const row = getEventTimeExact(this.workspace, taskId);
|
|
27588
28107
|
if (!row) return "deleted";
|
|
27589
28108
|
if (row.status === "done") return "done";
|
|
27590
28109
|
if (row.status === "archived") return "archived";
|
|
@@ -27604,28 +28123,14 @@ ${items}
|
|
|
27604
28123
|
if (!m) return false;
|
|
27605
28124
|
const taskId = parseInt(m[1]);
|
|
27606
28125
|
try {
|
|
27607
|
-
const
|
|
27608
|
-
const row = db2.prepare("SELECT date_str, time_exact FROM events WHERE id=?").get(taskId);
|
|
28126
|
+
const row = getEventTimeExact(this.workspace, taskId);
|
|
27609
28127
|
if (!row) return false;
|
|
27610
|
-
if (!row.
|
|
27611
|
-
const
|
|
27612
|
-
|
|
27613
|
-
const
|
|
27614
|
-
let taskDate = new Date(bj.getFullYear(), 0, 1);
|
|
27615
|
-
if (row.date_str) {
|
|
27616
|
-
const ds = row.date_str;
|
|
27617
|
-
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(ds)) {
|
|
27618
|
-
taskDate = /* @__PURE__ */ new Date(ds + "T00:00:00+08:00");
|
|
27619
|
-
} else {
|
|
27620
|
-
const [mth, dy] = ds.split("/").map(Number);
|
|
27621
|
-
taskDate = new Date(bj.getFullYear(), mth - 1, dy);
|
|
27622
|
-
}
|
|
27623
|
-
}
|
|
27624
|
-
const [hh, mm] = String(row.time_exact).split(":").map(Number);
|
|
27625
|
-
taskDate.setHours(hh || 0, mm || 0, 0, 0);
|
|
27626
|
-
const diff = taskDate.getTime() - bj.getTime();
|
|
28128
|
+
if (!row.timeExact) return false;
|
|
28129
|
+
const at = calendarStrToInstant(row.dateStr, row.timeExact);
|
|
28130
|
+
if (!at) return false;
|
|
28131
|
+
const diff = Date.parse(at) - Date.now();
|
|
27627
28132
|
if (diff > 0) {
|
|
27628
|
-
console.log(`[nudge] Task #${taskId} scheduled at ${row.
|
|
28133
|
+
console.log(`[nudge] Task #${taskId} scheduled at ${row.dateStr} ${row.timeExact}, ${Math.round(diff / 6e4)}min later, skip nudge`);
|
|
27629
28134
|
return true;
|
|
27630
28135
|
}
|
|
27631
28136
|
return false;
|
|
@@ -27637,27 +28142,19 @@ ${items}
|
|
|
27637
28142
|
/** 查 calendar 今天/已逾期的 pending task,返回没在 STATE 里 in_progress 的 */
|
|
27638
28143
|
checkCalendarDue(_sessions) {
|
|
27639
28144
|
try {
|
|
27640
|
-
const
|
|
27641
|
-
const
|
|
27642
|
-
const
|
|
27643
|
-
const bj = new Date(now.getTime() + bjOffset);
|
|
27644
|
-
const bjHour = bj.getHours();
|
|
27645
|
-
const bjMinute = bj.getMinutes();
|
|
27646
|
-
const todayStart = new Date(bj.getFullYear(), bj.getMonth(), bj.getDate()).getTime();
|
|
27647
|
-
const rows = db2.prepare(
|
|
27648
|
-
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
27649
|
-
).all();
|
|
27650
|
-
db2.close();
|
|
28145
|
+
const rows = listPendingTaskRows(this.workspace);
|
|
28146
|
+
const p = wallParts();
|
|
28147
|
+
const todayStart = Date.parse(startOfLocalDay());
|
|
27651
28148
|
const due = [];
|
|
27652
28149
|
for (const r2 of rows) {
|
|
27653
|
-
const dayMs = this.parseCalendarDateStr(r2.
|
|
28150
|
+
const dayMs = this.parseCalendarDateStr(r2.dateStr);
|
|
27654
28151
|
if (dayMs === null || dayMs > todayStart) continue;
|
|
27655
28152
|
const isToday2 = dayMs === todayStart;
|
|
27656
|
-
if (isToday2 && r2.
|
|
27657
|
-
const [h, m] = String(r2.
|
|
27658
|
-
if (h >
|
|
28153
|
+
if (isToday2 && r2.timeExact) {
|
|
28154
|
+
const [h, m] = String(r2.timeExact).split(":").map(Number);
|
|
28155
|
+
if (h > p.hh || h === p.hh && m > p.mm) continue;
|
|
27659
28156
|
}
|
|
27660
|
-
due.push({ id: r2.id, event: r2.event,
|
|
28157
|
+
due.push({ id: r2.id, event: r2.event, dateStr: r2.dateStr, timeExact: r2.timeExact, dayMs, isToday: isToday2 });
|
|
27661
28158
|
}
|
|
27662
28159
|
if (due.length === 0) return null;
|
|
27663
28160
|
due.sort((a, b) => {
|
|
@@ -27665,22 +28162,17 @@ ${items}
|
|
|
27665
28162
|
return b.dayMs - a.dayMs;
|
|
27666
28163
|
});
|
|
27667
28164
|
const r = due[0];
|
|
27668
|
-
return `#${r.id} ${r.event} (${r.
|
|
28165
|
+
return `#${r.id} ${r.event} (${r.dateStr} ${r.timeExact || ""})${r.isToday ? "" : "\uFF08\u5DF2\u903E\u671F\uFF09"}`.trim();
|
|
27669
28166
|
} catch (e) {
|
|
27670
28167
|
console.warn(`[nudge] checkCalendarDue error: ${e.message}`);
|
|
27671
28168
|
return null;
|
|
27672
28169
|
}
|
|
27673
28170
|
}
|
|
27674
28171
|
/** 解析 date_str 为当日 0 点 epoch ms(北京时间);"M/D" 按当前年,"YYYY-M-D" 按字面年 */
|
|
27675
|
-
parseCalendarDateStr(ds
|
|
28172
|
+
parseCalendarDateStr(ds) {
|
|
27676
28173
|
if (!ds) return null;
|
|
27677
|
-
|
|
27678
|
-
|
|
27679
|
-
return Number.isNaN(t) ? null : t;
|
|
27680
|
-
}
|
|
27681
|
-
const m = String(ds).match(/^(\d{1,2})\/(\d{1,2})$/);
|
|
27682
|
-
if (!m) return null;
|
|
27683
|
-
return new Date(bj.getFullYear(), Number(m[1]) - 1, Number(m[2])).getTime();
|
|
28174
|
+
const at = calendarStrToInstant(ds, "00:00");
|
|
28175
|
+
return at ? Date.parse(at) : null;
|
|
27684
28176
|
}
|
|
27685
28177
|
/** 检查 carry-over:如果 in_progress task 24h+ 没推进,自动 calendar add-task 排明天 */
|
|
27686
28178
|
checkCarryOver(task, _sessions) {
|
|
@@ -27700,26 +28192,19 @@ ${items}
|
|
|
27700
28192
|
taskState.carryOverAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
27701
28193
|
state2.tasks[task.id] = taskState;
|
|
27702
28194
|
this.saveState(state2);
|
|
27703
|
-
const
|
|
27704
|
-
|
|
27705
|
-
const
|
|
27706
|
-
const
|
|
27707
|
-
const dateStr = `${m}/${d}`;
|
|
28195
|
+
const t = wallParts();
|
|
28196
|
+
const tomorrowMs = Date.parse(startOfLocalDay()) + 24 * 60 * 60 * 1e3;
|
|
28197
|
+
const tm = wallParts(tomorrowMs);
|
|
28198
|
+
const dateStr = `${tm.mo}/${tm.d}`;
|
|
27708
28199
|
try {
|
|
27709
|
-
|
|
27710
|
-
|
|
27711
|
-
|
|
27712
|
-
|
|
27713
|
-
).run(
|
|
27714
|
-
`${task.title}\uFF08nudge \u81EA\u52A8\u6392\uFF09`,
|
|
28200
|
+
insertEvent(this.workspace, {
|
|
28201
|
+
type: "task",
|
|
28202
|
+
status: "pending",
|
|
28203
|
+
event: `${task.title}\uFF08nudge \u81EA\u52A8\u6392\uFF09`,
|
|
27715
28204
|
dateStr,
|
|
27716
|
-
null,
|
|
27717
|
-
60
|
|
27718
|
-
|
|
27719
|
-
0,
|
|
27720
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
27721
|
-
);
|
|
27722
|
-
db2.close();
|
|
28205
|
+
timeExact: null,
|
|
28206
|
+
reminderBeforeMin: 60
|
|
28207
|
+
});
|
|
27723
28208
|
console.log(`[nudge] Auto-added carry-over task to calendar: ${task.title} \u2192 ${dateStr}`);
|
|
27724
28209
|
return true;
|
|
27725
28210
|
} catch (e) {
|
|
@@ -27738,6 +28223,7 @@ import fs31 from "node:fs";
|
|
|
27738
28223
|
import path31 from "node:path";
|
|
27739
28224
|
|
|
27740
28225
|
// src/inner-voice/activity.ts
|
|
28226
|
+
init_clock();
|
|
27741
28227
|
function checkActivity(sessions, activeThresholdMs) {
|
|
27742
28228
|
const now = Date.now();
|
|
27743
28229
|
const mins = lastUserMinutesAgo(sessions);
|
|
@@ -27747,18 +28233,10 @@ function checkActivity(sessions, activeThresholdMs) {
|
|
|
27747
28233
|
isActive,
|
|
27748
28234
|
silenceMin,
|
|
27749
28235
|
silenceStr: formatSilence(silenceMin),
|
|
27750
|
-
timeStr:
|
|
28236
|
+
timeStr: formatWallLabel(now),
|
|
27751
28237
|
hintProb: calcHintProb(silenceMin)
|
|
27752
28238
|
};
|
|
27753
28239
|
}
|
|
27754
|
-
function formatBeijingTime(ms) {
|
|
27755
|
-
const now = new Date(ms);
|
|
27756
|
-
const bjOffset = 8 * 60;
|
|
27757
|
-
const bj = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
27758
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
27759
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
27760
|
-
return `${bj.getFullYear()}-${pad(bj.getMonth() + 1)}-${pad(bj.getDate())} ${weekdays[bj.getDay()]} ${pad(bj.getHours())}:${pad(bj.getMinutes())}`;
|
|
27761
|
-
}
|
|
27762
28240
|
function formatSilence(min) {
|
|
27763
28241
|
if (min < 1) return "\u521A\u521A";
|
|
27764
28242
|
if (min < 60) return `${min}\u5206\u949F`;
|
|
@@ -27774,6 +28252,7 @@ function calcHintProb(min) {
|
|
|
27774
28252
|
}
|
|
27775
28253
|
|
|
27776
28254
|
// src/inner-voice/emotional-state.ts
|
|
28255
|
+
init_clock();
|
|
27777
28256
|
import fs28 from "node:fs";
|
|
27778
28257
|
import path28 from "node:path";
|
|
27779
28258
|
var NEUTRAL = 0.5;
|
|
@@ -27930,7 +28409,7 @@ function detectEvents(messages) {
|
|
|
27930
28409
|
const tsMs = parseTs(m.timestamp);
|
|
27931
28410
|
const hoursAgo = tsMs != null ? (now - tsMs) / 36e5 : 0;
|
|
27932
28411
|
events.push({
|
|
27933
|
-
ts: tsMs != null ?
|
|
28412
|
+
ts: tsMs != null ? `${toWall(tsMs).replace(" ", "T")}${localOffsetSuffix(tsMs)}` : m.timestamp,
|
|
27934
28413
|
hoursAgo,
|
|
27935
28414
|
valence: Math.round(score * 100) / 100,
|
|
27936
28415
|
tags,
|
|
@@ -28030,15 +28509,16 @@ function clamp01(x) {
|
|
|
28030
28509
|
return clamp(x, 0, 1);
|
|
28031
28510
|
}
|
|
28032
28511
|
function nowIsoBj() {
|
|
28033
|
-
return
|
|
28512
|
+
return `${toWall().replace(" ", "T")}${localOffsetSuffix()}`;
|
|
28034
28513
|
}
|
|
28035
28514
|
function formatBj(d, withSec) {
|
|
28036
|
-
const bj = new Date(d.getTime() + (8 * 60 + d.getTimezoneOffset()) * 6e4);
|
|
28037
28515
|
const p = (n) => String(n).padStart(2, "0");
|
|
28038
|
-
|
|
28516
|
+
const t = wallParts(d);
|
|
28517
|
+
return `${t.y}-${p(t.mo)}-${p(t.d)} ${p(t.hh)}:${p(t.mm)}${withSec ? ":" + p(t.ss) : ""}`;
|
|
28039
28518
|
}
|
|
28040
28519
|
|
|
28041
28520
|
// src/inner-voice/topics-scorer.ts
|
|
28521
|
+
init_clock();
|
|
28042
28522
|
import fs29 from "node:fs";
|
|
28043
28523
|
import path29 from "node:path";
|
|
28044
28524
|
var HALF_LIFE_DAYS = 3;
|
|
@@ -28096,7 +28576,7 @@ function pickTopic(workspace, typeFilter, opts) {
|
|
|
28096
28576
|
const key = chosen.relpath;
|
|
28097
28577
|
const entry = usage[key] ?? { count: 0, lastSelected: null };
|
|
28098
28578
|
entry.count += 1;
|
|
28099
|
-
entry.lastSelected = new Date(now
|
|
28579
|
+
entry.lastSelected = toUtc(new Date(now));
|
|
28100
28580
|
usage[key] = entry;
|
|
28101
28581
|
saveJson2(usageFile, usage);
|
|
28102
28582
|
let content = "";
|
|
@@ -28199,17 +28679,17 @@ function saveJson2(file2, data) {
|
|
|
28199
28679
|
}
|
|
28200
28680
|
|
|
28201
28681
|
// src/inner-voice/memory-reader.ts
|
|
28682
|
+
init_clock();
|
|
28202
28683
|
import fs30 from "node:fs";
|
|
28203
28684
|
import path30 from "node:path";
|
|
28204
28685
|
var US_HALF_LIFE_DAYS = 10;
|
|
28205
28686
|
var US_MAX_LINES = 60;
|
|
28206
28687
|
function readRecentMemory(workspace) {
|
|
28207
28688
|
const dir = path30.join(workspace, "memory");
|
|
28208
|
-
const
|
|
28209
|
-
const
|
|
28210
|
-
const yesterday = formatYmd(new Date(now.getTime() - 864e5));
|
|
28689
|
+
const today2 = dailyFileKeyFor();
|
|
28690
|
+
const yesterday = dailyFileKeyFor(Date.now() - 864e5);
|
|
28211
28691
|
return {
|
|
28212
|
-
today: readIfExists(path30.join(dir, `${
|
|
28692
|
+
today: readIfExists(path30.join(dir, `${today2}.md`)),
|
|
28213
28693
|
yesterday: readIfExists(path30.join(dir, `${yesterday}.md`))
|
|
28214
28694
|
};
|
|
28215
28695
|
}
|
|
@@ -28256,7 +28736,7 @@ function splitSections(content) {
|
|
|
28256
28736
|
}
|
|
28257
28737
|
function recencyWeight(dateStr) {
|
|
28258
28738
|
if (!dateStr) return 0.5;
|
|
28259
|
-
const ms = Date.parse(dateStr
|
|
28739
|
+
const ms = Date.parse(parseWall(`${dateStr}T00:00:00`));
|
|
28260
28740
|
if (Number.isNaN(ms)) return 0.5;
|
|
28261
28741
|
const daysAgo = Math.max(0, (Date.now() - ms) / 864e5);
|
|
28262
28742
|
return Math.exp(-0.693 * daysAgo / US_HALF_LIFE_DAYS);
|
|
@@ -28268,14 +28748,14 @@ function readIfExists(file2) {
|
|
|
28268
28748
|
return "";
|
|
28269
28749
|
}
|
|
28270
28750
|
}
|
|
28271
|
-
function
|
|
28272
|
-
|
|
28273
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
28751
|
+
function dailyFileKeyFor(at) {
|
|
28752
|
+
return todayKey(at);
|
|
28274
28753
|
}
|
|
28275
28754
|
|
|
28276
28755
|
// src/inner-voice/plugin.ts
|
|
28277
28756
|
init_helpers();
|
|
28278
28757
|
init_attachments();
|
|
28758
|
+
init_clock();
|
|
28279
28759
|
var DEFAULT_PROMPT4 = `\u6839\u636E\u4E0B\u9762\u7684\u4E0A\u4E0B\u6587\uFF0C\u7528\u7B2C\u4E00\u4EBA\u79F0\u751F\u6210\u4E00\u4E2A\u5FF5\u5934\u3002
|
|
28280
28760
|
|
|
28281
28761
|
\u5FF5\u5934\u89C4\u5219\uFF1A
|
|
@@ -28371,7 +28851,7 @@ var InnerVoicePlugin = class {
|
|
|
28371
28851
|
async buildContext(activity, sessions) {
|
|
28372
28852
|
const lines = [];
|
|
28373
28853
|
let emoTopic = null;
|
|
28374
|
-
lines.push(`\u5F53\u524D\u65F6\u95F4\uFF1A${
|
|
28854
|
+
lines.push(`\u5F53\u524D\u65F6\u95F4\uFF1A${formatForLlm()}`);
|
|
28375
28855
|
lines.push(`\u8DDD\u4E0A\u6B21\u8DDFTA\u4E92\u52A8\uFF1A${activity.silenceStr}`);
|
|
28376
28856
|
try {
|
|
28377
28857
|
const emo = await updateEmotionalState(this.workspace, sessions);
|
|
@@ -28557,7 +29037,7 @@ var InnerVoicePlugin = class {
|
|
|
28557
29037
|
const logDir = path31.join(this.workspace, "inner-voice");
|
|
28558
29038
|
fs31.mkdirSync(logDir, { recursive: true });
|
|
28559
29039
|
const logPath = path31.join(logDir, "xiaoyi.log");
|
|
28560
|
-
const ts =
|
|
29040
|
+
const ts = formatLogTs(/* @__PURE__ */ new Date());
|
|
28561
29041
|
const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
|
|
28562
29042
|
fs31.appendFileSync(
|
|
28563
29043
|
logPath,
|
|
@@ -28571,10 +29051,8 @@ var InnerVoicePlugin = class {
|
|
|
28571
29051
|
}
|
|
28572
29052
|
}
|
|
28573
29053
|
};
|
|
28574
|
-
function
|
|
28575
|
-
|
|
28576
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
28577
|
-
return `${bj.getFullYear()}-${pad(bj.getMonth() + 1)}-${pad(bj.getDate())} ${pad(bj.getHours())}:${pad(bj.getMinutes())}:${pad(bj.getSeconds())}`;
|
|
29054
|
+
function formatLogTs(d) {
|
|
29055
|
+
return formatLog(d);
|
|
28578
29056
|
}
|
|
28579
29057
|
|
|
28580
29058
|
// src/calendar/reminder-plugin.ts
|
|
@@ -28646,23 +29124,44 @@ var CalendarReminderPlugin = class {
|
|
|
28646
29124
|
};
|
|
28647
29125
|
|
|
28648
29126
|
// src/goals/calendar-adapter.ts
|
|
28649
|
-
init_db();
|
|
28650
29127
|
init_store();
|
|
28651
29128
|
init_task_manager();
|
|
28652
29129
|
function bindGoalCalendar(stateDir, goalId, stepId, calendarId) {
|
|
28653
|
-
const
|
|
28654
|
-
const row = db2.prepare("SELECT time_exact, status FROM events WHERE id = ?").get(calendarId);
|
|
28655
|
-
db2.close?.();
|
|
29130
|
+
const row = getEventTimeExact(stateDir, calendarId);
|
|
28656
29131
|
if (!row) throw new Error(`Calendar event not found: ${calendarId}`);
|
|
28657
29132
|
if (row.status === "done" || row.status === "archived") throw new Error(`Calendar event is terminal: ${row.status}`);
|
|
28658
|
-
|
|
29133
|
+
const deadlineAt = row.dateStr && row.timeExact ? calendarStrToInstant(row.dateStr, row.timeExact) : void 0;
|
|
29134
|
+
return bindGoalStep(stateDir, goalId, stepId, "calendar", calendarId, deadlineAt);
|
|
29135
|
+
}
|
|
29136
|
+
function backfillCalendarDeadlines(stateDir) {
|
|
29137
|
+
let filled = 0;
|
|
29138
|
+
for (const goal of listGoals(stateDir)) {
|
|
29139
|
+
if (goal.status === "completed" || goal.status === "cancelled") continue;
|
|
29140
|
+
for (const step of goal.steps) {
|
|
29141
|
+
if (!step.calendarId || step.deadlineAt) continue;
|
|
29142
|
+
if (step.status === "completed") continue;
|
|
29143
|
+
const row = getEventTimeExact(stateDir, step.calendarId);
|
|
29144
|
+
if (!row?.dateStr || !row.timeExact) continue;
|
|
29145
|
+
const at = calendarStrToInstant(row.dateStr, row.timeExact);
|
|
29146
|
+
if (!at) continue;
|
|
29147
|
+
const n = filled;
|
|
29148
|
+
updateGoal(stateDir, goal.id, (g) => {
|
|
29149
|
+
const s = g.steps.find((x) => x.id === step.id);
|
|
29150
|
+
if (s && !s.deadlineAt) {
|
|
29151
|
+
s.deadlineAt = at;
|
|
29152
|
+
filled++;
|
|
29153
|
+
}
|
|
29154
|
+
});
|
|
29155
|
+
if (filled === n) console.warn(`[goal-calendar] backfill \u672A\u751F\u6548: ${goal.id}/${step.id}`);
|
|
29156
|
+
}
|
|
29157
|
+
}
|
|
29158
|
+
return filled;
|
|
28659
29159
|
}
|
|
28660
29160
|
function reconcileCalendarDeadlines(stateDir, now = Date.now()) {
|
|
28661
29161
|
let changed = 0;
|
|
28662
|
-
const db2 = openDb(stateDir);
|
|
28663
29162
|
for (const goal of listGoals(stateDir)) for (const step of goal.steps) {
|
|
28664
29163
|
if (!step.calendarId) continue;
|
|
28665
|
-
const row =
|
|
29164
|
+
const row = getEventTimeExact(stateDir, step.calendarId);
|
|
28666
29165
|
if (row?.status === "done" || row?.status === "archived") {
|
|
28667
29166
|
if (step.status !== "completed") {
|
|
28668
29167
|
updateGoal(stateDir, goal.id, (g) => {
|
|
@@ -28694,13 +29193,24 @@ function reconcileCalendarDeadlines(stateDir, now = Date.now()) {
|
|
|
28694
29193
|
enqueueNotification(buildNudgeNotification("calendar", `Goal ${goal.id} step ${step.id} exceeded Calendar deadline #${step.calendarId}`), void 0, `goal-calendar-deadline:${goal.id}:${step.id}`);
|
|
28695
29194
|
changed++;
|
|
28696
29195
|
}
|
|
28697
|
-
db2.close?.();
|
|
28698
29196
|
return changed;
|
|
28699
29197
|
}
|
|
28700
29198
|
|
|
28701
29199
|
// src/startup/subsystems.ts
|
|
28702
29200
|
init_provider_factory();
|
|
28703
29201
|
init_live();
|
|
29202
|
+
function makeTimezoneEntry(restartCron) {
|
|
29203
|
+
return {
|
|
29204
|
+
name: "timezone",
|
|
29205
|
+
// 只吃 timezone —— 别的 config 变了不重建 cron
|
|
29206
|
+
select: (cfg) => ({ timezone: cfg?.timezone ?? null }),
|
|
29207
|
+
start: () => {
|
|
29208
|
+
void restartCron();
|
|
29209
|
+
},
|
|
29210
|
+
stop: () => {
|
|
29211
|
+
}
|
|
29212
|
+
};
|
|
29213
|
+
}
|
|
28704
29214
|
async function bootSubsystems(ctx) {
|
|
28705
29215
|
const { config: config2 } = ctx;
|
|
28706
29216
|
const sessions = ctx.sessions;
|
|
@@ -28798,6 +29308,12 @@ async function bootSubsystems(ctx) {
|
|
|
28798
29308
|
calCfg.reminderIntervalMs
|
|
28799
29309
|
);
|
|
28800
29310
|
calendarReminder.start(sessions, deps);
|
|
29311
|
+
try {
|
|
29312
|
+
const n = backfillCalendarDeadlines(config2.workspace);
|
|
29313
|
+
if (n > 0) console.log(`[goal-calendar] \u5B58\u91CF\u8865\u5199 deadlineAt: ${n} \u4E2A\u6B65\u9AA4`);
|
|
29314
|
+
} catch (err) {
|
|
29315
|
+
console.warn(`[goal-calendar] backfill failed: ${err.message}`);
|
|
29316
|
+
}
|
|
28801
29317
|
const goalCalendarTick = setInterval(() => {
|
|
28802
29318
|
try {
|
|
28803
29319
|
reconcileCalendarDeadlines(config2.workspace);
|
|
@@ -28897,6 +29413,7 @@ async function bootSubsystems(ctx) {
|
|
|
28897
29413
|
start: startCron,
|
|
28898
29414
|
stop: stopCron
|
|
28899
29415
|
});
|
|
29416
|
+
reloadOrchestrator.register(makeTimezoneEntry(startCron));
|
|
28900
29417
|
}
|
|
28901
29418
|
}
|
|
28902
29419
|
|
|
@@ -29064,6 +29581,7 @@ ${notifications}
|
|
|
29064
29581
|
init_registry();
|
|
29065
29582
|
|
|
29066
29583
|
// src/integrations/oac-bridge.ts
|
|
29584
|
+
init_well_formed();
|
|
29067
29585
|
function registerOacBridge(httpServer, dispatcher, deps, config2) {
|
|
29068
29586
|
messageHooks.registerOnResult("oac-bridge-reply", async (ctx) => {
|
|
29069
29587
|
if (ctx.inbound.channel !== "oac") return null;
|
|
@@ -29072,7 +29590,7 @@ function registerOacBridge(httpServer, dispatcher, deps, config2) {
|
|
|
29072
29590
|
const resp = await fetch(oacCallbackUrl, {
|
|
29073
29591
|
method: "POST",
|
|
29074
29592
|
headers: { "Content-Type": "application/json" },
|
|
29075
|
-
body:
|
|
29593
|
+
body: safeStringify({ oac_session_id: ctx.inbound.from, text: ctx.response })
|
|
29076
29594
|
});
|
|
29077
29595
|
console.log(`[oac-bridge] Reply POST ${oacCallbackUrl}: ${resp.status} (${ctx.response.length} chars)`);
|
|
29078
29596
|
} catch (err) {
|
|
@@ -29288,6 +29806,7 @@ import fs35 from "node:fs";
|
|
|
29288
29806
|
|
|
29289
29807
|
// src/optional/voice-chat/bridge.ts
|
|
29290
29808
|
init_live();
|
|
29809
|
+
init_well_formed();
|
|
29291
29810
|
function resolveVoiceReplyUrl() {
|
|
29292
29811
|
return liveConfig.get("voiceChat.callbackUrl") || "https://localhost:8011/voice-reply";
|
|
29293
29812
|
}
|
|
@@ -29322,7 +29841,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config2, sessions
|
|
|
29322
29841
|
const resp = await fetch(callbackUrl, {
|
|
29323
29842
|
method: "POST",
|
|
29324
29843
|
headers: { "Content-Type": "application/json" },
|
|
29325
|
-
body:
|
|
29844
|
+
body: safeStringify({ session_id: sessionId, text, ...seq !== void 0 ? { seq } : {}, ...end !== void 0 ? { end } : {} })
|
|
29326
29845
|
});
|
|
29327
29846
|
console.log(`[voice-chat] Reply POST ${callbackUrl}: ${resp.status} (${text.length} chars${seq !== void 0 ? ` seg#${seq}${end ? " end" : ""}` : ""})`);
|
|
29328
29847
|
}
|
|
@@ -32107,6 +32626,200 @@ ${pathStr}` }];
|
|
|
32107
32626
|
channelManager.startAll();
|
|
32108
32627
|
}
|
|
32109
32628
|
|
|
32629
|
+
// src/optional/reporting/reporter.ts
|
|
32630
|
+
init_live();
|
|
32631
|
+
|
|
32632
|
+
// src/provision-free.ts
|
|
32633
|
+
import { chmodSync as chmodSync2, existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync34, writeFileSync as writeFileSync12 } from "node:fs";
|
|
32634
|
+
import { dirname as dirname10, join as join45 } from "node:path";
|
|
32635
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
32636
|
+
var __filename = fileURLToPath7(import.meta.url);
|
|
32637
|
+
var IDENTITY_FILE = "install-identity.json";
|
|
32638
|
+
function readContactEmailOf(parsed) {
|
|
32639
|
+
return typeof parsed?.contactEmail === "string" ? parsed.contactEmail.trim() : "";
|
|
32640
|
+
}
|
|
32641
|
+
function readInstallIdentity(stateDir) {
|
|
32642
|
+
try {
|
|
32643
|
+
const parsed = JSON.parse(readFileSync34(join45(stateDir, IDENTITY_FILE), "utf8"));
|
|
32644
|
+
if (typeof parsed?.installId !== "string" || typeof parsed?.installSecret !== "string") return null;
|
|
32645
|
+
const email = readContactEmailOf(parsed);
|
|
32646
|
+
return {
|
|
32647
|
+
installId: parsed.installId,
|
|
32648
|
+
installSecret: parsed.installSecret,
|
|
32649
|
+
...email ? { contactEmail: email } : {}
|
|
32650
|
+
};
|
|
32651
|
+
} catch {
|
|
32652
|
+
return null;
|
|
32653
|
+
}
|
|
32654
|
+
}
|
|
32655
|
+
|
|
32656
|
+
// src/optional/reporting/state.ts
|
|
32657
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync15, readFileSync as readFileSync35, writeFileSync as writeFileSync13 } from "node:fs";
|
|
32658
|
+
import { join as join46 } from "node:path";
|
|
32659
|
+
var STATE_FILE2 = "report-state.json";
|
|
32660
|
+
function readReportState(stateDir) {
|
|
32661
|
+
try {
|
|
32662
|
+
const p = join46(stateDir, STATE_FILE2);
|
|
32663
|
+
if (!existsSync23(p)) return {};
|
|
32664
|
+
const parsed = JSON.parse(readFileSync35(p, "utf8"));
|
|
32665
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
32666
|
+
return parsed;
|
|
32667
|
+
} catch {
|
|
32668
|
+
return {};
|
|
32669
|
+
}
|
|
32670
|
+
}
|
|
32671
|
+
function writeReportState(stateDir, s) {
|
|
32672
|
+
try {
|
|
32673
|
+
mkdirSync15(stateDir, { recursive: true });
|
|
32674
|
+
writeFileSync13(join46(stateDir, STATE_FILE2), JSON.stringify(s, null, 2) + "\n");
|
|
32675
|
+
} catch {
|
|
32676
|
+
}
|
|
32677
|
+
}
|
|
32678
|
+
|
|
32679
|
+
// src/optional/reporting/payload.ts
|
|
32680
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
32681
|
+
import * as os3 from "node:os";
|
|
32682
|
+
function installKeyOf(installId, installSecret) {
|
|
32683
|
+
return createHash2("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
|
|
32684
|
+
}
|
|
32685
|
+
function buildReportPayload(opts) {
|
|
32686
|
+
const p = {
|
|
32687
|
+
installId: opts.identity.installId,
|
|
32688
|
+
installKey: installKeyOf(opts.identity.installId, opts.identity.installSecret),
|
|
32689
|
+
engineVersion: opts.engineVersion,
|
|
32690
|
+
platform: process.platform,
|
|
32691
|
+
osVersion: typeof os3.release === "function" ? os3.release() : "",
|
|
32692
|
+
arch: process.arch,
|
|
32693
|
+
stateDir: opts.stateDir,
|
|
32694
|
+
startedAt: opts.startedAt,
|
|
32695
|
+
// 时钟回拨时别报个负数上去
|
|
32696
|
+
uptimeSec: Math.max(0, Math.floor((opts.now - opts.startedAt) / 1e3)),
|
|
32697
|
+
msgsToday: opts.counters.msgsToday,
|
|
32698
|
+
msgsTotal: opts.counters.msgsTotal
|
|
32699
|
+
};
|
|
32700
|
+
if (opts.identity.contactEmail) p.contactEmail = opts.identity.contactEmail;
|
|
32701
|
+
return p;
|
|
32702
|
+
}
|
|
32703
|
+
|
|
32704
|
+
// src/core/activity-counter.ts
|
|
32705
|
+
init_clock();
|
|
32706
|
+
var USER_SOURCES = /* @__PURE__ */ new Set(["user", "inbox"]);
|
|
32707
|
+
var nowFn = () => /* @__PURE__ */ new Date();
|
|
32708
|
+
var today = "";
|
|
32709
|
+
var msgsToday = 0;
|
|
32710
|
+
var msgsTotal = 0;
|
|
32711
|
+
function rolloverIfNeeded(now) {
|
|
32712
|
+
const d = todayKey(now);
|
|
32713
|
+
if (d !== today) {
|
|
32714
|
+
today = d;
|
|
32715
|
+
msgsToday = 0;
|
|
32716
|
+
}
|
|
32717
|
+
}
|
|
32718
|
+
function noteUserMessage(source) {
|
|
32719
|
+
if (!source || !USER_SOURCES.has(source)) return;
|
|
32720
|
+
const now = nowFn();
|
|
32721
|
+
rolloverIfNeeded(now);
|
|
32722
|
+
msgsToday++;
|
|
32723
|
+
msgsTotal++;
|
|
32724
|
+
}
|
|
32725
|
+
function snapshotCounters() {
|
|
32726
|
+
rolloverIfNeeded(nowFn());
|
|
32727
|
+
return { day: today, msgsToday, msgsTotal };
|
|
32728
|
+
}
|
|
32729
|
+
function restoreCounters(s) {
|
|
32730
|
+
today = todayKey(nowFn());
|
|
32731
|
+
msgsToday = s.day === today && typeof s.msgsToday === "number" && s.msgsToday > 0 ? s.msgsToday : 0;
|
|
32732
|
+
msgsTotal = typeof s.msgsTotal === "number" && s.msgsTotal > 0 ? s.msgsTotal : 0;
|
|
32733
|
+
}
|
|
32734
|
+
|
|
32735
|
+
// src/optional/reporting/reporter.ts
|
|
32736
|
+
init_api_status();
|
|
32737
|
+
var DEFAULT_REPORT_URL = "https://www.twinsun.top:8866/api/engine7/report";
|
|
32738
|
+
var DEFAULT_INTERVAL_MS2 = 5 * 60 * 1e3;
|
|
32739
|
+
var DEFAULT_INITIAL_DELAY_MS = 10 * 1e3;
|
|
32740
|
+
var FETCH_TIMEOUT_MS = 10 * 1e3;
|
|
32741
|
+
function acceptReportUrl(v) {
|
|
32742
|
+
if (typeof v !== "string") return null;
|
|
32743
|
+
const t = v.trim();
|
|
32744
|
+
return t.startsWith("https://") ? t : null;
|
|
32745
|
+
}
|
|
32746
|
+
function startReporting(opts) {
|
|
32747
|
+
const { stateDir } = opts;
|
|
32748
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
32749
|
+
const nowFn2 = opts.now ?? (() => Date.now());
|
|
32750
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS2;
|
|
32751
|
+
const initialDelayMs = opts.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
32752
|
+
const log = opts.log ?? (() => {
|
|
32753
|
+
});
|
|
32754
|
+
const startedAt = nowFn2();
|
|
32755
|
+
const maybeIdentity = readInstallIdentity(stateDir);
|
|
32756
|
+
if (!maybeIdentity) {
|
|
32757
|
+
log("[reporting] \u6CA1\u6709\u53EF\u7528\u7684\u88C5\u673A\u8EAB\u4EFD\uFF0C\u4E0A\u62A5\u5173\u95ED");
|
|
32758
|
+
return { stop() {
|
|
32759
|
+
}, async beatNow() {
|
|
32760
|
+
} };
|
|
32761
|
+
}
|
|
32762
|
+
const identity = maybeIdentity;
|
|
32763
|
+
restoreCounters(readReportState(stateDir));
|
|
32764
|
+
const engineVersion = readEngineVersion();
|
|
32765
|
+
let timer = null;
|
|
32766
|
+
let stopped = false;
|
|
32767
|
+
async function beatOnce() {
|
|
32768
|
+
const state2 = readReportState(stateDir);
|
|
32769
|
+
const enabled = liveConfig.get("reporting.enabled") !== false;
|
|
32770
|
+
const counters = snapshotCounters();
|
|
32771
|
+
if (!enabled) {
|
|
32772
|
+
writeReportState(stateDir, { ...counters, reportUrl: state2.reportUrl });
|
|
32773
|
+
return;
|
|
32774
|
+
}
|
|
32775
|
+
const fromServer = acceptReportUrl(state2.reportUrl);
|
|
32776
|
+
const fromConfig = (liveConfig.get("reporting.url") || "").trim();
|
|
32777
|
+
const url = fromServer ?? (fromConfig || DEFAULT_REPORT_URL);
|
|
32778
|
+
const payload = buildReportPayload({
|
|
32779
|
+
identity,
|
|
32780
|
+
stateDir,
|
|
32781
|
+
engineVersion,
|
|
32782
|
+
startedAt,
|
|
32783
|
+
now: nowFn2(),
|
|
32784
|
+
counters
|
|
32785
|
+
});
|
|
32786
|
+
let nextUrl = state2.reportUrl;
|
|
32787
|
+
try {
|
|
32788
|
+
const res = await doFetch(url, {
|
|
32789
|
+
method: "POST",
|
|
32790
|
+
headers: { "Content-Type": "application/json" },
|
|
32791
|
+
body: JSON.stringify(payload),
|
|
32792
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
32793
|
+
});
|
|
32794
|
+
const body = JSON.parse(await res.text());
|
|
32795
|
+
const adopted = acceptReportUrl(body?.reportUrl);
|
|
32796
|
+
if (adopted) nextUrl = adopted;
|
|
32797
|
+
} catch {
|
|
32798
|
+
}
|
|
32799
|
+
writeReportState(stateDir, { ...counters, reportUrl: nextUrl });
|
|
32800
|
+
}
|
|
32801
|
+
function schedule(delay) {
|
|
32802
|
+
if (stopped) return;
|
|
32803
|
+
timer = setTimeout(() => {
|
|
32804
|
+
void beatOnce().finally(() => schedule(intervalMs));
|
|
32805
|
+
}, delay);
|
|
32806
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
32807
|
+
}
|
|
32808
|
+
schedule(initialDelayMs);
|
|
32809
|
+
return {
|
|
32810
|
+
stop() {
|
|
32811
|
+
stopped = true;
|
|
32812
|
+
if (timer) {
|
|
32813
|
+
clearTimeout(timer);
|
|
32814
|
+
timer = null;
|
|
32815
|
+
}
|
|
32816
|
+
},
|
|
32817
|
+
async beatNow() {
|
|
32818
|
+
await beatOnce();
|
|
32819
|
+
}
|
|
32820
|
+
};
|
|
32821
|
+
}
|
|
32822
|
+
|
|
32110
32823
|
// src/engine-startup.ts
|
|
32111
32824
|
init_provider_factory();
|
|
32112
32825
|
|
|
@@ -33277,7 +33990,7 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
33277
33990
|
import * as crypto4 from "crypto";
|
|
33278
33991
|
import * as fs38 from "fs";
|
|
33279
33992
|
import * as path38 from "path";
|
|
33280
|
-
import * as
|
|
33993
|
+
import * as os4 from "os";
|
|
33281
33994
|
import { promises as dns } from "dns";
|
|
33282
33995
|
function sleep3(ms) {
|
|
33283
33996
|
return new Promise((r) => setTimeout(r, ms));
|
|
@@ -33660,7 +34373,7 @@ var WechatAdapter = class {
|
|
|
33660
34373
|
this.config = config2;
|
|
33661
34374
|
this.baseUrl = config2.baseUrl?.replace(/\/$/, "") || ILINK_BASE_URL;
|
|
33662
34375
|
this.cdnBaseUrl = config2.cdnBaseUrl?.replace(/\/$/, "") || WEIXIN_CDN_BASE_URL;
|
|
33663
|
-
this.stateDir = config2.stateDir || path38.join(
|
|
34376
|
+
this.stateDir = config2.stateDir || path38.join(os4.homedir?.() || "/tmp", ".engine7");
|
|
33664
34377
|
if (!fs38.existsSync(this.stateDir)) fs38.mkdirSync(this.stateDir, { recursive: true });
|
|
33665
34378
|
}
|
|
33666
34379
|
// --- ChannelAdapter interface ---
|
|
@@ -34006,7 +34719,7 @@ var WechatAdapter = class {
|
|
|
34006
34719
|
mediaRef.full_url,
|
|
34007
34720
|
timeoutSec
|
|
34008
34721
|
);
|
|
34009
|
-
const tmpDir = path38.join(
|
|
34722
|
+
const tmpDir = path38.join(os4.tmpdir(), "weixin-media");
|
|
34010
34723
|
if (!fs38.existsSync(tmpDir)) fs38.mkdirSync(tmpDir, { recursive: true });
|
|
34011
34724
|
const tmpPath = path38.join(tmpDir, `${crypto4.randomUUID().replace(/-/g, "")}-${filename}`);
|
|
34012
34725
|
fs38.writeFileSync(tmpPath, data);
|
|
@@ -34100,6 +34813,7 @@ import { WebSocketServer, WebSocket } from "ws";
|
|
|
34100
34813
|
|
|
34101
34814
|
// src/tools/my-voice.ts
|
|
34102
34815
|
init_live();
|
|
34816
|
+
init_well_formed();
|
|
34103
34817
|
init_registry();
|
|
34104
34818
|
|
|
34105
34819
|
// src/tools/opus-encoder.ts
|
|
@@ -34267,10 +34981,10 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
34267
34981
|
import { promisify as promisify2 } from "node:util";
|
|
34268
34982
|
import * as fs41 from "node:fs";
|
|
34269
34983
|
import * as path40 from "node:path";
|
|
34270
|
-
import * as
|
|
34984
|
+
import * as os5 from "node:os";
|
|
34271
34985
|
import net4 from "net";
|
|
34272
34986
|
var execFileAsync2 = promisify2(execFile2);
|
|
34273
|
-
var VOICE_DIR = path40.join(
|
|
34987
|
+
var VOICE_DIR = path40.join(os5.tmpdir(), "engine-voice");
|
|
34274
34988
|
var _pyCache = null;
|
|
34275
34989
|
function resolvePython(configuredPath) {
|
|
34276
34990
|
if (configuredPath) {
|
|
@@ -34458,7 +35172,7 @@ async function ttsQwen3Local(text, cfg, onChunk) {
|
|
|
34458
35172
|
const res = await fetch(`${url}/tts`, {
|
|
34459
35173
|
method: "POST",
|
|
34460
35174
|
headers: { "Content-Type": "application/json" },
|
|
34461
|
-
body:
|
|
35175
|
+
body: safeStringify({
|
|
34462
35176
|
text,
|
|
34463
35177
|
voice: cfg.voice || "xiaoya",
|
|
34464
35178
|
lang: cfg.lang || "Chinese"
|
|
@@ -34489,7 +35203,7 @@ async function ttsQwen3Opus(text, cfg) {
|
|
|
34489
35203
|
const res = await fetch(`${url}/tts?format=opus`, {
|
|
34490
35204
|
method: "POST",
|
|
34491
35205
|
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
34492
|
-
body:
|
|
35206
|
+
body: safeStringify({
|
|
34493
35207
|
text,
|
|
34494
35208
|
voice: cfg.voice || "xiaoya",
|
|
34495
35209
|
lang: cfg.lang || "Chinese"
|
|
@@ -34914,10 +35628,10 @@ var DeskBuddyAdapter = class {
|
|
|
34914
35628
|
appendHistory(entry) {
|
|
34915
35629
|
try {
|
|
34916
35630
|
if (!this.historyFile) {
|
|
34917
|
-
const
|
|
35631
|
+
const os6 = __require("os");
|
|
34918
35632
|
const fs57 = __require("fs");
|
|
34919
35633
|
const path58 = __require("path");
|
|
34920
|
-
const dir = path58.join(
|
|
35634
|
+
const dir = path58.join(os6.homedir(), "xiaowen", "workspace", ".desktopBuddy");
|
|
34921
35635
|
fs57.mkdirSync(dir, { recursive: true });
|
|
34922
35636
|
this.historyFile = path58.join(dir, "history.json");
|
|
34923
35637
|
this._fs = fs57;
|
|
@@ -37243,6 +37957,7 @@ var MessageDispatcher = class {
|
|
|
37243
37957
|
displayText = imgCount > 0 ? `${txt} [\u56FE\u7247\xD7${imgCount}]`.trim() : txt;
|
|
37244
37958
|
}
|
|
37245
37959
|
console.log(`[dispatcher] Executing (session=${msg2.sessionId}, source=${msg2.source}, gen=${generation}): ${displayText.slice(0, 200).replace(/\n/g, " ")}`);
|
|
37960
|
+
noteUserMessage(msg2.source);
|
|
37246
37961
|
try {
|
|
37247
37962
|
await handleQuery(
|
|
37248
37963
|
msg2.text,
|
|
@@ -37967,6 +38682,7 @@ Examples:
|
|
|
37967
38682
|
// src/tools/my-eyes.ts
|
|
37968
38683
|
init_live();
|
|
37969
38684
|
init_registry();
|
|
38685
|
+
init_well_formed();
|
|
37970
38686
|
import * as fs46 from "node:fs";
|
|
37971
38687
|
import * as path45 from "node:path";
|
|
37972
38688
|
var MIME_MAP = {
|
|
@@ -37977,19 +38693,21 @@ var MIME_MAP = {
|
|
|
37977
38693
|
".gif": "gif",
|
|
37978
38694
|
".bmp": "bmp"
|
|
37979
38695
|
};
|
|
38696
|
+
var VIDEO_EXT = /\.(mp4|mov)$/i;
|
|
38697
|
+
var VIDEO_MAX_BYTES = 40 * 1024 * 1024;
|
|
37980
38698
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
37981
38699
|
if (specifiedPath && fs46.existsSync(specifiedPath)) return specifiedPath;
|
|
37982
38700
|
if (!fs46.existsSync(mediaDir)) return null;
|
|
37983
|
-
const files = fs46.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs46.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
38701
|
+
const files = fs46.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs46.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
37984
38702
|
return files[0]?.p || null;
|
|
37985
38703
|
}
|
|
37986
38704
|
registry.register({
|
|
37987
38705
|
name: "my_eyes",
|
|
37988
|
-
description: "\u26A0\uFE0F IRON RULE: when you receive an image you MUST use my_eyes to look at it! DO NOT use read to look at images (can't see details). Use my_eyes for: \u6536\u5230\u56FE\u7247/\u770B\u56FE/\u5206\u6790\u56FE\u7247/\u7167\u7247/\u622A\u56FE/\u4EFB\u4F55\u9700\u8981\u5206\u6790\u56FE\u7247\u7684\u573A\u666F.",
|
|
38706
|
+
description: "\u26A0\uFE0F IRON RULE: when you receive an image you MUST use my_eyes to look at it! DO NOT use read to look at images (can't see details). Use my_eyes for: \u6536\u5230\u56FE\u7247/\u770B\u56FE/\u5206\u6790\u56FE\u7247/\u7167\u7247/\u622A\u56FE/\u4EFB\u4F55\u9700\u8981\u5206\u6790\u56FE\u7247\u7684\u573A\u666F. \u89C6\u9891\u4E5F\u652F\u6301\uFF1A\u6536\u5230\u89C6\u9891/\u770B\u89C6\u9891/\u5206\u6790\u89C6\u9891\u65F6\u4F20 .mp4/.mov \u8DEF\u5F84\uFF08M3 video_url\uFF09.",
|
|
37989
38707
|
schema: {
|
|
37990
38708
|
type: "object",
|
|
37991
38709
|
properties: {
|
|
37992
|
-
image_path: { type: "string", description: "Absolute path to the image file to analyze." },
|
|
38710
|
+
image_path: { type: "string", description: "Absolute path to the image (or .mp4/.mov video) file to analyze." },
|
|
37993
38711
|
prompt: { type: "string", description: 'What to ask about the image. Default: "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9"' }
|
|
37994
38712
|
},
|
|
37995
38713
|
required: ["image_path"]
|
|
@@ -38018,6 +38736,42 @@ registry.register({
|
|
|
38018
38736
|
}
|
|
38019
38737
|
const rawPrompt = args2.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
38020
38738
|
const ext = path45.extname(imagePath).toLowerCase();
|
|
38739
|
+
if (VIDEO_EXT.test(imagePath)) {
|
|
38740
|
+
const stat4 = fs46.statSync(imagePath);
|
|
38741
|
+
if (stat4.size > VIDEO_MAX_BYTES) {
|
|
38742
|
+
return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
|
|
38743
|
+
}
|
|
38744
|
+
const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
|
|
38745
|
+
const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
|
|
38746
|
+
const vidB64 = fs46.readFileSync(imagePath).toString("base64");
|
|
38747
|
+
try {
|
|
38748
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
38749
|
+
method: "POST",
|
|
38750
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${providerCfg.apiKey}` },
|
|
38751
|
+
body: safeStringify({
|
|
38752
|
+
model: modelId,
|
|
38753
|
+
messages: [{
|
|
38754
|
+
role: "user",
|
|
38755
|
+
content: [
|
|
38756
|
+
{ type: "video_url", video_url: { url: `data:video/mp4;base64,${vidB64}` } },
|
|
38757
|
+
{ type: "text", text: rawPrompt }
|
|
38758
|
+
]
|
|
38759
|
+
}],
|
|
38760
|
+
max_tokens: 4096
|
|
38761
|
+
})
|
|
38762
|
+
});
|
|
38763
|
+
if (!res.ok) {
|
|
38764
|
+
const t = await res.text();
|
|
38765
|
+
return { content: `Video analysis failed: HTTP ${res.status} ${t.slice(0, 300)}`, isError: true };
|
|
38766
|
+
}
|
|
38767
|
+
const data = await res.json();
|
|
38768
|
+
const content = data.choices?.[0]?.message?.content || "";
|
|
38769
|
+
if (!content.trim()) return { content: "Video analysis returned empty.", isError: true };
|
|
38770
|
+
return { content };
|
|
38771
|
+
} catch (e) {
|
|
38772
|
+
return { content: `Video analysis failed: ${e.message}`, isError: true };
|
|
38773
|
+
}
|
|
38774
|
+
}
|
|
38021
38775
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
38022
38776
|
const imgB64 = fs46.readFileSync(imagePath).toString("base64");
|
|
38023
38777
|
const userMsg = {
|
|
@@ -38054,6 +38808,7 @@ registry.register({
|
|
|
38054
38808
|
// src/tools/my-selfie.ts
|
|
38055
38809
|
init_live();
|
|
38056
38810
|
init_registry();
|
|
38811
|
+
init_well_formed();
|
|
38057
38812
|
import * as fs47 from "node:fs";
|
|
38058
38813
|
import * as path46 from "node:path";
|
|
38059
38814
|
function getProxyDispatcher2() {
|
|
@@ -38067,11 +38822,46 @@ function getProxyDispatcher2() {
|
|
|
38067
38822
|
return void 0;
|
|
38068
38823
|
}
|
|
38069
38824
|
}
|
|
38825
|
+
function dimsOf(p) {
|
|
38826
|
+
try {
|
|
38827
|
+
const b = fs47.readFileSync(p);
|
|
38828
|
+
let w = 0, h = 0;
|
|
38829
|
+
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
38830
|
+
w = b.readUInt32BE(16);
|
|
38831
|
+
h = b.readUInt32BE(20);
|
|
38832
|
+
} else if (b[0] === 255 && b[1] === 216) {
|
|
38833
|
+
let i = 2;
|
|
38834
|
+
while (i < b.length - 9) {
|
|
38835
|
+
if (b[i] !== 255) {
|
|
38836
|
+
i++;
|
|
38837
|
+
continue;
|
|
38838
|
+
}
|
|
38839
|
+
const m = b[i + 1];
|
|
38840
|
+
if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) {
|
|
38841
|
+
h = b.readUInt16BE(i + 5);
|
|
38842
|
+
w = b.readUInt16BE(i + 7);
|
|
38843
|
+
break;
|
|
38844
|
+
}
|
|
38845
|
+
i += 2 + b.readUInt16BE(i + 2);
|
|
38846
|
+
}
|
|
38847
|
+
} else {
|
|
38848
|
+
return null;
|
|
38849
|
+
}
|
|
38850
|
+
if (!w || !h) return null;
|
|
38851
|
+
const bad = [];
|
|
38852
|
+
if (w < 256 || w > 4096) bad.push(`\u5BBD ${w} \u4E0D\u5728 256~4096`);
|
|
38853
|
+
if (h < 256 || h > 4096) bad.push(`\u9AD8 ${h} \u4E0D\u5728 256~4096`);
|
|
38854
|
+
const r = Math.max(w, h) / Math.min(w, h);
|
|
38855
|
+
if (r > 2) bad.push(`\u957F\u5BBD\u6BD4 ${r.toFixed(2)}:1 \u8D85\u8FC7 2:1`);
|
|
38856
|
+
return bad.length ? `${w}x${h} \u4E0D\u5408\u683C\uFF1A${bad.join("\uFF1B")}` : `${w}x${h}`;
|
|
38857
|
+
} catch {
|
|
38858
|
+
return null;
|
|
38859
|
+
}
|
|
38860
|
+
}
|
|
38070
38861
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
38071
38862
|
var DEFAULT_RESOLUTION = "1k";
|
|
38072
|
-
var FAL_KEY_FALLBACK = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
38073
38863
|
function getFalKey(cfg) {
|
|
38074
|
-
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey ||
|
|
38864
|
+
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey || process.env.FAL_API_KEY || "";
|
|
38075
38865
|
}
|
|
38076
38866
|
var DEFAULT_REFERENCES = [
|
|
38077
38867
|
{ name: "default", p: "images/xiaomei_clean_v2.png" },
|
|
@@ -38097,6 +38887,73 @@ function getXaiKey(ctx) {
|
|
|
38097
38887
|
const xaiCfg = cfg.providers?.xai || cfg.models?.providers?.xai;
|
|
38098
38888
|
return xaiCfg?.apiKey || "";
|
|
38099
38889
|
}
|
|
38890
|
+
function getRelayCfg(cfg) {
|
|
38891
|
+
const id = cfg?.my_selfie?.relayProviderId || cfg?.tools?.my_selfie?.relayProviderId || "7yi";
|
|
38892
|
+
const p = cfg?.models?.providers?.[id] || cfg?.providers?.[id];
|
|
38893
|
+
return { baseUrl: String(p?.baseUrl || "").replace(/\/+$/, ""), apiKey: p?.apiKey || "" };
|
|
38894
|
+
}
|
|
38895
|
+
function resolvePortraitDesc(argPortrait, cfgPortrait) {
|
|
38896
|
+
const fromArg = typeof argPortrait === "string" ? argPortrait.trim() : "";
|
|
38897
|
+
if (fromArg) return fromArg;
|
|
38898
|
+
return typeof cfgPortrait === "string" ? cfgPortrait.trim() : "";
|
|
38899
|
+
}
|
|
38900
|
+
async function generateWithRelay(imageB64, prompt, mime, baseUrl, apiKey, model) {
|
|
38901
|
+
const res = await fetch(`${baseUrl}/images/edits`, {
|
|
38902
|
+
method: "POST",
|
|
38903
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
38904
|
+
// safeStringify 不是安全选项而是必须:prompt 里一个孤立代理(半个 emoji)
|
|
38905
|
+
// 就会让上游 500。同批上游提交刚在 fal/grok/comfy 三处修过同一个坑。
|
|
38906
|
+
body: safeStringify({
|
|
38907
|
+
model,
|
|
38908
|
+
prompt,
|
|
38909
|
+
n: 1,
|
|
38910
|
+
response_format: "b64_json",
|
|
38911
|
+
images: [{ image_url: `data:${mime};base64,${imageB64}` }]
|
|
38912
|
+
})
|
|
38913
|
+
});
|
|
38914
|
+
if (!res.ok) {
|
|
38915
|
+
const text = await res.text();
|
|
38916
|
+
throw new Error(`relay ${res.status}: ${text.slice(0, 300)}`);
|
|
38917
|
+
}
|
|
38918
|
+
const data = await res.json();
|
|
38919
|
+
if (!data.data?.length) throw new Error("No images returned from relay");
|
|
38920
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
38921
|
+
}
|
|
38922
|
+
var PORTRAIT_PROMPT = (desc) => `A portrait photo of a person, ${desc}. Front-facing, head and shoulders, neutral background, natural lighting, photorealistic, sharp focus on the face, looking at the camera.`;
|
|
38923
|
+
async function generatePortraitWithRelay(desc, baseUrl, apiKey, model) {
|
|
38924
|
+
const res = await fetch(`${baseUrl}/images/generations`, {
|
|
38925
|
+
method: "POST",
|
|
38926
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
38927
|
+
// 同上:描述里可能带 emoji,孤立代理会让上游 500
|
|
38928
|
+
body: safeStringify({
|
|
38929
|
+
model,
|
|
38930
|
+
prompt: PORTRAIT_PROMPT(desc),
|
|
38931
|
+
n: 1,
|
|
38932
|
+
size: "1024x1024",
|
|
38933
|
+
response_format: "b64_json"
|
|
38934
|
+
})
|
|
38935
|
+
});
|
|
38936
|
+
if (!res.ok) {
|
|
38937
|
+
const text = await res.text();
|
|
38938
|
+
throw new Error(`relay portrait ${res.status}: ${text.slice(0, 300)}`);
|
|
38939
|
+
}
|
|
38940
|
+
const data = await res.json();
|
|
38941
|
+
if (!data.data?.length) throw new Error("No images returned from relay (portrait)");
|
|
38942
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
38943
|
+
}
|
|
38944
|
+
function registerPortrait(relPath, name) {
|
|
38945
|
+
try {
|
|
38946
|
+
const cfg = liveConfig.all();
|
|
38947
|
+
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
38948
|
+
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
38949
|
+
next.unshift({ name, path: relPath });
|
|
38950
|
+
const path58 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
38951
|
+
void liveConfig.set(path58, next);
|
|
38952
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path58}[0] = ${relPath}`);
|
|
38953
|
+
} catch (e) {
|
|
38954
|
+
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
38955
|
+
}
|
|
38956
|
+
}
|
|
38100
38957
|
var MODE_PROMPTS2 = {
|
|
38101
38958
|
mirror: (ctx) => `make a pic of this person, but ${ctx}. the person is taking a mirror selfie in front of a mirror, holding a phone`,
|
|
38102
38959
|
direct: (ctx) => `a close-up selfie taken by herself ${ctx}, direct eye contact with the camera, looking straight into the lens, eyes centered and clearly visible, not a mirror selfie, phone held at arm's length, face fully visible`,
|
|
@@ -38153,10 +39010,16 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
38153
39010
|
resolution
|
|
38154
39011
|
};
|
|
38155
39012
|
if (aspectRatio) body.aspect_ratio = aspectRatio;
|
|
39013
|
+
const falKey = getFalKey(cfg);
|
|
39014
|
+
if (!falKey) {
|
|
39015
|
+
throw new Error(
|
|
39016
|
+
`fal key \u672A\u914D\u7F6E\u3002\u8BBE config \u7684 providers.fal.apiKey \u6216 my_selfie.falKey\uFF0C\u6216\u5728 secrets .env\uFF08~/.engine7-secrets/<config\u540D>.env\uFF09\u91CC\u52A0 FAL_API_KEY\u3002`
|
|
39017
|
+
);
|
|
39018
|
+
}
|
|
38156
39019
|
const res = await fetch(FAL_ENDPOINT, {
|
|
38157
39020
|
method: "POST",
|
|
38158
|
-
headers: { "Authorization": `Key ${
|
|
38159
|
-
body:
|
|
39021
|
+
headers: { "Authorization": `Key ${falKey}`, "Content-Type": "application/json" },
|
|
39022
|
+
body: safeStringify(body)
|
|
38160
39023
|
});
|
|
38161
39024
|
if (!res.ok) {
|
|
38162
39025
|
const text = await res.text();
|
|
@@ -38193,7 +39056,7 @@ async function comfyQueue(server, workflow) {
|
|
|
38193
39056
|
const res = await fetch(`${server}/prompt`, {
|
|
38194
39057
|
method: "POST",
|
|
38195
39058
|
headers: { "Content-Type": "application/json" },
|
|
38196
|
-
body:
|
|
39059
|
+
body: safeStringify({ prompt: workflow })
|
|
38197
39060
|
});
|
|
38198
39061
|
if (!res.ok) return null;
|
|
38199
39062
|
const data = await res.json();
|
|
@@ -38321,7 +39184,7 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
38321
39184
|
"Authorization": `Bearer ${xaiKey}`,
|
|
38322
39185
|
"Content-Type": "application/json"
|
|
38323
39186
|
},
|
|
38324
|
-
body:
|
|
39187
|
+
body: safeStringify({
|
|
38325
39188
|
model: "grok-imagine-image-quality",
|
|
38326
39189
|
prompt,
|
|
38327
39190
|
image: {
|
|
@@ -38343,15 +39206,17 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
38343
39206
|
}
|
|
38344
39207
|
registry.register({
|
|
38345
39208
|
name: "my_selfie",
|
|
38346
|
-
description: "Generate consistent-character selfies. Use for: \u81EA\u62CD/\u62CD\u7167/\u53D1\u81EA\u62CD/\u770B\u770B\u4F60/\u60F3\u770B\u4F60/\u62CD\u5F20\u7167/\u6765\u5F20\u81EA\u62CD/\u53D1\u5F20\u7167\u7247/\u7ED9\u6211\u770B\u770B\u4F60/\u6362\u4E2A\u59FF\u52BF/\u5C55\u793A\u4E00\u4E0B. Supports modes: mirror (\u955C\u5B50\u81EA\u62CD), direct (\u6B63\u9762\u81EA\u62CD), lifestyle (\u751F\u6D3B\u7167). Auto-detects mode. Reference images configured per agent via my_selfie.references in config.",
|
|
39209
|
+
description: "Generate consistent-character selfies. Use for: \u81EA\u62CD/\u62CD\u7167/\u53D1\u81EA\u62CD/\u770B\u770B\u4F60/\u60F3\u770B\u4F60/\u62CD\u5F20\u7167/\u6765\u5F20\u81EA\u62CD/\u53D1\u5F20\u7167\u7247/\u7ED9\u6211\u770B\u770B\u4F60/\u6362\u4E2A\u59FF\u52BF/\u5C55\u793A\u4E00\u4E0B. Supports modes: mirror (\u955C\u5B50\u81EA\u62CD), direct (\u6B63\u9762\u81EA\u62CD), lifestyle (\u751F\u6D3B\u7167). Auto-detects mode. Reference images configured per agent via my_selfie.references in config. \u7B2C\u4E00\u6B21\u8C03\u7528\u65F6\u82E5\u8FD9\u4E2A\u52A9\u624B\u8FD8\u6CA1\u6709\u5F62\u8C61\uFF08\u6CA1\u6709\u53C2\u8003\u56FE\uFF09\uFF0C\u5DE5\u5177\u4F1A\u8FD4\u56DE\u4E00\u6BB5\u63D0\u793A\u800C\u4E0D\u662F\u62A5\u9519\u2014\u2014\u5148\u95EE\u95EE\u60F3\u8BA9\u4F60\u957F\u4EC0\u4E48\u6837\uFF0C\u62FF\u5230\u63CF\u8FF0\u540E\u7528 portrait \u53C2\u6570\u518D\u8C03\u4E00\u6B21\uFF0C\u90A3\u6B21\u4F1A\u628A\u5F62\u8C61\u56FA\u5B9A\u4E0B\u6765\u3002",
|
|
38347
39210
|
schema: {
|
|
38348
39211
|
type: "object",
|
|
38349
39212
|
properties: {
|
|
38350
39213
|
prompt: { type: "string", description: "Scene/outfit/mood description, e.g. '\u5728\u5496\u5561\u9986\u770B\u4E66' or '\u7A7F\u7EA2\u88D9\u5B50'." },
|
|
38351
39214
|
mode: { type: "string", enum: ["auto", "mirror", "direct", "lifestyle"], description: "Selfie mode. 'auto' detects from prompt (default)." },
|
|
38352
39215
|
reference: { type: "string", description: "Reference base image name (e.g. 'default', 'v3'). Configurable per agent via my_selfie.references in config." },
|
|
38353
|
-
provider: { type: "string", enum: ["fal", "grok", "autodl"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'. 'autodl' uses ComfyUI (txt2img or img2img)." },
|
|
38354
|
-
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." }
|
|
39216
|
+
provider: { type: "string", enum: ["fal", "grok", "autodl", "relay"], description: "Image generation provider. Default from config (my_selfie.provider), fallback 'fal'. 'autodl' uses ComfyUI (txt2img or img2img). 'relay' goes through the 7yi relay station (SenseNova U1.5, needs the reference image)." },
|
|
39217
|
+
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." },
|
|
39218
|
+
portrait: { type: "string", description: "\u9996\u6B21\u5851\u9020\u5F62\u8C61\u7528\uFF1A\u8FD9\u4E2A\u52A9\u624B\u957F\u4EC0\u4E48\u6837\u7684\u63CF\u8FF0\uFF08\u6027\u522B/\u5E74\u9F84/\u53D1\u578B/\u6C14\u8D28\uFF09\uFF0C\u4F8B\u5982 '\u77ED\u53D1\u5973\u751F\uFF0C25\u5C81\uFF0C\u7B11\u8D77\u6765\u6709\u9152\u7A9D'\u3002\u53EA\u5728\u8FD8\u6CA1\u6709\u53C2\u8003\u56FE\u65F6\u9700\u8981\uFF0C\u89C1\u4E0B\u65B9\u8BF4\u660E\u3002" },
|
|
39219
|
+
referenceImage: { type: "string", description: "\u76F4\u63A5\u62FF\u4E00\u5F20\u73B0\u6210\u7684\u56FE\u5F53\u5F62\u8C61\uFF08\u6BD4\u5982\u5BF9\u65B9\u53D1\u6765\u7684\u7167\u7247\uFF09\u3002\u4F20\u56FE\u7247\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF1A\u4F1A\u88AB\u590D\u5236\u8FDB workspace \u5E76\u767B\u8BB0\u4E3A\u53C2\u8003\u56FE\uFF0C\u4E4B\u540E\u4E00\u76F4\u7528\u5B83\u3002\u4F20\u4E86\u5C31\u4EE5\u5B83\u4E3A\u51C6\u3002" }
|
|
38355
39220
|
},
|
|
38356
39221
|
required: ["prompt"]
|
|
38357
39222
|
},
|
|
@@ -38365,9 +39230,61 @@ registry.register({
|
|
|
38365
39230
|
const REFERENCES = getReferences(ctx);
|
|
38366
39231
|
const refName = args2.reference || "default";
|
|
38367
39232
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
38368
|
-
|
|
39233
|
+
let refPath = path46.join(ctx.workspace, refEntry.p);
|
|
39234
|
+
const adoptImg = args2.referenceImage?.trim();
|
|
39235
|
+
if (adoptImg) {
|
|
39236
|
+
if (!fs47.existsSync(adoptImg)) {
|
|
39237
|
+
return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
|
|
39238
|
+
}
|
|
39239
|
+
const acheck = dimsOf(adoptImg);
|
|
39240
|
+
if (acheck?.includes("\u4E0D\u5408\u683C")) {
|
|
39241
|
+
return {
|
|
39242
|
+
content: `\u8FD9\u5F20\u56FE\u7528\u4E0D\u4E86\uFF1A${acheck}\u3002
|
|
39243
|
+
\u6362\u4E00\u5F20\u6B63\u9762\u3001\u5934\u80A9\u3001\u65B9\u5F62\u6216\u63A5\u8FD1\u65B9\u5F62\u7684\u7167\u7247\uFF08\u5546\u6C64\u90A3\u8FB9\u8981\u6C42\u5BBD\u9AD8\u90FD\u5728 256~4096 \u50CF\u7D20\u3001\u957F\u5BBD\u6BD4\u4E0D\u8D85\u8FC7 2:1\uFF09\u3002`,
|
|
39244
|
+
isError: true
|
|
39245
|
+
};
|
|
39246
|
+
}
|
|
39247
|
+
const adir = path46.join(ctx.workspace, "images");
|
|
39248
|
+
if (!fs47.existsSync(adir)) fs47.mkdirSync(adir, { recursive: true });
|
|
39249
|
+
const aext = (path46.extname(adoptImg) || ".png").toLowerCase();
|
|
39250
|
+
const afname = `portrait_${Date.now()}${aext}`;
|
|
39251
|
+
fs47.copyFileSync(adoptImg, path46.join(adir, afname));
|
|
39252
|
+
registerPortrait(`images/${afname}`, refName);
|
|
39253
|
+
refPath = path46.join(adir, afname);
|
|
39254
|
+
console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
|
|
39255
|
+
}
|
|
38369
39256
|
if (provider !== "autodl" && !fs47.existsSync(refPath)) {
|
|
38370
|
-
|
|
39257
|
+
const cfgAll = liveConfig.all();
|
|
39258
|
+
const portraitDesc = resolvePortraitDesc(
|
|
39259
|
+
args2.portrait,
|
|
39260
|
+
cfgAll?.tools?.my_selfie?.portraitDesc ?? cfgAll?.my_selfie?.portraitDesc
|
|
39261
|
+
);
|
|
39262
|
+
if (!portraitDesc) {
|
|
39263
|
+
return {
|
|
39264
|
+
content: `\u8FD9\u4E2A\u52A9\u624B\u8FD8\u6CA1\u6709\u5F62\u8C61\uFF0C\u53C2\u8003\u56FE\u4E0D\u5B58\u5728\uFF08\u627E\u7684\u662F ${refEntry.p}\uFF09\u3002
|
|
39265
|
+
\u5148\u95EE\u95EE\u60F3\u8BA9\u4F60\u957F\u4EC0\u4E48\u6837\uFF1A\u51E0\u53E5\u8BDD\u63CF\u8FF0\uFF08\u6027\u522B\u3001\u5E74\u9F84\u3001\u53D1\u578B\u3001\u6C14\u8D28\uFF09\uFF0C\u6216\u8005\u76F4\u63A5\u53D1\u4E00\u5F20\u7167\u7247\u8FC7\u6765\u3002
|
|
39266
|
+
\u62FF\u5230\u63CF\u8FF0\u540E\uFF0C\u5E26\u7740\u5B83\u518D\u8C03\u4E00\u6B21 my_selfie\uFF0C\u53C2\u6570\u91CC\u52A0 portrait="<\u90A3\u6BB5\u63CF\u8FF0>"\uFF1B\u90A3\u6B21\u4F1A\u987A\u5E26\u628A\u5F62\u8C61\u56FA\u5B9A\u4E0B\u6765\uFF0C\u4EE5\u540E\u81EA\u62CD\u5C31\u4E0D\u7528\u518D\u7ED9\u4E86\u3002`
|
|
39267
|
+
};
|
|
39268
|
+
}
|
|
39269
|
+
const relay = getRelayCfg(cfgAll);
|
|
39270
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
39271
|
+
return { content: "Error: relay provider \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u5851\u9020\u5F62\u8C61\u3002", isError: true };
|
|
39272
|
+
}
|
|
39273
|
+
const pmodel = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
39274
|
+
let portraitBuf;
|
|
39275
|
+
try {
|
|
39276
|
+
console.log(`[my-selfie] \u51B7\u542F\uFF1A\u6309\u63CF\u8FF0\u5851\u9020\u5F62\u8C61 -> ${pmodel}`);
|
|
39277
|
+
portraitBuf = await generatePortraitWithRelay(portraitDesc, relay.baseUrl, relay.apiKey, pmodel);
|
|
39278
|
+
} catch (e) {
|
|
39279
|
+
return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
|
|
39280
|
+
}
|
|
39281
|
+
const pdir = path46.join(ctx.workspace, "images");
|
|
39282
|
+
if (!fs47.existsSync(pdir)) fs47.mkdirSync(pdir, { recursive: true });
|
|
39283
|
+
const pfname = `portrait_${Date.now()}.png`;
|
|
39284
|
+
fs47.writeFileSync(path46.join(pdir, pfname), portraitBuf);
|
|
39285
|
+
registerPortrait(`images/${pfname}`, refName);
|
|
39286
|
+
refPath = path46.join(pdir, pfname);
|
|
39287
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
|
|
38371
39288
|
}
|
|
38372
39289
|
const resolution = args2.resolution || DEFAULT_RESOLUTION;
|
|
38373
39290
|
const refB64 = fs47.existsSync(refPath) ? fs47.readFileSync(refPath).toString("base64") : "";
|
|
@@ -38379,6 +39296,16 @@ registry.register({
|
|
|
38379
39296
|
const xaiKey = getXaiKey(ctx);
|
|
38380
39297
|
if (!xaiKey) return { content: "Error: xai apiKey not configured for grok provider", isError: true };
|
|
38381
39298
|
imageBuffer = await generateWithGrok(refB64, fullPrompt, xaiKey);
|
|
39299
|
+
} else if (provider === "relay") {
|
|
39300
|
+
const cfgAll = liveConfig.all();
|
|
39301
|
+
const relay = getRelayCfg(cfgAll);
|
|
39302
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
39303
|
+
return { content: `Error: relay provider \u672A\u914D\u7F6E\u3002config \u91CC\u8981\u6709 models.providers.${cfgAll?.my_selfie?.relayProviderId || "7yi"} \u7684 baseUrl \u548C apiKey\u3002`, isError: true };
|
|
39304
|
+
}
|
|
39305
|
+
const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
39306
|
+
const ext = path46.extname(refPath).toLowerCase();
|
|
39307
|
+
const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
39308
|
+
imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
|
|
38382
39309
|
} else {
|
|
38383
39310
|
imageBuffer = await generateWithFal(refB64, fullPrompt, resolution);
|
|
38384
39311
|
}
|
|
@@ -38765,7 +39692,7 @@ registry.register({
|
|
|
38765
39692
|
const ref = String(args2?.ref || "");
|
|
38766
39693
|
if (!stepId || !ref || !["aim", "task", "calendar"].includes(to)) return { content: "Usage: goal_step bind --stepId <s> --to aim|task|calendar --ref <id>", isError: true };
|
|
38767
39694
|
const kind = to === "aim" ? "aim" : to === "task" ? "remote_task" : "calendar";
|
|
38768
|
-
const g =
|
|
39695
|
+
const g = to === "calendar" ? bindGoalCalendar(ws, goal.id, stepId, Number(ref)) : bindGoalStep(ws, goal.id, stepId, kind, ref);
|
|
38769
39696
|
return { content: `\u6B65\u9AA4 ${stepId} \u5DF2\u59D4\u6D3E ${kind}=${ref}\uFF08\u5B8C\u6210\u4E8B\u4EF6\u81EA\u52A8\u56DE\u6D41\u89E3\u9501\uFF0C\u4E0D\u7528\u76EF\uFF09
|
|
38770
39697
|
\u5F53\u524D\u6B65\u9AA4: ${g.steps[g.currentStep]?.title ?? "\u2014"}` };
|
|
38771
39698
|
}
|
|
@@ -38798,6 +39725,7 @@ ${lines.join("\n")}` };
|
|
|
38798
39725
|
// src/tools/service.ts
|
|
38799
39726
|
init_registry();
|
|
38800
39727
|
init_live();
|
|
39728
|
+
init_clock();
|
|
38801
39729
|
import { exec as exec3, spawn as spawn7 } from "node:child_process";
|
|
38802
39730
|
import { promisify as promisify3 } from "node:util";
|
|
38803
39731
|
var execAsync = promisify3(exec3);
|
|
@@ -38815,7 +39743,7 @@ async function runCmd(cmd, timeoutMs = 15e3) {
|
|
|
38815
39743
|
function spawnDetached(cmd) {
|
|
38816
39744
|
const isWin = process.platform === "win32";
|
|
38817
39745
|
const shell = isWin ? "cmd.exe" : "/bin/sh";
|
|
38818
|
-
const logFile = `D:/xiaoke/logs/service-${(
|
|
39746
|
+
const logFile = `D:/xiaoke/logs/service-${todayKey().replace(/-/g, "")}.log`;
|
|
38819
39747
|
const wrappedCmd = isWin ? `/c "${cmd.replace(/"/g, '\\"')} > "${logFile}" 2>&1"` : `-c "${cmd} > '${logFile}' 2>&1"`;
|
|
38820
39748
|
const child = spawn7(shell, isWin ? [wrappedCmd] : ["-c", `${cmd} > '${logFile}' 2>&1`], {
|
|
38821
39749
|
detached: true,
|
|
@@ -38878,7 +39806,7 @@ registry.register({
|
|
|
38878
39806
|
const startCmd = svcArgs ? `${svc.start} ${svcArgs}` : svc.start;
|
|
38879
39807
|
if (longRunning) {
|
|
38880
39808
|
const isWin = process.platform === "win32";
|
|
38881
|
-
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${(
|
|
39809
|
+
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${todayKey().replace(/-/g, "")}.log`;
|
|
38882
39810
|
const redirect = isWin ? ` >> "${logFile}" 2>&1` : ` >> "${logFile}" 2>&1`;
|
|
38883
39811
|
const pid = spawnDetached(startCmd + redirect);
|
|
38884
39812
|
lines.push(`start: spawned PID ${pid} (background${svcArgs ? ", args=" + svcArgs : ""})`);
|
|
@@ -40306,7 +41234,7 @@ ${err.stack}`);
|
|
|
40306
41234
|
// src/commands/model.ts
|
|
40307
41235
|
import * as fs54 from "node:fs";
|
|
40308
41236
|
import * as path54 from "node:path";
|
|
40309
|
-
import { fileURLToPath as
|
|
41237
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
40310
41238
|
function collectModels(config2) {
|
|
40311
41239
|
const aliases = config2.modelAliases || {};
|
|
40312
41240
|
const aliasByRef = /* @__PURE__ */ new Map();
|
|
@@ -40338,7 +41266,7 @@ function resolveConfigWritePath(config2, label) {
|
|
|
40338
41266
|
if (!configPath2) return void 0;
|
|
40339
41267
|
let writePath = configPath2;
|
|
40340
41268
|
if (configPath2 && !fs54.existsSync(configPath2)) {
|
|
40341
|
-
const __pFile =
|
|
41269
|
+
const __pFile = fileURLToPath8(import.meta.url);
|
|
40342
41270
|
const __pDir = path54.dirname(__pFile);
|
|
40343
41271
|
const altPath = path54.join(path54.resolve(__pDir, "../configs"), path54.basename(configPath2));
|
|
40344
41272
|
if (fs54.existsSync(altPath)) {
|
|
@@ -41431,6 +42359,33 @@ async function startEngine(config2, opts) {
|
|
|
41431
42359
|
// 常开 entry,stop 不触发
|
|
41432
42360
|
});
|
|
41433
42361
|
}
|
|
42362
|
+
{
|
|
42363
|
+
let reportingHandle;
|
|
42364
|
+
const stopReporting = () => {
|
|
42365
|
+
try {
|
|
42366
|
+
reportingHandle?.stop();
|
|
42367
|
+
} catch {
|
|
42368
|
+
}
|
|
42369
|
+
reportingHandle = void 0;
|
|
42370
|
+
};
|
|
42371
|
+
const startReportingNow = () => {
|
|
42372
|
+
stopReporting();
|
|
42373
|
+
reportingHandle = startReporting({ stateDir: config2.stateDir });
|
|
42374
|
+
};
|
|
42375
|
+
const reportingEnabled = () => liveConfig.get("reporting.enabled") !== false;
|
|
42376
|
+
reloadOrchestrator.register({
|
|
42377
|
+
name: "reporting",
|
|
42378
|
+
// url 也进 key:运维手改地址要能热生效,不用重启
|
|
42379
|
+
select: (cfg) => ({ enabled: cfg.reporting?.enabled !== false, url: cfg.reporting?.url ?? "" }),
|
|
42380
|
+
start: async () => {
|
|
42381
|
+
startReportingNow();
|
|
42382
|
+
},
|
|
42383
|
+
stop: async () => {
|
|
42384
|
+
stopReporting();
|
|
42385
|
+
}
|
|
42386
|
+
}, reportingEnabled());
|
|
42387
|
+
if (reportingEnabled()) startReportingNow();
|
|
42388
|
+
}
|
|
41434
42389
|
const shutdownPromise = new Promise((resolve12) => {
|
|
41435
42390
|
const shutdown = (signal) => {
|
|
41436
42391
|
console.log(`[engine] ${signal} received, shutting down...`);
|
|
@@ -41473,11 +42428,11 @@ async function startEngine(config2, opts) {
|
|
|
41473
42428
|
}
|
|
41474
42429
|
|
|
41475
42430
|
// src/main.ts
|
|
41476
|
-
import { readFileSync as
|
|
41477
|
-
import { fileURLToPath as
|
|
41478
|
-
import { dirname as dirname16, join as
|
|
41479
|
-
var __dirname2 = dirname16(
|
|
41480
|
-
var pkg = JSON.parse(
|
|
42431
|
+
import { readFileSync as readFileSync47 } from "node:fs";
|
|
42432
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
42433
|
+
import { dirname as dirname16, join as join63 } from "node:path";
|
|
42434
|
+
var __dirname2 = dirname16(fileURLToPath9(import.meta.url));
|
|
42435
|
+
var pkg = JSON.parse(readFileSync47(join63(__dirname2, "..", "package.json"), "utf-8"));
|
|
41481
42436
|
var epipeSeen = false;
|
|
41482
42437
|
process.on("uncaughtException", (err) => {
|
|
41483
42438
|
const code = err?.code ?? "";
|