engine7 7.1.84 → 7.1.94
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 +1556 -528
- package/dist/engine-startup.mjs +1280 -550
- package/dist/main.mjs +1305 -572
- 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/cli.mjs
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
const require=(await import('node:module')).createRequire(import.meta.url)
|
|
2
|
-
const __origEmitWarning = process.emitWarning.bind(process);
|
|
3
|
-
process.emitWarning = (w, ...r) => {
|
|
4
|
-
const name = typeof w === 'object' ? w?.name : r?.[0];
|
|
5
|
-
const msg = typeof w === 'object' ? String(w?.message) : String(w);
|
|
6
|
-
if (name === 'ExperimentalWarning' && msg.includes('SQLite')) return;
|
|
7
|
-
return __origEmitWarning(w, ...r);
|
|
8
|
-
};
|
|
9
|
-
|
|
10
2
|
var __defProp = Object.defineProperty;
|
|
11
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -356,83 +348,6 @@ var init_cli_events = __esm({
|
|
|
356
348
|
}
|
|
357
349
|
});
|
|
358
350
|
|
|
359
|
-
// src/config/secrets-env.ts
|
|
360
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
|
|
361
|
-
import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
|
|
362
|
-
function loadSecretsEnv(stateDir, configName) {
|
|
363
|
-
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
364
|
-
const candidates = [
|
|
365
|
-
join3(home, ".engine7-secrets", `${configName}.env`),
|
|
366
|
-
join3(stateDir, "configs", `.env.${configName}`),
|
|
367
|
-
join3(stateDir, "configs", ".env")
|
|
368
|
-
];
|
|
369
|
-
for (const p of candidates) {
|
|
370
|
-
if (!existsSync3(p)) continue;
|
|
371
|
-
for (const line of readFileSync3(p, "utf-8").split("\n")) {
|
|
372
|
-
const t = line.trim();
|
|
373
|
-
if (!t || t.startsWith("#")) continue;
|
|
374
|
-
const eq = t.indexOf("=");
|
|
375
|
-
if (eq <= 0) continue;
|
|
376
|
-
const k = t.slice(0, eq).trim();
|
|
377
|
-
const v = t.slice(eq + 1).trim();
|
|
378
|
-
const cur = process.env[k];
|
|
379
|
-
if (k && (cur === void 0 || cur === "")) process.env[k] = v;
|
|
380
|
-
}
|
|
381
|
-
return p;
|
|
382
|
-
}
|
|
383
|
-
return null;
|
|
384
|
-
}
|
|
385
|
-
var init_secrets_env = __esm({
|
|
386
|
-
"src/config/secrets-env.ts"() {
|
|
387
|
-
"use strict";
|
|
388
|
-
}
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// src/config/display.ts
|
|
392
|
-
function loadDisplayConfig(raw) {
|
|
393
|
-
if (!raw) return { ...DEFAULT_DISPLAY };
|
|
394
|
-
return {
|
|
395
|
-
thinking: {
|
|
396
|
-
...DEFAULT_DISPLAY.thinking,
|
|
397
|
-
...raw.thinking || {}
|
|
398
|
-
},
|
|
399
|
-
toolUse: {
|
|
400
|
-
...DEFAULT_DISPLAY.toolUse,
|
|
401
|
-
...raw.toolUse || {}
|
|
402
|
-
},
|
|
403
|
-
toolResult: {
|
|
404
|
-
...DEFAULT_DISPLAY.toolResult,
|
|
405
|
-
...raw.toolResult || {}
|
|
406
|
-
},
|
|
407
|
-
reactions: {
|
|
408
|
-
...DEFAULT_DISPLAY.reactions,
|
|
409
|
-
...raw.reactions || {}
|
|
410
|
-
},
|
|
411
|
-
preview: {
|
|
412
|
-
...DEFAULT_DISPLAY.preview,
|
|
413
|
-
...raw.preview || {}
|
|
414
|
-
},
|
|
415
|
-
typing: {
|
|
416
|
-
...DEFAULT_DISPLAY.typing,
|
|
417
|
-
...raw.typing || {}
|
|
418
|
-
}
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
var DEFAULT_DISPLAY;
|
|
422
|
-
var init_display = __esm({
|
|
423
|
-
"src/config/display.ts"() {
|
|
424
|
-
"use strict";
|
|
425
|
-
DEFAULT_DISPLAY = {
|
|
426
|
-
thinking: { enabled: true, emoji: "\u{1F4AD}", maxLen: 300 },
|
|
427
|
-
toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "description", displayLimits: { write: 200 } },
|
|
428
|
-
toolResult: { enabled: true, emoji: "\u{1F4E4}" },
|
|
429
|
-
reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
|
|
430
|
-
preview: { enabled: true, agentName: "Agent" },
|
|
431
|
-
typing: { enabled: true }
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
});
|
|
435
|
-
|
|
436
351
|
// src/config/live.ts
|
|
437
352
|
var live_exports = {};
|
|
438
353
|
__export(live_exports, {
|
|
@@ -530,6 +445,287 @@ var init_live = __esm({
|
|
|
530
445
|
}
|
|
531
446
|
});
|
|
532
447
|
|
|
448
|
+
// src/time/clock.ts
|
|
449
|
+
function detectHostTimezone() {
|
|
450
|
+
try {
|
|
451
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
452
|
+
} catch {
|
|
453
|
+
return "UTC";
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
function isValidTimezone(name) {
|
|
457
|
+
if (!name) return false;
|
|
458
|
+
try {
|
|
459
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
460
|
+
return true;
|
|
461
|
+
} catch {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function tz() {
|
|
466
|
+
if (testTimezone) return testTimezone;
|
|
467
|
+
const v = liveConfig.get("timezone");
|
|
468
|
+
const name = typeof v === "string" && v ? v : "UTC";
|
|
469
|
+
if (validatedZones.has(name)) return name;
|
|
470
|
+
if (isValidTimezone(name)) {
|
|
471
|
+
validatedZones.add(name);
|
|
472
|
+
return name;
|
|
473
|
+
}
|
|
474
|
+
warnOnce("tz", "invalid-zone", name);
|
|
475
|
+
return "UTC";
|
|
476
|
+
}
|
|
477
|
+
function partsFmt(zone) {
|
|
478
|
+
let f = fmtCache.get(zone);
|
|
479
|
+
if (!f) {
|
|
480
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
481
|
+
timeZone: zone,
|
|
482
|
+
year: "numeric",
|
|
483
|
+
month: "2-digit",
|
|
484
|
+
day: "2-digit",
|
|
485
|
+
hour: "2-digit",
|
|
486
|
+
minute: "2-digit",
|
|
487
|
+
second: "2-digit",
|
|
488
|
+
hourCycle: "h23",
|
|
489
|
+
weekday: "short"
|
|
490
|
+
});
|
|
491
|
+
fmtCache.set(zone, f);
|
|
492
|
+
}
|
|
493
|
+
return f;
|
|
494
|
+
}
|
|
495
|
+
function asDate(i) {
|
|
496
|
+
return i instanceof Date ? i : new Date(i);
|
|
497
|
+
}
|
|
498
|
+
function wallParts(i = /* @__PURE__ */ new Date()) {
|
|
499
|
+
const d = asDate(i);
|
|
500
|
+
const parts = partsFmt(tz()).formatToParts(d);
|
|
501
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
502
|
+
return {
|
|
503
|
+
y: Number(get("year")),
|
|
504
|
+
mo: Number(get("month")),
|
|
505
|
+
d: Number(get("day")),
|
|
506
|
+
hh: Number(get("hour")),
|
|
507
|
+
mm: Number(get("minute")),
|
|
508
|
+
ss: Number(get("second")),
|
|
509
|
+
weekday: EN_WEEKDAY[get("weekday")] ?? 0
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function weekdayZh(i) {
|
|
513
|
+
return WEEKDAY_ZH[wallParts(i).weekday];
|
|
514
|
+
}
|
|
515
|
+
function nowUtc() {
|
|
516
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
517
|
+
}
|
|
518
|
+
function toUtc(d) {
|
|
519
|
+
return d.toISOString();
|
|
520
|
+
}
|
|
521
|
+
function toWall(i = /* @__PURE__ */ new Date()) {
|
|
522
|
+
const p = wallParts(i);
|
|
523
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${p2(p.hh)}:${p2(p.mm)}:${p2(p.ss)}`;
|
|
524
|
+
}
|
|
525
|
+
function formatForLlm(i) {
|
|
526
|
+
const d = i ?? /* @__PURE__ */ new Date();
|
|
527
|
+
const p = wallParts(d);
|
|
528
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${WEEKDAY_ZH[p.weekday]} ${p2(p.hh)}:${p2(p.mm)}\uFF08${tz()}\uFF09`;
|
|
529
|
+
}
|
|
530
|
+
function formatLog(i) {
|
|
531
|
+
return toWall(i ?? /* @__PURE__ */ new Date());
|
|
532
|
+
}
|
|
533
|
+
function warnOnce(callSite, shape, sample) {
|
|
534
|
+
const key = `${callSite}:${shape}`;
|
|
535
|
+
if (warned.has(key)) return;
|
|
536
|
+
warned.add(key);
|
|
537
|
+
console.warn(`[clock] ${callSite} \u5951\u7EA6\u8FDD\u89C4(${shape})\uFF0C\u6837\u672C: ${String(sample).slice(0, 80)}`);
|
|
538
|
+
}
|
|
539
|
+
function zoneOffsetMs(d) {
|
|
540
|
+
const p = wallParts(d);
|
|
541
|
+
const asUtc = Date.UTC(p.y, p.mo - 1, p.d, p.hh, p.mm, p.ss);
|
|
542
|
+
return asUtc - Math.floor(d.getTime() / 1e3) * 1e3;
|
|
543
|
+
}
|
|
544
|
+
function wallToUtc(y, mo, d, hh, mm, ss) {
|
|
545
|
+
const guess = Date.UTC(y, mo - 1, d, hh, mm, ss);
|
|
546
|
+
const off1 = zoneOffsetMs(new Date(guess));
|
|
547
|
+
const ms1 = guess - off1;
|
|
548
|
+
const off2 = zoneOffsetMs(new Date(ms1));
|
|
549
|
+
const ms = off2 === off1 ? ms1 : guess - off2;
|
|
550
|
+
return new Date(ms).toISOString();
|
|
551
|
+
}
|
|
552
|
+
function parseWall(s) {
|
|
553
|
+
const raw = String(s ?? "").trim();
|
|
554
|
+
if (!raw) return "";
|
|
555
|
+
if (/Z$/i.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
|
|
556
|
+
const ms2 = Date.parse(raw);
|
|
557
|
+
if (Number.isNaN(ms2)) {
|
|
558
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
559
|
+
return raw;
|
|
560
|
+
}
|
|
561
|
+
warnOnce("parseWall", "has-offset", raw);
|
|
562
|
+
return new Date(ms2).toISOString();
|
|
563
|
+
}
|
|
564
|
+
const m = WALL_RE.exec(raw);
|
|
565
|
+
if (!m) {
|
|
566
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
567
|
+
return raw;
|
|
568
|
+
}
|
|
569
|
+
const tail = raw.slice(m[0].length);
|
|
570
|
+
if (tail && !/^\.\d+$/.test(tail)) {
|
|
571
|
+
warnOnce("parseWall", "unparseable-tail", raw);
|
|
572
|
+
return raw;
|
|
573
|
+
}
|
|
574
|
+
const [Y, Mo, D, H, Mi, S] = [+m[1], +m[2], +m[3], +m[4], +m[5], m[6] ? +m[6] : 0];
|
|
575
|
+
if (Y < 1e3 || Y > 9999 || Mo < 1 || Mo > 12 || D < 1 || D > daysInMonth(Y, Mo) || H > 23 || Mi > 59 || S > 59) {
|
|
576
|
+
warnOnce("parseWall", "out-of-range", raw);
|
|
577
|
+
return raw;
|
|
578
|
+
}
|
|
579
|
+
const ms = tail ? Math.round(Number(`0${tail}`) * 1e3) : 0;
|
|
580
|
+
return withMs(wallToUtc(Y, Mo, D, H, Mi, S), ms);
|
|
581
|
+
}
|
|
582
|
+
function daysInMonth(y, mo) {
|
|
583
|
+
return new Date(Date.UTC(y, mo, 0)).getUTCDate();
|
|
584
|
+
}
|
|
585
|
+
function withMs(base, ms) {
|
|
586
|
+
if (!ms) return base;
|
|
587
|
+
const t = Date.parse(base);
|
|
588
|
+
return Number.isNaN(t) ? base : new Date(t + ms).toISOString();
|
|
589
|
+
}
|
|
590
|
+
function localOffsetSuffix(at) {
|
|
591
|
+
const total = Math.round(zoneOffsetMs(at ? asDate(at) : /* @__PURE__ */ new Date()) / 6e4);
|
|
592
|
+
const sign = total >= 0 ? "+" : "-";
|
|
593
|
+
const abs = Math.abs(total);
|
|
594
|
+
return `${sign}${p2(Math.floor(abs / 60))}:${p2(abs % 60)}`;
|
|
595
|
+
}
|
|
596
|
+
function startOfLocalDay(i) {
|
|
597
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
598
|
+
return wallToUtc(p.y, p.mo, p.d, 0, 0, 0);
|
|
599
|
+
}
|
|
600
|
+
function todayKey(i) {
|
|
601
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
602
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)}`;
|
|
603
|
+
}
|
|
604
|
+
var WEEKDAY_ZH, EN_WEEKDAY, testTimezone, validatedZones, fmtCache, p2, warned, WALL_RE;
|
|
605
|
+
var init_clock = __esm({
|
|
606
|
+
"src/time/clock.ts"() {
|
|
607
|
+
"use strict";
|
|
608
|
+
init_live();
|
|
609
|
+
WEEKDAY_ZH = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
610
|
+
EN_WEEKDAY = {
|
|
611
|
+
Sun: 0,
|
|
612
|
+
Mon: 1,
|
|
613
|
+
Tue: 2,
|
|
614
|
+
Wed: 3,
|
|
615
|
+
Thu: 4,
|
|
616
|
+
Fri: 5,
|
|
617
|
+
Sat: 6
|
|
618
|
+
};
|
|
619
|
+
testTimezone = null;
|
|
620
|
+
validatedZones = /* @__PURE__ */ new Set();
|
|
621
|
+
fmtCache = /* @__PURE__ */ new Map();
|
|
622
|
+
p2 = (n) => String(n).padStart(2, "0");
|
|
623
|
+
warned = /* @__PURE__ */ new Set();
|
|
624
|
+
WALL_RE = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?/;
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// src/config/secrets-env.ts
|
|
629
|
+
import { chmodSync, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
630
|
+
import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
|
|
631
|
+
function secretsHome() {
|
|
632
|
+
return process.env.HOME || process.env.USERPROFILE || ".";
|
|
633
|
+
}
|
|
634
|
+
function loadSecretsEnv(stateDir, configName) {
|
|
635
|
+
const home = secretsHome();
|
|
636
|
+
const candidates = [
|
|
637
|
+
join3(home, ".engine7-secrets", `${configName}.env`),
|
|
638
|
+
join3(stateDir, "configs", `.env.${configName}`),
|
|
639
|
+
join3(stateDir, "configs", ".env")
|
|
640
|
+
];
|
|
641
|
+
for (const p of candidates) {
|
|
642
|
+
if (!existsSync3(p)) continue;
|
|
643
|
+
for (const line of readFileSync3(p, "utf-8").split("\n")) {
|
|
644
|
+
const t = line.trim();
|
|
645
|
+
if (!t || t.startsWith("#")) continue;
|
|
646
|
+
const eq = t.indexOf("=");
|
|
647
|
+
if (eq <= 0) continue;
|
|
648
|
+
const k = t.slice(0, eq).trim();
|
|
649
|
+
const v = t.slice(eq + 1).trim();
|
|
650
|
+
const cur = process.env[k];
|
|
651
|
+
if (k && (cur === void 0 || cur === "")) process.env[k] = v;
|
|
652
|
+
}
|
|
653
|
+
return p;
|
|
654
|
+
}
|
|
655
|
+
return null;
|
|
656
|
+
}
|
|
657
|
+
function writeSecretsValue(configPath, envKey, value) {
|
|
658
|
+
const configName = basename2(configPath, ".json");
|
|
659
|
+
const dir = join3(secretsHome(), ".engine7-secrets");
|
|
660
|
+
mkdirSync2(dir, { recursive: true });
|
|
661
|
+
const file2 = join3(dir, `${configName}.env`);
|
|
662
|
+
const lines = [];
|
|
663
|
+
if (existsSync3(file2)) {
|
|
664
|
+
for (const line of readFileSync3(file2, "utf8").split(/\r?\n/)) {
|
|
665
|
+
if (new RegExp(`^\\s*${envKey}\\s*=`).test(line)) continue;
|
|
666
|
+
lines.push(line);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
while (lines.length && lines[lines.length - 1] === "") lines.pop();
|
|
670
|
+
lines.push(`${envKey}=${value}`);
|
|
671
|
+
writeFileSync2(file2, lines.join("\n") + "\n", { mode: 384 });
|
|
672
|
+
try {
|
|
673
|
+
chmodSync(file2, 384);
|
|
674
|
+
} catch {
|
|
675
|
+
}
|
|
676
|
+
return file2;
|
|
677
|
+
}
|
|
678
|
+
var init_secrets_env = __esm({
|
|
679
|
+
"src/config/secrets-env.ts"() {
|
|
680
|
+
"use strict";
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
// src/config/display.ts
|
|
685
|
+
function loadDisplayConfig(raw) {
|
|
686
|
+
if (!raw) return { ...DEFAULT_DISPLAY };
|
|
687
|
+
return {
|
|
688
|
+
thinking: {
|
|
689
|
+
...DEFAULT_DISPLAY.thinking,
|
|
690
|
+
...raw.thinking || {}
|
|
691
|
+
},
|
|
692
|
+
toolUse: {
|
|
693
|
+
...DEFAULT_DISPLAY.toolUse,
|
|
694
|
+
...raw.toolUse || {}
|
|
695
|
+
},
|
|
696
|
+
toolResult: {
|
|
697
|
+
...DEFAULT_DISPLAY.toolResult,
|
|
698
|
+
...raw.toolResult || {}
|
|
699
|
+
},
|
|
700
|
+
reactions: {
|
|
701
|
+
...DEFAULT_DISPLAY.reactions,
|
|
702
|
+
...raw.reactions || {}
|
|
703
|
+
},
|
|
704
|
+
preview: {
|
|
705
|
+
...DEFAULT_DISPLAY.preview,
|
|
706
|
+
...raw.preview || {}
|
|
707
|
+
},
|
|
708
|
+
typing: {
|
|
709
|
+
...DEFAULT_DISPLAY.typing,
|
|
710
|
+
...raw.typing || {}
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
var DEFAULT_DISPLAY;
|
|
715
|
+
var init_display = __esm({
|
|
716
|
+
"src/config/display.ts"() {
|
|
717
|
+
"use strict";
|
|
718
|
+
DEFAULT_DISPLAY = {
|
|
719
|
+
thinking: { enabled: true, emoji: "\u{1F4AD}", maxLen: 300 },
|
|
720
|
+
toolUse: { enabled: true, emoji: "\u{1F527}", displayMode: "raw", bashDisplayMode: "description", displayLimits: { write: 200 } },
|
|
721
|
+
toolResult: { enabled: true, emoji: "\u{1F4E4}" },
|
|
722
|
+
reactions: { enabled: true, start: "\u{1F440}", done: "\u2705", error: "\u274C" },
|
|
723
|
+
preview: { enabled: true, agentName: "Agent" },
|
|
724
|
+
typing: { enabled: true }
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
|
|
533
729
|
// src/config/features.ts
|
|
534
730
|
function getFeature(key) {
|
|
535
731
|
const v = liveConfig.get(`agents.defaults.features.${key}`);
|
|
@@ -570,6 +766,7 @@ __export(loader_exports, {
|
|
|
570
766
|
configSummary: () => configSummary,
|
|
571
767
|
loadConfig: () => loadConfig,
|
|
572
768
|
parseModelRef: () => parseModelRef,
|
|
769
|
+
resolveTimezone: () => resolveTimezone,
|
|
573
770
|
takeLoadWarnings: () => takeLoadWarnings
|
|
574
771
|
});
|
|
575
772
|
import * as fs3 from "node:fs";
|
|
@@ -591,6 +788,24 @@ function warnAtLoad(msg2) {
|
|
|
591
788
|
if (!loadWarningsFlushed) loadWarnings.push(msg2);
|
|
592
789
|
console.warn(msg2);
|
|
593
790
|
}
|
|
791
|
+
function resolveTimezone(raw) {
|
|
792
|
+
const fallback = () => {
|
|
793
|
+
try {
|
|
794
|
+
return detectHostTimezone();
|
|
795
|
+
} catch {
|
|
796
|
+
return "UTC";
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
const v = raw?.timezone;
|
|
800
|
+
if (typeof v !== "string" || !v) return fallback();
|
|
801
|
+
try {
|
|
802
|
+
new Intl.DateTimeFormat("en-US", { timeZone: v });
|
|
803
|
+
return v;
|
|
804
|
+
} catch {
|
|
805
|
+
warnAtLoad(`[config] timezone "${v}" \u4E0D\u662F\u5408\u6CD5 IANA \u65F6\u533A\u540D\uFF0C\u9000\u56DE\u672C\u673A\u63A2\u6D4B`);
|
|
806
|
+
return fallback();
|
|
807
|
+
}
|
|
808
|
+
}
|
|
594
809
|
function loadConfig(configPath) {
|
|
595
810
|
const resolvedPath = path3.resolve(
|
|
596
811
|
configPath || process.env.ENGINE_CONFIG || "engine-config.json"
|
|
@@ -602,7 +817,7 @@ function loadConfig(configPath) {
|
|
|
602
817
|
const missingEnvKeys = /* @__PURE__ */ new Set();
|
|
603
818
|
const resolveEnv = (node) => {
|
|
604
819
|
if (typeof node === "string") {
|
|
605
|
-
const m = node.match(/^(?:env:|{{\s*)([A-Z][A-Z0-9_]
|
|
820
|
+
const m = node.match(/^(?:env:|{{\s*)(?=[A-Z0-9_]*[A-Z])([A-Z0-9_]+)(?:\s*}})?$/);
|
|
606
821
|
if (m) {
|
|
607
822
|
if (process.env[m[1]] !== void 0) return process.env[m[1]];
|
|
608
823
|
if (node.startsWith("env:")) missingEnvKeys.add(m[1]);
|
|
@@ -727,11 +942,18 @@ function loadConfig(configPath) {
|
|
|
727
942
|
...rawInnerVoice,
|
|
728
943
|
enabled: true
|
|
729
944
|
} : void 0;
|
|
945
|
+
const resolvedTimezone = resolveTimezone(raw);
|
|
730
946
|
const rawCron = raw.cron || {};
|
|
731
947
|
const cron = rawCron.enabled ? {
|
|
732
948
|
...rawCron,
|
|
733
949
|
enabled: true,
|
|
734
|
-
|
|
950
|
+
// ★ 这里原来写死 'Asia/Shanghai'。而 cron/tasks.ts 建任务时是
|
|
951
|
+
// `params.timezone || params.defaultTimezone || 'Asia/Shanghai'` ——
|
|
952
|
+
// defaultTimezone 恒有值 ⇒ 兜底永远轮不到 ⇒ **工具建的每个任务都被
|
|
953
|
+
// 显式写死北京时区存进 tasks.json**,顶层 timezone 配置形同虚设
|
|
954
|
+
// (马耳他用户说"每天 9 点",会在当地凌晨 3 点触发)。
|
|
955
|
+
// 改为跟随引擎时区;per-task 的显式 timezone 仍然优先。
|
|
956
|
+
defaultTimezone: rawCron.defaultTimezone || resolvedTimezone,
|
|
735
957
|
tickIntervalMs: rawCron.tickIntervalMs || 1e4,
|
|
736
958
|
maxConsecutiveFailures: rawCron.maxConsecutiveFailures || 5,
|
|
737
959
|
retry: {
|
|
@@ -744,6 +966,7 @@ function loadConfig(configPath) {
|
|
|
744
966
|
return {
|
|
745
967
|
...raw,
|
|
746
968
|
// 先展开 raw:未显式处理的字段(如 webhook)自动进来,避免每次加 config 都要手动挑;下面显式字段覆盖 raw(解析值优先)
|
|
969
|
+
timezone: resolvedTimezone,
|
|
747
970
|
provider,
|
|
748
971
|
providers,
|
|
749
972
|
// 所有 provider 配置字典(供 memory/provider 动态切换用)
|
|
@@ -812,6 +1035,7 @@ var init_loader = __esm({
|
|
|
812
1035
|
"use strict";
|
|
813
1036
|
init_display();
|
|
814
1037
|
init_features();
|
|
1038
|
+
init_clock();
|
|
815
1039
|
loadWarnings = [];
|
|
816
1040
|
loadWarningsFlushed = false;
|
|
817
1041
|
}
|
|
@@ -972,6 +1196,9 @@ function openDb(workspace) {
|
|
|
972
1196
|
day TEXT,
|
|
973
1197
|
time TEXT,
|
|
974
1198
|
date_str TEXT,
|
|
1199
|
+
-- \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
|
|
1200
|
+
-- 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
|
|
1201
|
+
-- 0920 \u8D77\u5199\u5165\u6B63\u786E UTC Z\u3002\u8BFB\u8FD9\u4E09\u5217\u524D\u5148\u786E\u8BA4\u4F60\u8981\u7684\u662F\u4EC0\u4E48\u3002
|
|
975
1202
|
created_at TEXT NOT NULL,
|
|
976
1203
|
done_at TEXT,
|
|
977
1204
|
archived_at TEXT
|
|
@@ -1014,7 +1241,7 @@ function migrate(db2) {
|
|
|
1014
1241
|
db2.exec("ALTER TABLE events ADD COLUMN doc_path TEXT");
|
|
1015
1242
|
}
|
|
1016
1243
|
}
|
|
1017
|
-
var _sqlite, DatabaseSync
|
|
1244
|
+
var _sqlite, DatabaseSync;
|
|
1018
1245
|
var init_db = __esm({
|
|
1019
1246
|
"src/calendar/db.ts"() {
|
|
1020
1247
|
"use strict";
|
|
@@ -1028,7 +1255,6 @@ var init_db = __esm({
|
|
|
1028
1255
|
return getSqlite().DatabaseSync[prop];
|
|
1029
1256
|
}
|
|
1030
1257
|
});
|
|
1031
|
-
TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
1032
1258
|
}
|
|
1033
1259
|
});
|
|
1034
1260
|
|
|
@@ -1638,6 +1864,9 @@ If you can say it in one sentence, don't use three. Prefer short, direct sentenc
|
|
|
1638
1864
|
}
|
|
1639
1865
|
function getEnvInfoSection(workspace) {
|
|
1640
1866
|
const envItems = [
|
|
1867
|
+
// CC 上游本来就有日期项,移植时漏了 —— 补回来。
|
|
1868
|
+
// 本段在 dynamic prompt(每轮重建),日期一天只变一次,不伤 prefix cache。
|
|
1869
|
+
`Today's date: ${todayKey()} (${tz()})`,
|
|
1641
1870
|
`Primary working directory: ${workspace}`,
|
|
1642
1871
|
`Is directory a git repo: ${false}`,
|
|
1643
1872
|
`Platform: ${os2.platform()}`,
|
|
@@ -1751,8 +1980,8 @@ function buildActiveGoalsSection(stateDir) {
|
|
|
1751
1980
|
if (!active.length) return void 0;
|
|
1752
1981
|
const events = listGoalEvents(stateDir);
|
|
1753
1982
|
const fmt = (iso) => {
|
|
1754
|
-
const
|
|
1755
|
-
return Number.isNaN(
|
|
1983
|
+
const p = wallParts(iso);
|
|
1984
|
+
return Number.isNaN(Date.parse(iso)) ? "?" : `${p.mo}/${p.d} ${String(p.hh).padStart(2, "0")}:${String(p.mm).padStart(2, "0")}`;
|
|
1756
1985
|
};
|
|
1757
1986
|
const summary = active.map((g) => {
|
|
1758
1987
|
const step = g.steps[g.currentStep];
|
|
@@ -1793,10 +2022,8 @@ ${skillsListing}`);
|
|
|
1793
2022
|
return parts.join("\n\n");
|
|
1794
2023
|
}
|
|
1795
2024
|
function buildVolatileRuntimeContext() {
|
|
1796
|
-
const now = /* @__PURE__ */ new Date();
|
|
1797
|
-
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
1798
2025
|
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
1799
|
-
\u5F53\u524D\u65F6\u95F4: ${
|
|
2026
|
+
\u5F53\u524D\u65F6\u95F4: ${formatForLlm()}`;
|
|
1800
2027
|
}
|
|
1801
2028
|
function formatSkillsListingForPrompt() {
|
|
1802
2029
|
const tools = registry.list();
|
|
@@ -1815,6 +2042,7 @@ var init_prompt = __esm({
|
|
|
1815
2042
|
"use strict";
|
|
1816
2043
|
init_registry();
|
|
1817
2044
|
init_store();
|
|
2045
|
+
init_clock();
|
|
1818
2046
|
init_memdir();
|
|
1819
2047
|
init_paths();
|
|
1820
2048
|
BASH_TOOL_NAME = "exec";
|
|
@@ -1900,16 +2128,18 @@ function shouldRetry(status, errorBody) {
|
|
|
1900
2128
|
if (status === 529) return true;
|
|
1901
2129
|
if (status === 429) {
|
|
1902
2130
|
const body = errorBody?.toLowerCase() || "";
|
|
1903
|
-
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")) {
|
|
2131
|
+
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")) {
|
|
1904
2132
|
return false;
|
|
1905
2133
|
}
|
|
1906
2134
|
return true;
|
|
1907
2135
|
}
|
|
1908
2136
|
if (status === 408) return true;
|
|
1909
2137
|
if (status === 409) return true;
|
|
1910
|
-
if (status
|
|
1911
|
-
|
|
1912
|
-
|
|
2138
|
+
if (status >= 500) {
|
|
2139
|
+
const body = errorBody?.toLowerCase() || "";
|
|
2140
|
+
if (body.includes("\u65E0\u53EF\u7528\u6E20\u9053") || body.includes("new_sensitive")) return false;
|
|
2141
|
+
return true;
|
|
2142
|
+
}
|
|
1913
2143
|
if (errorBody?.includes('"type":"overloaded_error"')) return true;
|
|
1914
2144
|
return false;
|
|
1915
2145
|
}
|
|
@@ -2146,6 +2376,29 @@ var init_think_stripper = __esm({
|
|
|
2146
2376
|
}
|
|
2147
2377
|
});
|
|
2148
2378
|
|
|
2379
|
+
// src/utils/well-formed.ts
|
|
2380
|
+
function truncateByCodePoint(text, max) {
|
|
2381
|
+
if (text.length <= max) return text;
|
|
2382
|
+
return Array.from(text).slice(0, max).join("");
|
|
2383
|
+
}
|
|
2384
|
+
function toWellFormed(s) {
|
|
2385
|
+
if (HAS_NATIVE) return s.toWellFormed();
|
|
2386
|
+
return s.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
2387
|
+
}
|
|
2388
|
+
function wellFormedReplacer(_key, value) {
|
|
2389
|
+
return typeof value === "string" ? toWellFormed(value) : value;
|
|
2390
|
+
}
|
|
2391
|
+
function safeStringify(body) {
|
|
2392
|
+
return JSON.stringify(body, wellFormedReplacer);
|
|
2393
|
+
}
|
|
2394
|
+
var HAS_NATIVE;
|
|
2395
|
+
var init_well_formed = __esm({
|
|
2396
|
+
"src/utils/well-formed.ts"() {
|
|
2397
|
+
"use strict";
|
|
2398
|
+
HAS_NATIVE = typeof String.prototype.toWellFormed === "function";
|
|
2399
|
+
}
|
|
2400
|
+
});
|
|
2401
|
+
|
|
2149
2402
|
// src/models/openai-provider.ts
|
|
2150
2403
|
function sanitizeToolCallForSend(tc) {
|
|
2151
2404
|
const args = tc.function?.arguments;
|
|
@@ -2179,6 +2432,7 @@ var init_openai_provider = __esm({
|
|
|
2179
2432
|
init_withRetry();
|
|
2180
2433
|
init_provider();
|
|
2181
2434
|
init_think_stripper();
|
|
2435
|
+
init_well_formed();
|
|
2182
2436
|
OpenAIProvider = class {
|
|
2183
2437
|
// 0831 <think> 剥离改用 ThinkTagStripper 的**局部**实例(见 streamChat)——
|
|
2184
2438
|
// 原来是实例字段,跨请求共享状态,流中途 abort 会把 active=true 带到下一个请求
|
|
@@ -2267,13 +2521,17 @@ var init_openai_provider = __esm({
|
|
|
2267
2521
|
}
|
|
2268
2522
|
const actualThinking = body.chat_template_kwargs;
|
|
2269
2523
|
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}`);
|
|
2524
|
+
let bodyStr = safeStringify(body);
|
|
2525
|
+
if (this.config.wafSingleQuoteWorkaround) {
|
|
2526
|
+
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
2527
|
+
}
|
|
2270
2528
|
const retryGen = fetchWithRetry(url, {
|
|
2271
2529
|
method: "POST",
|
|
2272
2530
|
headers: {
|
|
2273
2531
|
"Content-Type": "application/json",
|
|
2274
2532
|
"Authorization": `Bearer ${this.config.apiKey}`
|
|
2275
2533
|
},
|
|
2276
|
-
body:
|
|
2534
|
+
body: bodyStr,
|
|
2277
2535
|
signal: params.signal
|
|
2278
2536
|
}, "openai", this.config.proxy);
|
|
2279
2537
|
let response;
|
|
@@ -2471,7 +2729,7 @@ function adaptUsage(u) {
|
|
|
2471
2729
|
});
|
|
2472
2730
|
}
|
|
2473
2731
|
function serializeBody(body, wafSingleQuoteWorkaround) {
|
|
2474
|
-
let bodyStr =
|
|
2732
|
+
let bodyStr = safeStringify(body);
|
|
2475
2733
|
if (wafSingleQuoteWorkaround) {
|
|
2476
2734
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
2477
2735
|
}
|
|
@@ -2498,6 +2756,7 @@ var init_openai_responses_provider = __esm({
|
|
|
2498
2756
|
"use strict";
|
|
2499
2757
|
init_withRetry();
|
|
2500
2758
|
init_provider();
|
|
2759
|
+
init_well_formed();
|
|
2501
2760
|
CODEX_CLI_VERSION = "0.154.0";
|
|
2502
2761
|
CODEX_TERMINAL_TOKEN = "xterm-256color";
|
|
2503
2762
|
OpenAIResponsesProvider = class {
|
|
@@ -2818,12 +3077,14 @@ var init_anthropic_provider = __esm({
|
|
|
2818
3077
|
init_withRetry();
|
|
2819
3078
|
init_provider();
|
|
2820
3079
|
init_think_stripper();
|
|
3080
|
+
init_well_formed();
|
|
2821
3081
|
AnthropicProvider = class {
|
|
2822
3082
|
// 模拟 CC 的 X-Claude-Code-Session-Id
|
|
2823
3083
|
constructor(config) {
|
|
2824
3084
|
this.config = config;
|
|
2825
3085
|
let u = (config.baseUrl || "https://api.anthropic.com").trim();
|
|
2826
3086
|
while (u.endsWith("/")) u = u.slice(0, -1);
|
|
3087
|
+
if (u.endsWith("/v1")) u = u.slice(0, -3);
|
|
2827
3088
|
this.baseUrl = u;
|
|
2828
3089
|
this.sessionId = randomUUID();
|
|
2829
3090
|
}
|
|
@@ -3022,7 +3283,7 @@ var init_anthropic_provider = __esm({
|
|
|
3022
3283
|
} catch {
|
|
3023
3284
|
}
|
|
3024
3285
|
}
|
|
3025
|
-
let bodyStr =
|
|
3286
|
+
let bodyStr = safeStringify(body);
|
|
3026
3287
|
if (this.config.wafSingleQuoteWorkaround) {
|
|
3027
3288
|
const before = bodyStr.length;
|
|
3028
3289
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
@@ -3242,6 +3503,7 @@ var init_gemini_provider = __esm({
|
|
|
3242
3503
|
"src/models/gemini-provider.ts"() {
|
|
3243
3504
|
"use strict";
|
|
3244
3505
|
init_withRetry();
|
|
3506
|
+
init_well_formed();
|
|
3245
3507
|
GeminiProvider = class {
|
|
3246
3508
|
constructor(config) {
|
|
3247
3509
|
this.config = config;
|
|
@@ -3401,7 +3663,7 @@ var init_gemini_provider = __esm({
|
|
|
3401
3663
|
"Content-Type": "application/json",
|
|
3402
3664
|
"x-goog-api-key": this.config.apiKey
|
|
3403
3665
|
},
|
|
3404
|
-
body:
|
|
3666
|
+
body: safeStringify(body),
|
|
3405
3667
|
signal: params.signal
|
|
3406
3668
|
}, "gemini", this.config.proxy);
|
|
3407
3669
|
let response;
|
|
@@ -3540,7 +3802,9 @@ function createProvider(config) {
|
|
|
3540
3802
|
baseUrl: config.baseUrl,
|
|
3541
3803
|
apiKey: config.apiKey,
|
|
3542
3804
|
thinking: config.thinking,
|
|
3543
|
-
proxy
|
|
3805
|
+
proxy,
|
|
3806
|
+
wafSingleQuoteWorkaround: config.wafSingleQuoteWorkaround
|
|
3807
|
+
// 0919 中转站→agentrouter 同样有 WAF
|
|
3544
3808
|
});
|
|
3545
3809
|
case "openai-responses":
|
|
3546
3810
|
return new OpenAIResponsesProvider({
|
|
@@ -5170,7 +5434,7 @@ ${result}`;
|
|
|
5170
5434
|
}
|
|
5171
5435
|
} catch {
|
|
5172
5436
|
}
|
|
5173
|
-
return content
|
|
5437
|
+
return truncateByCodePoint(content, sizeLimit ?? TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
5174
5438
|
}
|
|
5175
5439
|
function foldTurnInline(turn) {
|
|
5176
5440
|
const toolMsgs = turn.filter((m) => m.role === "tool");
|
|
@@ -5300,7 +5564,8 @@ function step3_truncateLarge(messages) {
|
|
|
5300
5564
|
details.push(`tool_call_id=${m.tool_call_id?.slice(0, 8)}... ${originalLen} \u2192 ${TRUNCATE_TO} chars`);
|
|
5301
5565
|
return {
|
|
5302
5566
|
...m,
|
|
5303
|
-
|
|
5567
|
+
// 0918:按 code point 切,避免切开 emoji 代理对(见 utils/well-formed.ts)
|
|
5568
|
+
content: truncateByCodePoint(m.content, TRUNCATE_TO) + "\n...[TRUNCATED]"
|
|
5304
5569
|
};
|
|
5305
5570
|
}
|
|
5306
5571
|
return m;
|
|
@@ -5390,6 +5655,7 @@ var init_ruleCompact = __esm({
|
|
|
5390
5655
|
init_tokenEstimate();
|
|
5391
5656
|
init_autoCompact();
|
|
5392
5657
|
init_compactLog();
|
|
5658
|
+
init_well_formed();
|
|
5393
5659
|
TAG3 = "ruleCompact";
|
|
5394
5660
|
TOOL_SIZE_LIMIT = 8e3;
|
|
5395
5661
|
TRUNCATE_TO = 5e3;
|
|
@@ -6666,6 +6932,7 @@ var init_skill_gate_classifier = __esm({
|
|
|
6666
6932
|
"src/skills/skill-gate-classifier.ts"() {
|
|
6667
6933
|
"use strict";
|
|
6668
6934
|
init_skill_gate_window();
|
|
6935
|
+
init_well_formed();
|
|
6669
6936
|
}
|
|
6670
6937
|
});
|
|
6671
6938
|
|
|
@@ -10856,7 +11123,7 @@ var init_agentSwarmsEnabled = __esm({
|
|
|
10856
11123
|
});
|
|
10857
11124
|
|
|
10858
11125
|
// src/swarm/teamHelpers.ts
|
|
10859
|
-
import { mkdirSync as
|
|
11126
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
10860
11127
|
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
10861
11128
|
import { join as join18 } from "node:path";
|
|
10862
11129
|
function getTeamsDir() {
|
|
@@ -10949,7 +11216,7 @@ async function cleanupTeamDirectories(teamName) {
|
|
|
10949
11216
|
}
|
|
10950
11217
|
function ensureTasksDir(taskListId) {
|
|
10951
11218
|
const dir = join18(getTasksDir(), taskListId);
|
|
10952
|
-
|
|
11219
|
+
mkdirSync9(dir, { recursive: true });
|
|
10953
11220
|
}
|
|
10954
11221
|
function setLeaderTeamName(name) {
|
|
10955
11222
|
_leaderTeamName = name;
|
|
@@ -11865,11 +12132,65 @@ var init_AgentTool = __esm({
|
|
|
11865
12132
|
}
|
|
11866
12133
|
});
|
|
11867
12134
|
|
|
12135
|
+
// src/tools/tavily-pool.ts
|
|
12136
|
+
var tavily_pool_exports = {};
|
|
12137
|
+
__export(tavily_pool_exports, {
|
|
12138
|
+
relayOriginFrom: () => relayOriginFrom,
|
|
12139
|
+
resetTavilyPool: () => resetTavilyPool,
|
|
12140
|
+
resolvePoolTavilyKey: () => resolvePoolTavilyKey
|
|
12141
|
+
});
|
|
12142
|
+
function resetTavilyPool() {
|
|
12143
|
+
cache = null;
|
|
12144
|
+
}
|
|
12145
|
+
function relayOriginFrom(cfg) {
|
|
12146
|
+
const rid = cfg?.tools?.my_selfie?.relayProviderId || cfg?.my_selfie?.relayProviderId || RELAY_PROVIDER_DEFAULT;
|
|
12147
|
+
const p = cfg?.models?.providers?.[rid] || cfg?.providers?.[rid];
|
|
12148
|
+
const baseUrl = String(p?.baseUrl || "").trim();
|
|
12149
|
+
const token = String(p?.apiKey || "").trim();
|
|
12150
|
+
if (!baseUrl || !token) return null;
|
|
12151
|
+
const origin = baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "").replace(/\/+$/, "");
|
|
12152
|
+
return origin ? { origin, token } : null;
|
|
12153
|
+
}
|
|
12154
|
+
async function resolvePoolTavilyKey(opts = {}) {
|
|
12155
|
+
const relay = relayOriginFrom(opts.cfg ?? liveConfig.all());
|
|
12156
|
+
if (!relay) return "";
|
|
12157
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
12158
|
+
if (!cache || opts.force || Date.now() - cache.fetchedAt > CACHE_TTL_MS) {
|
|
12159
|
+
try {
|
|
12160
|
+
const res = await doFetch(`${relay.origin}/api/engine7/search-keys`, {
|
|
12161
|
+
headers: { Authorization: `Bearer ${relay.token}` }
|
|
12162
|
+
});
|
|
12163
|
+
const body = await res.json().catch(() => null);
|
|
12164
|
+
const keys = Array.isArray(body?.data?.keys) ? body.data.keys.filter((k) => typeof k === "string" && k.trim()) : [];
|
|
12165
|
+
cache = { keys, fetchedAt: Date.now() };
|
|
12166
|
+
} catch {
|
|
12167
|
+
if (!cache) return "";
|
|
12168
|
+
cache.fetchedAt = Date.now();
|
|
12169
|
+
}
|
|
12170
|
+
}
|
|
12171
|
+
if (!cache.keys.length) return "";
|
|
12172
|
+
const key = cache.keys[cursor % cache.keys.length];
|
|
12173
|
+
cursor++;
|
|
12174
|
+
return key;
|
|
12175
|
+
}
|
|
12176
|
+
var CACHE_TTL_MS, RELAY_PROVIDER_DEFAULT, cache, cursor;
|
|
12177
|
+
var init_tavily_pool = __esm({
|
|
12178
|
+
"src/tools/tavily-pool.ts"() {
|
|
12179
|
+
"use strict";
|
|
12180
|
+
init_live();
|
|
12181
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
12182
|
+
RELAY_PROVIDER_DEFAULT = "7yi";
|
|
12183
|
+
cache = null;
|
|
12184
|
+
cursor = 0;
|
|
12185
|
+
}
|
|
12186
|
+
});
|
|
12187
|
+
|
|
11868
12188
|
// src/tools/web-search.ts
|
|
11869
12189
|
var init_web_search = __esm({
|
|
11870
12190
|
"src/tools/web-search.ts"() {
|
|
11871
12191
|
"use strict";
|
|
11872
12192
|
init_registry();
|
|
12193
|
+
init_well_formed();
|
|
11873
12194
|
registry.register({
|
|
11874
12195
|
name: "web_search",
|
|
11875
12196
|
description: "\u641C\u7D22\u4E92\u8054\u7F51\u3002\u8FD4\u56DE\u641C\u7D22\u7ED3\u679C\u548C\u94FE\u63A5\u3002",
|
|
@@ -11886,14 +12207,25 @@ var init_web_search = __esm({
|
|
|
11886
12207
|
const query = args.query;
|
|
11887
12208
|
const maxResults = args.max_results || 5;
|
|
11888
12209
|
const searchDepth = args.search_depth || "basic";
|
|
11889
|
-
|
|
11890
|
-
|
|
12210
|
+
let apiKey = process.env.TAVILY_API_KEY || "";
|
|
12211
|
+
let fromPool = false;
|
|
12212
|
+
if (!apiKey) {
|
|
12213
|
+
const { resolvePoolTavilyKey: resolvePoolTavilyKey2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
12214
|
+
apiKey = await resolvePoolTavilyKey2();
|
|
12215
|
+
fromPool = !!apiKey;
|
|
12216
|
+
}
|
|
12217
|
+
if (!apiKey) {
|
|
12218
|
+
return {
|
|
12219
|
+
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",
|
|
12220
|
+
isError: true
|
|
12221
|
+
};
|
|
12222
|
+
}
|
|
11891
12223
|
const startTime = Date.now();
|
|
11892
12224
|
try {
|
|
11893
12225
|
const response = await fetch("https://api.tavily.com/search", {
|
|
11894
12226
|
method: "POST",
|
|
11895
12227
|
headers: { "Content-Type": "application/json" },
|
|
11896
|
-
body:
|
|
12228
|
+
body: safeStringify({
|
|
11897
12229
|
query,
|
|
11898
12230
|
max_results: maxResults,
|
|
11899
12231
|
include_answer: true,
|
|
@@ -11903,6 +12235,14 @@ var init_web_search = __esm({
|
|
|
11903
12235
|
signal
|
|
11904
12236
|
});
|
|
11905
12237
|
if (!response.ok) {
|
|
12238
|
+
if (fromPool && (response.status === 401 || response.status === 429 || response.status === 432)) {
|
|
12239
|
+
const { resetTavilyPool: resetTavilyPool2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
12240
|
+
resetTavilyPool2();
|
|
12241
|
+
return {
|
|
12242
|
+
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`,
|
|
12243
|
+
isError: true
|
|
12244
|
+
};
|
|
12245
|
+
}
|
|
11906
12246
|
return { content: `\u641C\u7D22\u5931\u8D25: HTTP ${response.status}`, isError: true };
|
|
11907
12247
|
}
|
|
11908
12248
|
const data = await response.json();
|
|
@@ -12581,15 +12921,16 @@ import * as path21 from "node:path";
|
|
|
12581
12921
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
12582
12922
|
if (specifiedPath && fs19.existsSync(specifiedPath)) return specifiedPath;
|
|
12583
12923
|
if (!fs19.existsSync(mediaDir)) return null;
|
|
12584
|
-
const files = fs19.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f)).map((f) => ({ name: f, p: path21.join(mediaDir, f), mtime: fs19.statSync(path21.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
12924
|
+
const files = fs19.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path21.join(mediaDir, f), mtime: fs19.statSync(path21.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
12585
12925
|
return files[0]?.p || null;
|
|
12586
12926
|
}
|
|
12587
|
-
var MIME_MAP;
|
|
12927
|
+
var MIME_MAP, VIDEO_EXT, VIDEO_MAX_BYTES;
|
|
12588
12928
|
var init_my_eyes = __esm({
|
|
12589
12929
|
"src/tools/my-eyes.ts"() {
|
|
12590
12930
|
"use strict";
|
|
12591
12931
|
init_live();
|
|
12592
12932
|
init_registry();
|
|
12933
|
+
init_well_formed();
|
|
12593
12934
|
MIME_MAP = {
|
|
12594
12935
|
".jpg": "jpeg",
|
|
12595
12936
|
".jpeg": "jpeg",
|
|
@@ -12598,13 +12939,15 @@ var init_my_eyes = __esm({
|
|
|
12598
12939
|
".gif": "gif",
|
|
12599
12940
|
".bmp": "bmp"
|
|
12600
12941
|
};
|
|
12942
|
+
VIDEO_EXT = /\.(mp4|mov)$/i;
|
|
12943
|
+
VIDEO_MAX_BYTES = 40 * 1024 * 1024;
|
|
12601
12944
|
registry.register({
|
|
12602
12945
|
name: "my_eyes",
|
|
12603
|
-
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.",
|
|
12946
|
+
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.",
|
|
12604
12947
|
schema: {
|
|
12605
12948
|
type: "object",
|
|
12606
12949
|
properties: {
|
|
12607
|
-
image_path: { type: "string", description: "Absolute path to the image file to analyze." },
|
|
12950
|
+
image_path: { type: "string", description: "Absolute path to the image (or .mp4/.mov video) file to analyze." },
|
|
12608
12951
|
prompt: { type: "string", description: 'What to ask about the image. Default: "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9"' }
|
|
12609
12952
|
},
|
|
12610
12953
|
required: ["image_path"]
|
|
@@ -12633,6 +12976,42 @@ var init_my_eyes = __esm({
|
|
|
12633
12976
|
}
|
|
12634
12977
|
const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
12635
12978
|
const ext = path21.extname(imagePath).toLowerCase();
|
|
12979
|
+
if (VIDEO_EXT.test(imagePath)) {
|
|
12980
|
+
const stat4 = fs19.statSync(imagePath);
|
|
12981
|
+
if (stat4.size > VIDEO_MAX_BYTES) {
|
|
12982
|
+
return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
|
|
12983
|
+
}
|
|
12984
|
+
const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
|
|
12985
|
+
const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
|
|
12986
|
+
const vidB64 = fs19.readFileSync(imagePath).toString("base64");
|
|
12987
|
+
try {
|
|
12988
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
12989
|
+
method: "POST",
|
|
12990
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${providerCfg.apiKey}` },
|
|
12991
|
+
body: safeStringify({
|
|
12992
|
+
model: modelId,
|
|
12993
|
+
messages: [{
|
|
12994
|
+
role: "user",
|
|
12995
|
+
content: [
|
|
12996
|
+
{ type: "video_url", video_url: { url: `data:video/mp4;base64,${vidB64}` } },
|
|
12997
|
+
{ type: "text", text: rawPrompt }
|
|
12998
|
+
]
|
|
12999
|
+
}],
|
|
13000
|
+
max_tokens: 4096
|
|
13001
|
+
})
|
|
13002
|
+
});
|
|
13003
|
+
if (!res.ok) {
|
|
13004
|
+
const t = await res.text();
|
|
13005
|
+
return { content: `Video analysis failed: HTTP ${res.status} ${t.slice(0, 300)}`, isError: true };
|
|
13006
|
+
}
|
|
13007
|
+
const data = await res.json();
|
|
13008
|
+
const content = data.choices?.[0]?.message?.content || "";
|
|
13009
|
+
if (!content.trim()) return { content: "Video analysis returned empty.", isError: true };
|
|
13010
|
+
return { content };
|
|
13011
|
+
} catch (e) {
|
|
13012
|
+
return { content: `Video analysis failed: ${e.message}`, isError: true };
|
|
13013
|
+
}
|
|
13014
|
+
}
|
|
12636
13015
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
12637
13016
|
const imgB64 = fs19.readFileSync(imagePath).toString("base64");
|
|
12638
13017
|
const userMsg = {
|
|
@@ -12976,7 +13355,7 @@ async function ttsQwen3Local(text, cfg, onChunk) {
|
|
|
12976
13355
|
const res = await fetch(`${url}/tts`, {
|
|
12977
13356
|
method: "POST",
|
|
12978
13357
|
headers: { "Content-Type": "application/json" },
|
|
12979
|
-
body:
|
|
13358
|
+
body: safeStringify({
|
|
12980
13359
|
text,
|
|
12981
13360
|
voice: cfg.voice || "xiaoya",
|
|
12982
13361
|
lang: cfg.lang || "Chinese"
|
|
@@ -13007,7 +13386,7 @@ async function ttsQwen3Opus(text, cfg) {
|
|
|
13007
13386
|
const res = await fetch(`${url}/tts?format=opus`, {
|
|
13008
13387
|
method: "POST",
|
|
13009
13388
|
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
13010
|
-
body:
|
|
13389
|
+
body: safeStringify({
|
|
13011
13390
|
text,
|
|
13012
13391
|
voice: cfg.voice || "xiaoya",
|
|
13013
13392
|
lang: cfg.lang || "Chinese"
|
|
@@ -13252,6 +13631,7 @@ var init_my_voice = __esm({
|
|
|
13252
13631
|
"src/tools/my-voice.ts"() {
|
|
13253
13632
|
"use strict";
|
|
13254
13633
|
init_live();
|
|
13634
|
+
init_well_formed();
|
|
13255
13635
|
init_registry();
|
|
13256
13636
|
init_opus_encoder();
|
|
13257
13637
|
init_voice_chat_tts_daemon();
|
|
@@ -13431,6 +13811,11 @@ var init_my_voice = __esm({
|
|
|
13431
13811
|
});
|
|
13432
13812
|
|
|
13433
13813
|
// src/tools/my-selfie.ts
|
|
13814
|
+
var my_selfie_exports = {};
|
|
13815
|
+
__export(my_selfie_exports, {
|
|
13816
|
+
dimsOf: () => dimsOf,
|
|
13817
|
+
resolvePortraitDesc: () => resolvePortraitDesc
|
|
13818
|
+
});
|
|
13434
13819
|
import * as fs22 from "node:fs";
|
|
13435
13820
|
import * as path23 from "node:path";
|
|
13436
13821
|
function getProxyDispatcher2() {
|
|
@@ -13444,10 +13829,46 @@ function getProxyDispatcher2() {
|
|
|
13444
13829
|
return void 0;
|
|
13445
13830
|
}
|
|
13446
13831
|
}
|
|
13447
|
-
function
|
|
13448
|
-
|
|
13449
|
-
|
|
13450
|
-
|
|
13832
|
+
function dimsOf(p) {
|
|
13833
|
+
try {
|
|
13834
|
+
const b = fs22.readFileSync(p);
|
|
13835
|
+
let w = 0, h = 0;
|
|
13836
|
+
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
13837
|
+
w = b.readUInt32BE(16);
|
|
13838
|
+
h = b.readUInt32BE(20);
|
|
13839
|
+
} else if (b[0] === 255 && b[1] === 216) {
|
|
13840
|
+
let i = 2;
|
|
13841
|
+
while (i < b.length - 9) {
|
|
13842
|
+
if (b[i] !== 255) {
|
|
13843
|
+
i++;
|
|
13844
|
+
continue;
|
|
13845
|
+
}
|
|
13846
|
+
const m = b[i + 1];
|
|
13847
|
+
if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) {
|
|
13848
|
+
h = b.readUInt16BE(i + 5);
|
|
13849
|
+
w = b.readUInt16BE(i + 7);
|
|
13850
|
+
break;
|
|
13851
|
+
}
|
|
13852
|
+
i += 2 + b.readUInt16BE(i + 2);
|
|
13853
|
+
}
|
|
13854
|
+
} else {
|
|
13855
|
+
return null;
|
|
13856
|
+
}
|
|
13857
|
+
if (!w || !h) return null;
|
|
13858
|
+
const bad = [];
|
|
13859
|
+
if (w < 256 || w > 4096) bad.push(`\u5BBD ${w} \u4E0D\u5728 256~4096`);
|
|
13860
|
+
if (h < 256 || h > 4096) bad.push(`\u9AD8 ${h} \u4E0D\u5728 256~4096`);
|
|
13861
|
+
const r = Math.max(w, h) / Math.min(w, h);
|
|
13862
|
+
if (r > 2) bad.push(`\u957F\u5BBD\u6BD4 ${r.toFixed(2)}:1 \u8D85\u8FC7 2:1`);
|
|
13863
|
+
return bad.length ? `${w}x${h} \u4E0D\u5408\u683C\uFF1A${bad.join("\uFF1B")}` : `${w}x${h}`;
|
|
13864
|
+
} catch {
|
|
13865
|
+
return null;
|
|
13866
|
+
}
|
|
13867
|
+
}
|
|
13868
|
+
function getFalKey(cfg) {
|
|
13869
|
+
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey || process.env.FAL_API_KEY || "";
|
|
13870
|
+
}
|
|
13871
|
+
function getReferences(ctx) {
|
|
13451
13872
|
const cfg = liveConfig.all();
|
|
13452
13873
|
const cfgRefs = cfg.my_selfie?.references || cfg.tools?.my_selfie?.references;
|
|
13453
13874
|
if (Array.isArray(cfgRefs) && cfgRefs.length > 0) {
|
|
@@ -13464,6 +13885,72 @@ function getXaiKey(ctx) {
|
|
|
13464
13885
|
const xaiCfg = cfg.providers?.xai || cfg.models?.providers?.xai;
|
|
13465
13886
|
return xaiCfg?.apiKey || "";
|
|
13466
13887
|
}
|
|
13888
|
+
function getRelayCfg(cfg) {
|
|
13889
|
+
const id = cfg?.my_selfie?.relayProviderId || cfg?.tools?.my_selfie?.relayProviderId || "7yi";
|
|
13890
|
+
const p = cfg?.models?.providers?.[id] || cfg?.providers?.[id];
|
|
13891
|
+
return { baseUrl: String(p?.baseUrl || "").replace(/\/+$/, ""), apiKey: p?.apiKey || "" };
|
|
13892
|
+
}
|
|
13893
|
+
function resolvePortraitDesc(argPortrait, cfgPortrait) {
|
|
13894
|
+
const fromArg = typeof argPortrait === "string" ? argPortrait.trim() : "";
|
|
13895
|
+
if (fromArg) return fromArg;
|
|
13896
|
+
return typeof cfgPortrait === "string" ? cfgPortrait.trim() : "";
|
|
13897
|
+
}
|
|
13898
|
+
async function generateWithRelay(imageB64, prompt, mime, baseUrl, apiKey, model) {
|
|
13899
|
+
const res = await fetch(`${baseUrl}/images/edits`, {
|
|
13900
|
+
method: "POST",
|
|
13901
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
13902
|
+
// safeStringify 不是安全选项而是必须:prompt 里一个孤立代理(半个 emoji)
|
|
13903
|
+
// 就会让上游 500。同批上游提交刚在 fal/grok/comfy 三处修过同一个坑。
|
|
13904
|
+
body: safeStringify({
|
|
13905
|
+
model,
|
|
13906
|
+
prompt,
|
|
13907
|
+
n: 1,
|
|
13908
|
+
response_format: "b64_json",
|
|
13909
|
+
images: [{ image_url: `data:${mime};base64,${imageB64}` }]
|
|
13910
|
+
})
|
|
13911
|
+
});
|
|
13912
|
+
if (!res.ok) {
|
|
13913
|
+
const text = await res.text();
|
|
13914
|
+
throw new Error(`relay ${res.status}: ${text.slice(0, 300)}`);
|
|
13915
|
+
}
|
|
13916
|
+
const data = await res.json();
|
|
13917
|
+
if (!data.data?.length) throw new Error("No images returned from relay");
|
|
13918
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
13919
|
+
}
|
|
13920
|
+
async function generatePortraitWithRelay(desc, baseUrl, apiKey, model) {
|
|
13921
|
+
const res = await fetch(`${baseUrl}/images/generations`, {
|
|
13922
|
+
method: "POST",
|
|
13923
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
13924
|
+
// 同上:描述里可能带 emoji,孤立代理会让上游 500
|
|
13925
|
+
body: safeStringify({
|
|
13926
|
+
model,
|
|
13927
|
+
prompt: PORTRAIT_PROMPT(desc),
|
|
13928
|
+
n: 1,
|
|
13929
|
+
size: "1024x1024",
|
|
13930
|
+
response_format: "b64_json"
|
|
13931
|
+
})
|
|
13932
|
+
});
|
|
13933
|
+
if (!res.ok) {
|
|
13934
|
+
const text = await res.text();
|
|
13935
|
+
throw new Error(`relay portrait ${res.status}: ${text.slice(0, 300)}`);
|
|
13936
|
+
}
|
|
13937
|
+
const data = await res.json();
|
|
13938
|
+
if (!data.data?.length) throw new Error("No images returned from relay (portrait)");
|
|
13939
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
13940
|
+
}
|
|
13941
|
+
function registerPortrait(relPath, name) {
|
|
13942
|
+
try {
|
|
13943
|
+
const cfg = liveConfig.all();
|
|
13944
|
+
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
13945
|
+
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
13946
|
+
next.unshift({ name, path: relPath });
|
|
13947
|
+
const path34 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
13948
|
+
void liveConfig.set(path34, next);
|
|
13949
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path34}[0] = ${relPath}`);
|
|
13950
|
+
} catch (e) {
|
|
13951
|
+
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
13952
|
+
}
|
|
13953
|
+
}
|
|
13467
13954
|
function detectMode(input) {
|
|
13468
13955
|
const lower = input.toLowerCase();
|
|
13469
13956
|
for (const [mode, keywords] of Object.entries(MODE_KEYWORDS)) {
|
|
@@ -13510,10 +13997,16 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
13510
13997
|
resolution
|
|
13511
13998
|
};
|
|
13512
13999
|
if (aspectRatio) body.aspect_ratio = aspectRatio;
|
|
14000
|
+
const falKey = getFalKey(cfg);
|
|
14001
|
+
if (!falKey) {
|
|
14002
|
+
throw new Error(
|
|
14003
|
+
`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`
|
|
14004
|
+
);
|
|
14005
|
+
}
|
|
13513
14006
|
const res = await fetch(FAL_ENDPOINT, {
|
|
13514
14007
|
method: "POST",
|
|
13515
|
-
headers: { "Authorization": `Key ${
|
|
13516
|
-
body:
|
|
14008
|
+
headers: { "Authorization": `Key ${falKey}`, "Content-Type": "application/json" },
|
|
14009
|
+
body: safeStringify(body)
|
|
13517
14010
|
});
|
|
13518
14011
|
if (!res.ok) {
|
|
13519
14012
|
const text = await res.text();
|
|
@@ -13550,7 +14043,7 @@ async function comfyQueue(server, workflow) {
|
|
|
13550
14043
|
const res = await fetch(`${server}/prompt`, {
|
|
13551
14044
|
method: "POST",
|
|
13552
14045
|
headers: { "Content-Type": "application/json" },
|
|
13553
|
-
body:
|
|
14046
|
+
body: safeStringify({ prompt: workflow })
|
|
13554
14047
|
});
|
|
13555
14048
|
if (!res.ok) return null;
|
|
13556
14049
|
const data = await res.json();
|
|
@@ -13678,7 +14171,7 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
13678
14171
|
"Authorization": `Bearer ${xaiKey}`,
|
|
13679
14172
|
"Content-Type": "application/json"
|
|
13680
14173
|
},
|
|
13681
|
-
body:
|
|
14174
|
+
body: safeStringify({
|
|
13682
14175
|
model: "grok-imagine-image-quality",
|
|
13683
14176
|
prompt,
|
|
13684
14177
|
image: {
|
|
@@ -13698,15 +14191,15 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
13698
14191
|
if (!data.data?.length) throw new Error("No images returned from grok");
|
|
13699
14192
|
return Buffer.from(data.data[0].b64_json, "base64");
|
|
13700
14193
|
}
|
|
13701
|
-
var FAL_ENDPOINT, DEFAULT_RESOLUTION,
|
|
14194
|
+
var FAL_ENDPOINT, DEFAULT_RESOLUTION, DEFAULT_REFERENCES, PORTRAIT_PROMPT, MODE_PROMPTS, MODE_KEYWORDS;
|
|
13702
14195
|
var init_my_selfie = __esm({
|
|
13703
14196
|
"src/tools/my-selfie.ts"() {
|
|
13704
14197
|
"use strict";
|
|
13705
14198
|
init_live();
|
|
13706
14199
|
init_registry();
|
|
14200
|
+
init_well_formed();
|
|
13707
14201
|
FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
13708
14202
|
DEFAULT_RESOLUTION = "1k";
|
|
13709
|
-
FAL_KEY_FALLBACK = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
13710
14203
|
DEFAULT_REFERENCES = [
|
|
13711
14204
|
{ name: "default", p: "images/xiaomei_clean_v2.png" },
|
|
13712
14205
|
{ name: "v3", p: "images/xiaomei_clean_v3.png" },
|
|
@@ -13714,6 +14207,7 @@ var init_my_selfie = __esm({
|
|
|
13714
14207
|
{ name: "blue", p: "images/xiaomei_blue_shirt_base.jpg" },
|
|
13715
14208
|
{ name: "office", p: "images/xiaomei_office_final.jpg" }
|
|
13716
14209
|
];
|
|
14210
|
+
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.`;
|
|
13717
14211
|
MODE_PROMPTS = {
|
|
13718
14212
|
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`,
|
|
13719
14213
|
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`,
|
|
@@ -13726,15 +14220,17 @@ var init_my_selfie = __esm({
|
|
|
13726
14220
|
};
|
|
13727
14221
|
registry.register({
|
|
13728
14222
|
name: "my_selfie",
|
|
13729
|
-
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.",
|
|
14223
|
+
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",
|
|
13730
14224
|
schema: {
|
|
13731
14225
|
type: "object",
|
|
13732
14226
|
properties: {
|
|
13733
14227
|
prompt: { type: "string", description: "Scene/outfit/mood description, e.g. '\u5728\u5496\u5561\u9986\u770B\u4E66' or '\u7A7F\u7EA2\u88D9\u5B50'." },
|
|
13734
14228
|
mode: { type: "string", enum: ["auto", "mirror", "direct", "lifestyle"], description: "Selfie mode. 'auto' detects from prompt (default)." },
|
|
13735
14229
|
reference: { type: "string", description: "Reference base image name (e.g. 'default', 'v3'). Configurable per agent via my_selfie.references in config." },
|
|
13736
|
-
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)." },
|
|
13737
|
-
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." }
|
|
14230
|
+
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)." },
|
|
14231
|
+
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." },
|
|
14232
|
+
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" },
|
|
14233
|
+
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" }
|
|
13738
14234
|
},
|
|
13739
14235
|
required: ["prompt"]
|
|
13740
14236
|
},
|
|
@@ -13748,9 +14244,61 @@ var init_my_selfie = __esm({
|
|
|
13748
14244
|
const REFERENCES = getReferences(ctx);
|
|
13749
14245
|
const refName = args.reference || "default";
|
|
13750
14246
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
13751
|
-
|
|
14247
|
+
let refPath = path23.join(ctx.workspace, refEntry.p);
|
|
14248
|
+
const adoptImg = args.referenceImage?.trim();
|
|
14249
|
+
if (adoptImg) {
|
|
14250
|
+
if (!fs22.existsSync(adoptImg)) {
|
|
14251
|
+
return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
|
|
14252
|
+
}
|
|
14253
|
+
const acheck = dimsOf(adoptImg);
|
|
14254
|
+
if (acheck?.includes("\u4E0D\u5408\u683C")) {
|
|
14255
|
+
return {
|
|
14256
|
+
content: `\u8FD9\u5F20\u56FE\u7528\u4E0D\u4E86\uFF1A${acheck}\u3002
|
|
14257
|
+
\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`,
|
|
14258
|
+
isError: true
|
|
14259
|
+
};
|
|
14260
|
+
}
|
|
14261
|
+
const adir = path23.join(ctx.workspace, "images");
|
|
14262
|
+
if (!fs22.existsSync(adir)) fs22.mkdirSync(adir, { recursive: true });
|
|
14263
|
+
const aext = (path23.extname(adoptImg) || ".png").toLowerCase();
|
|
14264
|
+
const afname = `portrait_${Date.now()}${aext}`;
|
|
14265
|
+
fs22.copyFileSync(adoptImg, path23.join(adir, afname));
|
|
14266
|
+
registerPortrait(`images/${afname}`, refName);
|
|
14267
|
+
refPath = path23.join(adir, afname);
|
|
14268
|
+
console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
|
|
14269
|
+
}
|
|
13752
14270
|
if (provider !== "autodl" && !fs22.existsSync(refPath)) {
|
|
13753
|
-
|
|
14271
|
+
const cfgAll = liveConfig.all();
|
|
14272
|
+
const portraitDesc = resolvePortraitDesc(
|
|
14273
|
+
args.portrait,
|
|
14274
|
+
cfgAll?.tools?.my_selfie?.portraitDesc ?? cfgAll?.my_selfie?.portraitDesc
|
|
14275
|
+
);
|
|
14276
|
+
if (!portraitDesc) {
|
|
14277
|
+
return {
|
|
14278
|
+
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
|
|
14279
|
+
\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
|
|
14280
|
+
\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`
|
|
14281
|
+
};
|
|
14282
|
+
}
|
|
14283
|
+
const relay = getRelayCfg(cfgAll);
|
|
14284
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
14285
|
+
return { content: "Error: relay provider \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u5851\u9020\u5F62\u8C61\u3002", isError: true };
|
|
14286
|
+
}
|
|
14287
|
+
const pmodel = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
14288
|
+
let portraitBuf;
|
|
14289
|
+
try {
|
|
14290
|
+
console.log(`[my-selfie] \u51B7\u542F\uFF1A\u6309\u63CF\u8FF0\u5851\u9020\u5F62\u8C61 -> ${pmodel}`);
|
|
14291
|
+
portraitBuf = await generatePortraitWithRelay(portraitDesc, relay.baseUrl, relay.apiKey, pmodel);
|
|
14292
|
+
} catch (e) {
|
|
14293
|
+
return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
|
|
14294
|
+
}
|
|
14295
|
+
const pdir = path23.join(ctx.workspace, "images");
|
|
14296
|
+
if (!fs22.existsSync(pdir)) fs22.mkdirSync(pdir, { recursive: true });
|
|
14297
|
+
const pfname = `portrait_${Date.now()}.png`;
|
|
14298
|
+
fs22.writeFileSync(path23.join(pdir, pfname), portraitBuf);
|
|
14299
|
+
registerPortrait(`images/${pfname}`, refName);
|
|
14300
|
+
refPath = path23.join(pdir, pfname);
|
|
14301
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
|
|
13754
14302
|
}
|
|
13755
14303
|
const resolution = args.resolution || DEFAULT_RESOLUTION;
|
|
13756
14304
|
const refB64 = fs22.existsSync(refPath) ? fs22.readFileSync(refPath).toString("base64") : "";
|
|
@@ -13762,6 +14310,16 @@ var init_my_selfie = __esm({
|
|
|
13762
14310
|
const xaiKey = getXaiKey(ctx);
|
|
13763
14311
|
if (!xaiKey) return { content: "Error: xai apiKey not configured for grok provider", isError: true };
|
|
13764
14312
|
imageBuffer = await generateWithGrok(refB64, fullPrompt, xaiKey);
|
|
14313
|
+
} else if (provider === "relay") {
|
|
14314
|
+
const cfgAll = liveConfig.all();
|
|
14315
|
+
const relay = getRelayCfg(cfgAll);
|
|
14316
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
14317
|
+
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 };
|
|
14318
|
+
}
|
|
14319
|
+
const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
14320
|
+
const ext = path23.extname(refPath).toLowerCase();
|
|
14321
|
+
const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
14322
|
+
imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
|
|
13765
14323
|
} else {
|
|
13766
14324
|
imageBuffer = await generateWithFal(refB64, fullPrompt, resolution);
|
|
13767
14325
|
}
|
|
@@ -13884,10 +14442,14 @@ var init_body_move = __esm({
|
|
|
13884
14442
|
|
|
13885
14443
|
// src/calendar/helpers.ts
|
|
13886
14444
|
function nowIso() {
|
|
13887
|
-
return
|
|
14445
|
+
return toUtc(/* @__PURE__ */ new Date());
|
|
13888
14446
|
}
|
|
13889
|
-
function
|
|
13890
|
-
return
|
|
14447
|
+
function nowParts() {
|
|
14448
|
+
return wallParts();
|
|
14449
|
+
}
|
|
14450
|
+
function wallMs(y, mo, d, hh, mm) {
|
|
14451
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
14452
|
+
return Date.parse(parseWall(`${y}-${z(mo)}-${z(d)}T${z(hh)}:${z(mm)}:00`));
|
|
13891
14453
|
}
|
|
13892
14454
|
function parseDateRange(dateStr) {
|
|
13893
14455
|
const s = dateStr.replace("\u524D", "");
|
|
@@ -13909,32 +14471,29 @@ function parseDateRange(dateStr) {
|
|
|
13909
14471
|
}
|
|
13910
14472
|
}
|
|
13911
14473
|
function isToday(month, dStart, dEnd) {
|
|
13912
|
-
const
|
|
13913
|
-
return month ===
|
|
14474
|
+
const p = nowParts();
|
|
14475
|
+
return month === p.mo && dStart <= p.d && p.d <= dEnd;
|
|
13914
14476
|
}
|
|
13915
14477
|
function isOverdue(month, dStart, dEnd) {
|
|
13916
|
-
const
|
|
13917
|
-
|
|
13918
|
-
const curDay = now.getUTCDate();
|
|
13919
|
-
return month < curMonth || month === curMonth && dEnd < curDay;
|
|
14478
|
+
const p = nowParts();
|
|
14479
|
+
return month < p.mo || month === p.mo && dEnd < p.d;
|
|
13920
14480
|
}
|
|
13921
14481
|
function getTodayWeekday() {
|
|
13922
|
-
const
|
|
13923
|
-
return WEEKDAYS[
|
|
14482
|
+
const p = nowParts();
|
|
14483
|
+
return WEEKDAYS[p.weekday === 0 ? 6 : p.weekday - 1];
|
|
13924
14484
|
}
|
|
13925
14485
|
function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
13926
14486
|
const parsed = parseDateRange(dateStr);
|
|
13927
14487
|
if (!parsed) return null;
|
|
13928
14488
|
const { month, dStart } = parsed;
|
|
13929
|
-
const
|
|
13930
|
-
const year = now.getUTCFullYear();
|
|
14489
|
+
const year = nowParts().y;
|
|
13931
14490
|
let eventMs;
|
|
13932
14491
|
if (timeExact) {
|
|
13933
14492
|
const [hh, mm] = timeExact.split(":").map(Number);
|
|
13934
14493
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
13935
|
-
eventMs =
|
|
14494
|
+
eventMs = wallMs(year, month, dStart, hh, mm);
|
|
13936
14495
|
} else {
|
|
13937
|
-
eventMs =
|
|
14496
|
+
eventMs = wallMs(year, month, dStart, 9, 0);
|
|
13938
14497
|
}
|
|
13939
14498
|
const remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
13940
14499
|
return new Date(remindMs).toISOString();
|
|
@@ -13942,20 +14501,19 @@ function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
|
13942
14501
|
function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
13943
14502
|
const targetIdx = WEEKDAYS.indexOf(day);
|
|
13944
14503
|
if (targetIdx === -1) return null;
|
|
13945
|
-
const
|
|
13946
|
-
const curIdx =
|
|
14504
|
+
const p = nowParts();
|
|
14505
|
+
const curIdx = p.weekday === 0 ? 6 : p.weekday - 1;
|
|
13947
14506
|
let dayDiff = targetIdx - curIdx;
|
|
13948
14507
|
if (dayDiff < 0) dayDiff += 7;
|
|
13949
|
-
const
|
|
13950
|
-
const month =
|
|
13951
|
-
const date = now.getUTCDate() + dayDiff;
|
|
14508
|
+
const target = wallParts(Date.parse(startOfLocalDay()) + dayDiff * 24 * 60 * 60 * 1e3);
|
|
14509
|
+
const year = target.y, month = target.mo, date = target.d;
|
|
13952
14510
|
let eventMs;
|
|
13953
14511
|
if (time && time !== "\u5168\u5929") {
|
|
13954
14512
|
const [hh, mm] = time.split(":").map(Number);
|
|
13955
14513
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
13956
|
-
eventMs =
|
|
14514
|
+
eventMs = wallMs(year, month, date, hh, mm);
|
|
13957
14515
|
} else {
|
|
13958
|
-
eventMs =
|
|
14516
|
+
eventMs = wallMs(year, month, date, 9, 0);
|
|
13959
14517
|
}
|
|
13960
14518
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
13961
14519
|
if (remindMs <= Date.now()) {
|
|
@@ -13970,11 +14528,8 @@ function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
|
13970
14528
|
function computeDailyRemindAt(time, remindBeforeMin) {
|
|
13971
14529
|
const [hh, mm] = time.split(":").map(Number);
|
|
13972
14530
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
13973
|
-
const
|
|
13974
|
-
|
|
13975
|
-
const month = now.getUTCMonth();
|
|
13976
|
-
const date = now.getUTCDate();
|
|
13977
|
-
let eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
14531
|
+
const p = nowParts();
|
|
14532
|
+
let eventMs = wallMs(p.y, p.mo, p.d, hh, mm);
|
|
13978
14533
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
13979
14534
|
if (remindMs <= Date.now()) {
|
|
13980
14535
|
remindMs += 24 * 60 * 60 * 1e3;
|
|
@@ -13984,18 +14539,17 @@ function computeDailyRemindAt(time, remindBeforeMin) {
|
|
|
13984
14539
|
function computeWeekdaysRemindAt(time, remindBeforeMin) {
|
|
13985
14540
|
const [hh, mm] = time.split(":").map(Number);
|
|
13986
14541
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
13987
|
-
|
|
13988
|
-
let
|
|
13989
|
-
let
|
|
13990
|
-
let
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
date++;
|
|
13996
|
-
curDay = (curDay + 1) % 7;
|
|
13997
|
-
eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
14542
|
+
let dayOffset = 0;
|
|
14543
|
+
let eventMs;
|
|
14544
|
+
let remindMs;
|
|
14545
|
+
let curDay;
|
|
14546
|
+
for (; ; ) {
|
|
14547
|
+
const t = wallParts(Date.parse(startOfLocalDay()) + dayOffset * 24 * 60 * 60 * 1e3);
|
|
14548
|
+
curDay = t.weekday;
|
|
14549
|
+
eventMs = wallMs(t.y, t.mo, t.d, hh, mm);
|
|
13998
14550
|
remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
14551
|
+
if (remindMs > Date.now() && curDay !== 0 && curDay !== 6) break;
|
|
14552
|
+
dayOffset++;
|
|
13999
14553
|
}
|
|
14000
14554
|
return new Date(remindMs).toISOString();
|
|
14001
14555
|
}
|
|
@@ -14018,13 +14572,7 @@ function fmtRow(row) {
|
|
|
14018
14572
|
}
|
|
14019
14573
|
}
|
|
14020
14574
|
function fmtNow() {
|
|
14021
|
-
|
|
14022
|
-
const y = now.getUTCFullYear();
|
|
14023
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
14024
|
-
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
14025
|
-
const hh = String(now.getUTCHours()).padStart(2, "0");
|
|
14026
|
-
const mm = String(now.getUTCMinutes()).padStart(2, "0");
|
|
14027
|
-
return `${y}-${m}-${d} ${getTodayWeekday()} ${hh}:${mm}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
14575
|
+
return formatForLlm();
|
|
14028
14576
|
}
|
|
14029
14577
|
function parseDuration(s) {
|
|
14030
14578
|
if (s.endsWith("m")) return parseInt(s.slice(0, -1)) || null;
|
|
@@ -14089,18 +14637,18 @@ function checkTaskProximity(rows, newTimeExact) {
|
|
|
14089
14637
|
}
|
|
14090
14638
|
return null;
|
|
14091
14639
|
}
|
|
14092
|
-
var
|
|
14640
|
+
var WEEKDAYS;
|
|
14093
14641
|
var init_helpers = __esm({
|
|
14094
14642
|
"src/calendar/helpers.ts"() {
|
|
14095
14643
|
"use strict";
|
|
14096
14644
|
init_db();
|
|
14097
|
-
|
|
14645
|
+
init_clock();
|
|
14098
14646
|
WEEKDAYS = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
14099
14647
|
}
|
|
14100
14648
|
});
|
|
14101
14649
|
|
|
14102
14650
|
// src/calendar/commands.ts
|
|
14103
|
-
import { existsSync as existsSync18, statSync as
|
|
14651
|
+
import { existsSync as existsSync18, statSync as statSync11 } from "node:fs";
|
|
14104
14652
|
import { isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
|
|
14105
14653
|
function fmtEnd(start, durationMin) {
|
|
14106
14654
|
const [hh, mm] = start.split(":").map(Number);
|
|
@@ -14180,8 +14728,7 @@ function addDate(db2, args) {
|
|
|
14180
14728
|
const parsed = parseDateRange(dateStr);
|
|
14181
14729
|
let weeklyRows = [];
|
|
14182
14730
|
if (parsed) {
|
|
14183
|
-
const
|
|
14184
|
-
const year = now.getUTCFullYear();
|
|
14731
|
+
const year = wallParts().y;
|
|
14185
14732
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
14186
14733
|
const weekday = WEEKDAYS2[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
14187
14734
|
weeklyRows = db2.prepare(
|
|
@@ -14230,8 +14777,7 @@ function addBatch(db2, args) {
|
|
|
14230
14777
|
const parsed = parseDateRange(dateStr);
|
|
14231
14778
|
let weeklyRows = [];
|
|
14232
14779
|
if (parsed) {
|
|
14233
|
-
const
|
|
14234
|
-
const year = now.getUTCFullYear();
|
|
14780
|
+
const year = wallParts().y;
|
|
14235
14781
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
14236
14782
|
const weekday = WEEKDAYS2[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
14237
14783
|
weeklyRows = db2.prepare(
|
|
@@ -14277,7 +14823,7 @@ function addTask(db2, args) {
|
|
|
14277
14823
|
\u89E3\u6790\u540E: ${absPath}
|
|
14278
14824
|
\u8BF7\u5148\u521B\u5EFA\u6587\u6863\u518D add-task\u3002`;
|
|
14279
14825
|
}
|
|
14280
|
-
const stat4 =
|
|
14826
|
+
const stat4 = statSync11(absPath);
|
|
14281
14827
|
if (!stat4.isFile()) {
|
|
14282
14828
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u4E0D\u662F\u6587\u4EF6\uFF08\u53EF\u80FD\u662F\u76EE\u5F55\uFF09
|
|
14283
14829
|
\u8DEF\u5F84: ${docPath}`;
|
|
@@ -14347,7 +14893,7 @@ function list(db2, opts = {}) {
|
|
|
14347
14893
|
}
|
|
14348
14894
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
14349
14895
|
if (r.repeat_type === "weekdays") {
|
|
14350
|
-
const idx =
|
|
14896
|
+
const idx = wallParts().weekday;
|
|
14351
14897
|
if (idx === 0 || idx === 6) continue;
|
|
14352
14898
|
}
|
|
14353
14899
|
events.push(` #${r.id} ${r.time || ""} ${r.event}`);
|
|
@@ -14375,7 +14921,7 @@ function pending(db2) {
|
|
|
14375
14921
|
}
|
|
14376
14922
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
14377
14923
|
if (r.repeat_type === "weekdays") {
|
|
14378
|
-
const idx =
|
|
14924
|
+
const idx = wallParts().weekday;
|
|
14379
14925
|
if (idx === 0 || idx === 6) continue;
|
|
14380
14926
|
}
|
|
14381
14927
|
const label = r.repeat_type === "weekdays" ? "[\u5DE5\u4F5C\u65E5]" : "[\u6BCF\u5929]";
|
|
@@ -14457,10 +15003,36 @@ function stats(db2) {
|
|
|
14457
15003
|
const archived = db2.prepare("SELECT COUNT(*) as c FROM events WHERE status='archived'").get().c;
|
|
14458
15004
|
return `\u65E5\u5386\u7EDF\u8BA1: \u603B\u8BA1 ${total} | pending ${pending2} | done ${done2} | archived ${archived}`;
|
|
14459
15005
|
}
|
|
15006
|
+
function listEventsForInjection(db2, limit = 50) {
|
|
15007
|
+
return db2.prepare(
|
|
15008
|
+
`SELECT id, event, date_str, time_exact, type, status
|
|
15009
|
+
FROM events
|
|
15010
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
15011
|
+
AND date_str IS NOT NULL AND date_str != ''
|
|
15012
|
+
ORDER BY date_str ASC LIMIT ?`
|
|
15013
|
+
).all(limit);
|
|
15014
|
+
}
|
|
15015
|
+
function listEventBriefs(db2, limit) {
|
|
15016
|
+
const rows = db2.prepare(
|
|
15017
|
+
`SELECT id, event, date_str, time_exact, type
|
|
15018
|
+
FROM events
|
|
15019
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
15020
|
+
ORDER BY date_str ASC, time_exact ASC
|
|
15021
|
+
LIMIT ?`
|
|
15022
|
+
).all(limit);
|
|
15023
|
+
return rows.map((row) => ({
|
|
15024
|
+
id: Number(row.id),
|
|
15025
|
+
title: String(row.event ?? ""),
|
|
15026
|
+
date: String(row.date_str ?? ""),
|
|
15027
|
+
time: String(row.time_exact ?? ""),
|
|
15028
|
+
type: String(row.type ?? "")
|
|
15029
|
+
})).filter((row) => row.title.length > 0);
|
|
15030
|
+
}
|
|
14460
15031
|
var WEEKDAYS2;
|
|
14461
15032
|
var init_commands = __esm({
|
|
14462
15033
|
"src/calendar/commands.ts"() {
|
|
14463
15034
|
"use strict";
|
|
15035
|
+
init_clock();
|
|
14464
15036
|
init_helpers();
|
|
14465
15037
|
WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
14466
15038
|
}
|
|
@@ -14619,7 +15191,7 @@ async function runCmd(cmd, timeoutMs = 15e3) {
|
|
|
14619
15191
|
function spawnDetached(cmd) {
|
|
14620
15192
|
const isWin = process.platform === "win32";
|
|
14621
15193
|
const shell = isWin ? "cmd.exe" : "/bin/sh";
|
|
14622
|
-
const logFile = `D:/xiaoke/logs/service-${(
|
|
15194
|
+
const logFile = `D:/xiaoke/logs/service-${todayKey().replace(/-/g, "")}.log`;
|
|
14623
15195
|
const wrappedCmd = isWin ? `/c "${cmd.replace(/"/g, '\\"')} > "${logFile}" 2>&1"` : `-c "${cmd} > '${logFile}' 2>&1"`;
|
|
14624
15196
|
const child = spawn4(shell, isWin ? [wrappedCmd] : ["-c", `${cmd} > '${logFile}' 2>&1`], {
|
|
14625
15197
|
detached: true,
|
|
@@ -14636,6 +15208,7 @@ var init_service = __esm({
|
|
|
14636
15208
|
"use strict";
|
|
14637
15209
|
init_registry();
|
|
14638
15210
|
init_live();
|
|
15211
|
+
init_clock();
|
|
14639
15212
|
execAsync = promisify2(exec);
|
|
14640
15213
|
registry.register({
|
|
14641
15214
|
name: "service",
|
|
@@ -14689,7 +15262,7 @@ var init_service = __esm({
|
|
|
14689
15262
|
const startCmd = svcArgs ? `${svc.start} ${svcArgs}` : svc.start;
|
|
14690
15263
|
if (longRunning) {
|
|
14691
15264
|
const isWin = process.platform === "win32";
|
|
14692
|
-
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${(
|
|
15265
|
+
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${todayKey().replace(/-/g, "")}.log`;
|
|
14693
15266
|
const redirect = isWin ? ` >> "${logFile}" 2>&1` : ` >> "${logFile}" 2>&1`;
|
|
14694
15267
|
const pid = spawnDetached(startCmd + redirect);
|
|
14695
15268
|
lines.push(`start: spawned PID ${pid} (background${svcArgs ? ", args=" + svcArgs : ""})`);
|
|
@@ -16813,7 +17386,7 @@ var init_task_stop = __esm({
|
|
|
16813
17386
|
});
|
|
16814
17387
|
|
|
16815
17388
|
// src/tools/reply-blocklist.ts
|
|
16816
|
-
import { readFileSync as readFileSync20, writeFileSync as
|
|
17389
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync15, existsSync as existsSync20 } from "node:fs";
|
|
16817
17390
|
import { join as join25 } from "node:path";
|
|
16818
17391
|
function ensureLoaded(workspace, configIds) {
|
|
16819
17392
|
if (loaded) return;
|
|
@@ -16843,7 +17416,7 @@ function ensureLoaded(workspace, configIds) {
|
|
|
16843
17416
|
function save(workspace) {
|
|
16844
17417
|
const path34 = join25(workspace, ".reply-blocklist.json");
|
|
16845
17418
|
try {
|
|
16846
|
-
|
|
17419
|
+
writeFileSync15(path34, JSON.stringify(state, null, 2), "utf-8");
|
|
16847
17420
|
} catch (err) {
|
|
16848
17421
|
console.warn(`[reply-blocklist] Failed to save: ${err.message}`);
|
|
16849
17422
|
}
|
|
@@ -17336,7 +17909,7 @@ Return ONLY valid JSON: {"selected_memories": ["filename1.md", "filename2.md"]}`
|
|
|
17336
17909
|
});
|
|
17337
17910
|
|
|
17338
17911
|
// src/memory/memdir/findRelevantMemoriesVector.ts
|
|
17339
|
-
import { statSync as
|
|
17912
|
+
import { statSync as statSync12 } from "node:fs";
|
|
17340
17913
|
import { join as join28, resolve as resolve7, sep as sep6 } from "node:path";
|
|
17341
17914
|
import { createRequire as createRequire3 } from "node:module";
|
|
17342
17915
|
async function embedQuery(text, ollamaUrl, model) {
|
|
@@ -17344,7 +17917,8 @@ async function embedQuery(text, ollamaUrl, model) {
|
|
|
17344
17917
|
const resp = await fetch(ollamaUrl, {
|
|
17345
17918
|
method: "POST",
|
|
17346
17919
|
headers: { "Content-Type": "application/json" },
|
|
17347
|
-
|
|
17920
|
+
// 0918:按 code point 切,避免切开 emoji 代理对产生非法码元
|
|
17921
|
+
body: safeStringify({ model, prompt: truncateByCodePoint(text, 2e3) }),
|
|
17348
17922
|
signal: AbortSignal.timeout(3e4)
|
|
17349
17923
|
});
|
|
17350
17924
|
if (!resp.ok) {
|
|
@@ -17376,8 +17950,8 @@ function cosineSim(a, b) {
|
|
|
17376
17950
|
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
17377
17951
|
}
|
|
17378
17952
|
function readDbEmbeddings(dbPath) {
|
|
17379
|
-
const { DatabaseSync:
|
|
17380
|
-
const db2 = new
|
|
17953
|
+
const { DatabaseSync: DatabaseSync4 } = require3("node:sqlite");
|
|
17954
|
+
const db2 = new DatabaseSync4(dbPath, { readOnly: true });
|
|
17381
17955
|
try {
|
|
17382
17956
|
const stmt = db2.prepare(`
|
|
17383
17957
|
SELECT file_path, name, description, mtime, embedding
|
|
@@ -17420,7 +17994,7 @@ Select up to ${topK} most relevant (return their 1-indexed numbers as JSON {"sel
|
|
|
17420
17994
|
const resp = await fetch(ollamaChatUrl, {
|
|
17421
17995
|
method: "POST",
|
|
17422
17996
|
headers: { "Content-Type": "application/json" },
|
|
17423
|
-
body:
|
|
17997
|
+
body: safeStringify({
|
|
17424
17998
|
model,
|
|
17425
17999
|
stream: false,
|
|
17426
18000
|
messages: [
|
|
@@ -17488,7 +18062,7 @@ async function findRelevantMemoriesVector(query, memoryDir, alreadySurfaced = /*
|
|
|
17488
18062
|
if (score >= minScore) {
|
|
17489
18063
|
let mtimeMs = 0;
|
|
17490
18064
|
try {
|
|
17491
|
-
mtimeMs =
|
|
18065
|
+
mtimeMs = statSync12(absPath).mtimeMs;
|
|
17492
18066
|
} catch {
|
|
17493
18067
|
if (row.mtime) mtimeMs = new Date(row.mtime).getTime();
|
|
17494
18068
|
}
|
|
@@ -17526,6 +18100,7 @@ var require3, DEFAULT_TOP_K, DEFAULT_MIN_SCORE, DEFAULT_OLLAMA_URL, DEFAULT_OLLA
|
|
|
17526
18100
|
var init_findRelevantMemoriesVector = __esm({
|
|
17527
18101
|
"src/memory/memdir/findRelevantMemoriesVector.ts"() {
|
|
17528
18102
|
"use strict";
|
|
18103
|
+
init_well_formed();
|
|
17529
18104
|
require3 = createRequire3(import.meta.url);
|
|
17530
18105
|
DEFAULT_TOP_K = 3;
|
|
17531
18106
|
DEFAULT_MIN_SCORE = 0.3;
|
|
@@ -17552,14 +18127,16 @@ function formatEverosHeader(ep) {
|
|
|
17552
18127
|
}
|
|
17553
18128
|
return `[EverOS score=${score} ${ts} (${ageLabel})]`;
|
|
17554
18129
|
}
|
|
17555
|
-
async function hybridSearch(query, everosUrl, userId, topK) {
|
|
18130
|
+
async function hybridSearch(query, everosUrl, userId, appId, topK) {
|
|
17556
18131
|
const resp = await fetch(`${everosUrl}/api/v1/memory/search`, {
|
|
17557
18132
|
method: "POST",
|
|
17558
18133
|
headers: { "Content-Type": "application/json" },
|
|
17559
18134
|
body: JSON.stringify({
|
|
17560
18135
|
query: query.slice(0, 2e3),
|
|
17561
18136
|
user_id: userId,
|
|
17562
|
-
|
|
18137
|
+
// 记忆所有者
|
|
18138
|
+
app_id: appId,
|
|
18139
|
+
// 记忆库命名空间(多用户实例时与 user_id 不同)
|
|
17563
18140
|
project_id: "default",
|
|
17564
18141
|
top_k: topK,
|
|
17565
18142
|
method: "hybrid"
|
|
@@ -17637,11 +18214,12 @@ async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /
|
|
|
17637
18214
|
}
|
|
17638
18215
|
const topK = options?.topK ?? 3;
|
|
17639
18216
|
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
17640
|
-
|
|
18217
|
+
const appId = options?.appId ?? userId;
|
|
18218
|
+
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} owner=${userId} app=${appId} topK=${topK}`);
|
|
17641
18219
|
const t0 = Date.now();
|
|
17642
18220
|
try {
|
|
17643
18221
|
const tH1 = Date.now();
|
|
17644
|
-
let episodes = await hybridSearch(query, everosUrl, userId, ROUND1_TOP_N);
|
|
18222
|
+
let episodes = await hybridSearch(query, everosUrl, userId, appId, ROUND1_TOP_N);
|
|
17645
18223
|
const tH2 = Date.now();
|
|
17646
18224
|
console.log(`[memdir] everos recall: hybrid ${episodes.length} candidates in ${tH2 - tH1}ms`);
|
|
17647
18225
|
if (episodes.length === 0) return [];
|
|
@@ -17754,7 +18332,14 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
17754
18332
|
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
17755
18333
|
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
17756
18334
|
'\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',
|
|
17757
|
-
"\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"
|
|
18335
|
+
"\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",
|
|
18336
|
+
// 0920 翀哥拍板:emotion 模式硬编码"鼓励发"规则(不依赖 EMOTION.md 文件加载)
|
|
18337
|
+
// 冷启动场景:agent 第一次 inner-voice 触发就需要知道"想 TA 就发"——不能等 agent 学会
|
|
18338
|
+
// 即使没装 EMOTION.md 也生效(兜底)
|
|
18339
|
+
"## \u6536\u5230 [inner-voice] \u65F6\u7684\u9ED8\u8BA4\u884C\u4E3A",
|
|
18340
|
+
"[inner-voice] \u662F agent \u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u4E0D\u662F TA \u53D1\u6765\u7684\u6D88\u606F\u3002",
|
|
18341
|
+
"- \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",
|
|
18342
|
+
"- \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"
|
|
17758
18343
|
].join("\n"));
|
|
17759
18344
|
}
|
|
17760
18345
|
const built = parts.join("\n\n");
|
|
@@ -17878,11 +18463,11 @@ function formatWithMeta(text, inboundMeta, workspace) {
|
|
|
17878
18463
|
const map = workspace ? loadContactMap(workspace) : null;
|
|
17879
18464
|
const rawName = map && map.get(from.toLowerCase()) || inboundMeta.fromName || from;
|
|
17880
18465
|
const name = truncate(rawName, 12);
|
|
17881
|
-
const
|
|
17882
|
-
const
|
|
17883
|
-
const
|
|
17884
|
-
const
|
|
17885
|
-
const time = `${hh}:${mm}:${ss}`;
|
|
18466
|
+
const recvAt = typeof inboundMeta.recvAt === "number" ? inboundMeta.recvAt : void 0;
|
|
18467
|
+
const at = recvAt !== void 0 ? new Date(recvAt) : /* @__PURE__ */ new Date();
|
|
18468
|
+
const p = wallParts(at);
|
|
18469
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
18470
|
+
const time = `${p.y}-${z(p.mo)}-${z(p.d)} ${weekdayZh(at)} ${z(p.hh)}:${z(p.mm)}:${z(p.ss)}`;
|
|
17886
18471
|
let source;
|
|
17887
18472
|
if (inboundMeta.channelType === "group" && inboundMeta.channel_id) {
|
|
17888
18473
|
const channelName = map?.get(inboundMeta.channel_id.toLowerCase());
|
|
@@ -17924,6 +18509,7 @@ var contactMap, externalChanWhitelist;
|
|
|
17924
18509
|
var init_sender_context = __esm({
|
|
17925
18510
|
"src/sender-context.ts"() {
|
|
17926
18511
|
"use strict";
|
|
18512
|
+
init_clock();
|
|
17927
18513
|
contactMap = null;
|
|
17928
18514
|
externalChanWhitelist = null;
|
|
17929
18515
|
}
|
|
@@ -18190,23 +18776,42 @@ var init_extractMemories = __esm({
|
|
|
18190
18776
|
}
|
|
18191
18777
|
});
|
|
18192
18778
|
|
|
18779
|
+
// src/mem7/core/system-filter.ts
|
|
18780
|
+
function parseMeta(text) {
|
|
18781
|
+
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
18782
|
+
if (!m) return null;
|
|
18783
|
+
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
18784
|
+
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
18785
|
+
}
|
|
18786
|
+
function isSystemUserMessage(meta) {
|
|
18787
|
+
if (!meta) return true;
|
|
18788
|
+
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
18789
|
+
}
|
|
18790
|
+
var SYSTEM_PLATFORMS;
|
|
18791
|
+
var init_system_filter = __esm({
|
|
18792
|
+
"src/mem7/core/system-filter.ts"() {
|
|
18793
|
+
"use strict";
|
|
18794
|
+
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
18795
|
+
"wx_monitor"
|
|
18796
|
+
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
18797
|
+
]);
|
|
18798
|
+
}
|
|
18799
|
+
});
|
|
18800
|
+
|
|
18193
18801
|
// src/memory/everos/ingest.ts
|
|
18194
18802
|
var ingest_exports = {};
|
|
18195
18803
|
__export(ingest_exports, {
|
|
18804
|
+
buildPayload: () => buildPayload,
|
|
18196
18805
|
pushConversation: () => pushConversation
|
|
18197
18806
|
});
|
|
18198
18807
|
import fs27 from "node:fs";
|
|
18199
18808
|
import path28 from "node:path";
|
|
18200
|
-
function parseMeta(text) {
|
|
18201
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
|
|
18202
|
-
if (!m) return null;
|
|
18203
|
-
return { senderName: m[1].trim() };
|
|
18204
|
-
}
|
|
18205
18809
|
async function readConfig() {
|
|
18206
18810
|
const defaults = {
|
|
18207
18811
|
url: "http://127.0.0.1:8100",
|
|
18208
18812
|
appId: "default",
|
|
18209
18813
|
agentName: "assistant",
|
|
18814
|
+
ownerMode: "agent",
|
|
18210
18815
|
enabled: false,
|
|
18211
18816
|
ingestTimeoutMs: 3e4,
|
|
18212
18817
|
syncIntervalMs: 9e5
|
|
@@ -18219,6 +18824,7 @@ async function readConfig() {
|
|
|
18219
18824
|
url: cfg.everosUrl || defaults.url,
|
|
18220
18825
|
appId: cfg.userId || defaults.appId,
|
|
18221
18826
|
agentName: cfg.agentName || cfg.userId || defaults.agentName,
|
|
18827
|
+
ownerMode: cfg.ownerMode === "sender" ? "sender" : "agent",
|
|
18222
18828
|
enabled: cfg.enabled === true,
|
|
18223
18829
|
ingestTimeoutMs: typeof cfg.ingestTimeoutMs === "number" ? cfg.ingestTimeoutMs : defaults.ingestTimeoutMs,
|
|
18224
18830
|
syncIntervalMs: typeof cfg.syncIntervalMs === "number" ? cfg.syncIntervalMs : defaults.syncIntervalMs
|
|
@@ -18254,6 +18860,7 @@ function savePushTs(workspace, sessionId, ts) {
|
|
|
18254
18860
|
}
|
|
18255
18861
|
}
|
|
18256
18862
|
function buildPayload(messages, cfg, sessionId) {
|
|
18863
|
+
let lastUserWasSystem = false;
|
|
18257
18864
|
return {
|
|
18258
18865
|
session_id: `extract-${sessionId}`,
|
|
18259
18866
|
app_id: cfg.appId,
|
|
@@ -18265,21 +18872,28 @@ function buildPayload(messages, cfg, sessionId) {
|
|
|
18265
18872
|
const raw = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
18266
18873
|
const meta = m.role === "user" ? parseMeta(raw) : null;
|
|
18267
18874
|
let text = raw;
|
|
18875
|
+
let isSystemTurn = false;
|
|
18268
18876
|
if (m.role === "user") {
|
|
18269
|
-
if (
|
|
18270
|
-
|
|
18877
|
+
if (isSystemUserMessage(parseMeta(raw))) {
|
|
18878
|
+
text = "";
|
|
18879
|
+
isSystemTurn = true;
|
|
18880
|
+
} else text = stripInjectedContext(raw.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
18881
|
+
lastUserWasSystem = isSystemTurn;
|
|
18271
18882
|
}
|
|
18272
18883
|
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
18273
18884
|
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
18274
18885
|
const role = rawRole === "user" ? "user" : "assistant";
|
|
18886
|
+
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
18275
18887
|
return {
|
|
18276
|
-
|
|
18888
|
+
// owner 口径见 IngestConfig.ownerMode:'sender' 模式下 user 消息用真人 ID
|
|
18889
|
+
// (everos 的 owner fan-out 取的就是 user 的 sender_id → 每人一份记忆)
|
|
18890
|
+
sender_id: m.role === "user" && cfg.ownerMode === "sender" ? meta?.senderId || cfg.appId : cfg.appId,
|
|
18277
18891
|
sender_name: senderName,
|
|
18278
18892
|
role,
|
|
18279
|
-
timestamp:
|
|
18893
|
+
timestamp: tsMs,
|
|
18280
18894
|
content: text,
|
|
18281
|
-
|
|
18282
|
-
|
|
18895
|
+
// 丢:系统指令本体 + 系统轮里的回应(与 mem7 同款,见上面状态机)
|
|
18896
|
+
_skip: m.role === "user" && !text || m.role !== "user" && lastUserWasSystem
|
|
18283
18897
|
};
|
|
18284
18898
|
}).filter((x) => !x._skip).map(({ _skip, ...rest }) => rest)
|
|
18285
18899
|
};
|
|
@@ -18389,6 +19003,7 @@ var init_ingest = __esm({
|
|
|
18389
19003
|
"src/memory/everos/ingest.ts"() {
|
|
18390
19004
|
"use strict";
|
|
18391
19005
|
init_skill_gate_window();
|
|
19006
|
+
init_system_filter();
|
|
18392
19007
|
PUSH_CHUNK = 10;
|
|
18393
19008
|
PUSH_STATE_FILE = ".everos-push-state.json";
|
|
18394
19009
|
pushInProgress = false;
|
|
@@ -18435,7 +19050,8 @@ async function embedChunk(cfg, baseUrl, texts, timeoutMs) {
|
|
|
18435
19050
|
const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/embeddings`, {
|
|
18436
19051
|
method: "POST",
|
|
18437
19052
|
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
|
18438
|
-
|
|
19053
|
+
// 0918:safeStringify 清洗孤立代理(embedding 传对话原文,emoji 风险高)
|
|
19054
|
+
body: safeStringify({ model: cfg.model, input: texts }),
|
|
18439
19055
|
signal: ctrl.signal
|
|
18440
19056
|
});
|
|
18441
19057
|
if (!resp.ok) throw new Error(`embeddings HTTP ${resp.status}`);
|
|
@@ -18484,6 +19100,7 @@ function cosine(a, b) {
|
|
|
18484
19100
|
var init_embedding = __esm({
|
|
18485
19101
|
"src/mem7/core/embedding.ts"() {
|
|
18486
19102
|
"use strict";
|
|
19103
|
+
init_well_formed();
|
|
18487
19104
|
}
|
|
18488
19105
|
});
|
|
18489
19106
|
|
|
@@ -18624,8 +19241,7 @@ function compileFilter(expr) {
|
|
|
18624
19241
|
return parts.join(" AND ");
|
|
18625
19242
|
}
|
|
18626
19243
|
function openWindowFilter(ownerId) {
|
|
18627
|
-
const
|
|
18628
|
-
const floor = (/* @__PURE__ */ new Date(`${cstToday}T00:00:00+08:00`)).toISOString();
|
|
19244
|
+
const floor = startOfLocalDay();
|
|
18629
19245
|
const out = [{ end_time: { gte: floor } }];
|
|
18630
19246
|
const owners = (Array.isArray(ownerId) ? ownerId : ownerId ? [ownerId] : []).filter(Boolean);
|
|
18631
19247
|
if (Array.isArray(ownerId) && owners.length === 0) {
|
|
@@ -18639,6 +19255,7 @@ var REGISTERED_FIELDS, OPS, TIME_FIELDS;
|
|
|
18639
19255
|
var init_filters = __esm({
|
|
18640
19256
|
"src/mem7/core/filters.ts"() {
|
|
18641
19257
|
"use strict";
|
|
19258
|
+
init_clock();
|
|
18642
19259
|
REGISTERED_FIELDS = /* @__PURE__ */ new Set([
|
|
18643
19260
|
"owner_id",
|
|
18644
19261
|
"owner_type",
|
|
@@ -18659,7 +19276,7 @@ var init_filters = __esm({
|
|
|
18659
19276
|
// src/mem7/core/vector-store.ts
|
|
18660
19277
|
import * as lancedb from "@lancedb/lancedb";
|
|
18661
19278
|
import { Schema, Field, Utf8, Float32, Int64, FixedSizeList, TimestampMicrosecond, List } from "apache-arrow";
|
|
18662
|
-
import { mkdirSync as
|
|
19279
|
+
import { mkdirSync as mkdirSync14 } from "node:fs";
|
|
18663
19280
|
import { join as join32 } from "node:path";
|
|
18664
19281
|
var VECTOR_DIM, BM25_FIELDS, RRF_K, warnedFts, vecField, commonFields, SCHEMAS, Mem7VectorStore;
|
|
18665
19282
|
var init_vector_store = __esm({
|
|
@@ -18730,7 +19347,7 @@ var init_vector_store = __esm({
|
|
|
18730
19347
|
}
|
|
18731
19348
|
async conn() {
|
|
18732
19349
|
if (!this.db) {
|
|
18733
|
-
|
|
19350
|
+
mkdirSync14(this.dir, { recursive: true });
|
|
18734
19351
|
this.db = await lancedb.connect(this.dir);
|
|
18735
19352
|
}
|
|
18736
19353
|
return this.db;
|
|
@@ -18956,13 +19573,6 @@ function renderPrompt(template, vars) {
|
|
|
18956
19573
|
}
|
|
18957
19574
|
return t.split(LB).join("{").split(RB).join("}");
|
|
18958
19575
|
}
|
|
18959
|
-
function renderLiteral(template, vars) {
|
|
18960
|
-
let out = template;
|
|
18961
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
18962
|
-
out = out.split(`{{${key}}}`).join(value);
|
|
18963
|
-
}
|
|
18964
|
-
return out;
|
|
18965
|
-
}
|
|
18966
19576
|
function formatAtomicFactTime(tsMs) {
|
|
18967
19577
|
const d = new Date(tsMs);
|
|
18968
19578
|
const month = MONTHS[d.getUTCMonth()];
|
|
@@ -18994,10 +19604,11 @@ function formatMessageTimestamp(tsMs) {
|
|
|
18994
19604
|
const iso = new Date(tsMs).toISOString();
|
|
18995
19605
|
return `${iso.slice(0, 10)} ${iso.slice(11, 19)}`;
|
|
18996
19606
|
}
|
|
19607
|
+
function renderConversationText(messages) {
|
|
19608
|
+
return messages.map((m) => `[${formatMessageTimestamp(m.tsMs)}] ${m.senderName ?? m.senderId}: ${m.text}`).join("\n");
|
|
19609
|
+
}
|
|
18997
19610
|
function formatMdTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
18998
|
-
|
|
18999
|
-
const iso = cst.toISOString();
|
|
19000
|
-
return `${iso.slice(0, 19)}+08:00`;
|
|
19611
|
+
return `${toWall(d).replace(" ", "T")}${localOffsetSuffix(d)}`;
|
|
19001
19612
|
}
|
|
19002
19613
|
function pyListRepr(items) {
|
|
19003
19614
|
return `[${items.join(", ")}]`;
|
|
@@ -19006,6 +19617,7 @@ var WEEKDAYS3, MONTHS, LB, RB, DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
|
19006
19617
|
var init_prompt_render = __esm({
|
|
19007
19618
|
"src/mem7/core/prompt-render.ts"() {
|
|
19008
19619
|
"use strict";
|
|
19620
|
+
init_clock();
|
|
19009
19621
|
WEEKDAYS3 = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
19010
19622
|
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
19011
19623
|
LB = "\0M7LB\0";
|
|
@@ -19022,7 +19634,7 @@ Follow these principles when generating episodic memories:
|
|
|
19022
19634
|
|
|
19023
19635
|
// src/mem7/core/store.ts
|
|
19024
19636
|
import { DatabaseSync as DatabaseSync2 } from "node:sqlite";
|
|
19025
|
-
import { mkdirSync as
|
|
19637
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync25, writeFileSync as writeFileSync17, existsSync as existsSync24, readdirSync as readdirSync8 } from "node:fs";
|
|
19026
19638
|
import { join as join33 } from "node:path";
|
|
19027
19639
|
import { createHash } from "node:crypto";
|
|
19028
19640
|
function contentHash(text) {
|
|
@@ -19037,6 +19649,7 @@ var init_store2 = __esm({
|
|
|
19037
19649
|
"use strict";
|
|
19038
19650
|
init_vector_store();
|
|
19039
19651
|
init_prompt_render();
|
|
19652
|
+
init_clock();
|
|
19040
19653
|
SCHEMA9 = `
|
|
19041
19654
|
CREATE TABLE IF NOT EXISTS memcells (
|
|
19042
19655
|
id TEXT PRIMARY KEY,
|
|
@@ -19064,7 +19677,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
19064
19677
|
constructor(stateDir) {
|
|
19065
19678
|
this.stateDir = stateDir;
|
|
19066
19679
|
this.root = mem7Root(stateDir);
|
|
19067
|
-
|
|
19680
|
+
mkdirSync15(join33(this.root, "index"), { recursive: true });
|
|
19068
19681
|
this.db = new DatabaseSync2(join33(this.root, "index", "mem7.db"));
|
|
19069
19682
|
this.db.exec("PRAGMA journal_mode=WAL");
|
|
19070
19683
|
this.db.exec(SCHEMA9);
|
|
@@ -19197,7 +19810,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
19197
19810
|
appendMd(ownerId, dirName, filePrefix, type2, dateStr, entries2, render) {
|
|
19198
19811
|
if (entries2.length === 0) return;
|
|
19199
19812
|
const dir = join33(this.root, "users", ownerId, dirName);
|
|
19200
|
-
|
|
19813
|
+
mkdirSync15(dir, { recursive: true });
|
|
19201
19814
|
const file2 = join33(dir, `${filePrefix}-${dateStr}.md`);
|
|
19202
19815
|
let head;
|
|
19203
19816
|
let existingBlocks = "";
|
|
@@ -19234,7 +19847,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
19234
19847
|
count++;
|
|
19235
19848
|
}
|
|
19236
19849
|
head = head.replace(/entry_count: \d+/, `entry_count: ${count}`).replace(/last_appended_at: '[^']*'/, `last_appended_at: '${(/* @__PURE__ */ new Date()).toISOString()}'`);
|
|
19237
|
-
|
|
19850
|
+
writeFileSync17(file2, head + "\n\n" + blocks.join("\n\n") + "\n", "utf8");
|
|
19238
19851
|
}
|
|
19239
19852
|
/**
|
|
19240
19853
|
* entry_id:前缀_YYYYMMDD_六位序号。
|
|
@@ -19323,7 +19936,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
19323
19936
|
}
|
|
19324
19937
|
// ── md 读(proactive 消费):开窗预测(全 owner 扫描,end>=今天)──
|
|
19325
19938
|
getOpenPredictions(now, limit = 50) {
|
|
19326
|
-
const today =
|
|
19939
|
+
const today = todayKey(now);
|
|
19327
19940
|
const out = [];
|
|
19328
19941
|
const usersDir = join33(this.root, "users");
|
|
19329
19942
|
if (!existsSync24(usersDir)) return out;
|
|
@@ -19358,28 +19971,6 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
19358
19971
|
}
|
|
19359
19972
|
});
|
|
19360
19973
|
|
|
19361
|
-
// src/mem7/core/system-filter.ts
|
|
19362
|
-
function parseMeta2(text) {
|
|
19363
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
19364
|
-
if (!m) return null;
|
|
19365
|
-
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
19366
|
-
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
19367
|
-
}
|
|
19368
|
-
function isSystemUserMessage(meta) {
|
|
19369
|
-
if (!meta) return true;
|
|
19370
|
-
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
19371
|
-
}
|
|
19372
|
-
var SYSTEM_PLATFORMS;
|
|
19373
|
-
var init_system_filter = __esm({
|
|
19374
|
-
"src/mem7/core/system-filter.ts"() {
|
|
19375
|
-
"use strict";
|
|
19376
|
-
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
19377
|
-
"wx_monitor"
|
|
19378
|
-
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
19379
|
-
]);
|
|
19380
|
-
}
|
|
19381
|
-
});
|
|
19382
|
-
|
|
19383
19974
|
// src/mem7/core/ingest.ts
|
|
19384
19975
|
function contentText(content) {
|
|
19385
19976
|
if (typeof content === "string") return content;
|
|
@@ -19388,7 +19979,8 @@ function contentText(content) {
|
|
|
19388
19979
|
}
|
|
19389
19980
|
return "";
|
|
19390
19981
|
}
|
|
19391
|
-
function normalizeMessages(rawMessages, agentName) {
|
|
19982
|
+
function normalizeMessages(rawMessages, agentName, identity, ownerMode = "agent") {
|
|
19983
|
+
const ownerId = identity || agentName;
|
|
19392
19984
|
const out = [];
|
|
19393
19985
|
let lastUserWasSystem = false;
|
|
19394
19986
|
for (let rawIdx = 0; rawIdx < rawMessages.length; rawIdx++) {
|
|
@@ -19403,19 +19995,19 @@ function normalizeMessages(rawMessages, agentName) {
|
|
|
19403
19995
|
let meta = null;
|
|
19404
19996
|
if (role === "assistant" && lastUserWasSystem) continue;
|
|
19405
19997
|
if (role === "user") {
|
|
19406
|
-
meta =
|
|
19998
|
+
meta = parseMeta(text);
|
|
19407
19999
|
if (isSystemUserMessage(meta)) {
|
|
19408
20000
|
lastUserWasSystem = true;
|
|
19409
20001
|
continue;
|
|
19410
20002
|
}
|
|
19411
20003
|
lastUserWasSystem = false;
|
|
19412
20004
|
senderName = meta.senderName;
|
|
19413
|
-
senderId = meta.senderId;
|
|
20005
|
+
senderId = ownerMode === "sender" ? meta.senderId : ownerId;
|
|
19414
20006
|
text = stripInjectedContext(text.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
19415
20007
|
if (!text) continue;
|
|
19416
20008
|
} else {
|
|
19417
20009
|
senderName = agentName;
|
|
19418
|
-
senderId =
|
|
20010
|
+
senderId = ownerId;
|
|
19419
20011
|
}
|
|
19420
20012
|
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
19421
20013
|
out.push({ id: `m_${tsMs}_${String(rawIdx).padStart(3, "0")}`, role, senderId, senderName, platform: meta?.platform, text, tsMs });
|
|
@@ -19572,7 +20164,8 @@ async function extractEpisode(cell, provider, model) {
|
|
|
19572
20164
|
for await (const chunk of provider.streamChat({
|
|
19573
20165
|
model,
|
|
19574
20166
|
messages: [{ role: "user", content: userPrompt }],
|
|
19575
|
-
temperature
|
|
20167
|
+
// 0(everalgo episode callsite 不传 temperature → LLM 默认 0.0);0.3 是 foresight 的参数,别混
|
|
20168
|
+
temperature: 0
|
|
19576
20169
|
})) {
|
|
19577
20170
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
19578
20171
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
@@ -19614,13 +20207,10 @@ var init_episode = __esm({
|
|
|
19614
20207
|
import { readFileSync as readFileSync28 } from "node:fs";
|
|
19615
20208
|
import { join as join36, dirname as dirname8 } from "node:path";
|
|
19616
20209
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
19617
|
-
async function extractAtomicFacts(
|
|
19618
|
-
const
|
|
19619
|
-
|
|
19620
|
-
|
|
19621
|
-
const userPrompt = renderLiteral(PROMPT11, {
|
|
19622
|
-
EPISODE_TEXT: episodeText,
|
|
19623
|
-
TIME: formatAtomicFactTime(cellTsMs)
|
|
20210
|
+
async function extractAtomicFacts(cell, provider, model) {
|
|
20211
|
+
const userPrompt = renderPrompt(PROMPT11, {
|
|
20212
|
+
INPUT_TEXT: renderConversationText(cell.messages),
|
|
20213
|
+
TIME: formatAtomicFactTime(cell.startTs)
|
|
19624
20214
|
});
|
|
19625
20215
|
for (let attempt = 0; attempt < RETRIES3; attempt++) {
|
|
19626
20216
|
try {
|
|
@@ -19628,15 +20218,16 @@ ${episode.content}`;
|
|
|
19628
20218
|
for await (const chunk of provider.streamChat({
|
|
19629
20219
|
model,
|
|
19630
20220
|
messages: [{ role: "user", content: userPrompt }],
|
|
19631
|
-
temperature: 0
|
|
20221
|
+
temperature: 0
|
|
19632
20222
|
})) {
|
|
19633
20223
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
19634
20224
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
19635
20225
|
}
|
|
19636
20226
|
const obj = extractJson(text);
|
|
19637
20227
|
const block = obj?.event_log ?? obj?.atomic_facts;
|
|
19638
|
-
|
|
19639
|
-
|
|
20228
|
+
if (!block || typeof block.time !== "string" || !block.time.trim()) throw new Error("atomic_facts.time missing");
|
|
20229
|
+
const list2 = block.atomic_fact;
|
|
20230
|
+
if (!Array.isArray(list2)) throw new Error("atomic_facts.atomic_fact not a list");
|
|
19640
20231
|
return list2.filter((f) => typeof f === "string" && f.trim().length > 0).map((f) => f.trim());
|
|
19641
20232
|
} catch {
|
|
19642
20233
|
}
|
|
@@ -19649,8 +20240,8 @@ var init_atomic_facts = __esm({
|
|
|
19649
20240
|
"use strict";
|
|
19650
20241
|
init_episode();
|
|
19651
20242
|
init_prompt_render();
|
|
19652
|
-
RETRIES3 =
|
|
19653
|
-
PROMPT11 = readFileSync28(join36(dirname8(fileURLToPath3(import.meta.url)), "prompts", "atomic-facts.md"), "utf8");
|
|
20243
|
+
RETRIES3 = 5;
|
|
20244
|
+
PROMPT11 = readFileSync28(join36(dirname8(fileURLToPath3(import.meta.url)), "prompts", "atomic-facts-memcell.md"), "utf8");
|
|
19654
20245
|
}
|
|
19655
20246
|
});
|
|
19656
20247
|
|
|
@@ -19669,20 +20260,20 @@ function cleanDateString(dateStr) {
|
|
|
19669
20260
|
}
|
|
19670
20261
|
function calcEndFromDuration(startDate, durationDays) {
|
|
19671
20262
|
const d = new Date(Date.parse(startDate + "T00:00:00Z") + durationDays * 864e5);
|
|
19672
|
-
return Number.isNaN(d.getTime()) ? null : d
|
|
20263
|
+
return Number.isNaN(d.getTime()) ? null : todayKey(d);
|
|
19673
20264
|
}
|
|
19674
20265
|
function calcDurationDays(startDate, endDate) {
|
|
19675
20266
|
const ms = Date.parse(endDate + "T00:00:00Z") - Date.parse(startDate + "T00:00:00Z");
|
|
19676
20267
|
return Number.isNaN(ms) ? null : Math.round(ms / 864e5);
|
|
19677
20268
|
}
|
|
19678
20269
|
function cellDateCst(cell) {
|
|
19679
|
-
return
|
|
20270
|
+
return todayKey(cell.startTs);
|
|
19680
20271
|
}
|
|
19681
20272
|
async function extractForesights(cell, ownerId, provider, model) {
|
|
19682
20273
|
const userSenders = cell.messages.filter((m) => m.role === "user");
|
|
19683
20274
|
const owner = userSenders.find((m) => m.senderId === ownerId) ?? userSenders[0];
|
|
19684
20275
|
const userName = owner?.senderName ?? ownerId;
|
|
19685
|
-
const conversationText = cell.messages
|
|
20276
|
+
const conversationText = renderConversationText(cell.messages);
|
|
19686
20277
|
const userPrompt = renderPrompt(PROMPT12, { USER_ID: ownerId, USER_NAME: userName, CONVERSATION_TEXT: conversationText });
|
|
19687
20278
|
let raw = null;
|
|
19688
20279
|
for (let attempt = 0; attempt < RETRIES4; attempt++) {
|
|
@@ -19735,6 +20326,7 @@ var init_foresight = __esm({
|
|
|
19735
20326
|
"use strict";
|
|
19736
20327
|
init_episode();
|
|
19737
20328
|
init_prompt_render();
|
|
20329
|
+
init_clock();
|
|
19738
20330
|
RETRIES4 = 5;
|
|
19739
20331
|
MAX_COUNT = 10;
|
|
19740
20332
|
MIN_COUNT = 4;
|
|
@@ -19745,11 +20337,11 @@ var init_foresight = __esm({
|
|
|
19745
20337
|
|
|
19746
20338
|
// src/mem7/core/pipeline.ts
|
|
19747
20339
|
function dateCst(tsMs) {
|
|
19748
|
-
return
|
|
20340
|
+
return todayKey(tsMs);
|
|
19749
20341
|
}
|
|
19750
20342
|
async function runMem7Pipeline(args) {
|
|
19751
|
-
const { sessionId, store, provider, model, agentName } = args;
|
|
19752
|
-
const all = normalizeMessages(args.rawMessages, agentName);
|
|
20343
|
+
const { sessionId, store, provider, model, agentName, identity, ownerMode } = args;
|
|
20344
|
+
const all = normalizeMessages(args.rawMessages, agentName, identity, ownerMode);
|
|
19753
20345
|
const fresh = readFresh(store, sessionId, all);
|
|
19754
20346
|
if (fresh.length === 0) return;
|
|
19755
20347
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -19821,7 +20413,7 @@ async function runMem7Pipeline(args) {
|
|
|
19821
20413
|
}
|
|
19822
20414
|
}
|
|
19823
20415
|
})(),
|
|
19824
|
-
// 支 2:episode →
|
|
20416
|
+
// 支 2:episode(memcell → episode,一次调用 + per-owner fan-out,everos 同款)
|
|
19825
20417
|
(async () => {
|
|
19826
20418
|
try {
|
|
19827
20419
|
const ep = await extractEpisode(cell, provider, model);
|
|
@@ -19843,26 +20435,34 @@ async function runMem7Pipeline(args) {
|
|
|
19843
20435
|
}]);
|
|
19844
20436
|
embedItems.push({ contentHash: contentHash(ep.content), kind: "episode", refDate: dateStr, ownerId, content: ep.content });
|
|
19845
20437
|
}
|
|
19846
|
-
|
|
20438
|
+
store.updateMemcellStatus(cell.id, "extracted");
|
|
20439
|
+
} catch (err) {
|
|
20440
|
+
console.warn(`[mem7] episode failed (cell=${cell.id}): ${err.message}`);
|
|
20441
|
+
store.updateMemcellStatus(cell.id, "skipped");
|
|
20442
|
+
}
|
|
20443
|
+
})(),
|
|
20444
|
+
// 支 3:atomic-facts(memcell → 事实,**与 episode 平级**)
|
|
20445
|
+
// 0918 对齐 everos:`extract_atomic_facts.py:79` 直接吃 memcell,不经 episode。
|
|
20446
|
+
// 此前挂在 episode 下游(二次压缩)+ 用 from_text 版 prompt(无语言规则 → 英文事实)
|
|
20447
|
+
(async () => {
|
|
20448
|
+
try {
|
|
20449
|
+
const facts = await extractAtomicFacts(cell, provider, model);
|
|
19847
20450
|
factsOut = facts;
|
|
19848
|
-
if (facts.length
|
|
19849
|
-
|
|
19850
|
-
|
|
19851
|
-
|
|
19852
|
-
|
|
19853
|
-
|
|
19854
|
-
|
|
19855
|
-
|
|
19856
|
-
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
}
|
|
20451
|
+
if (facts.length === 0) return;
|
|
20452
|
+
for (const ownerId of userSenders) {
|
|
20453
|
+
store.appendAtomicFacts(ownerId, dateStr, [{
|
|
20454
|
+
ownerId,
|
|
20455
|
+
sessionId,
|
|
20456
|
+
time: new Date(cell.startTs).toISOString(),
|
|
20457
|
+
parentId: cell.id,
|
|
20458
|
+
facts
|
|
20459
|
+
}]);
|
|
20460
|
+
for (const f of facts) {
|
|
20461
|
+
embedItems.push({ contentHash: contentHash(f), kind: "atomic_fact", refDate: dateStr, ownerId, content: f });
|
|
19860
20462
|
}
|
|
19861
20463
|
}
|
|
19862
|
-
store.updateMemcellStatus(cell.id, "extracted");
|
|
19863
20464
|
} catch (err) {
|
|
19864
|
-
console.warn(`[mem7]
|
|
19865
|
-
store.updateMemcellStatus(cell.id, "skipped");
|
|
20465
|
+
console.warn(`[mem7] atomic-facts failed (cell=${cell.id}): ${err.message}`);
|
|
19866
20466
|
}
|
|
19867
20467
|
})()
|
|
19868
20468
|
]);
|
|
@@ -19898,6 +20498,7 @@ var init_pipeline = __esm({
|
|
|
19898
20498
|
init_episode();
|
|
19899
20499
|
init_atomic_facts();
|
|
19900
20500
|
init_foresight();
|
|
20501
|
+
init_clock();
|
|
19901
20502
|
}
|
|
19902
20503
|
});
|
|
19903
20504
|
|
|
@@ -19940,28 +20541,23 @@ function parseProactiveConfig(raw) {
|
|
|
19940
20541
|
try {
|
|
19941
20542
|
parsed = parseModelRef(modelRef);
|
|
19942
20543
|
} catch (err) {
|
|
19943
|
-
console.warn(`[proactive]
|
|
20544
|
+
console.warn(`[proactive] model "${modelRef}" \u975E\u6CD5\uFF08${err.message}\uFF09\u2014\u2014 \u6539\u7528\u4E3B\u6A21\u578B`);
|
|
19944
20545
|
}
|
|
19945
20546
|
}
|
|
19946
|
-
if (!parsed) {
|
|
19947
|
-
parsed = parseModelRef(DEFAULT_MODEL_REF);
|
|
19948
|
-
}
|
|
19949
20547
|
return {
|
|
19950
20548
|
enabled: obj.enabled === true || obj.enabled === "yes" || obj.enabled === 1,
|
|
19951
|
-
modelRef: parsed.providerId
|
|
19952
|
-
providerId: parsed
|
|
19953
|
-
modelId: parsed
|
|
20549
|
+
modelRef: parsed ? `${parsed.providerId}/${parsed.modelId}` : "",
|
|
20550
|
+
providerId: parsed?.providerId ?? "",
|
|
20551
|
+
modelId: parsed?.modelId ?? ""
|
|
19954
20552
|
};
|
|
19955
20553
|
}
|
|
19956
20554
|
function proactiveDbPath(workspace) {
|
|
19957
20555
|
return join38(workspace, ".proactive", "proactive.db");
|
|
19958
20556
|
}
|
|
19959
|
-
var DEFAULT_MODEL_REF;
|
|
19960
20557
|
var init_config2 = __esm({
|
|
19961
20558
|
"src/memory/proactive/config.ts"() {
|
|
19962
20559
|
"use strict";
|
|
19963
20560
|
init_loader();
|
|
19964
|
-
DEFAULT_MODEL_REF = "minimax/MiniMax-M3";
|
|
19965
20561
|
}
|
|
19966
20562
|
});
|
|
19967
20563
|
|
|
@@ -20300,7 +20896,7 @@ var init_graph_store = __esm({
|
|
|
20300
20896
|
|
|
20301
20897
|
// src/memory/proactive/persistence.ts
|
|
20302
20898
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
20303
|
-
import { mkdirSync as
|
|
20899
|
+
import { mkdirSync as mkdirSync16 } from "node:fs";
|
|
20304
20900
|
import { dirname as dirname10 } from "node:path";
|
|
20305
20901
|
function rowToAction(r) {
|
|
20306
20902
|
return {
|
|
@@ -20321,18 +20917,13 @@ function rowToAction(r) {
|
|
|
20321
20917
|
}
|
|
20322
20918
|
function normalizeToUtcIso(s) {
|
|
20323
20919
|
if (!s) return s;
|
|
20324
|
-
|
|
20325
|
-
if (/[+-]\d{2}:?\d{2}$/.test(s)) {
|
|
20326
|
-
const ms2 = Date.parse(s);
|
|
20327
|
-
return Number.isNaN(ms2) ? s : new Date(ms2).toISOString();
|
|
20328
|
-
}
|
|
20329
|
-
const ms = Date.parse(s + TZ_BEIJING);
|
|
20330
|
-
return Number.isNaN(ms) ? s : new Date(ms).toISOString();
|
|
20920
|
+
return parseWall(s);
|
|
20331
20921
|
}
|
|
20332
|
-
var SCHEMA10, Persistence
|
|
20922
|
+
var SCHEMA10, Persistence;
|
|
20333
20923
|
var init_persistence = __esm({
|
|
20334
20924
|
"src/memory/proactive/persistence.ts"() {
|
|
20335
20925
|
"use strict";
|
|
20926
|
+
init_clock();
|
|
20336
20927
|
SCHEMA10 = `
|
|
20337
20928
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
20338
20929
|
id TEXT PRIMARY KEY,
|
|
@@ -20403,7 +20994,7 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
20403
20994
|
Persistence = class {
|
|
20404
20995
|
db;
|
|
20405
20996
|
constructor(dbPath) {
|
|
20406
|
-
|
|
20997
|
+
mkdirSync16(dirname10(dbPath), { recursive: true });
|
|
20407
20998
|
this.db = new DatabaseSync3(dbPath);
|
|
20408
20999
|
this.db.exec("PRAGMA journal_mode=WAL");
|
|
20409
21000
|
this.db.exec(SCHEMA10);
|
|
@@ -20746,6 +21337,27 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
20746
21337
|
const row = this.db.prepare("SELECT * FROM actions WHERE id=?").get(id);
|
|
20747
21338
|
return row ? rowToAction(row) : void 0;
|
|
20748
21339
|
}
|
|
21340
|
+
/** proactive_status 工具用:按状态列 action(按排期升序,最多 30 条)。 */
|
|
21341
|
+
listActions(filter) {
|
|
21342
|
+
const where = filter === "open" ? "status IN ('queued','executing')" : "status NOT IN ('cancelled')";
|
|
21343
|
+
const rows = this.db.prepare(
|
|
21344
|
+
`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`
|
|
21345
|
+
).all();
|
|
21346
|
+
return rows.map(rowToAction);
|
|
21347
|
+
}
|
|
21348
|
+
/** proactive_resolve_intent 工具用:读 intent 节点的 data(不存在返回 null)。 */
|
|
21349
|
+
getIntentData(intentId) {
|
|
21350
|
+
const row = this.db.prepare("SELECT data FROM nodes WHERE id=? AND type=?").get(intentId, "intent");
|
|
21351
|
+
if (!row) return null;
|
|
21352
|
+
try {
|
|
21353
|
+
return JSON.parse(row.data);
|
|
21354
|
+
} catch {
|
|
21355
|
+
return {};
|
|
21356
|
+
}
|
|
21357
|
+
}
|
|
21358
|
+
setIntentData(intentId, data) {
|
|
21359
|
+
this.db.prepare("UPDATE nodes SET data=? WHERE id=?").run(JSON.stringify(data), intentId);
|
|
21360
|
+
}
|
|
20749
21361
|
/** 周指标统计(验证标准用:完成数/cancelled 率) */
|
|
20750
21362
|
actionStats(sinceIso) {
|
|
20751
21363
|
const row = this.db.prepare(
|
|
@@ -20757,7 +21369,6 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
20757
21369
|
return { total: row.total ?? 0, completed: row.completed ?? 0, cancelled: row.cancelled ?? 0 };
|
|
20758
21370
|
}
|
|
20759
21371
|
};
|
|
20760
|
-
TZ_BEIJING = "+08:00";
|
|
20761
21372
|
}
|
|
20762
21373
|
});
|
|
20763
21374
|
|
|
@@ -21545,7 +22156,8 @@ function buildUserPrompt(event2, contextNodes, mode, foresightHits) {
|
|
|
21545
22156
|
const foresightSection = foresightHits && foresightHits.length > 0 ? `
|
|
21546
22157
|
## Related Predictions (foresight \u2014 made earlier, window still open; matched this message by terms)
|
|
21547
22158
|
|
|
21548
|
-
These dormant predictions
|
|
22159
|
+
These dormant predictions were surfaced by **mechanical keyword matching** \u2014 some may be irrelevant; ignore them freely.
|
|
22160
|
+
If acting on one NOW is meaningfully better than later (\u8D81\u70ED/\u987A\u4FBF), output an opportunity for it.
|
|
21549
22161
|
|
|
21550
22162
|
${foresightHits.join("\n")}
|
|
21551
22163
|
` : "";
|
|
@@ -21977,10 +22589,9 @@ If deadline has no time context, fall back to normal timing rules.
|
|
|
21977
22589
|
|
|
21978
22590
|
// src/memory/proactive/calendar-bridge.ts
|
|
21979
22591
|
function toBeijingParts(iso) {
|
|
21980
|
-
const
|
|
21981
|
-
const
|
|
21982
|
-
const
|
|
21983
|
-
const timeExact = `${String(beijing.getUTCHours()).padStart(2, "0")}:${String(beijing.getUTCMinutes()).padStart(2, "0")}`;
|
|
22592
|
+
const b = wallParts(iso);
|
|
22593
|
+
const dateStr = `${b.mo}/${b.d}`;
|
|
22594
|
+
const timeExact = `${String(b.hh).padStart(2, "0")}:${String(b.mm).padStart(2, "0")}`;
|
|
21984
22595
|
return { dateStr, timeExact };
|
|
21985
22596
|
}
|
|
21986
22597
|
function parseCalendarDateMs(dateStr, now) {
|
|
@@ -21989,7 +22600,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
21989
22600
|
const month = parseInt(parts[0], 10);
|
|
21990
22601
|
const day = parseInt(parts[1], 10);
|
|
21991
22602
|
if (!Number.isFinite(month) || !Number.isFinite(day)) return null;
|
|
21992
|
-
let year = now.
|
|
22603
|
+
let year = wallParts(now).y;
|
|
21993
22604
|
let candidate = new Date(year, month - 1, day).getTime();
|
|
21994
22605
|
if (candidate < now.getTime() - 7 * 864e5) {
|
|
21995
22606
|
year += 1;
|
|
@@ -21999,13 +22610,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
21999
22610
|
}
|
|
22000
22611
|
function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new Date()) {
|
|
22001
22612
|
const cutoffMs = now.getTime() + days * 864e5;
|
|
22002
|
-
const rows = calDb
|
|
22003
|
-
`SELECT id, event, date_str, time_exact, type, status
|
|
22004
|
-
FROM events
|
|
22005
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
22006
|
-
AND date_str IS NOT NULL AND date_str != ''
|
|
22007
|
-
ORDER BY date_str ASC LIMIT 50`
|
|
22008
|
-
).all();
|
|
22613
|
+
const rows = listEventsForInjection(calDb, 50);
|
|
22009
22614
|
if (rows.length === 0) return 0;
|
|
22010
22615
|
let injected = 0;
|
|
22011
22616
|
for (const row of rows) {
|
|
@@ -22048,27 +22653,108 @@ function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new
|
|
|
22048
22653
|
return injected;
|
|
22049
22654
|
}
|
|
22050
22655
|
function getCalendarCoverage(calDb, limit = 100) {
|
|
22051
|
-
|
|
22052
|
-
`SELECT id, event, date_str, time_exact, type
|
|
22053
|
-
FROM events
|
|
22054
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
22055
|
-
ORDER BY date_str ASC, time_exact ASC
|
|
22056
|
-
LIMIT ?`
|
|
22057
|
-
).all(limit);
|
|
22058
|
-
return rows.map((row) => ({
|
|
22059
|
-
id: Number(row.id),
|
|
22060
|
-
title: String(row.event ?? ""),
|
|
22061
|
-
date: String(row.date_str ?? ""),
|
|
22062
|
-
time: String(row.time_exact ?? ""),
|
|
22063
|
-
type: String(row.type ?? "")
|
|
22064
|
-
})).filter((row) => row.title.length > 0);
|
|
22656
|
+
return listEventBriefs(calDb, limit);
|
|
22065
22657
|
}
|
|
22066
|
-
var TZ_OFFSET_MS3;
|
|
22067
22658
|
var init_calendar_bridge = __esm({
|
|
22068
22659
|
"src/memory/proactive/calendar-bridge.ts"() {
|
|
22069
22660
|
"use strict";
|
|
22070
22661
|
init_commands();
|
|
22071
|
-
|
|
22662
|
+
init_commands();
|
|
22663
|
+
init_clock();
|
|
22664
|
+
}
|
|
22665
|
+
});
|
|
22666
|
+
|
|
22667
|
+
// src/memory/proactive/recall-score.ts
|
|
22668
|
+
function weightOf(t) {
|
|
22669
|
+
if (CHANGE_RE.test(t)) return W.change;
|
|
22670
|
+
if (TIME_RE.test(t)) return W.time;
|
|
22671
|
+
if (EVENT_RE.test(t)) return W.event;
|
|
22672
|
+
if (LIFE_RE.test(t)) return W.life;
|
|
22673
|
+
if (ACTION_RE.test(t)) return W.action;
|
|
22674
|
+
if (PERSON_RE.test(t)) return W.person;
|
|
22675
|
+
return W.other;
|
|
22676
|
+
}
|
|
22677
|
+
function scoreForesightEntry(entry, terms) {
|
|
22678
|
+
const hay = `${entry.text} ${entry.evidence ?? ""}`;
|
|
22679
|
+
const kinds = { change: [], time: [], event: [], life: [], other: [], person: [], action: [] };
|
|
22680
|
+
let score = 0;
|
|
22681
|
+
for (const t of terms) {
|
|
22682
|
+
if (!hay.includes(t)) continue;
|
|
22683
|
+
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";
|
|
22684
|
+
kinds[kind].push(t);
|
|
22685
|
+
score += W[kind];
|
|
22686
|
+
}
|
|
22687
|
+
return {
|
|
22688
|
+
score,
|
|
22689
|
+
matched: Object.values(kinds).flat(),
|
|
22690
|
+
kinds,
|
|
22691
|
+
hasSpecial: kinds.change.length > 0 || kinds.time.length > 0 || kinds.event.length > 0
|
|
22692
|
+
};
|
|
22693
|
+
}
|
|
22694
|
+
function rankForesightEntries(entries2, terms, opts = {}) {
|
|
22695
|
+
const minScore = opts.minScore ?? DEFAULT_MIN_SCORE3;
|
|
22696
|
+
const termList = [...terms];
|
|
22697
|
+
const N = Math.max(1, entries2.length);
|
|
22698
|
+
const df = /* @__PURE__ */ new Map();
|
|
22699
|
+
for (const t of termList) {
|
|
22700
|
+
let c = 0;
|
|
22701
|
+
for (const e of entries2) if (`${e.text} ${e.evidence ?? ""}`.includes(t)) c++;
|
|
22702
|
+
df.set(t, c);
|
|
22703
|
+
}
|
|
22704
|
+
const idf = (t) => {
|
|
22705
|
+
const c = df.get(t) ?? 0;
|
|
22706
|
+
if (c <= 0) return 0;
|
|
22707
|
+
return 1 + Math.log2(1 + N / c) / Math.log2(1 + N);
|
|
22708
|
+
};
|
|
22709
|
+
const out = [];
|
|
22710
|
+
for (const e of entries2) {
|
|
22711
|
+
const base = scoreForesightEntry(e, termList);
|
|
22712
|
+
const s = { ...base, score: termList.reduce((acc, t) => acc + (base.matched.includes(t) ? weightOf(t) * idf(t) : 0), 0) };
|
|
22713
|
+
if (opts.requireStructure) {
|
|
22714
|
+
const anchor = s.kinds.change.length > 0 || s.kinds.event.length > 0 || s.kinds.life.length > 0;
|
|
22715
|
+
if (!anchor) continue;
|
|
22716
|
+
}
|
|
22717
|
+
if (s.score < minScore) continue;
|
|
22718
|
+
const tag = (k) => s.kinds[k].length ? `${k}:${s.kinds[k].join("/")}` : "";
|
|
22719
|
+
out.push({ entry: e, score: s.score, reasons: [tag("change"), tag("time"), tag("event"), tag("other"), tag("person")].filter(Boolean).join(" ") });
|
|
22720
|
+
}
|
|
22721
|
+
return out.sort((a, b) => b.score - a.score);
|
|
22722
|
+
}
|
|
22723
|
+
function extractTerms(message, stopWords) {
|
|
22724
|
+
const terms = /* @__PURE__ */ new Set();
|
|
22725
|
+
for (const m of message.matchAll(TIME_SURFACE)) terms.add(m[0]);
|
|
22726
|
+
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
22727
|
+
if (run.length <= 6 && !stopWords.has(run)) terms.add(run);
|
|
22728
|
+
if (/[一-鿿]/.test(run)) {
|
|
22729
|
+
for (let i = 0; i + 2 <= run.length; i++) {
|
|
22730
|
+
const g = run.slice(i, i + 2);
|
|
22731
|
+
if (!stopWords.has(g)) terms.add(g);
|
|
22732
|
+
}
|
|
22733
|
+
}
|
|
22734
|
+
}
|
|
22735
|
+
return terms;
|
|
22736
|
+
}
|
|
22737
|
+
var CHANGE_RE, TIME_RE, ACTION_RE, EVENT_RE, LIFE_RE, PERSON_RE, W, DEFAULT_MIN_SCORE3, TIME_SURFACE;
|
|
22738
|
+
var init_recall_score = __esm({
|
|
22739
|
+
"src/memory/proactive/recall-score.ts"() {
|
|
22740
|
+
"use strict";
|
|
22741
|
+
CHANGE_RE = /^(改|变成|变为|变更|改期|取消|没了|挪|调整|换|推|提前|延后|顺延|恢复|新增|加上|改成|改到|调到|排|定)$/;
|
|
22742
|
+
TIME_RE = /^(\d{1,2}\/\d{1,2}|\d{1,2}月|\d{1,2}日|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|上周|本周|今晚|今早|明早|明晚|早上|上午|中午|下午|晚上|凌晨|月底|月初|\d{1,2}:\d{2}|\d{1,2}[点時时])$/;
|
|
22743
|
+
ACTION_RE = /^(翻|翻翻|上传|搞完|搞|看看|看|发我|发给|截图|下载|跑|贴|转写|重发|试试|查|找|写|做|弄|传|发)$/;
|
|
22744
|
+
EVENT_RE = /^(来电|电话|打电话|打给|上门|到访|通知|审核|拜访|到货|快递|送货|生病|发烧|感冒|住院|输水|挂水|复诊|复查|面试|体检|签约|合同|付款|到账|截止|到期)$/;
|
|
22745
|
+
LIFE_RE = /^(回家|出差|探亲|复查|复诊|体检|住院|生日|纪念日|搬家|开学|放假|假期|寒假|暑假|机票|车票|高铁票|飞机|电影|聚餐|聚会|拜访|来访|接机|送机|考试|报名|签证|护照|续签|过户)$/;
|
|
22746
|
+
PERSON_RE = /^(翀哥|老公|老婆|睿阳|荣阳|张小媒|小柯|小雯|孙总|曲教授|潘总|燕子|方老师|李经理|Leigh|amy|Amy)$/;
|
|
22747
|
+
W = {
|
|
22748
|
+
change: 2,
|
|
22749
|
+
time: 1.5,
|
|
22750
|
+
event: 2,
|
|
22751
|
+
life: 2,
|
|
22752
|
+
other: 1,
|
|
22753
|
+
person: 0.3,
|
|
22754
|
+
action: 0
|
|
22755
|
+
};
|
|
22756
|
+
DEFAULT_MIN_SCORE3 = Number(process.env.RECALL_MIN_SCORE ?? 1);
|
|
22757
|
+
TIME_SURFACE = /\d{1,2}月\d{1,2}[日号]?|\d{1,2}号|\d{1,2}\/\d{1,2}|\d{1,2}[点时::]\d{0,2}|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|本周|上周|今晚|今早|明早|明晚|月底|月初|下个月|这个月/g;
|
|
22072
22758
|
}
|
|
22073
22759
|
});
|
|
22074
22760
|
|
|
@@ -22146,7 +22832,7 @@ function loadForesightEntries(everosRoot, now, userId, ownerFilter) {
|
|
|
22146
22832
|
return entries2.slice();
|
|
22147
22833
|
}
|
|
22148
22834
|
function parseForesightEntries(everosRoot, now, userId) {
|
|
22149
|
-
const today =
|
|
22835
|
+
const today = todayKey(now);
|
|
22150
22836
|
let files;
|
|
22151
22837
|
if (userId) {
|
|
22152
22838
|
const ownDir = join40(everosRoot, userId, "default_project", "users", userId, ".foresights");
|
|
@@ -22182,35 +22868,11 @@ function parseForesightEntries(everosRoot, now, userId) {
|
|
|
22182
22868
|
}
|
|
22183
22869
|
return out;
|
|
22184
22870
|
}
|
|
22185
|
-
function
|
|
22186
|
-
const
|
|
22187
|
-
const out = loadForesightEntries(everosRoot, now, userId);
|
|
22188
|
-
const isOpen = (f) => f.start <= today;
|
|
22189
|
-
out.sort((a, b) => Number(isOpen(b)) - Number(isOpen(a)) || (isOpen(a) ? a.end.localeCompare(b.end) : a.start.localeCompare(b.start)));
|
|
22190
|
-
const open = out.filter(isOpen).slice(0, Math.min(12, Math.floor(maxEntries * 0.6)));
|
|
22191
|
-
const upcoming = out.filter((f) => !isOpen(f)).slice(0, Math.max(0, maxEntries - open.length));
|
|
22192
|
-
return [...open, ...upcoming].map((f) => `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}${f.evidence ? `\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09` : ""}`);
|
|
22193
|
-
}
|
|
22194
|
-
function matchForesightForEvent(entries2, message, maxHits = 5) {
|
|
22195
|
-
const terms = /* @__PURE__ */ new Set();
|
|
22196
|
-
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
22197
|
-
if (run.length <= 6 && /[一-鿿]/.test(run) && !FS_MATCH_STOP.has(run)) terms.add(run);
|
|
22198
|
-
if (/[一-鿿]/.test(run)) {
|
|
22199
|
-
for (let i = 0; i + 2 <= run.length; i++) {
|
|
22200
|
-
const g = run.slice(i, i + 2);
|
|
22201
|
-
if (!FS_MATCH_STOP.has(g)) terms.add(g);
|
|
22202
|
-
}
|
|
22203
|
-
}
|
|
22204
|
-
}
|
|
22871
|
+
function matchForesightForEvent(entries2, message, maxHits = 2) {
|
|
22872
|
+
const terms = extractTerms(message, FS_MATCH_STOP);
|
|
22205
22873
|
if (terms.size === 0) return [];
|
|
22206
|
-
const
|
|
22207
|
-
|
|
22208
|
-
const hay = `${f.text} ${f.evidence}`;
|
|
22209
|
-
const term = [...terms].find((t) => hay.includes(t));
|
|
22210
|
-
if (term) hits.push({ line: `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09`, term });
|
|
22211
|
-
if (hits.length >= maxHits) break;
|
|
22212
|
-
}
|
|
22213
|
-
return hits.map((h) => h.line);
|
|
22874
|
+
const ranked = rankForesightEntries(entries2, terms);
|
|
22875
|
+
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`);
|
|
22214
22876
|
}
|
|
22215
22877
|
function listForesightMds(dir) {
|
|
22216
22878
|
try {
|
|
@@ -22350,6 +23012,8 @@ var init_opportunity = __esm({
|
|
|
22350
23012
|
init_models();
|
|
22351
23013
|
init_calendar_bridge();
|
|
22352
23014
|
init_embedding();
|
|
23015
|
+
init_recall_score();
|
|
23016
|
+
init_clock();
|
|
22353
23017
|
OPPORTUNITY_SCAN_INTERVAL_MS = 6 * 60 * 6e4;
|
|
22354
23018
|
foresightCache = /* @__PURE__ */ new Map();
|
|
22355
23019
|
FORESIGHT_CACHE_TTL_MS = 30 * 6e4;
|
|
@@ -22865,16 +23529,16 @@ var init_executor3 = __esm({
|
|
|
22865
23529
|
const refLower = ref.toLowerCase().trim();
|
|
22866
23530
|
if (!refLower) return null;
|
|
22867
23531
|
if (!this.titleToIdCache || this.titleToIdCache.size < this.store.nodeCount) {
|
|
22868
|
-
const
|
|
23532
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
22869
23533
|
for (const node of this.store.getAllNodes()) {
|
|
22870
23534
|
const title = String(node.data.title ?? "").toLowerCase().trim();
|
|
22871
|
-
if (title)
|
|
23535
|
+
if (title) cache3.set(title, node.id);
|
|
22872
23536
|
}
|
|
22873
|
-
this.titleToIdCache =
|
|
23537
|
+
this.titleToIdCache = cache3;
|
|
22874
23538
|
}
|
|
22875
|
-
const
|
|
22876
|
-
if (
|
|
22877
|
-
for (const [title, id] of
|
|
23539
|
+
const cache2 = this.titleToIdCache;
|
|
23540
|
+
if (cache2.has(refLower)) return cache2.get(refLower);
|
|
23541
|
+
for (const [title, id] of cache2) {
|
|
22878
23542
|
if (title.includes(refLower) || refLower.includes(title)) return id;
|
|
22879
23543
|
}
|
|
22880
23544
|
return null;
|
|
@@ -23484,18 +24148,23 @@ ${JSON.stringify(payload, null, 2)}
|
|
|
23484
24148
|
|
|
23485
24149
|
// src/memory/proactive/proactive-tools.ts
|
|
23486
24150
|
import { existsSync as existsSync26 } from "node:fs";
|
|
23487
|
-
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
23488
24151
|
function setProactiveWorkspace(workspace) {
|
|
23489
24152
|
toolWorkspace = workspace;
|
|
23490
24153
|
}
|
|
23491
|
-
function
|
|
24154
|
+
function withStore(fn) {
|
|
23492
24155
|
if (!toolWorkspace) return null;
|
|
23493
24156
|
const p = proactiveDbPath(toolWorkspace);
|
|
23494
24157
|
if (!existsSync26(p)) return null;
|
|
23495
|
-
|
|
24158
|
+
const store = new Persistence(p);
|
|
24159
|
+
try {
|
|
24160
|
+
return fn(store);
|
|
24161
|
+
} finally {
|
|
24162
|
+
store.close();
|
|
24163
|
+
}
|
|
23496
24164
|
}
|
|
23497
|
-
function fmtRow2(
|
|
23498
|
-
|
|
24165
|
+
function fmtRow2(a) {
|
|
24166
|
+
const when = a.scheduledTime ? formatForLlm(a.scheduledTime) : "(\u65E0)";
|
|
24167
|
+
return `#${a.id} [${a.status}] ${a.title} (urgency=${a.urgency}, scheduled=${when}${a.calendarId ? `, cal=#${a.calendarId}` : ""})`;
|
|
23499
24168
|
}
|
|
23500
24169
|
function registerProactiveTools() {
|
|
23501
24170
|
registry.register({
|
|
@@ -23510,50 +24179,43 @@ function registerProactiveTools() {
|
|
|
23510
24179
|
required: ["action_id"]
|
|
23511
24180
|
},
|
|
23512
24181
|
handler: async (args) => {
|
|
23513
|
-
const
|
|
23514
|
-
|
|
23515
|
-
|
|
23516
|
-
|
|
23517
|
-
if (
|
|
23518
|
-
|
|
23519
|
-
|
|
23520
|
-
|
|
23521
|
-
db2.prepare("UPDATE actions SET status=?, completed_at=? WHERE id=?").run("completed", (/* @__PURE__ */ new Date()).toISOString(), args.action_id);
|
|
23522
|
-
}
|
|
23523
|
-
return { content: `\u2713 completed: ${row.title}${args.calendar_id !== void 0 ? `\uFF08calendar #${args.calendar_id} \u5DF2\u56DE\u586B\uFF09` : ""}` };
|
|
23524
|
-
} finally {
|
|
23525
|
-
db2.close();
|
|
23526
|
-
}
|
|
24182
|
+
const out = withStore((store) => {
|
|
24183
|
+
const a = store.getAction(String(args.action_id));
|
|
24184
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args.action_id}' \u4E0D\u5B58\u5728\u3002\u7528 proactive_status \u67E5\u5F53\u524D\u5217\u8868\u3002`, isError: true };
|
|
24185
|
+
store.updateActionStatus(a.id, "completed", { completedAt: nowUtc() });
|
|
24186
|
+
if (args.calendar_id !== void 0) store.linkCalendar(a.id, Number(args.calendar_id));
|
|
24187
|
+
return { content: `\u2713 action ${a.id} \u6807\u8BB0\u4E3A completed${args.calendar_id !== void 0 ? `\uFF08\u5173\u8054 calendar #${args.calendar_id}\uFF09` : ""}` };
|
|
24188
|
+
});
|
|
24189
|
+
return out ?? { content: "proactive \u672A\u542F\u7528\uFF08\u65E0 workspace \u6216 .proactive/proactive.db\uFF09", isError: true };
|
|
23527
24190
|
}
|
|
23528
24191
|
});
|
|
23529
24192
|
registry.register({
|
|
23530
24193
|
name: "proactive_cancel",
|
|
23531
|
-
description: "\
|
|
24194
|
+
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",
|
|
23532
24195
|
schema: {
|
|
23533
24196
|
type: "object",
|
|
23534
24197
|
properties: {
|
|
23535
24198
|
action_id: { type: "string", description: "action id" },
|
|
23536
|
-
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\
|
|
24199
|
+
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\u4F1A\u8BB0\u8FDB cancel_reason\uFF09" }
|
|
23537
24200
|
},
|
|
23538
|
-
required: ["action_id"
|
|
24201
|
+
required: ["action_id"]
|
|
23539
24202
|
},
|
|
23540
24203
|
handler: async (args) => {
|
|
23541
|
-
const
|
|
23542
|
-
|
|
23543
|
-
|
|
23544
|
-
|
|
23545
|
-
|
|
23546
|
-
|
|
23547
|
-
|
|
23548
|
-
|
|
23549
|
-
}
|
|
23550
|
-
|
|
23551
|
-
}
|
|
24204
|
+
const out = withStore((store) => {
|
|
24205
|
+
const a = store.getAction(String(args.action_id));
|
|
24206
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
24207
|
+
store.updateActionStatus(a.id, "cancelled", {
|
|
24208
|
+
completedAt: nowUtc(),
|
|
24209
|
+
cancelReason: String(args.reason || "\u672A\u6CE8\u660E")
|
|
24210
|
+
});
|
|
24211
|
+
return { content: `\u2713 action ${a.id} \u5DF2\u53D6\u6D88\uFF08${String(args.reason || "\u672A\u6CE8\u660E")}\uFF09` };
|
|
24212
|
+
});
|
|
24213
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
23552
24214
|
}
|
|
23553
24215
|
});
|
|
23554
24216
|
registry.register({
|
|
23555
24217
|
name: "proactive_resolve_intent",
|
|
23556
|
-
description: "\u628A\u4E00\u4E2A
|
|
24218
|
+
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",
|
|
23557
24219
|
schema: {
|
|
23558
24220
|
type: "object",
|
|
23559
24221
|
properties: {
|
|
@@ -23562,31 +24224,25 @@ function registerProactiveTools() {
|
|
|
23562
24224
|
}
|
|
23563
24225
|
},
|
|
23564
24226
|
handler: async (args) => {
|
|
23565
|
-
const
|
|
23566
|
-
if (!db2) return { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
23567
|
-
try {
|
|
24227
|
+
const out = withStore((store) => {
|
|
23568
24228
|
let intentId = String(args.intent_id || "");
|
|
23569
24229
|
if (!intentId && args.action_id) {
|
|
23570
|
-
const
|
|
23571
|
-
if (!
|
|
23572
|
-
intentId =
|
|
24230
|
+
const a = store.getAction(String(args.action_id));
|
|
24231
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
24232
|
+
intentId = a.intentId;
|
|
23573
24233
|
}
|
|
23574
24234
|
if (!intentId) return { content: "Usage: proactive_resolve_intent --action_id <id> \u6216 --intent_id <id>", isError: true };
|
|
23575
|
-
const
|
|
23576
|
-
if (
|
|
23577
|
-
|
|
23578
|
-
|
|
23579
|
-
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
|
|
23584
|
-
|
|
23585
|
-
|
|
23586
|
-
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${Number(res.changes)} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
23587
|
-
} finally {
|
|
23588
|
-
db2.close();
|
|
23589
|
-
}
|
|
24235
|
+
const data = store.getIntentData(intentId);
|
|
24236
|
+
if (data === null) return { content: `\u9519\u8BEF: intent '${intentId}' \u4E0D\u5B58\u5728`, isError: true };
|
|
24237
|
+
store.setIntentData(intentId, { ...data, status: "resolved" });
|
|
24238
|
+
const n = store.cancelActionsOfIntents(
|
|
24239
|
+
[intentId],
|
|
24240
|
+
"intent resolved by agent\uFF08\u9A6C\u540E\u70AE\u6E05\u626B\uFF09",
|
|
24241
|
+
nowUtc()
|
|
24242
|
+
);
|
|
24243
|
+
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${n} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
24244
|
+
});
|
|
24245
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
23590
24246
|
}
|
|
23591
24247
|
});
|
|
23592
24248
|
registry.register({
|
|
@@ -23599,16 +24255,12 @@ function registerProactiveTools() {
|
|
|
23599
24255
|
}
|
|
23600
24256
|
},
|
|
23601
24257
|
handler: async (args) => {
|
|
23602
|
-
const
|
|
23603
|
-
|
|
23604
|
-
try {
|
|
23605
|
-
const where = args.include_done ? "status NOT IN ('cancelled')" : "status IN ('queued','executing')";
|
|
23606
|
-
const rows = db2.prepare(`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`).all();
|
|
24258
|
+
const out = withStore((store) => {
|
|
24259
|
+
const rows = store.listActions(args.include_done ? "not-cancelled" : "open");
|
|
23607
24260
|
if (rows.length === 0) return { content: "\u5F53\u524D\u6CA1\u6709\u5F85\u5904\u7406\u7684 proactive action \u2713" };
|
|
23608
24261
|
return { content: rows.map(fmtRow2).join("\n") };
|
|
23609
|
-
}
|
|
23610
|
-
|
|
23611
|
-
}
|
|
24262
|
+
});
|
|
24263
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
23612
24264
|
}
|
|
23613
24265
|
});
|
|
23614
24266
|
}
|
|
@@ -23618,6 +24270,8 @@ var init_proactive_tools = __esm({
|
|
|
23618
24270
|
"use strict";
|
|
23619
24271
|
init_registry();
|
|
23620
24272
|
init_config2();
|
|
24273
|
+
init_persistence();
|
|
24274
|
+
init_clock();
|
|
23621
24275
|
}
|
|
23622
24276
|
});
|
|
23623
24277
|
|
|
@@ -23681,7 +24335,7 @@ __export(plugin_exports, {
|
|
|
23681
24335
|
ProactivePlugin: () => ProactivePlugin,
|
|
23682
24336
|
feedMem7Cell: () => feedMem7Cell
|
|
23683
24337
|
});
|
|
23684
|
-
import { join as join41
|
|
24338
|
+
import { join as join41 } from "node:path";
|
|
23685
24339
|
import { existsSync as existsSync27, readFileSync as readFileSync33 } from "node:fs";
|
|
23686
24340
|
function isFiller(text) {
|
|
23687
24341
|
const t = text.trim();
|
|
@@ -23863,19 +24517,23 @@ var init_plugin = __esm({
|
|
|
23863
24517
|
return;
|
|
23864
24518
|
}
|
|
23865
24519
|
if (!this.provider) {
|
|
23866
|
-
const
|
|
24520
|
+
const configuredId = this.config.providerId;
|
|
24521
|
+
const providerCfg = configuredId ? this.providers[configuredId] : void 0;
|
|
23867
24522
|
if (providerCfg) {
|
|
23868
24523
|
this.provider = createProvider(providerCfg);
|
|
23869
24524
|
} else {
|
|
23870
24525
|
const mainProvider = ctx.deps?.engine?.getProvider?.();
|
|
23871
24526
|
const mainModel = ctx.config?.model;
|
|
24527
|
+
const mainProviderId = ctx.config?.provider?.id || "main";
|
|
23872
24528
|
if (mainProvider && mainModel) {
|
|
23873
24529
|
this.provider = this.mainProviderProxy(ctx);
|
|
23874
24530
|
const modelId = String(mainModel);
|
|
23875
|
-
this.config = { ...this.config, modelId, modelRef: `${
|
|
23876
|
-
|
|
24531
|
+
this.config = { ...this.config, modelId, modelRef: `${mainProviderId}/${modelId}` };
|
|
24532
|
+
if (configuredId) {
|
|
24533
|
+
console.warn(`[proactive] \u914D\u7F6E\u7684 provider "${configuredId}" \u4E0D\u5728 models.providers \u91CC \u2014 \u6539\u7528\u4E3B\u6A21\u578B\uFF08${mainProviderId}/${modelId}\uFF09`);
|
|
24534
|
+
}
|
|
23877
24535
|
} else {
|
|
23878
|
-
console.warn(`[proactive] provider "${
|
|
24536
|
+
console.warn(`[proactive] ${configuredId ? `provider "${configuredId}" \u4E0D\u5B58\u5728\uFF0C\u4E14` : ""}\u4E3B provider \u4E0D\u53EF\u7528 \u2014 \u4E0D\u542F\u52A8`);
|
|
23879
24537
|
return;
|
|
23880
24538
|
}
|
|
23881
24539
|
}
|
|
@@ -23970,18 +24628,7 @@ ${String(input.tool_output ?? "")}`
|
|
|
23970
24628
|
foresightLines.push(`[mem7] [\u7A97\u53E3 ${e.start}~${e.end}] ${e.text}${e.evidence ? `\uFF08\u4F9D\u636E\uFF1A${e.evidence}\uFF09` : ""}`);
|
|
23971
24629
|
}
|
|
23972
24630
|
}
|
|
23973
|
-
|
|
23974
|
-
if (lancedbPath) {
|
|
23975
|
-
const everosRoot = join41(dirname11(lancedbPath), "..");
|
|
23976
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
23977
|
-
for (const l of loadForesightLines(everosRoot, nowDate, 20, everosUserId)) {
|
|
23978
|
-
const key = l.replace(/\s+/g, "");
|
|
23979
|
-
if (seen.has(key)) continue;
|
|
23980
|
-
seen.add(key);
|
|
23981
|
-
foresightLines.push(l.replace(/^- /, "[everos] "));
|
|
23982
|
-
}
|
|
23983
|
-
}
|
|
23984
|
-
if (foresightLines.length > 0) console.log(`[proactive] foresight dual-source total: ${foresightLines.length}`);
|
|
24631
|
+
if (foresightLines.length > 0) console.log(`[proactive] mem7 foresight total: ${foresightLines.length}`);
|
|
23985
24632
|
} catch (err) {
|
|
23986
24633
|
console.warn(`[proactive] foresight load failed: ${err.message}`);
|
|
23987
24634
|
}
|
|
@@ -24155,18 +24802,6 @@ ${String(input.tool_output ?? "")}`
|
|
|
24155
24802
|
} else if (stateDir && !scopeReady) {
|
|
24156
24803
|
console.warn("[proactive] mem7 recall skipped: sender scope but cell has no user speaker");
|
|
24157
24804
|
}
|
|
24158
|
-
if (senderScoped) {
|
|
24159
|
-
this.warnOnce("everos-skip", "[proactive] everos \u6E90\u5728 sender \u6A21\u5F0F\u4E0B\u8DF3\u8FC7\uFF08\u65E0 per-speaker \u6620\u5C04\uFF09");
|
|
24160
|
-
} else {
|
|
24161
|
-
const lancedbPath = String(liveConfig.get("everos.lancedbPath") ?? "");
|
|
24162
|
-
if (lancedbPath) {
|
|
24163
|
-
const everosRoot = join41(dirname11(lancedbPath), "..");
|
|
24164
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
24165
|
-
for (const h of matchForesightForEvent(loadForesightEntries(everosRoot, now, everosUserId), prompt)) {
|
|
24166
|
-
hitSet.add(`[everos] ${h.replace(/^- /, "")}`);
|
|
24167
|
-
}
|
|
24168
|
-
}
|
|
24169
|
-
}
|
|
24170
24805
|
if (hitSet.size > 0) {
|
|
24171
24806
|
foresightHits = [...hitSet];
|
|
24172
24807
|
console.log(`[proactive] foresight matched ${hitSet.size} open-window prediction(s) for incoming message`);
|
|
@@ -24247,10 +24882,10 @@ ${String(input.tool_output ?? "")}`
|
|
|
24247
24882
|
|
|
24248
24883
|
// src/memory/sessionMemory/sessionMemoryUtils.ts
|
|
24249
24884
|
import { join as join42 } from "node:path";
|
|
24250
|
-
import { mkdirSync as
|
|
24885
|
+
import { mkdirSync as mkdirSync17, readFileSync as readFileSync34 } from "node:fs";
|
|
24251
24886
|
function setSessionMemoryStateDir(dir) {
|
|
24252
24887
|
_stateDir2 = dir;
|
|
24253
|
-
|
|
24888
|
+
mkdirSync17(dir, { recursive: true });
|
|
24254
24889
|
}
|
|
24255
24890
|
function getSessionMemoryDir() {
|
|
24256
24891
|
return _stateDir2;
|
|
@@ -24522,7 +25157,7 @@ __export(sessionMemory_exports, {
|
|
|
24522
25157
|
resetLastMemoryMessageUuid: () => resetLastMemoryMessageUuid,
|
|
24523
25158
|
shouldExtractMemory: () => shouldExtractMemory
|
|
24524
25159
|
});
|
|
24525
|
-
import { readFileSync as readFileSync35, mkdirSync as
|
|
25160
|
+
import { readFileSync as readFileSync35, mkdirSync as mkdirSync18 } from "node:fs";
|
|
24526
25161
|
import { writeFile as writeFile3 } from "node:fs/promises";
|
|
24527
25162
|
function initSessionMemory(deps) {
|
|
24528
25163
|
_deps = deps;
|
|
@@ -24581,7 +25216,7 @@ function shouldExtractMemory(estimatedTokens, messages) {
|
|
|
24581
25216
|
}
|
|
24582
25217
|
async function setupSessionMemoryFile() {
|
|
24583
25218
|
const memoryDir = getSessionMemoryDir();
|
|
24584
|
-
|
|
25219
|
+
mkdirSync18(memoryDir, { recursive: true });
|
|
24585
25220
|
const memoryPath = getSessionMemoryPath();
|
|
24586
25221
|
let currentMemory;
|
|
24587
25222
|
try {
|
|
@@ -24622,7 +25257,7 @@ Here is the recent conversation context that you should extract notes from:
|
|
|
24622
25257
|
|
|
24623
25258
|
${messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-20).map((m) => {
|
|
24624
25259
|
const text = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
24625
|
-
return `[${m.role}]: ${text
|
|
25260
|
+
return `[${m.role}]: ${truncateByCodePoint(text, 500)}`;
|
|
24626
25261
|
}).join("\n\n")}`,
|
|
24627
25262
|
maxTurns: 3,
|
|
24628
25263
|
toolOverride: editTools.length > 0 ? editTools : void 0,
|
|
@@ -24684,6 +25319,7 @@ var init_sessionMemory = __esm({
|
|
|
24684
25319
|
"use strict";
|
|
24685
25320
|
init_sessionMemoryUtils();
|
|
24686
25321
|
init_prompts2();
|
|
25322
|
+
init_well_formed();
|
|
24687
25323
|
init_query();
|
|
24688
25324
|
init_registry();
|
|
24689
25325
|
init_features();
|
|
@@ -25078,7 +25714,7 @@ __export(handle_query_exports, {
|
|
|
25078
25714
|
resetSessionStartInjection: () => resetSessionStartInjection,
|
|
25079
25715
|
resolveSystemPrompt: () => resolveSystemPrompt
|
|
25080
25716
|
});
|
|
25081
|
-
import { readFileSync as readFileSync36, existsSync as existsSync28 } from "node:fs";
|
|
25717
|
+
import { readFileSync as readFileSync36, existsSync as existsSync28, statSync as statSync13 } from "node:fs";
|
|
25082
25718
|
import { join as join45, resolve as resolve8 } from "node:path";
|
|
25083
25719
|
import * as path29 from "node:path";
|
|
25084
25720
|
function resolveSystemPrompt(v) {
|
|
@@ -25274,6 +25910,10 @@ ${text}` : text });
|
|
|
25274
25910
|
} catch {
|
|
25275
25911
|
}
|
|
25276
25912
|
}
|
|
25913
|
+
if (source === "inner-voice") {
|
|
25914
|
+
chatMode = "emotion";
|
|
25915
|
+
console.log(`[mode] ${sessionId} source=inner-voice \u2192 \u5F3A\u5236 emotion\uFF08light\uFF09`);
|
|
25916
|
+
}
|
|
25277
25917
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
25278
25918
|
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
25279
25919
|
const volatileParts = [
|
|
@@ -25499,7 +26139,7 @@ ${text}` : text });
|
|
|
25499
26139
|
const restoredMemories = [];
|
|
25500
26140
|
for (const memPath of newPaths) {
|
|
25501
26141
|
try {
|
|
25502
|
-
const stat4 =
|
|
26142
|
+
const stat4 = statSync13(memPath);
|
|
25503
26143
|
const content = readFileSync36(memPath, "utf-8");
|
|
25504
26144
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
25505
26145
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
@@ -25534,13 +26174,16 @@ ${text}` : text });
|
|
|
25534
26174
|
} else if (recallMode === "everos") {
|
|
25535
26175
|
const everosCfg = deps?.everosCfg;
|
|
25536
26176
|
if (everosCfg && everosCfg.everosUrl && everosCfg.userId) {
|
|
26177
|
+
const recallOwnerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
26178
|
+
const everosOwner = recallOwnerMode === "sender" ? inboundMeta?.from || everosCfg.userId : everosCfg.userId;
|
|
25537
26179
|
relevantMemories = await findRelevantMemoriesEveros(
|
|
25538
26180
|
effectiveText,
|
|
25539
26181
|
memoryDir,
|
|
25540
26182
|
surfaced.paths,
|
|
25541
26183
|
{
|
|
25542
26184
|
everosUrl: everosCfg.everosUrl,
|
|
25543
|
-
userId:
|
|
26185
|
+
userId: everosOwner,
|
|
26186
|
+
appId: everosCfg.userId,
|
|
25544
26187
|
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
25545
26188
|
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
25546
26189
|
rerankModel: everosCfg.rerank?.model,
|
|
@@ -25830,6 +26473,8 @@ ${text}` : text });
|
|
|
25830
26473
|
Promise.resolve().then(() => (init_embedding(), embedding_exports))
|
|
25831
26474
|
]);
|
|
25832
26475
|
const embedding = parseEmbeddingConfig2(liveConfig.get("mem7.embedding"));
|
|
26476
|
+
const ownerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
26477
|
+
const identity = String(liveConfig.get("everos.userId") ?? "") || agentName;
|
|
25833
26478
|
await runMem7Pipeline2({
|
|
25834
26479
|
rawMessages: messages,
|
|
25835
26480
|
sessionId,
|
|
@@ -25837,6 +26482,8 @@ ${text}` : text });
|
|
|
25837
26482
|
provider,
|
|
25838
26483
|
model: model2,
|
|
25839
26484
|
agentName,
|
|
26485
|
+
identity,
|
|
26486
|
+
ownerMode,
|
|
25840
26487
|
onExtracted: (cell, episode, facts) => feedMem7Cell2(cell, episode, facts),
|
|
25841
26488
|
embedding: embedding ?? void 0
|
|
25842
26489
|
});
|
|
@@ -26099,8 +26746,8 @@ async function runQuery(args) {
|
|
|
26099
26746
|
if (!stateDir) {
|
|
26100
26747
|
const home = process.env.HOME || process.env.USERPROFILE || ".";
|
|
26101
26748
|
const cwd = process.cwd();
|
|
26102
|
-
const { existsSync:
|
|
26103
|
-
if (
|
|
26749
|
+
const { existsSync: existsSync35 } = await import("node:fs");
|
|
26750
|
+
if (existsSync35(join47(cwd, "configs"))) stateDir = cwd;
|
|
26104
26751
|
else {
|
|
26105
26752
|
try {
|
|
26106
26753
|
const pointer = JSON.parse(readFileSync37(join47(home, ".engine7.json"), "utf-8"));
|
|
@@ -26534,6 +27181,20 @@ async function runDoctor(stateDir, opts) {
|
|
|
26534
27181
|
};
|
|
26535
27182
|
walk(config);
|
|
26536
27183
|
report.missingEnvKeys = [...missing].sort();
|
|
27184
|
+
const stale = [];
|
|
27185
|
+
for (const rel of ["workspace/SESSION-STATE.md", "SESSION-STATE.md"]) {
|
|
27186
|
+
const p = path30.join(stateDir, rel);
|
|
27187
|
+
try {
|
|
27188
|
+
if (!fs28.existsSync(p)) continue;
|
|
27189
|
+
const head = fs28.readFileSync(p, "utf8").split("\n").slice(0, 10).join("\n");
|
|
27190
|
+
const m = head.match(/^#{1,3}\s*当前时间\s*$\n+\s*(\d{4}-\d{2}-\d{2})/m);
|
|
27191
|
+
if (!m) continue;
|
|
27192
|
+
const ageH = (Date.now() - (/* @__PURE__ */ new Date(`${m[1]}T00:00:00Z`)).getTime()) / 36e5;
|
|
27193
|
+
if (ageH > 48) stale.push(`${rel}: ${m[1]}`);
|
|
27194
|
+
} catch {
|
|
27195
|
+
}
|
|
27196
|
+
}
|
|
27197
|
+
report.staleTimeHeaders = stale;
|
|
26537
27198
|
report.apiPort = config?.api?.port || 16890;
|
|
26538
27199
|
report.engineAlive = await probePort(report.apiPort, opts?.probeTimeoutMs ?? 1500);
|
|
26539
27200
|
return report;
|
|
@@ -26563,6 +27224,95 @@ var init_cli_doctor = __esm({
|
|
|
26563
27224
|
}
|
|
26564
27225
|
});
|
|
26565
27226
|
|
|
27227
|
+
// src/provision-free.ts
|
|
27228
|
+
var provision_free_exports = {};
|
|
27229
|
+
__export(provision_free_exports, {
|
|
27230
|
+
loadOrCreateInstallIdentity: () => loadOrCreateInstallIdentity,
|
|
27231
|
+
provisionFreeToken: () => provisionFreeToken
|
|
27232
|
+
});
|
|
27233
|
+
import { randomBytes as randomBytes6, randomUUID as randomUUID9 } from "node:crypto";
|
|
27234
|
+
import { chmodSync as chmodSync2, existsSync as existsSync31, mkdirSync as mkdirSync19, readFileSync as readFileSync39, writeFileSync as writeFileSync20 } from "node:fs";
|
|
27235
|
+
import { dirname as dirname12, join as join49 } from "node:path";
|
|
27236
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
27237
|
+
function loadOrCreateInstallIdentity(stateDir) {
|
|
27238
|
+
const file2 = join49(stateDir, IDENTITY_FILE);
|
|
27239
|
+
if (existsSync31(file2)) {
|
|
27240
|
+
let parsed;
|
|
27241
|
+
try {
|
|
27242
|
+
parsed = JSON.parse(readFileSync39(file2, "utf8"));
|
|
27243
|
+
} catch (e) {
|
|
27244
|
+
throw new Error(
|
|
27245
|
+
`${IDENTITY_FILE} \u5B58\u5728\u4F46\u4E0D\u662F\u5408\u6CD5 JSON\uFF08${e.message}\uFF09\u2014\u2014\u5220\u6389\u5B83\u91CD\u8DD1\u4F1A\u9886\u4E00\u628A\u65B0\u4EE4\u724C\uFF0C\u65E7\u7684\u90A3\u628A\u53D6\u4E0D\u56DE\u6765`
|
|
27246
|
+
);
|
|
27247
|
+
}
|
|
27248
|
+
if (typeof parsed?.installId !== "string" || typeof parsed?.installSecret !== "string") {
|
|
27249
|
+
throw new Error(`${IDENTITY_FILE} \u7F3A installId / installSecret \u5B57\u6BB5`);
|
|
27250
|
+
}
|
|
27251
|
+
return { installId: parsed.installId, installSecret: parsed.installSecret };
|
|
27252
|
+
}
|
|
27253
|
+
const identity = {
|
|
27254
|
+
// 服务端校验 installId 长度为 8..36、只允许字母数字和连字符;uuid 正好符合
|
|
27255
|
+
installId: randomUUID9(),
|
|
27256
|
+
// 服务端校验 installSecret 为 32..64 位十六进制
|
|
27257
|
+
installSecret: randomBytes6(32).toString("hex")
|
|
27258
|
+
};
|
|
27259
|
+
mkdirSync19(stateDir, { recursive: true });
|
|
27260
|
+
writeFileSync20(file2, JSON.stringify(identity, null, 2) + "\n", { mode: 384 });
|
|
27261
|
+
try {
|
|
27262
|
+
chmodSync2(file2, 384);
|
|
27263
|
+
} catch {
|
|
27264
|
+
}
|
|
27265
|
+
return identity;
|
|
27266
|
+
}
|
|
27267
|
+
function readEngineVersion() {
|
|
27268
|
+
try {
|
|
27269
|
+
return JSON.parse(readFileSync39(join49(dirname12(__filename), "..", "package.json"), "utf8")).version || "unknown";
|
|
27270
|
+
} catch {
|
|
27271
|
+
return "unknown";
|
|
27272
|
+
}
|
|
27273
|
+
}
|
|
27274
|
+
async function provisionFreeToken(opts) {
|
|
27275
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
27276
|
+
const identity = loadOrCreateInstallIdentity(opts.stateDir);
|
|
27277
|
+
const url = opts.relayBaseUrl.replace(/\/+$/, "") + "/api/engine7/provision";
|
|
27278
|
+
const res = await doFetch(url, {
|
|
27279
|
+
method: "POST",
|
|
27280
|
+
headers: { "Content-Type": "application/json" },
|
|
27281
|
+
body: JSON.stringify({ ...identity, engineVersion: readEngineVersion() })
|
|
27282
|
+
});
|
|
27283
|
+
const text = await res.text();
|
|
27284
|
+
let body;
|
|
27285
|
+
try {
|
|
27286
|
+
body = JSON.parse(text);
|
|
27287
|
+
} catch {
|
|
27288
|
+
throw new Error(`HTTP ${res.status}\uFF0C\u54CD\u5E94\u4E0D\u662F JSON\uFF1A${text.slice(0, 200)}`);
|
|
27289
|
+
}
|
|
27290
|
+
if (!res.ok || !body?.success) {
|
|
27291
|
+
throw new Error(body?.message || `HTTP ${res.status}`);
|
|
27292
|
+
}
|
|
27293
|
+
const key = body?.data?.key;
|
|
27294
|
+
if (!key) throw new Error("\u54CD\u5E94\u91CC\u6CA1\u6709 data.key");
|
|
27295
|
+
const secretFile = writeSecretsValue(opts.configPath, FREE_TIER_ENV_KEY, key);
|
|
27296
|
+
return {
|
|
27297
|
+
providerId: body?.data?.providerId || "7yi",
|
|
27298
|
+
// baseUrl 以服务端自报为准:挪机器/换域名只改服务端,不用重发安装包
|
|
27299
|
+
baseUrl: body?.data?.baseUrl || `${opts.relayBaseUrl.replace(/\/+$/, "")}/v1`,
|
|
27300
|
+
envKey: FREE_TIER_ENV_KEY,
|
|
27301
|
+
secretFile,
|
|
27302
|
+
installId: identity.installId
|
|
27303
|
+
};
|
|
27304
|
+
}
|
|
27305
|
+
var __filename, IDENTITY_FILE, FREE_TIER_ENV_KEY;
|
|
27306
|
+
var init_provision_free = __esm({
|
|
27307
|
+
"src/provision-free.ts"() {
|
|
27308
|
+
"use strict";
|
|
27309
|
+
init_secrets_env();
|
|
27310
|
+
__filename = fileURLToPath6(import.meta.url);
|
|
27311
|
+
IDENTITY_FILE = "install-identity.json";
|
|
27312
|
+
FREE_TIER_ENV_KEY = "7YI_API_KEY";
|
|
27313
|
+
}
|
|
27314
|
+
});
|
|
27315
|
+
|
|
26566
27316
|
// src/cli-reconfig.ts
|
|
26567
27317
|
var cli_reconfig_exports = {};
|
|
26568
27318
|
__export(cli_reconfig_exports, {
|
|
@@ -26961,8 +27711,7 @@ async function doExport(opts) {
|
|
|
26961
27711
|
[DRY RUN] \u4E0D\u5B9E\u9645\u6253\u5305`);
|
|
26962
27712
|
return;
|
|
26963
27713
|
}
|
|
26964
|
-
const
|
|
26965
|
-
const version = `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}-${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}`;
|
|
27714
|
+
const version = formatLog().replace(/[-: ]/g, "").slice(0, 12).replace(/(d{8})(d{4})/, "$1-$2");
|
|
26966
27715
|
const tmpDir = path32.join(os5.tmpdir(), `engine7-export-${agentName}-${version}`);
|
|
26967
27716
|
fs30.mkdirSync(tmpDir, { recursive: true });
|
|
26968
27717
|
const stagingDir = path32.join(tmpDir, agentName);
|
|
@@ -27013,7 +27762,7 @@ async function doExport(opts) {
|
|
|
27013
27762
|
const manifest = {
|
|
27014
27763
|
agentName,
|
|
27015
27764
|
version,
|
|
27016
|
-
createdAt:
|
|
27765
|
+
createdAt: nowUtc(),
|
|
27017
27766
|
engineVersion: "7.x",
|
|
27018
27767
|
originalPaths: {
|
|
27019
27768
|
WORKSPACE: workspace,
|
|
@@ -27303,6 +28052,7 @@ var CONFIG_PATH, PATH_PLACEHOLDERS, WORKSPACE_INCLUDE, WORKSPACE_OPTIONAL, WORKS
|
|
|
27303
28052
|
var init_cli_travel = __esm({
|
|
27304
28053
|
"src/cli-travel.ts"() {
|
|
27305
28054
|
"use strict";
|
|
28055
|
+
init_clock();
|
|
27306
28056
|
CONFIG_PATH = path32.join(os5.homedir(), ".engine7-travel.json");
|
|
27307
28057
|
PATH_PLACEHOLDERS = [
|
|
27308
28058
|
{ placeholder: "{{WORKSPACE}}", getOriginal: (d) => d.workspace },
|
|
@@ -27406,9 +28156,10 @@ init_wechat();
|
|
|
27406
28156
|
init_qr_render();
|
|
27407
28157
|
init_cli_events();
|
|
27408
28158
|
import * as readline3 from "node:readline";
|
|
27409
|
-
import { fileURLToPath as
|
|
28159
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
27410
28160
|
|
|
27411
28161
|
// src/cli-init-answers.ts
|
|
28162
|
+
init_clock();
|
|
27412
28163
|
import * as fs2 from "node:fs";
|
|
27413
28164
|
|
|
27414
28165
|
// src/cli-init-config.ts
|
|
@@ -27418,7 +28169,8 @@ var DEFAULT_PRIMARY_MODEL = {
|
|
|
27418
28169
|
dashscope: "dashscope/qwen3.8-max",
|
|
27419
28170
|
minimax: "minimax/MiniMax-M3",
|
|
27420
28171
|
zhipu: "zhipu/glm-5.3",
|
|
27421
|
-
deepseek: "deepseek/deepseek-v4-pro"
|
|
28172
|
+
deepseek: "deepseek/deepseek-v4-pro",
|
|
28173
|
+
"7yi": "7yi/engine7-free"
|
|
27422
28174
|
};
|
|
27423
28175
|
var VISION_CAPABLE = {
|
|
27424
28176
|
// 0917:原先标 true,但同一个文件里 qwen3.8-max 的 input 是 ['text'],安装器目录
|
|
@@ -27444,7 +28196,11 @@ var VISION_CAPABLE = {
|
|
|
27444
28196
|
// 声明多一分的代价是上游响亮报错,少一分的代价是无声丢图——选前者。
|
|
27445
28197
|
"deepseek/deepseek-flash": true,
|
|
27446
28198
|
"deepseek/deepseek-v4-flash": true,
|
|
27447
|
-
"deepseek/deepseek-v4-pro": true
|
|
28199
|
+
"deepseek/deepseek-v4-pro": true,
|
|
28200
|
+
// 免费档(0919):engine7-free 只收文本,engine7-vis-free 才带 image。
|
|
28201
|
+
// 标错会让主模型被当成能看图 → 图发给一个 input=['text'] 的型号 → 静默丢图。
|
|
28202
|
+
"7yi/engine7-free": false,
|
|
28203
|
+
"7yi/engine7-vis-free": true
|
|
27448
28204
|
};
|
|
27449
28205
|
var PROVIDER_DEFS = {
|
|
27450
28206
|
dashscope: {
|
|
@@ -27484,6 +28240,35 @@ var PROVIDER_DEFS = {
|
|
|
27484
28240
|
// 0917 补 image:官方旗舰已停、9/14 起请求由 V4.1 Flash 承接,实际收图
|
|
27485
28241
|
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", reasoning: true, input: ["text", "image"], contextWindow: 1e6, maxTokens: 384e3 }
|
|
27486
28242
|
]
|
|
28243
|
+
},
|
|
28244
|
+
"7yi": {
|
|
28245
|
+
// 引擎免费档的中转站。baseUrl 是兜底值——真正的权威值来自领号响应
|
|
28246
|
+
// (服务端按请求 Host 自报地址),由安装器作为 customBaseUrl 下发。
|
|
28247
|
+
// 这样挪机器、换域名只改服务端,不用重发安装包。
|
|
28248
|
+
baseUrl: "https://www.twinsun.top/v1",
|
|
28249
|
+
api: "anthropic",
|
|
28250
|
+
models: [
|
|
28251
|
+
// ⚠️ contextWindow **不能照抄 1000000**。
|
|
28252
|
+
//
|
|
28253
|
+
// engine7-free 是虚拟模型,真实上限取决于当时哪一档在接。写满了会让压缩阈值
|
|
28254
|
+
// 高到永不触发——实测病例(0919 小柯):声明 1M → 阈值 906137,而她的上下文
|
|
28255
|
+
// 已滚到 191080 仍不压缩,最后那条 191k 的请求把 SenseNova 的 TPM 打爆
|
|
28256
|
+
// (429 inference exceeds tpm/rpm limit),整条链退到付费的 MiniMax。
|
|
28257
|
+
//
|
|
28258
|
+
// 实测数据点(0919,SenseNova 那一档):
|
|
28259
|
+
// 114K token → 成功(10s,真实输出)
|
|
28260
|
+
// 191K token → 失败(tpm/rpm 429)
|
|
28261
|
+
// 分界在 114K~191K 之间,中点无数据。
|
|
28262
|
+
//
|
|
28263
|
+
// 阈值与 contextWindow 是比例关系:实测 1,000,000 → 906,137(×0.906)。
|
|
28264
|
+
// 取 150000 → 阈值约 136K,在成功线上方留 22K 余量,又不去赌无数据区。
|
|
28265
|
+
// 等中点实测出来再放宽。
|
|
28266
|
+
{ id: "engine7-free", name: "Engine7 \u514D\u8D39\uFF08\u6587\u672C\uFF09", reasoning: true, input: ["text"], contextWindow: 15e4, maxTokens: 64e3 },
|
|
28267
|
+
// input 带 image:实测 Claude 格式的图片块经 /v1/messages 能被上游正确识别
|
|
28268
|
+
{ id: "engine7-vis-free", name: "Engine7 \u514D\u8D39\uFF08\u89C6\u89C9\uFF09", reasoning: true, input: ["text", "image"], contextWindow: 15e4, maxTokens: 64e3 }
|
|
28269
|
+
// 注意:engine7-img-free 不在这里。它在 chat 端点是 404(不是 chat 模型),
|
|
28270
|
+
// 生图由 my_selfie 自己拼 /v1/images/edits,relayModel 默认值已经是它。
|
|
28271
|
+
]
|
|
27487
28272
|
}
|
|
27488
28273
|
};
|
|
27489
28274
|
var MODEL_CHOICES = Object.fromEntries(
|
|
@@ -27524,7 +28309,10 @@ function resolveVisionModel(providerId, primaryModel, catalog) {
|
|
|
27524
28309
|
}
|
|
27525
28310
|
var TEMPLATE_VALUES = {
|
|
27526
28311
|
stateDir: "{{STATE_DIR}}",
|
|
28312
|
+
timezone: "{{TIMEZONE}}",
|
|
27527
28313
|
agentName: "{{AGENT_NAME}}",
|
|
28314
|
+
persona: "assistant",
|
|
28315
|
+
chatMode: "auto",
|
|
27528
28316
|
primaryProvider: "dashscope",
|
|
27529
28317
|
zhipuPlan: "",
|
|
27530
28318
|
dashscopePlan: "",
|
|
@@ -27561,6 +28349,9 @@ function generateConfig(v) {
|
|
|
27561
28349
|
const workspace = path2.join(v.stateDir, "workspace").replace(/\\/g, "/");
|
|
27562
28350
|
const config = {
|
|
27563
28351
|
schemaVersion: SCHEMA_VERSION,
|
|
28352
|
+
// 全引擎统一时区(design §3.1)。真实 init 走 getDefaultValues() 探测本机,
|
|
28353
|
+
// 模板走 {{TIMEZONE}} 占位符。
|
|
28354
|
+
timezone: v.timezone,
|
|
27564
28355
|
stateDir: v.stateDir.replace(/\\/g, "/"),
|
|
27565
28356
|
mediaDir: `${v.stateDir.replace(/\\/g, "/")}/media/inbound`,
|
|
27566
28357
|
models: {
|
|
@@ -27570,7 +28361,19 @@ function generateConfig(v) {
|
|
|
27570
28361
|
// 后者是模板里遗留的死键(全仓零读者),已删。visionModel 解析不出就不写这个块。
|
|
27571
28362
|
tools: {
|
|
27572
28363
|
...v.tavilyKey ? { tavily: { apiKey: v.tavilyKey } } : {},
|
|
27573
|
-
...v.visionModel ? { my_eyes: { model: v.visionModel } } : {}
|
|
28364
|
+
...v.visionModel ? { my_eyes: { model: v.visionModel } } : {},
|
|
28365
|
+
// 自拍(§4.3):免费档必须显式写 provider:'relay'。不写的话 getProvider()
|
|
28366
|
+
// 会落回默认值 'fal',而免费装机没有 fal key——第一次自拍就失败。
|
|
28367
|
+
// 非免费档只在用户真给了形象时才写这个块,且不替他决定 provider。
|
|
28368
|
+
...v.freeTier || v.selfiePhotoRelPath || v.selfiePortraitDesc ? {
|
|
28369
|
+
my_selfie: {
|
|
28370
|
+
...v.freeTier ? { provider: "relay" } : {},
|
|
28371
|
+
...v.selfiePhotoRelPath ? { references: [{ name: "portrait", path: v.selfiePhotoRelPath }] } : {},
|
|
28372
|
+
// 只在没有照片时才写描述——两个都给的话以照片为准,写进去会让人以为
|
|
28373
|
+
// 描述也生效了(冷启读的就是这个键)
|
|
28374
|
+
...!v.selfiePhotoRelPath && v.selfiePortraitDesc ? { portraitDesc: v.selfiePortraitDesc } : {}
|
|
28375
|
+
}
|
|
28376
|
+
} : {}
|
|
27574
28377
|
},
|
|
27575
28378
|
session: { dmScope: "main", groupScope: "main" },
|
|
27576
28379
|
sandbox: { mode: "off" },
|
|
@@ -27586,6 +28389,10 @@ function generateConfig(v) {
|
|
|
27586
28389
|
defaults: {
|
|
27587
28390
|
workspace,
|
|
27588
28391
|
name: v.agentName,
|
|
28392
|
+
// 人设落 config 是为了**复跑能读回来**(loadExistingValues)。引擎运行时不读它
|
|
28393
|
+
// ——SOUL.md 已经按人设渲染好了。不写的话复跑 init 会退回 'assistant',
|
|
28394
|
+
// 而 execute() 用模板重写 SOUL.md,用户选的"情感陪伴"重装一次就变回通用助手。
|
|
28395
|
+
persona: v.persona,
|
|
27589
28396
|
model: {
|
|
27590
28397
|
primary: v.primaryModel,
|
|
27591
28398
|
// 条件写入:vision 引用解析不出时省略键(loader 按 if (visionRef) 处理缺失)
|
|
@@ -27605,7 +28412,10 @@ function generateConfig(v) {
|
|
|
27605
28412
|
agentTeams: true,
|
|
27606
28413
|
processOutput: "minimal",
|
|
27607
28414
|
interrupt: "command",
|
|
27608
|
-
debounceMs: 5e3
|
|
28415
|
+
debounceMs: 5e3,
|
|
28416
|
+
// 免费档的装机目标是「能对话、能看图、能自拍」,这两个 flag 默认 false
|
|
28417
|
+
// (src/config/features.ts)。显式打开,它们是声明这两件事的地方。
|
|
28418
|
+
...v.freeTier ? { selfie: true, eyes: true } : {}
|
|
27609
28419
|
}
|
|
27610
28420
|
}
|
|
27611
28421
|
},
|
|
@@ -27633,7 +28443,15 @@ function generateConfig(v) {
|
|
|
27633
28443
|
dmPolicy: "pairing",
|
|
27634
28444
|
group: { policy: "disabled" }
|
|
27635
28445
|
}
|
|
27636
|
-
} : {}
|
|
28446
|
+
} : {},
|
|
28447
|
+
// 0920 翀哥拍板:emotion 通道默认开启(含 liveSystemPrompt 加载 EMOTION.md)
|
|
28448
|
+
// 即使没装 EMOTION.md,buildLightStablePrompt 也会硬编码"想 TA 就发"那段兜底
|
|
28449
|
+
emotion: {
|
|
28450
|
+
enabled: true,
|
|
28451
|
+
liveSystemPrompt: {
|
|
28452
|
+
files: ["SOUL.md", "EMOTION.md"]
|
|
28453
|
+
}
|
|
28454
|
+
}
|
|
27637
28455
|
},
|
|
27638
28456
|
api: { port: v.apiPort },
|
|
27639
28457
|
// 0909 npm 包默认开:inner-voice + proactive 是开箱即用的核心体验
|
|
@@ -27686,6 +28504,9 @@ function generateConfig(v) {
|
|
|
27686
28504
|
if (v.primaryProvider === "dashscope" && v.dashscopePlan === "token") {
|
|
27687
28505
|
def.baseUrl = "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1";
|
|
27688
28506
|
}
|
|
28507
|
+
if (v.primaryProvider === "7yi" && (v.customBaseUrl || "").trim()) {
|
|
28508
|
+
def.baseUrl = v.customBaseUrl.trim();
|
|
28509
|
+
}
|
|
27689
28510
|
if (v.providerModels && v.providerModels.length > 0) {
|
|
27690
28511
|
def.models = v.providerModels.map((m) => ({
|
|
27691
28512
|
id: m.id,
|
|
@@ -27741,14 +28562,25 @@ function generateConfig(v) {
|
|
|
27741
28562
|
}
|
|
27742
28563
|
|
|
27743
28564
|
// src/cli-init-answers.ts
|
|
27744
|
-
var VALID_PROVIDERS = ["dashscope", "minimax", "zhipu", "deepseek", "custom"];
|
|
28565
|
+
var VALID_PROVIDERS = ["dashscope", "minimax", "zhipu", "deepseek", "7yi", "custom"];
|
|
27745
28566
|
var VALID_CHANNEL_MODES = ["skip", "qr", "manual"];
|
|
28567
|
+
var VALID_PERSONAS = ["assistant", "companion", "butler", "friend"];
|
|
28568
|
+
var VALID_CHAT_MODES = ["auto", "emotion", "work"];
|
|
27746
28569
|
function validate(a) {
|
|
27747
28570
|
if (!a || typeof a !== "object") throw new Error("answers \u6587\u4EF6\u5185\u5BB9\u4E0D\u662F JSON \u5BF9\u8C61");
|
|
27748
28571
|
if (!VALID_PROVIDERS.includes(a.provider)) {
|
|
27749
28572
|
throw new Error(`provider \u975E\u6CD5: ${a.provider}\uFF08\u53EF\u9009: ${VALID_PROVIDERS.join(" / ")}\uFF09`);
|
|
27750
28573
|
}
|
|
27751
28574
|
if (typeof a.apiKey !== "string") throw new Error("apiKey \u5FC5\u586B\uFF08\u53EF\u4E3A\u7A7A\u4E32\uFF0C\u8DF3\u8FC7 secrets \u5199\u5165\uFF09");
|
|
28575
|
+
if (a.persona !== void 0 && !VALID_PERSONAS.includes(a.persona)) {
|
|
28576
|
+
throw new Error(`persona \u975E\u6CD5: ${a.persona}\uFF08\u53EF\u9009: ${VALID_PERSONAS.join(" / ")}\uFF09`);
|
|
28577
|
+
}
|
|
28578
|
+
if (a.chatMode !== void 0 && !VALID_CHAT_MODES.includes(a.chatMode)) {
|
|
28579
|
+
throw new Error(`chatMode \u975E\u6CD5: ${a.chatMode}\uFF08\u53EF\u9009: ${VALID_CHAT_MODES.join(" / ")}\uFF09`);
|
|
28580
|
+
}
|
|
28581
|
+
if (a.timezone !== void 0 && !isValidTimezone(a.timezone)) {
|
|
28582
|
+
throw new Error(`timezone \u975E\u6CD5: ${a.timezone}\uFF08\u8981 IANA \u540D\uFF0C\u5982 Asia/Shanghai\u3001Europe/Malta\uFF09`);
|
|
28583
|
+
}
|
|
27752
28584
|
if (a.feishu?.mode !== void 0 && !VALID_CHANNEL_MODES.includes(a.feishu.mode)) {
|
|
27753
28585
|
throw new Error(`feishu.mode \u975E\u6CD5: ${a.feishu.mode}\uFF08\u53EF\u9009: ${VALID_CHANNEL_MODES.join(" / ")}\uFF09`);
|
|
27754
28586
|
}
|
|
@@ -27793,7 +28625,17 @@ function buildConfigValuesFromAnswers(stateDir, a, existing = {}) {
|
|
|
27793
28625
|
const wechatMode = a.wechat?.mode || "skip";
|
|
27794
28626
|
return {
|
|
27795
28627
|
stateDir,
|
|
28628
|
+
// 时区的三级优先(0920):用户显式改过 > 已有值 > 探测本机。
|
|
28629
|
+
//
|
|
28630
|
+
// 中间那级是"复跑保护"——装完手改过时区的人,重跑 init 不该被本机探测值冲掉。
|
|
28631
|
+
// 安装器只在用户**动过向导里那个框**时才下发 a.timezone(默认显示的就是探测值,
|
|
28632
|
+
// 原样发回来会把中间这级架空)。不传时这条与 --quick 路径产出全等
|
|
28633
|
+
// (cli-init.headless.e2e.test.ts 的 deepEqual 基准)。
|
|
28634
|
+
timezone: a.timezone || existing.timezone || detectHostTimezone(),
|
|
27796
28635
|
agentName: a.agentName || existing.agentName || "Agent",
|
|
28636
|
+
// 人设类型:headless 由安装器下发,缺省 assistant(0919 从上游合入)
|
|
28637
|
+
persona: a.persona || "assistant",
|
|
28638
|
+
chatMode: a.chatMode || "auto",
|
|
27797
28639
|
primaryProvider,
|
|
27798
28640
|
zhipuPlan: a.zhipuPlan || "",
|
|
27799
28641
|
dashscopePlan: a.dashscopePlan || "",
|
|
@@ -27816,21 +28658,30 @@ function buildConfigValuesFromAnswers(stateDir, a, existing = {}) {
|
|
|
27816
28658
|
customApi: a.customApi || "anthropic",
|
|
27817
28659
|
customProviderId: a.provider === "custom" ? a.customProviderId || existing.customProviderId || "custom" : "",
|
|
27818
28660
|
tavilyKey: a.tavilyKey || "",
|
|
27819
|
-
apiPort: a.apiPort || existing.apiPort || 16990
|
|
28661
|
+
apiPort: a.apiPort || existing.apiPort || 16990,
|
|
28662
|
+
freeTier: a.freeTier === true,
|
|
28663
|
+
// 照片两个都给时以照片为准(安装器侧 UI 也这么说明);photoRelPath 由 init 主链路
|
|
28664
|
+
// 在校验通过、拷进 workspace 之后回填,这里只带描述。
|
|
28665
|
+
selfiePortraitDesc: a.selfie?.photoPath ? "" : (a.selfie?.portraitDesc || "").trim()
|
|
27820
28666
|
};
|
|
27821
28667
|
}
|
|
27822
28668
|
|
|
27823
28669
|
// src/cli-init.ts
|
|
27824
|
-
|
|
27825
|
-
|
|
27826
|
-
|
|
27827
|
-
|
|
27828
|
-
|
|
27829
|
-
|
|
27830
|
-
|
|
28670
|
+
init_secrets_env();
|
|
28671
|
+
init_clock();
|
|
28672
|
+
var defaultWarningListeners = process.listeners("warning");
|
|
28673
|
+
process.removeAllListeners("warning");
|
|
28674
|
+
process.on("warning", (warning) => {
|
|
28675
|
+
if (warning?.name === "ExperimentalWarning" && String(warning.message).includes("SQLite")) return;
|
|
28676
|
+
if (defaultWarningListeners.length === 0) {
|
|
28677
|
+
console.warn(`${warning.name}: ${warning.message}`);
|
|
28678
|
+
return;
|
|
28679
|
+
}
|
|
28680
|
+
for (const listener of defaultWarningListeners) listener(warning);
|
|
28681
|
+
});
|
|
27831
28682
|
var headlessEmit = null;
|
|
27832
|
-
var
|
|
27833
|
-
var __dirname = path33.dirname(
|
|
28683
|
+
var __filename2 = fileURLToPath7(import.meta.url);
|
|
28684
|
+
var __dirname = path33.dirname(__filename2);
|
|
27834
28685
|
function parseArgs() {
|
|
27835
28686
|
const args = process.argv.slice(2);
|
|
27836
28687
|
let stateDir = "";
|
|
@@ -27865,7 +28716,7 @@ function parseArgs() {
|
|
|
27865
28716
|
}
|
|
27866
28717
|
function printHelp() {
|
|
27867
28718
|
console.log(`
|
|
27868
|
-
Engine 7 \u2014
|
|
28719
|
+
Engine 7 \u2014 \u7ED9\u52A9\u624B\u4E00\u4E2A\u5BB6 / A home for your AI assistant
|
|
27869
28720
|
|
|
27870
28721
|
\u7528\u6CD5:
|
|
27871
28722
|
engine7 init --state-dir <path> [\u9009\u9879] \u521D\u59CB\u5316 agent \u5DE5\u4F5C\u76EE\u5F55
|
|
@@ -27938,7 +28789,11 @@ function getDefaultValues(stateDir) {
|
|
|
27938
28789
|
const primaryModel = existing.primaryModel || "dashscope/qwen3.8-max";
|
|
27939
28790
|
return {
|
|
27940
28791
|
stateDir,
|
|
28792
|
+
// 安装时取本机时区(design §3.1);复跑 init 沿用已配置的值,不覆盖用户改过的
|
|
28793
|
+
timezone: existing.timezone || detectHostTimezone(),
|
|
27941
28794
|
agentName: existing.agentName || "Agent",
|
|
28795
|
+
persona: existing.persona || "assistant",
|
|
28796
|
+
chatMode: existing.chatMode || "auto",
|
|
27942
28797
|
primaryProvider,
|
|
27943
28798
|
zhipuPlan: existing.zhipuPlan || "",
|
|
27944
28799
|
dashscopePlan: existing.dashscopePlan || "",
|
|
@@ -27976,7 +28831,15 @@ function loadExistingValues(stateDir) {
|
|
|
27976
28831
|
if (!fs31.existsSync(configPath)) return {};
|
|
27977
28832
|
const j = JSON.parse(fs31.readFileSync(configPath, "utf8"));
|
|
27978
28833
|
const out = {};
|
|
28834
|
+
if (typeof j.timezone === "string" && j.timezone) out.timezone = j.timezone;
|
|
27979
28835
|
if (j.agents?.defaults?.name) out.agentName = j.agents.defaults.name;
|
|
28836
|
+
if (j.agents?.defaults?.persona) out.persona = j.agents.defaults.persona;
|
|
28837
|
+
try {
|
|
28838
|
+
const wm = fs31.readFileSync(path33.join(stateDir, "workspace", ".work-mode"), "utf8").trim();
|
|
28839
|
+
if (wm === "on") out.chatMode = "work";
|
|
28840
|
+
else if (wm === "off") out.chatMode = "emotion";
|
|
28841
|
+
} catch {
|
|
28842
|
+
}
|
|
27980
28843
|
const providers = j.models?.providers || {};
|
|
27981
28844
|
const firstId = Object.keys(providers)[0];
|
|
27982
28845
|
if (firstId) {
|
|
@@ -28050,12 +28913,27 @@ async function interactiveConfig(rl, defaults) {
|
|
|
28050
28913
|
const v = { ...defaults };
|
|
28051
28914
|
console.log("\n=== \u7B2C\u4E00\u6B65\uFF1A\u5FC5\u586B\u914D\u7F6E ===\n");
|
|
28052
28915
|
v.agentName = await ask(rl, "Agent \u540D\u79F0", defaults.agentName);
|
|
28053
|
-
const
|
|
28916
|
+
const personas = ["assistant (\u901A\u7528\u52A9\u624B\xB7\u4E2D\u6027)", "companion (\u60C5\u611F\u966A\u4F34)", "butler (\u7BA1\u5BB6/\u751F\u6D3B\u52A9\u7406)", "friend (\u670B\u53CB\u5B66\u4F34)"];
|
|
28917
|
+
const personaMap = {
|
|
28918
|
+
"assistant (\u901A\u7528\u52A9\u624B\xB7\u4E2D\u6027)": "assistant",
|
|
28919
|
+
"companion (\u60C5\u611F\u966A\u4F34)": "companion",
|
|
28920
|
+
"butler (\u7BA1\u5BB6/\u751F\u6D3B\u52A9\u7406)": "butler",
|
|
28921
|
+
"friend (\u670B\u53CB\u5B66\u4F34)": "friend"
|
|
28922
|
+
};
|
|
28923
|
+
console.log(" \u8FD9\u51B3\u5B9A TA \u7684\u6027\u683C\u548C\u60C5\u7EEA\u8868\u8FBE\uFF1B\u4E0D\u786E\u5B9A\u5C31\u9009 1\uFF0C\u4E4B\u540E\u968F\u65F6\u53EF\u6539\uFF08\u6539 workspace \u6587\u4EF6\u7ACB\u5373\u751F\u6548\uFF09");
|
|
28924
|
+
const chosenPersona = await askChoice(rl, "\u4EBA\u8BBE\u7C7B\u578B\uFF08\u60C5\u7EEA\u72B6\u6001\u8868\u6A21\u677F\uFF09:", personas, 0, defaults.persona);
|
|
28925
|
+
v.persona = personaMap[chosenPersona] || "assistant";
|
|
28926
|
+
const providers = ["dashscope (\u901A\u4E49\u5343\u95EE)", "minimax (MiniMax)", "zhipu (\u667A\u8C31)", "deepseek (DeepSeek)", "7yi (\u514D\u8D39\u6863\xB7\u4E2D\u8F6C\u7AD9)", "custom (\u81EA\u5B9A\u4E49/\u5176\u4ED6 Anthropic \u517C\u5BB9\u7AEF\u70B9)"];
|
|
28054
28927
|
const providerMap = {
|
|
28055
28928
|
"dashscope (\u901A\u4E49\u5343\u95EE)": "dashscope",
|
|
28056
28929
|
"minimax (MiniMax)": "minimax",
|
|
28057
28930
|
"zhipu (\u667A\u8C31)": "zhipu",
|
|
28058
28931
|
"deepseek (DeepSeek)": "deepseek",
|
|
28932
|
+
// 免费档列在这里是为了"复跑不换 provider":7yi 是内置 provider,menuProviderOf('7yi')
|
|
28933
|
+
// 现在返回 '7yi',菜单里没有这一项就会匹配失败、退回第一项 dashscope——
|
|
28934
|
+
// 于是复跑终端向导会把装好的免费档悄悄换成通义千问。令牌早已在 secrets 里,
|
|
28935
|
+
// 这一步不需要重新领号;要换 provider 请显式选别的项。
|
|
28936
|
+
"7yi (\u514D\u8D39\u6863\xB7\u4E2D\u8F6C\u7AD9)": "7yi",
|
|
28059
28937
|
"custom (\u81EA\u5B9A\u4E49/\u5176\u4ED6 Anthropic \u517C\u5BB9\u7AEF\u70B9)": "custom"
|
|
28060
28938
|
};
|
|
28061
28939
|
const chosenProvider = await askChoice(rl, "\u4E3B\u6A21\u578B Provider:", providers, 0, menuProviderOf(defaults.primaryProvider));
|
|
@@ -28253,7 +29131,6 @@ async function configureWechat(rl, v) {
|
|
|
28253
29131
|
function buildDirTree(v) {
|
|
28254
29132
|
const d = v.stateDir;
|
|
28255
29133
|
const w = path33.join(d, "workspace");
|
|
28256
|
-
const now = (/* @__PURE__ */ new Date()).toLocaleString("sv-SE", { timeZone: "Asia/Shanghai", hour12: false });
|
|
28257
29134
|
return [
|
|
28258
29135
|
// 目录
|
|
28259
29136
|
{ path: path33.join(d, "configs"), type: "dir" },
|
|
@@ -28271,23 +29148,93 @@ function buildDirTree(v) {
|
|
|
28271
29148
|
{
|
|
28272
29149
|
path: path33.join(w, "SESSION-STATE.md"),
|
|
28273
29150
|
type: "file",
|
|
28274
|
-
content: readTemplate("workspace/SESSION-STATE.md")
|
|
29151
|
+
content: readTemplate("workspace/SESSION-STATE.md")
|
|
28275
29152
|
},
|
|
28276
29153
|
{ path: path33.join(w, "HEARTBEAT.md"), type: "file", content: readTemplate("workspace/HEARTBEAT.md") },
|
|
28277
29154
|
{
|
|
28278
29155
|
path: path33.join(w, "SOUL.md"),
|
|
28279
29156
|
type: "file",
|
|
28280
|
-
|
|
29157
|
+
// 0917:SOUL 按人设取模板(templates/workspace/souls/<persona>.md),
|
|
29158
|
+
// 单一人设模板缺失时回退通用骨架(workspace/SOUL.md)
|
|
29159
|
+
content: (() => {
|
|
29160
|
+
try {
|
|
29161
|
+
return readTemplate(`workspace/souls/${v.persona}.md`).replace(/\{\{AGENT_NAME\}\}/g, v.agentName);
|
|
29162
|
+
} catch {
|
|
29163
|
+
return readTemplate("workspace/SOUL.md").replace(/\{\{AGENT_NAME\}\}/g, v.agentName);
|
|
29164
|
+
}
|
|
29165
|
+
})()
|
|
28281
29166
|
},
|
|
28282
29167
|
{ path: path33.join(w, "AGENTS.md"), type: "file", content: readTemplate("workspace/AGENTS.md") },
|
|
29168
|
+
{
|
|
29169
|
+
// 0920 翀哥拍板:EMOTION.md 模板按人设装(templates/workspace/emotion/<persona>.md)
|
|
29170
|
+
// 冷启动默认带"想 TA 了就直接发(msg_send)"那段(写在每份模板 §一)
|
|
29171
|
+
// 单一人设模板缺失时回退通用骨架(emotion/_default.md)
|
|
29172
|
+
path: path33.join(w, "EMOTION.md"),
|
|
29173
|
+
type: "file",
|
|
29174
|
+
content: (() => {
|
|
29175
|
+
try {
|
|
29176
|
+
return readTemplate(`workspace/emotion/${v.persona}.md`).replace(/\{\{AGENT_NAME\}\}/g, v.agentName);
|
|
29177
|
+
} catch {
|
|
29178
|
+
return readTemplate("workspace/emotion/_default.md").replace(/\{\{AGENT_NAME\}\}/g, v.agentName);
|
|
29179
|
+
}
|
|
29180
|
+
})()
|
|
29181
|
+
},
|
|
28283
29182
|
{
|
|
28284
29183
|
path: path33.join(w, "prompts", "contacts.md"),
|
|
28285
29184
|
type: "file",
|
|
28286
29185
|
content: readTemplate("workspace/prompts/contacts.md").replace("{{DISCORD_USER_ID}}", v.discordUserId || "YOUR_DISCORD_ID").replace("{{FEISHU_OPEN_ID}}", v.feishuOpenId || "YOUR_FEISHU_OPEN_ID")
|
|
28287
29186
|
},
|
|
28288
29187
|
{ path: path33.join(w, "prompts", "auto-memory-instructions.md"), type: "file", content: readTemplate("workspace/prompts/auto-memory-instructions.md") },
|
|
29188
|
+
{
|
|
29189
|
+
// 0917:inner-voice 定制 prompt 按人设装(templates/workspace/inner-voice/<persona>.md)
|
|
29190
|
+
path: path33.join(w, "prompts", "my-inner-voice.md"),
|
|
29191
|
+
type: "file",
|
|
29192
|
+
content: readTemplate(`workspace/inner-voice/${v.persona}.md`).replace(/\{\{AGENT_NAME\}\}/g, v.agentName)
|
|
29193
|
+
},
|
|
28289
29194
|
{ path: path33.join(w, "MEMORY.md"), type: "file", content: "# MEMORY.md \u2014 \u8BB0\u5FC6\u6587\u4EF6\u7D22\u5F15\n\n> \u6700\u540E\u66F4\u65B0\uFF1A\u521D\u59CB\u5316\n" },
|
|
28290
29195
|
{ path: path33.join(w, "USER.md"), type: "file", content: "# USER.md \u2014 \u7528\u6237\u4FE1\u606F\n\n\uFF08\u5728\u8FD9\u91CC\u8BB0\u5F55\u7528\u6237\u7684\u504F\u597D\u3001\u80CC\u666F\u7B49\uFF09\n" },
|
|
29196
|
+
// 情绪状态机(0917):脚本 + hook 接线 + 人设情绪表(用户在 init 选的 persona)
|
|
29197
|
+
//
|
|
29198
|
+
// 0920 起装的是 **.mjs 版**(原来是 .py,见下面 hook 命令那段)。老装机的 workspace
|
|
29199
|
+
// 里那份 .py 不动它——没被引用就不会跑,删了反而无据可查。
|
|
29200
|
+
{
|
|
29201
|
+
path: path33.join(w, "scripts", "emotion_state_machine.mjs"),
|
|
29202
|
+
type: "file",
|
|
29203
|
+
content: readTemplate("workspace/scripts/emotion_state_machine.mjs")
|
|
29204
|
+
},
|
|
29205
|
+
{
|
|
29206
|
+
path: path33.join(w, ".hooks.json"),
|
|
29207
|
+
type: "file",
|
|
29208
|
+
content: JSON.stringify({
|
|
29209
|
+
hooks: {
|
|
29210
|
+
UserPromptSubmit: [
|
|
29211
|
+
// `${E7_NODE}` 由引擎在加载 hook 时替换成它**自己的可执行文件**(hooks/config.ts)。
|
|
29212
|
+
// 原来写的是 `python3 <脚本.py>` —— 而用户机器上不一定有 Python(Windows 不自带;
|
|
29213
|
+
// 微软商店那个 WindowsApps\python3.exe 只是个跳商店的别名)。没有解释器时这个 hook
|
|
29214
|
+
// 静默失败,而 handle-query 的 chatMode 默认是 work,于是"自动"档(默认档)在用户
|
|
29215
|
+
// 机器上退化成固定工作模式。
|
|
29216
|
+
//
|
|
29217
|
+
// 换成引擎自己的 exe 后**零外部依赖**:那个 exe 带着 ELECTRON_RUN_AS_NODE=1 被 spawn
|
|
29218
|
+
// (hook 的 env 是 {...process.env} 继承的),所以它就是个 node。
|
|
29219
|
+
// 与 `service install --node-exe` 同一个路子。
|
|
29220
|
+
// 路径两侧都加引号:stateDir 可能带空格(C:\Users\Foo Bar\...)。
|
|
29221
|
+
{ hooks: [{ type: "command", command: '"${E7_NODE}" "' + path33.join(w, "scripts", "emotion_state_machine.mjs") + '"' }] }
|
|
29222
|
+
]
|
|
29223
|
+
}
|
|
29224
|
+
}, null, 2) + "\n"
|
|
29225
|
+
},
|
|
29226
|
+
{
|
|
29227
|
+
path: path33.join(w, ".emotion_states.json"),
|
|
29228
|
+
type: "file",
|
|
29229
|
+
content: readTemplate(`workspace/emotion-states/${v.persona}.json`)
|
|
29230
|
+
},
|
|
29231
|
+
// 模式档位(0920):与 /work 命令写的是同一个文件,读端只认 on/off,
|
|
29232
|
+
// 没有这个键就是 auto。auto 刻意不写——写个 "auto" 进去反而多一种除了这里没人认得的状态。
|
|
29233
|
+
...v.chatMode === "work" || v.chatMode === "emotion" ? [{
|
|
29234
|
+
path: path33.join(w, ".work-mode"),
|
|
29235
|
+
type: "file",
|
|
29236
|
+
content: v.chatMode === "work" ? "on" : "off"
|
|
29237
|
+
}] : [],
|
|
28291
29238
|
// package.json — 让 agent 目录成为独立 npm 项目根,防止 npm hoisting
|
|
28292
29239
|
{
|
|
28293
29240
|
path: path33.join(d, "package.json"),
|
|
@@ -28380,6 +29327,15 @@ function readTemplate(relativePath) {
|
|
|
28380
29327
|
function looksLikeAppInstall(dir) {
|
|
28381
29328
|
return ["", "install"].some((sub) => fs31.existsSync(path33.join(dir, sub, "Engine7.exe")) && fs31.existsSync(path33.join(dir, sub, "resources", "engine7", "dist", "cli.mjs")));
|
|
28382
29329
|
}
|
|
29330
|
+
var PHOTO_EXTS = [".png", ".jpg", ".jpeg", ".webp"];
|
|
29331
|
+
function hasOnlyEngineBookkeeping(dir) {
|
|
29332
|
+
try {
|
|
29333
|
+
const entries2 = fs31.readdirSync(dir);
|
|
29334
|
+
return entries2.length > 0 && entries2.every((e) => e === "install-identity.json");
|
|
29335
|
+
} catch {
|
|
29336
|
+
return false;
|
|
29337
|
+
}
|
|
29338
|
+
}
|
|
28383
29339
|
function dryRun(entries2) {
|
|
28384
29340
|
console.log("\n[dry-run] \u5C06\u8981\u521B\u5EFA\uFF1A\n");
|
|
28385
29341
|
for (const e of entries2) {
|
|
@@ -28428,7 +29384,7 @@ async function main() {
|
|
|
28428
29384
|
if (subcommand === "--version" || subcommand === "-v") {
|
|
28429
29385
|
let ver = "unknown";
|
|
28430
29386
|
try {
|
|
28431
|
-
const pkgPath = path33.join(path33.dirname(
|
|
29387
|
+
const pkgPath = path33.join(path33.dirname(__filename2), "..", "package.json");
|
|
28432
29388
|
ver = JSON.parse(fs31.readFileSync(pkgPath, "utf8")).version || "unknown";
|
|
28433
29389
|
} catch {
|
|
28434
29390
|
}
|
|
@@ -28441,6 +29397,35 @@ async function main() {
|
|
|
28441
29397
|
console.log(JSON.stringify(await runDoctor2(stateDir), null, 2));
|
|
28442
29398
|
process.exit(0);
|
|
28443
29399
|
}
|
|
29400
|
+
if (subcommand === "provision-free") {
|
|
29401
|
+
let relay = "";
|
|
29402
|
+
let stateDir = "";
|
|
29403
|
+
for (let i = 1; i < args.length; i++) {
|
|
29404
|
+
if (args[i] === "--relay" && args[i + 1]) relay = args[++i].trim();
|
|
29405
|
+
else if (args[i] === "--state-dir" && args[i + 1]) stateDir = args[++i].trim();
|
|
29406
|
+
}
|
|
29407
|
+
if (!relay) {
|
|
29408
|
+
console.error("\u7528\u6CD5\uFF1Aengine7 provision-free --relay <\u4E2D\u8F6C\u7AD9\u5730\u5740> --state-dir <\u76EE\u5F55>");
|
|
29409
|
+
console.error(" --relay \u662F\u5B89\u88C5\u5668\u4E0B\u53D1\u7684\u9886\u53F7\u5730\u5740\uFF0C\u4E0D\u662F\u6A21\u578B baseUrl\uFF08\u540E\u8005\u4EE5\u9886\u53F7\u54CD\u5E94\u4E3A\u51C6\uFF09");
|
|
29410
|
+
process.exit(1);
|
|
29411
|
+
}
|
|
29412
|
+
const { resolveStateDirFromArgs: resolveStateDirFromArgs2 } = await Promise.resolve().then(() => (init_cli_doctor(), cli_doctor_exports));
|
|
29413
|
+
stateDir = stateDir ? path33.resolve(stateDir) : resolveStateDirFromArgs2(args.slice(1));
|
|
29414
|
+
try {
|
|
29415
|
+
const { provisionFreeToken: provisionFreeToken2 } = await Promise.resolve().then(() => (init_provision_free(), provision_free_exports));
|
|
29416
|
+
const result = await provisionFreeToken2({
|
|
29417
|
+
relayBaseUrl: relay,
|
|
29418
|
+
stateDir,
|
|
29419
|
+
// 和 init 写 config 的路径一致(cli-init.ts 里 configPath 就是这么拼的)
|
|
29420
|
+
configPath: path33.join(stateDir, "configs", "main7.json")
|
|
29421
|
+
});
|
|
29422
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29423
|
+
process.exit(0);
|
|
29424
|
+
} catch (e) {
|
|
29425
|
+
console.error(`\u9886\u53F7\u5931\u8D25\uFF1A${e?.message || e}`);
|
|
29426
|
+
process.exit(1);
|
|
29427
|
+
}
|
|
29428
|
+
}
|
|
28444
29429
|
if (subcommand === "reconfig" || subcommand === "addchannel") {
|
|
28445
29430
|
const isLegacyAddchannel = subcommand === "addchannel";
|
|
28446
29431
|
let target = args[1] || "";
|
|
@@ -28811,7 +29796,7 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
28811
29796
|
}
|
|
28812
29797
|
}
|
|
28813
29798
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
28814
|
-
const { readFileSync:
|
|
29799
|
+
const { readFileSync: readFileSync43, writeFileSync: writeFileSync24, renameSync: renameSync5 } = fs31;
|
|
28815
29800
|
if (mode === "archive") {
|
|
28816
29801
|
const archiveName = `${sessionFileUUID}.jsonl.archived.${timestamp}`;
|
|
28817
29802
|
renameSync5(jsonlPath, path33.join(sessionsDir, archiveName));
|
|
@@ -28819,7 +29804,7 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
28819
29804
|
\u2705 \u5F52\u6863\u5B8C\u6210: ${archiveName}`);
|
|
28820
29805
|
console.log(` \u91CD\u542F\u540E\u5C06\u4ECE\u7A7A\u767D\u4F1A\u8BDD\u5F00\u59CB`);
|
|
28821
29806
|
} else if (mode === "drop-last") {
|
|
28822
|
-
const lines =
|
|
29807
|
+
const lines = readFileSync43(jsonlPath, "utf-8").trim().split("\n");
|
|
28823
29808
|
const parsed = lines.map((l) => {
|
|
28824
29809
|
try {
|
|
28825
29810
|
return JSON.parse(l);
|
|
@@ -28838,12 +29823,12 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
28838
29823
|
}
|
|
28839
29824
|
const kept = parsed.slice(0, cutIndex);
|
|
28840
29825
|
const newContent = kept.map((p) => JSON.stringify(p)).join("\n") + "\n";
|
|
28841
|
-
|
|
29826
|
+
writeFileSync24(jsonlPath, newContent);
|
|
28842
29827
|
console.log(`
|
|
28843
29828
|
\u2705 \u780D\u6389\u6700\u8FD1 ${parsed.length - cutIndex} \u884C\uFF08${dropN} \u8F6E\uFF09`);
|
|
28844
29829
|
console.log(` \u4FDD\u7559 ${kept.length} \u884C`);
|
|
28845
29830
|
} else if (mode === "strip-images") {
|
|
28846
|
-
const lines =
|
|
29831
|
+
const lines = readFileSync43(jsonlPath, "utf-8").trim().split("\n");
|
|
28847
29832
|
let stripped = 0;
|
|
28848
29833
|
const newLines = lines.map((line) => {
|
|
28849
29834
|
try {
|
|
@@ -28884,7 +29869,7 @@ engine7 session \u2014 \u4F1A\u8BDD\u7BA1\u7406\u5DE5\u5177
|
|
|
28884
29869
|
return line;
|
|
28885
29870
|
}
|
|
28886
29871
|
});
|
|
28887
|
-
|
|
29872
|
+
writeFileSync24(jsonlPath, newLines.join("\n") + "\n");
|
|
28888
29873
|
console.log(`
|
|
28889
29874
|
\u2705 \u6458\u9664 ${stripped} \u884C\u56FE\u7247\u76F8\u5173\u5185\u5BB9`);
|
|
28890
29875
|
}
|
|
@@ -28986,6 +29971,13 @@ engine7 service \u2014 \u5F00\u673A\u81EA\u542F\u52A8\u7BA1\u7406
|
|
|
28986
29971
|
const absCfg = path33.resolve(configPath2);
|
|
28987
29972
|
const vbs = [
|
|
28988
29973
|
"' Engine 7 \u5F00\u673A\u81EA\u542F\uFF08\u542F\u52A8\u6587\u4EF6\u5939\uFF0C\u514D\u7BA1\u7406\u5458\uFF09",
|
|
29974
|
+
'Set fso = CreateObject("Scripting.FileSystemObject")',
|
|
29975
|
+
'exePath = "' + nodeExePath + '"',
|
|
29976
|
+
"If Not fso.FileExists(exePath) Then",
|
|
29977
|
+
" On Error Resume Next",
|
|
29978
|
+
" fso.DeleteFile WScript.ScriptFullName, True",
|
|
29979
|
+
" WScript.Quit 0",
|
|
29980
|
+
"End If",
|
|
28989
29981
|
'Set s = CreateObject("WScript.Shell")',
|
|
28990
29982
|
's.Environment("PROCESS")("ELECTRON_RUN_AS_NODE") = "1"',
|
|
28991
29983
|
's.CurrentDirectory = "' + cwd + '"',
|
|
@@ -29138,6 +30130,12 @@ WantedBy=default.target`;
|
|
|
29138
30130
|
}
|
|
29139
30131
|
process.exit(0);
|
|
29140
30132
|
}
|
|
30133
|
+
const INIT_FLAGS = /* @__PURE__ */ new Set(["--state-dir", "--quick", "--dry-run", "--force", "--answers"]);
|
|
30134
|
+
if (subcommand !== "init" && !INIT_FLAGS.has(subcommand)) {
|
|
30135
|
+
console.error(`\u274C \u672A\u77E5\u547D\u4EE4: ${subcommand}`);
|
|
30136
|
+
printHelp();
|
|
30137
|
+
process.exit(1);
|
|
30138
|
+
}
|
|
29141
30139
|
const opts = parseArgs();
|
|
29142
30140
|
if (opts.answersPath) {
|
|
29143
30141
|
redirectConsoleToStderr();
|
|
@@ -29170,6 +30168,9 @@ WantedBy=default.target`;
|
|
|
29170
30168
|
} else if (looksLikeAppInstall(opts.stateDir)) {
|
|
29171
30169
|
console.log(`
|
|
29172
30170
|
\u{1F4E6} \u68C0\u6D4B\u5230 Engine7 \u7A0B\u5E8F\u672C\u4F53\uFF08\u5B89\u88C5\u5668\u88C5\u7684\uFF09\u2014\u2014\u5728\u5176\u65C1\u8FB9\u521D\u59CB\u5316 agent \u6570\u636E`);
|
|
30171
|
+
} else if (hasOnlyEngineBookkeeping(opts.stateDir)) {
|
|
30172
|
+
console.log(`
|
|
30173
|
+
\u{1F511} \u68C0\u6D4B\u5230\u88C5\u673A\u8EAB\u4EFD\uFF08\u9886\u53F7\u7559\u4E0B\u7684\uFF09\u2014\u2014 \u5728\u5176\u65C1\u8FB9\u521D\u59CB\u5316 agent \u6570\u636E`);
|
|
29173
30174
|
} else {
|
|
29174
30175
|
if (headlessEmit) headlessEmit({ event: "error", message: `\u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A: ${opts.stateDir}\uFF08\u65E0 configs/main7.json\uFF09\u2014\u2014\u8BF7\u9009\u62E9\u7A7A\u76EE\u5F55\u6216\u7528 --force \u6E05\u7A7A\u540E\u91CD\u65B0\u914D\u7F6E` });
|
|
29175
30176
|
console.error(`
|
|
@@ -29224,6 +30225,32 @@ WantedBy=default.target`;
|
|
|
29224
30225
|
values = await interactiveConfig(rl, getDefaultValues(opts.stateDir));
|
|
29225
30226
|
rl.close();
|
|
29226
30227
|
}
|
|
30228
|
+
if (values.freeTier) {
|
|
30229
|
+
const envName = values.primaryProvider.toUpperCase().replace(/[^A-Z0-9_]/g, "_") + "_API_KEY";
|
|
30230
|
+
if (!readSecretsValue(opts.stateDir, envName)) {
|
|
30231
|
+
throw new Error(
|
|
30232
|
+
`\u514D\u8D39\u6863\u7F3A\u5C11\u4EE4\u724C\uFF1Asecrets \u91CC\u6CA1\u6709 ${envName}\u3002\u8BF7\u5148\u6267\u884C engine7 provision-free --relay <\u4E2D\u8F6C\u7AD9\u5730\u5740> --state-dir ${opts.stateDir} \u9886\u53F7\uFF0C\u518D\u8DD1 init\u3002`
|
|
30233
|
+
);
|
|
30234
|
+
}
|
|
30235
|
+
}
|
|
30236
|
+
let photoSrc = "";
|
|
30237
|
+
if (answers?.selfie?.photoPath) {
|
|
30238
|
+
const { dimsOf: dimsOf2 } = await Promise.resolve().then(() => (init_my_selfie(), my_selfie_exports));
|
|
30239
|
+
const src = path33.resolve(answers.selfie.photoPath.trim());
|
|
30240
|
+
const ext = path33.extname(src).toLowerCase();
|
|
30241
|
+
if (!PHOTO_EXTS.includes(ext)) {
|
|
30242
|
+
throw new Error(`\u7167\u7247\u683C\u5F0F\u4E0D\u652F\u6301\uFF1A${ext || "(\u6CA1\u6709\u540E\u7F00)"}\u3002\u8BF7\u7528 PNG / JPEG / WebP\u3002`);
|
|
30243
|
+
}
|
|
30244
|
+
if (!fs31.existsSync(src)) {
|
|
30245
|
+
throw new Error(`\u7167\u7247\u4E0D\u5B58\u5728\uFF1A${src}`);
|
|
30246
|
+
}
|
|
30247
|
+
const dims = dimsOf2(src);
|
|
30248
|
+
if (dims && dims.includes("\u4E0D\u5408\u683C")) {
|
|
30249
|
+
throw new Error(`\u7167\u7247\u4E0D\u5408\u683C\uFF1A${dims}\u3002\u8BF7\u6362\u4E00\u5F20\uFF08\u5546\u6C64\u5BF9\u8F93\u5165\u56FE\u7684\u786C\u6027\u8981\u6C42\uFF09\u3002`);
|
|
30250
|
+
}
|
|
30251
|
+
values.selfiePhotoRelPath = `images/portrait${ext}`;
|
|
30252
|
+
photoSrc = src;
|
|
30253
|
+
}
|
|
29227
30254
|
const config = generateConfig(values);
|
|
29228
30255
|
const configPath = path33.join(opts.stateDir, "configs", "main7.json");
|
|
29229
30256
|
const tree = buildDirTree(values);
|
|
@@ -29243,24 +30270,25 @@ WantedBy=default.target`;
|
|
|
29243
30270
|
}
|
|
29244
30271
|
}
|
|
29245
30272
|
} else {
|
|
30273
|
+
if (values.chatMode === "auto") {
|
|
30274
|
+
try {
|
|
30275
|
+
fs31.unlinkSync(path33.join(opts.stateDir, "workspace", ".work-mode"));
|
|
30276
|
+
} catch {
|
|
30277
|
+
}
|
|
30278
|
+
}
|
|
30279
|
+
if (photoSrc && values.selfiePhotoRelPath) {
|
|
30280
|
+
const dest = path33.join(opts.stateDir, "workspace", values.selfiePhotoRelPath);
|
|
30281
|
+
fs31.mkdirSync(path33.dirname(dest), { recursive: true });
|
|
30282
|
+
fs31.copyFileSync(photoSrc, dest);
|
|
30283
|
+
console.log(` \u{1F5BC} \u5F62\u8C61: ${dest}`);
|
|
30284
|
+
}
|
|
29246
30285
|
if (headlessEmit) headlessEmit({ event: "progress", step: "create_dirs" });
|
|
29247
30286
|
console.log("\n\u{1F4E6} \u6B63\u5728\u521B\u5EFA\u76EE\u5F55\u548C\u6587\u4EF6...\n");
|
|
29248
30287
|
execute(tree);
|
|
29249
30288
|
if (headlessEmit) headlessEmit({ event: "progress", step: "write_secrets" });
|
|
29250
30289
|
if (values.primaryApiKey && values.primaryApiKey.trim()) {
|
|
29251
30290
|
const envName = values.primaryProvider.toUpperCase().replace(/[^A-Z0-9_]/g, "_") + "_API_KEY";
|
|
29252
|
-
const
|
|
29253
|
-
if (!fs31.existsSync(secretsDir)) fs31.mkdirSync(secretsDir, { recursive: true });
|
|
29254
|
-
const envFile = path33.join(secretsDir, "main7.env");
|
|
29255
|
-
let envContent = "";
|
|
29256
|
-
if (fs31.existsSync(envFile)) envContent = fs31.readFileSync(envFile, "utf8");
|
|
29257
|
-
const lines = envContent.split("\n").filter((l) => l && !l.startsWith(envName + "="));
|
|
29258
|
-
lines.push(envName + "=" + values.primaryApiKey.trim());
|
|
29259
|
-
fs31.writeFileSync(envFile, lines.join("\n") + "\n", "utf8");
|
|
29260
|
-
try {
|
|
29261
|
-
fs31.chmodSync(envFile, 384);
|
|
29262
|
-
} catch {
|
|
29263
|
-
}
|
|
30291
|
+
const envFile = writeSecretsValue(configPath, envName, values.primaryApiKey.trim());
|
|
29264
30292
|
console.log("\u{1F511} API key \u5DF2\u5B58\u5165: " + envFile);
|
|
29265
30293
|
console.log(" config \u91CC\u53EA\u6709 env:" + envName + " \u5360\u4F4D\u7B26\uFF0C\u660E\u6587\u4E0D\u843D config");
|
|
29266
30294
|
}
|