engine7 7.1.84 → 7.1.95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +1630 -528
- package/dist/engine-startup.mjs +1530 -578
- package/dist/main.mjs +1558 -603
- package/package.json +6 -4
- package/templates/config.template.json +11 -0
- package/templates/workspace/SESSION-STATE.md +1 -1
- package/templates/workspace/emotion/_default.md +33 -0
- package/templates/workspace/emotion/assistant.md +36 -0
- package/templates/workspace/emotion/butler.md +36 -0
- package/templates/workspace/emotion/companion.md +36 -0
- package/templates/workspace/emotion/friend.md +36 -0
- package/templates/workspace/emotion-states/assistant.json +22 -0
- package/templates/workspace/emotion-states/butler.json +24 -0
- package/templates/workspace/emotion-states/companion.json +30 -0
- package/templates/workspace/emotion-states/friend.json +24 -0
- package/templates/workspace/inner-voice/assistant.md +9 -0
- package/templates/workspace/inner-voice/butler.md +9 -0
- package/templates/workspace/inner-voice/companion.md +9 -0
- package/templates/workspace/inner-voice/friend.md +9 -0
- package/templates/workspace/scripts/__pycache__/emotion_state_machine.cpython-310.pyc +0 -0
- package/templates/workspace/scripts/emotion_state_machine.mjs +588 -0
- package/templates/workspace/scripts/emotion_state_machine.py +551 -0
- package/templates/workspace/souls/assistant.md +20 -0
- package/templates/workspace/souls/butler.md +21 -0
- package/templates/workspace/souls/companion.md +20 -0
- package/templates/workspace/souls/friend.md +21 -0
package/dist/engine-startup.mjs
CHANGED
|
@@ -275,12 +275,15 @@ function normalizeHook(h) {
|
|
|
275
275
|
}
|
|
276
276
|
return {
|
|
277
277
|
type: "command",
|
|
278
|
-
command: h.command || "",
|
|
278
|
+
command: expandCommand(h.command || ""),
|
|
279
279
|
timeout: h.timeout,
|
|
280
280
|
async: h.async,
|
|
281
281
|
shell: h.shell
|
|
282
282
|
};
|
|
283
283
|
}
|
|
284
|
+
function expandCommand(command) {
|
|
285
|
+
return command.replace(/\$\{E7_NODE\}/g, process.execPath);
|
|
286
|
+
}
|
|
284
287
|
function registerCallbackHook(event2, hook) {
|
|
285
288
|
const list2 = callbackHooks.get(event2) || [];
|
|
286
289
|
if (hook.id) {
|
|
@@ -1771,6 +1774,29 @@ var init_microCompact = __esm({
|
|
|
1771
1774
|
}
|
|
1772
1775
|
});
|
|
1773
1776
|
|
|
1777
|
+
// src/utils/well-formed.ts
|
|
1778
|
+
function truncateByCodePoint(text, max) {
|
|
1779
|
+
if (text.length <= max) return text;
|
|
1780
|
+
return Array.from(text).slice(0, max).join("");
|
|
1781
|
+
}
|
|
1782
|
+
function toWellFormed(s) {
|
|
1783
|
+
if (HAS_NATIVE) return s.toWellFormed();
|
|
1784
|
+
return s.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
1785
|
+
}
|
|
1786
|
+
function wellFormedReplacer(_key, value) {
|
|
1787
|
+
return typeof value === "string" ? toWellFormed(value) : value;
|
|
1788
|
+
}
|
|
1789
|
+
function safeStringify(body) {
|
|
1790
|
+
return JSON.stringify(body, wellFormedReplacer);
|
|
1791
|
+
}
|
|
1792
|
+
var HAS_NATIVE;
|
|
1793
|
+
var init_well_formed = __esm({
|
|
1794
|
+
"src/utils/well-formed.ts"() {
|
|
1795
|
+
"use strict";
|
|
1796
|
+
HAS_NATIVE = typeof String.prototype.toWellFormed === "function";
|
|
1797
|
+
}
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1774
1800
|
// src/compact/ruleCompact.ts
|
|
1775
1801
|
function smartCompressToolResult(content, essentialFields, sizeLimit) {
|
|
1776
1802
|
if (content.length <= (sizeLimit ?? TOOL_SIZE_LIMIT)) return content;
|
|
@@ -1812,7 +1838,7 @@ ${result}`;
|
|
|
1812
1838
|
}
|
|
1813
1839
|
} catch {
|
|
1814
1840
|
}
|
|
1815
|
-
return content
|
|
1841
|
+
return truncateByCodePoint(content, sizeLimit ?? TRUNCATE_TO) + "\n...[TRUNCATED]";
|
|
1816
1842
|
}
|
|
1817
1843
|
function foldTurnInline(turn) {
|
|
1818
1844
|
const toolMsgs = turn.filter((m) => m.role === "tool");
|
|
@@ -1942,7 +1968,8 @@ function step3_truncateLarge(messages) {
|
|
|
1942
1968
|
details.push(`tool_call_id=${m.tool_call_id?.slice(0, 8)}... ${originalLen} \u2192 ${TRUNCATE_TO} chars`);
|
|
1943
1969
|
return {
|
|
1944
1970
|
...m,
|
|
1945
|
-
|
|
1971
|
+
// 0918:按 code point 切,避免切开 emoji 代理对(见 utils/well-formed.ts)
|
|
1972
|
+
content: truncateByCodePoint(m.content, TRUNCATE_TO) + "\n...[TRUNCATED]"
|
|
1946
1973
|
};
|
|
1947
1974
|
}
|
|
1948
1975
|
return m;
|
|
@@ -2032,6 +2059,7 @@ var init_ruleCompact = __esm({
|
|
|
2032
2059
|
init_tokenEstimate();
|
|
2033
2060
|
init_autoCompact();
|
|
2034
2061
|
init_compactLog();
|
|
2062
|
+
init_well_formed();
|
|
2035
2063
|
TAG3 = "ruleCompact";
|
|
2036
2064
|
TOOL_SIZE_LIMIT = 8e3;
|
|
2037
2065
|
TRUNCATE_TO = 5e3;
|
|
@@ -2355,6 +2383,9 @@ function openDb(workspace) {
|
|
|
2355
2383
|
day TEXT,
|
|
2356
2384
|
time TEXT,
|
|
2357
2385
|
date_str TEXT,
|
|
2386
|
+
-- \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
|
|
2387
|
+
-- 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
|
|
2388
|
+
-- 0920 \u8D77\u5199\u5165\u6B63\u786E UTC Z\u3002\u8BFB\u8FD9\u4E09\u5217\u524D\u5148\u786E\u8BA4\u4F60\u8981\u7684\u662F\u4EC0\u4E48\u3002
|
|
2358
2389
|
created_at TEXT NOT NULL,
|
|
2359
2390
|
done_at TEXT,
|
|
2360
2391
|
archived_at TEXT
|
|
@@ -2397,7 +2428,7 @@ function migrate(db2) {
|
|
|
2397
2428
|
db2.exec("ALTER TABLE events ADD COLUMN doc_path TEXT");
|
|
2398
2429
|
}
|
|
2399
2430
|
}
|
|
2400
|
-
var _sqlite, DatabaseSync
|
|
2431
|
+
var _sqlite, DatabaseSync;
|
|
2401
2432
|
var init_db = __esm({
|
|
2402
2433
|
"src/calendar/db.ts"() {
|
|
2403
2434
|
"use strict";
|
|
@@ -2411,7 +2442,6 @@ var init_db = __esm({
|
|
|
2411
2442
|
return getSqlite().DatabaseSync[prop];
|
|
2412
2443
|
}
|
|
2413
2444
|
});
|
|
2414
|
-
TZ_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
2415
2445
|
}
|
|
2416
2446
|
});
|
|
2417
2447
|
|
|
@@ -2540,15 +2570,6 @@ function bindAim(stateDir, goalId, stepId, aimId) {
|
|
|
2540
2570
|
step.note = `AIM assignment bound: ${aimId}`;
|
|
2541
2571
|
});
|
|
2542
2572
|
}
|
|
2543
|
-
function bindCalendar(stateDir, goalId, stepId, calendarId, deadlineAt) {
|
|
2544
|
-
return updateGoal(stateDir, goalId, (g) => {
|
|
2545
|
-
const step = g.steps.find((s) => s.id === stepId);
|
|
2546
|
-
if (!step) throw new Error(`Step not found: ${stepId}`);
|
|
2547
|
-
if (step.status === "completed") throw new Error("Cannot bind calendar to completed step");
|
|
2548
|
-
step.calendarId = calendarId;
|
|
2549
|
-
if (deadlineAt) step.deadlineAt = deadlineAt;
|
|
2550
|
-
});
|
|
2551
|
-
}
|
|
2552
2573
|
function applyAimEvent(stateDir, aimId, type2, note) {
|
|
2553
2574
|
const goal = listGoals(stateDir).find((g) => g.steps.some((s) => s.aimId === aimId));
|
|
2554
2575
|
if (!goal) return void 0;
|
|
@@ -2612,7 +2633,7 @@ function addGoalStep(stateDir, goalId, title, kind = "self") {
|
|
|
2612
2633
|
g.steps.push({ id, title, status: g.currentStep >= g.steps.length ? "active" : "pending", kind });
|
|
2613
2634
|
});
|
|
2614
2635
|
}
|
|
2615
|
-
function bindGoalStep(stateDir, goalId, stepId, kind, ref) {
|
|
2636
|
+
function bindGoalStep(stateDir, goalId, stepId, kind, ref, deadlineAt) {
|
|
2616
2637
|
return updateGoal(stateDir, goalId, (g) => {
|
|
2617
2638
|
const step = g.steps.find((s) => s.id === stepId);
|
|
2618
2639
|
if (!step) throw new Error(`Step not found: ${stepId}`);
|
|
@@ -2620,7 +2641,10 @@ function bindGoalStep(stateDir, goalId, stepId, kind, ref) {
|
|
|
2620
2641
|
step.kind = kind;
|
|
2621
2642
|
if (kind === "aim") step.aimId = String(ref);
|
|
2622
2643
|
else if (kind === "remote_task") step.taskId = String(ref);
|
|
2623
|
-
else if (kind === "calendar")
|
|
2644
|
+
else if (kind === "calendar") {
|
|
2645
|
+
step.calendarId = Number(ref);
|
|
2646
|
+
if (deadlineAt) step.deadlineAt = deadlineAt;
|
|
2647
|
+
}
|
|
2624
2648
|
step.note = `${kind} \u59D4\u6D3E: ${ref}\uFF08\u5B8C\u6210\u4E8B\u4EF6\u81EA\u52A8\u56DE\u6D41\u89E3\u9501\uFF09`;
|
|
2625
2649
|
});
|
|
2626
2650
|
}
|
|
@@ -3752,7 +3776,7 @@ async function classifyViaSidecar(url, context, text, opts = {}) {
|
|
|
3752
3776
|
const res = await doFetch(`${url.replace(/\/$/, "")}/classify`, {
|
|
3753
3777
|
method: "POST",
|
|
3754
3778
|
headers: { "Content-Type": "application/json" },
|
|
3755
|
-
body:
|
|
3779
|
+
body: safeStringify({ context: context.map((t) => ({ text: t })), text }),
|
|
3756
3780
|
signal: ac.signal
|
|
3757
3781
|
});
|
|
3758
3782
|
if (!res.ok) return null;
|
|
@@ -3769,6 +3793,7 @@ var init_skill_gate_classifier = __esm({
|
|
|
3769
3793
|
"src/skills/skill-gate-classifier.ts"() {
|
|
3770
3794
|
"use strict";
|
|
3771
3795
|
init_skill_gate_window();
|
|
3796
|
+
init_well_formed();
|
|
3772
3797
|
}
|
|
3773
3798
|
});
|
|
3774
3799
|
|
|
@@ -5315,16 +5340,18 @@ function shouldRetry(status, errorBody) {
|
|
|
5315
5340
|
if (status === 529) return true;
|
|
5316
5341
|
if (status === 429) {
|
|
5317
5342
|
const body = errorBody?.toLowerCase() || "";
|
|
5318
|
-
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")) {
|
|
5343
|
+
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")) {
|
|
5319
5344
|
return false;
|
|
5320
5345
|
}
|
|
5321
5346
|
return true;
|
|
5322
5347
|
}
|
|
5323
5348
|
if (status === 408) return true;
|
|
5324
5349
|
if (status === 409) return true;
|
|
5325
|
-
if (status
|
|
5326
|
-
|
|
5327
|
-
|
|
5350
|
+
if (status >= 500) {
|
|
5351
|
+
const body = errorBody?.toLowerCase() || "";
|
|
5352
|
+
if (body.includes("\u65E0\u53EF\u7528\u6E20\u9053") || body.includes("new_sensitive")) return false;
|
|
5353
|
+
return true;
|
|
5354
|
+
}
|
|
5328
5355
|
if (errorBody?.includes('"type":"overloaded_error"')) return true;
|
|
5329
5356
|
return false;
|
|
5330
5357
|
}
|
|
@@ -5594,6 +5621,7 @@ var init_openai_provider = __esm({
|
|
|
5594
5621
|
init_withRetry();
|
|
5595
5622
|
init_provider();
|
|
5596
5623
|
init_think_stripper();
|
|
5624
|
+
init_well_formed();
|
|
5597
5625
|
OpenAIProvider = class {
|
|
5598
5626
|
// 0831 <think> 剥离改用 ThinkTagStripper 的**局部**实例(见 streamChat)——
|
|
5599
5627
|
// 原来是实例字段,跨请求共享状态,流中途 abort 会把 active=true 带到下一个请求
|
|
@@ -5682,13 +5710,17 @@ var init_openai_provider = __esm({
|
|
|
5682
5710
|
}
|
|
5683
5711
|
const actualThinking = body.chat_template_kwargs;
|
|
5684
5712
|
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}`);
|
|
5713
|
+
let bodyStr = safeStringify(body);
|
|
5714
|
+
if (this.config.wafSingleQuoteWorkaround) {
|
|
5715
|
+
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
5716
|
+
}
|
|
5685
5717
|
const retryGen = fetchWithRetry(url, {
|
|
5686
5718
|
method: "POST",
|
|
5687
5719
|
headers: {
|
|
5688
5720
|
"Content-Type": "application/json",
|
|
5689
5721
|
"Authorization": `Bearer ${this.config.apiKey}`
|
|
5690
5722
|
},
|
|
5691
|
-
body:
|
|
5723
|
+
body: bodyStr,
|
|
5692
5724
|
signal: params.signal
|
|
5693
5725
|
}, "openai", this.config.proxy);
|
|
5694
5726
|
let response;
|
|
@@ -5886,7 +5918,7 @@ function adaptUsage(u) {
|
|
|
5886
5918
|
});
|
|
5887
5919
|
}
|
|
5888
5920
|
function serializeBody(body, wafSingleQuoteWorkaround) {
|
|
5889
|
-
let bodyStr =
|
|
5921
|
+
let bodyStr = safeStringify(body);
|
|
5890
5922
|
if (wafSingleQuoteWorkaround) {
|
|
5891
5923
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
5892
5924
|
}
|
|
@@ -5913,6 +5945,7 @@ var init_openai_responses_provider = __esm({
|
|
|
5913
5945
|
"use strict";
|
|
5914
5946
|
init_withRetry();
|
|
5915
5947
|
init_provider();
|
|
5948
|
+
init_well_formed();
|
|
5916
5949
|
CODEX_CLI_VERSION = "0.154.0";
|
|
5917
5950
|
CODEX_TERMINAL_TOKEN = "xterm-256color";
|
|
5918
5951
|
OpenAIResponsesProvider = class {
|
|
@@ -6233,12 +6266,14 @@ var init_anthropic_provider = __esm({
|
|
|
6233
6266
|
init_withRetry();
|
|
6234
6267
|
init_provider();
|
|
6235
6268
|
init_think_stripper();
|
|
6269
|
+
init_well_formed();
|
|
6236
6270
|
AnthropicProvider = class {
|
|
6237
6271
|
// 模拟 CC 的 X-Claude-Code-Session-Id
|
|
6238
6272
|
constructor(config) {
|
|
6239
6273
|
this.config = config;
|
|
6240
6274
|
let u = (config.baseUrl || "https://api.anthropic.com").trim();
|
|
6241
6275
|
while (u.endsWith("/")) u = u.slice(0, -1);
|
|
6276
|
+
if (u.endsWith("/v1")) u = u.slice(0, -3);
|
|
6242
6277
|
this.baseUrl = u;
|
|
6243
6278
|
this.sessionId = randomUUID3();
|
|
6244
6279
|
}
|
|
@@ -6437,7 +6472,7 @@ var init_anthropic_provider = __esm({
|
|
|
6437
6472
|
} catch {
|
|
6438
6473
|
}
|
|
6439
6474
|
}
|
|
6440
|
-
let bodyStr =
|
|
6475
|
+
let bodyStr = safeStringify(body);
|
|
6441
6476
|
if (this.config.wafSingleQuoteWorkaround) {
|
|
6442
6477
|
const before = bodyStr.length;
|
|
6443
6478
|
bodyStr = bodyStr.replace(/'/g, "\u2019");
|
|
@@ -6657,6 +6692,7 @@ var init_gemini_provider = __esm({
|
|
|
6657
6692
|
"src/models/gemini-provider.ts"() {
|
|
6658
6693
|
"use strict";
|
|
6659
6694
|
init_withRetry();
|
|
6695
|
+
init_well_formed();
|
|
6660
6696
|
GeminiProvider = class {
|
|
6661
6697
|
constructor(config) {
|
|
6662
6698
|
this.config = config;
|
|
@@ -6816,7 +6852,7 @@ var init_gemini_provider = __esm({
|
|
|
6816
6852
|
"Content-Type": "application/json",
|
|
6817
6853
|
"x-goog-api-key": this.config.apiKey
|
|
6818
6854
|
},
|
|
6819
|
-
body:
|
|
6855
|
+
body: safeStringify(body),
|
|
6820
6856
|
signal: params.signal
|
|
6821
6857
|
}, "gemini", this.config.proxy);
|
|
6822
6858
|
let response;
|
|
@@ -6955,7 +6991,9 @@ function createProvider(config) {
|
|
|
6955
6991
|
baseUrl: config.baseUrl,
|
|
6956
6992
|
apiKey: config.apiKey,
|
|
6957
6993
|
thinking: config.thinking,
|
|
6958
|
-
proxy
|
|
6994
|
+
proxy,
|
|
6995
|
+
wafSingleQuoteWorkaround: config.wafSingleQuoteWorkaround
|
|
6996
|
+
// 0919 中转站→agentrouter 同样有 WAF
|
|
6959
6997
|
});
|
|
6960
6998
|
case "openai-responses":
|
|
6961
6999
|
return new OpenAIResponsesProvider({
|
|
@@ -7072,12 +7110,205 @@ var init_features = __esm({
|
|
|
7072
7110
|
}
|
|
7073
7111
|
});
|
|
7074
7112
|
|
|
7113
|
+
// src/time/clock.ts
|
|
7114
|
+
function detectHostTimezone() {
|
|
7115
|
+
try {
|
|
7116
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
7117
|
+
} catch {
|
|
7118
|
+
return "UTC";
|
|
7119
|
+
}
|
|
7120
|
+
}
|
|
7121
|
+
function isValidTimezone(name) {
|
|
7122
|
+
if (!name) return false;
|
|
7123
|
+
try {
|
|
7124
|
+
new Intl.DateTimeFormat("en-US", { timeZone: name });
|
|
7125
|
+
return true;
|
|
7126
|
+
} catch {
|
|
7127
|
+
return false;
|
|
7128
|
+
}
|
|
7129
|
+
}
|
|
7130
|
+
function tz() {
|
|
7131
|
+
if (testTimezone) return testTimezone;
|
|
7132
|
+
const v = liveConfig.get("timezone");
|
|
7133
|
+
const name = typeof v === "string" && v ? v : "UTC";
|
|
7134
|
+
if (validatedZones.has(name)) return name;
|
|
7135
|
+
if (isValidTimezone(name)) {
|
|
7136
|
+
validatedZones.add(name);
|
|
7137
|
+
return name;
|
|
7138
|
+
}
|
|
7139
|
+
warnOnce("tz", "invalid-zone", name);
|
|
7140
|
+
return "UTC";
|
|
7141
|
+
}
|
|
7142
|
+
function partsFmt(zone) {
|
|
7143
|
+
let f = fmtCache.get(zone);
|
|
7144
|
+
if (!f) {
|
|
7145
|
+
f = new Intl.DateTimeFormat("en-US", {
|
|
7146
|
+
timeZone: zone,
|
|
7147
|
+
year: "numeric",
|
|
7148
|
+
month: "2-digit",
|
|
7149
|
+
day: "2-digit",
|
|
7150
|
+
hour: "2-digit",
|
|
7151
|
+
minute: "2-digit",
|
|
7152
|
+
second: "2-digit",
|
|
7153
|
+
hourCycle: "h23",
|
|
7154
|
+
weekday: "short"
|
|
7155
|
+
});
|
|
7156
|
+
fmtCache.set(zone, f);
|
|
7157
|
+
}
|
|
7158
|
+
return f;
|
|
7159
|
+
}
|
|
7160
|
+
function asDate(i) {
|
|
7161
|
+
return i instanceof Date ? i : new Date(i);
|
|
7162
|
+
}
|
|
7163
|
+
function wallParts(i = /* @__PURE__ */ new Date()) {
|
|
7164
|
+
const d = asDate(i);
|
|
7165
|
+
const parts = partsFmt(tz()).formatToParts(d);
|
|
7166
|
+
const get = (t) => parts.find((p) => p.type === t)?.value ?? "";
|
|
7167
|
+
return {
|
|
7168
|
+
y: Number(get("year")),
|
|
7169
|
+
mo: Number(get("month")),
|
|
7170
|
+
d: Number(get("day")),
|
|
7171
|
+
hh: Number(get("hour")),
|
|
7172
|
+
mm: Number(get("minute")),
|
|
7173
|
+
ss: Number(get("second")),
|
|
7174
|
+
weekday: EN_WEEKDAY[get("weekday")] ?? 0
|
|
7175
|
+
};
|
|
7176
|
+
}
|
|
7177
|
+
function weekdayZh(i) {
|
|
7178
|
+
return WEEKDAY_ZH[wallParts(i).weekday];
|
|
7179
|
+
}
|
|
7180
|
+
function nowUtc() {
|
|
7181
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
7182
|
+
}
|
|
7183
|
+
function toUtc(d) {
|
|
7184
|
+
return d.toISOString();
|
|
7185
|
+
}
|
|
7186
|
+
function toWall(i = /* @__PURE__ */ new Date()) {
|
|
7187
|
+
const p = wallParts(i);
|
|
7188
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${p2(p.hh)}:${p2(p.mm)}:${p2(p.ss)}`;
|
|
7189
|
+
}
|
|
7190
|
+
function formatForLlm(i) {
|
|
7191
|
+
const d = i ?? /* @__PURE__ */ new Date();
|
|
7192
|
+
const p = wallParts(d);
|
|
7193
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)} ${WEEKDAY_ZH[p.weekday]} ${p2(p.hh)}:${p2(p.mm)}\uFF08${tz()}\uFF09`;
|
|
7194
|
+
}
|
|
7195
|
+
function formatLog(i) {
|
|
7196
|
+
return toWall(i ?? /* @__PURE__ */ new Date());
|
|
7197
|
+
}
|
|
7198
|
+
function formatWallLabel(i) {
|
|
7199
|
+
return formatForLlm(i).replace(/\s*(.*)$/, "");
|
|
7200
|
+
}
|
|
7201
|
+
function warnOnce(callSite, shape, sample) {
|
|
7202
|
+
const key = `${callSite}:${shape}`;
|
|
7203
|
+
if (warned.has(key)) return;
|
|
7204
|
+
warned.add(key);
|
|
7205
|
+
console.warn(`[clock] ${callSite} \u5951\u7EA6\u8FDD\u89C4(${shape})\uFF0C\u6837\u672C: ${String(sample).slice(0, 80)}`);
|
|
7206
|
+
}
|
|
7207
|
+
function zoneOffsetMs(d) {
|
|
7208
|
+
const p = wallParts(d);
|
|
7209
|
+
const asUtc = Date.UTC(p.y, p.mo - 1, p.d, p.hh, p.mm, p.ss);
|
|
7210
|
+
return asUtc - Math.floor(d.getTime() / 1e3) * 1e3;
|
|
7211
|
+
}
|
|
7212
|
+
function wallToUtc(y, mo, d, hh, mm, ss) {
|
|
7213
|
+
const guess = Date.UTC(y, mo - 1, d, hh, mm, ss);
|
|
7214
|
+
const off1 = zoneOffsetMs(new Date(guess));
|
|
7215
|
+
const ms1 = guess - off1;
|
|
7216
|
+
const off2 = zoneOffsetMs(new Date(ms1));
|
|
7217
|
+
const ms = off2 === off1 ? ms1 : guess - off2;
|
|
7218
|
+
return new Date(ms).toISOString();
|
|
7219
|
+
}
|
|
7220
|
+
function parseWall(s) {
|
|
7221
|
+
const raw = String(s ?? "").trim();
|
|
7222
|
+
if (!raw) return "";
|
|
7223
|
+
if (/Z$/i.test(raw) || /[+-]\d{2}:?\d{2}$/.test(raw)) {
|
|
7224
|
+
const ms2 = Date.parse(raw);
|
|
7225
|
+
if (Number.isNaN(ms2)) {
|
|
7226
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
7227
|
+
return raw;
|
|
7228
|
+
}
|
|
7229
|
+
warnOnce("parseWall", "has-offset", raw);
|
|
7230
|
+
return new Date(ms2).toISOString();
|
|
7231
|
+
}
|
|
7232
|
+
const m = WALL_RE.exec(raw);
|
|
7233
|
+
if (!m) {
|
|
7234
|
+
warnOnce("parseWall", "unparseable", raw);
|
|
7235
|
+
return raw;
|
|
7236
|
+
}
|
|
7237
|
+
const tail = raw.slice(m[0].length);
|
|
7238
|
+
if (tail && !/^\.\d+$/.test(tail)) {
|
|
7239
|
+
warnOnce("parseWall", "unparseable-tail", raw);
|
|
7240
|
+
return raw;
|
|
7241
|
+
}
|
|
7242
|
+
const [Y, Mo, D, H, Mi, S] = [+m[1], +m[2], +m[3], +m[4], +m[5], m[6] ? +m[6] : 0];
|
|
7243
|
+
if (Y < 1e3 || Y > 9999 || Mo < 1 || Mo > 12 || D < 1 || D > daysInMonth(Y, Mo) || H > 23 || Mi > 59 || S > 59) {
|
|
7244
|
+
warnOnce("parseWall", "out-of-range", raw);
|
|
7245
|
+
return raw;
|
|
7246
|
+
}
|
|
7247
|
+
const ms = tail ? Math.round(Number(`0${tail}`) * 1e3) : 0;
|
|
7248
|
+
return withMs(wallToUtc(Y, Mo, D, H, Mi, S), ms);
|
|
7249
|
+
}
|
|
7250
|
+
function daysInMonth(y, mo) {
|
|
7251
|
+
return new Date(Date.UTC(y, mo, 0)).getUTCDate();
|
|
7252
|
+
}
|
|
7253
|
+
function withMs(base, ms) {
|
|
7254
|
+
if (!ms) return base;
|
|
7255
|
+
const t = Date.parse(base);
|
|
7256
|
+
return Number.isNaN(t) ? base : new Date(t + ms).toISOString();
|
|
7257
|
+
}
|
|
7258
|
+
function parseDateStrTime(dateStr, timeExact) {
|
|
7259
|
+
if (typeof dateStr !== "string" || typeof timeExact !== "string") return void 0;
|
|
7260
|
+
if (!/^\d{1,2}\/\d{1,2}$/.test(dateStr)) return void 0;
|
|
7261
|
+
if (!/^\d{2}:\d{2}$/.test(timeExact)) return void 0;
|
|
7262
|
+
const [mo, d] = dateStr.split("/").map(Number);
|
|
7263
|
+
if (mo < 1 || mo > 12 || d < 1 || d > 31) return void 0;
|
|
7264
|
+
const y = wallParts().y;
|
|
7265
|
+
return parseWall(`${y}-${p2(mo)}-${p2(d)}T${timeExact}:00`);
|
|
7266
|
+
}
|
|
7267
|
+
function localOffsetSuffix(at) {
|
|
7268
|
+
const total = Math.round(zoneOffsetMs(at ? asDate(at) : /* @__PURE__ */ new Date()) / 6e4);
|
|
7269
|
+
const sign = total >= 0 ? "+" : "-";
|
|
7270
|
+
const abs = Math.abs(total);
|
|
7271
|
+
return `${sign}${p2(Math.floor(abs / 60))}:${p2(abs % 60)}`;
|
|
7272
|
+
}
|
|
7273
|
+
function startOfLocalDay(i) {
|
|
7274
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
7275
|
+
return wallToUtc(p.y, p.mo, p.d, 0, 0, 0);
|
|
7276
|
+
}
|
|
7277
|
+
function todayKey(i) {
|
|
7278
|
+
const p = wallParts(i ?? /* @__PURE__ */ new Date());
|
|
7279
|
+
return `${p.y}-${p2(p.mo)}-${p2(p.d)}`;
|
|
7280
|
+
}
|
|
7281
|
+
var WEEKDAY_ZH, EN_WEEKDAY, testTimezone, validatedZones, fmtCache, p2, warned, WALL_RE;
|
|
7282
|
+
var init_clock = __esm({
|
|
7283
|
+
"src/time/clock.ts"() {
|
|
7284
|
+
"use strict";
|
|
7285
|
+
init_live();
|
|
7286
|
+
WEEKDAY_ZH = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
7287
|
+
EN_WEEKDAY = {
|
|
7288
|
+
Sun: 0,
|
|
7289
|
+
Mon: 1,
|
|
7290
|
+
Tue: 2,
|
|
7291
|
+
Wed: 3,
|
|
7292
|
+
Thu: 4,
|
|
7293
|
+
Fri: 5,
|
|
7294
|
+
Sat: 6
|
|
7295
|
+
};
|
|
7296
|
+
testTimezone = null;
|
|
7297
|
+
validatedZones = /* @__PURE__ */ new Set();
|
|
7298
|
+
fmtCache = /* @__PURE__ */ new Map();
|
|
7299
|
+
p2 = (n) => String(n).padStart(2, "0");
|
|
7300
|
+
warned = /* @__PURE__ */ new Set();
|
|
7301
|
+
WALL_RE = /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2}))?/;
|
|
7302
|
+
}
|
|
7303
|
+
});
|
|
7304
|
+
|
|
7075
7305
|
// src/config/loader.ts
|
|
7076
7306
|
var loader_exports = {};
|
|
7077
7307
|
__export(loader_exports, {
|
|
7078
7308
|
configSummary: () => configSummary,
|
|
7079
7309
|
loadConfig: () => loadConfig,
|
|
7080
7310
|
parseModelRef: () => parseModelRef,
|
|
7311
|
+
resolveTimezone: () => resolveTimezone,
|
|
7081
7312
|
takeLoadWarnings: () => takeLoadWarnings
|
|
7082
7313
|
});
|
|
7083
7314
|
import * as fs9 from "node:fs";
|
|
@@ -7099,6 +7330,24 @@ function warnAtLoad(msg2) {
|
|
|
7099
7330
|
if (!loadWarningsFlushed) loadWarnings.push(msg2);
|
|
7100
7331
|
console.warn(msg2);
|
|
7101
7332
|
}
|
|
7333
|
+
function resolveTimezone(raw) {
|
|
7334
|
+
const fallback = () => {
|
|
7335
|
+
try {
|
|
7336
|
+
return detectHostTimezone();
|
|
7337
|
+
} catch {
|
|
7338
|
+
return "UTC";
|
|
7339
|
+
}
|
|
7340
|
+
};
|
|
7341
|
+
const v = raw?.timezone;
|
|
7342
|
+
if (typeof v !== "string" || !v) return fallback();
|
|
7343
|
+
try {
|
|
7344
|
+
new Intl.DateTimeFormat("en-US", { timeZone: v });
|
|
7345
|
+
return v;
|
|
7346
|
+
} catch {
|
|
7347
|
+
warnAtLoad(`[config] timezone "${v}" \u4E0D\u662F\u5408\u6CD5 IANA \u65F6\u533A\u540D\uFF0C\u9000\u56DE\u672C\u673A\u63A2\u6D4B`);
|
|
7348
|
+
return fallback();
|
|
7349
|
+
}
|
|
7350
|
+
}
|
|
7102
7351
|
function loadConfig(configPath) {
|
|
7103
7352
|
const resolvedPath = path9.resolve(
|
|
7104
7353
|
configPath || process.env.ENGINE_CONFIG || "engine-config.json"
|
|
@@ -7110,7 +7359,7 @@ function loadConfig(configPath) {
|
|
|
7110
7359
|
const missingEnvKeys = /* @__PURE__ */ new Set();
|
|
7111
7360
|
const resolveEnv = (node) => {
|
|
7112
7361
|
if (typeof node === "string") {
|
|
7113
|
-
const m = node.match(/^(?:env:|{{\s*)([A-Z][A-Z0-9_]
|
|
7362
|
+
const m = node.match(/^(?:env:|{{\s*)(?=[A-Z0-9_]*[A-Z])([A-Z0-9_]+)(?:\s*}})?$/);
|
|
7114
7363
|
if (m) {
|
|
7115
7364
|
if (process.env[m[1]] !== void 0) return process.env[m[1]];
|
|
7116
7365
|
if (node.startsWith("env:")) missingEnvKeys.add(m[1]);
|
|
@@ -7235,11 +7484,18 @@ function loadConfig(configPath) {
|
|
|
7235
7484
|
...rawInnerVoice,
|
|
7236
7485
|
enabled: true
|
|
7237
7486
|
} : void 0;
|
|
7487
|
+
const resolvedTimezone = resolveTimezone(raw);
|
|
7238
7488
|
const rawCron = raw.cron || {};
|
|
7239
7489
|
const cron = rawCron.enabled ? {
|
|
7240
7490
|
...rawCron,
|
|
7241
7491
|
enabled: true,
|
|
7242
|
-
|
|
7492
|
+
// ★ 这里原来写死 'Asia/Shanghai'。而 cron/tasks.ts 建任务时是
|
|
7493
|
+
// `params.timezone || params.defaultTimezone || 'Asia/Shanghai'` ——
|
|
7494
|
+
// defaultTimezone 恒有值 ⇒ 兜底永远轮不到 ⇒ **工具建的每个任务都被
|
|
7495
|
+
// 显式写死北京时区存进 tasks.json**,顶层 timezone 配置形同虚设
|
|
7496
|
+
// (马耳他用户说"每天 9 点",会在当地凌晨 3 点触发)。
|
|
7497
|
+
// 改为跟随引擎时区;per-task 的显式 timezone 仍然优先。
|
|
7498
|
+
defaultTimezone: rawCron.defaultTimezone || resolvedTimezone,
|
|
7243
7499
|
tickIntervalMs: rawCron.tickIntervalMs || 1e4,
|
|
7244
7500
|
maxConsecutiveFailures: rawCron.maxConsecutiveFailures || 5,
|
|
7245
7501
|
retry: {
|
|
@@ -7252,6 +7508,7 @@ function loadConfig(configPath) {
|
|
|
7252
7508
|
return {
|
|
7253
7509
|
...raw,
|
|
7254
7510
|
// 先展开 raw:未显式处理的字段(如 webhook)自动进来,避免每次加 config 都要手动挑;下面显式字段覆盖 raw(解析值优先)
|
|
7511
|
+
timezone: resolvedTimezone,
|
|
7255
7512
|
provider,
|
|
7256
7513
|
providers,
|
|
7257
7514
|
// 所有 provider 配置字典(供 memory/provider 动态切换用)
|
|
@@ -7320,6 +7577,7 @@ var init_loader = __esm({
|
|
|
7320
7577
|
"use strict";
|
|
7321
7578
|
init_display();
|
|
7322
7579
|
init_features();
|
|
7580
|
+
init_clock();
|
|
7323
7581
|
loadWarnings = [];
|
|
7324
7582
|
loadWarningsFlushed = false;
|
|
7325
7583
|
}
|
|
@@ -7819,26 +8077,26 @@ async function writeToMailbox(recipientName, message, teamName) {
|
|
|
7819
8077
|
await writeFile2(inboxPath, "[]", { encoding: "utf-8", flag: "wx" });
|
|
7820
8078
|
} catch {
|
|
7821
8079
|
}
|
|
7822
|
-
const
|
|
8080
|
+
const release3 = await acquireLock(inboxPath);
|
|
7823
8081
|
try {
|
|
7824
8082
|
const messages = await readMailbox(recipientName, teamName);
|
|
7825
8083
|
messages.push({ ...message, read: false });
|
|
7826
8084
|
await writeFile2(inboxPath, JSON.stringify(messages, null, 2), "utf-8");
|
|
7827
8085
|
console.log(`[mailbox] Wrote message to ${recipientName} from ${message.from} (team=${teamName || "default"}, inbox=${inboxPath})`);
|
|
7828
8086
|
} finally {
|
|
7829
|
-
await
|
|
8087
|
+
await release3();
|
|
7830
8088
|
}
|
|
7831
8089
|
}
|
|
7832
8090
|
async function markMessagesAsRead(agentName, teamName) {
|
|
7833
8091
|
const inboxPath = getInboxPath(agentName, teamName);
|
|
7834
|
-
const
|
|
8092
|
+
const release3 = await acquireLock(inboxPath);
|
|
7835
8093
|
try {
|
|
7836
8094
|
const messages = await readMailbox(agentName, teamName);
|
|
7837
8095
|
if (messages.length === 0) return;
|
|
7838
8096
|
for (const m of messages) m.read = true;
|
|
7839
8097
|
await writeFile2(inboxPath, JSON.stringify(messages, null, 2), "utf-8");
|
|
7840
8098
|
} finally {
|
|
7841
|
-
await
|
|
8099
|
+
await release3();
|
|
7842
8100
|
}
|
|
7843
8101
|
}
|
|
7844
8102
|
async function clearMailbox(agentName, teamName) {
|
|
@@ -8509,23 +8767,42 @@ var init_extractMemories = __esm({
|
|
|
8509
8767
|
}
|
|
8510
8768
|
});
|
|
8511
8769
|
|
|
8770
|
+
// src/mem7/core/system-filter.ts
|
|
8771
|
+
function parseMeta(text) {
|
|
8772
|
+
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
8773
|
+
if (!m) return null;
|
|
8774
|
+
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
8775
|
+
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
8776
|
+
}
|
|
8777
|
+
function isSystemUserMessage(meta) {
|
|
8778
|
+
if (!meta) return true;
|
|
8779
|
+
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
8780
|
+
}
|
|
8781
|
+
var SYSTEM_PLATFORMS;
|
|
8782
|
+
var init_system_filter = __esm({
|
|
8783
|
+
"src/mem7/core/system-filter.ts"() {
|
|
8784
|
+
"use strict";
|
|
8785
|
+
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
8786
|
+
"wx_monitor"
|
|
8787
|
+
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
8788
|
+
]);
|
|
8789
|
+
}
|
|
8790
|
+
});
|
|
8791
|
+
|
|
8512
8792
|
// src/memory/everos/ingest.ts
|
|
8513
8793
|
var ingest_exports = {};
|
|
8514
8794
|
__export(ingest_exports, {
|
|
8795
|
+
buildPayload: () => buildPayload,
|
|
8515
8796
|
pushConversation: () => pushConversation
|
|
8516
8797
|
});
|
|
8517
8798
|
import fs20 from "node:fs";
|
|
8518
8799
|
import path19 from "node:path";
|
|
8519
|
-
function parseMeta(text) {
|
|
8520
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+)\s*[^\]]*\]/);
|
|
8521
|
-
if (!m) return null;
|
|
8522
|
-
return { senderName: m[1].trim() };
|
|
8523
|
-
}
|
|
8524
8800
|
async function readConfig2() {
|
|
8525
8801
|
const defaults = {
|
|
8526
8802
|
url: "http://127.0.0.1:8100",
|
|
8527
8803
|
appId: "default",
|
|
8528
8804
|
agentName: "assistant",
|
|
8805
|
+
ownerMode: "agent",
|
|
8529
8806
|
enabled: false,
|
|
8530
8807
|
ingestTimeoutMs: 3e4,
|
|
8531
8808
|
syncIntervalMs: 9e5
|
|
@@ -8538,6 +8815,7 @@ async function readConfig2() {
|
|
|
8538
8815
|
url: cfg.everosUrl || defaults.url,
|
|
8539
8816
|
appId: cfg.userId || defaults.appId,
|
|
8540
8817
|
agentName: cfg.agentName || cfg.userId || defaults.agentName,
|
|
8818
|
+
ownerMode: cfg.ownerMode === "sender" ? "sender" : "agent",
|
|
8541
8819
|
enabled: cfg.enabled === true,
|
|
8542
8820
|
ingestTimeoutMs: typeof cfg.ingestTimeoutMs === "number" ? cfg.ingestTimeoutMs : defaults.ingestTimeoutMs,
|
|
8543
8821
|
syncIntervalMs: typeof cfg.syncIntervalMs === "number" ? cfg.syncIntervalMs : defaults.syncIntervalMs
|
|
@@ -8573,6 +8851,7 @@ function savePushTs(workspace, sessionId, ts) {
|
|
|
8573
8851
|
}
|
|
8574
8852
|
}
|
|
8575
8853
|
function buildPayload(messages, cfg, sessionId) {
|
|
8854
|
+
let lastUserWasSystem = false;
|
|
8576
8855
|
return {
|
|
8577
8856
|
session_id: `extract-${sessionId}`,
|
|
8578
8857
|
app_id: cfg.appId,
|
|
@@ -8584,21 +8863,28 @@ function buildPayload(messages, cfg, sessionId) {
|
|
|
8584
8863
|
const raw = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
8585
8864
|
const meta = m.role === "user" ? parseMeta(raw) : null;
|
|
8586
8865
|
let text = raw;
|
|
8866
|
+
let isSystemTurn = false;
|
|
8587
8867
|
if (m.role === "user") {
|
|
8588
|
-
if (
|
|
8589
|
-
|
|
8868
|
+
if (isSystemUserMessage(parseMeta(raw))) {
|
|
8869
|
+
text = "";
|
|
8870
|
+
isSystemTurn = true;
|
|
8871
|
+
} else text = stripInjectedContext(raw.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
8872
|
+
lastUserWasSystem = isSystemTurn;
|
|
8590
8873
|
}
|
|
8591
8874
|
const senderName = meta?.senderName ?? (m.role === "assistant" ? cfg.agentName : void 0) ?? m.role;
|
|
8592
8875
|
const rawRole = m.role === "toolResult" ? "tool" : m.role;
|
|
8593
8876
|
const role = rawRole === "user" ? "user" : "assistant";
|
|
8877
|
+
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
8594
8878
|
return {
|
|
8595
|
-
|
|
8879
|
+
// owner 口径见 IngestConfig.ownerMode:'sender' 模式下 user 消息用真人 ID
|
|
8880
|
+
// (everos 的 owner fan-out 取的就是 user 的 sender_id → 每人一份记忆)
|
|
8881
|
+
sender_id: m.role === "user" && cfg.ownerMode === "sender" ? meta?.senderId || cfg.appId : cfg.appId,
|
|
8596
8882
|
sender_name: senderName,
|
|
8597
8883
|
role,
|
|
8598
|
-
timestamp:
|
|
8884
|
+
timestamp: tsMs,
|
|
8599
8885
|
content: text,
|
|
8600
|
-
|
|
8601
|
-
|
|
8886
|
+
// 丢:系统指令本体 + 系统轮里的回应(与 mem7 同款,见上面状态机)
|
|
8887
|
+
_skip: m.role === "user" && !text || m.role !== "user" && lastUserWasSystem
|
|
8602
8888
|
};
|
|
8603
8889
|
}).filter((x) => !x._skip).map(({ _skip, ...rest }) => rest)
|
|
8604
8890
|
};
|
|
@@ -8708,6 +8994,7 @@ var init_ingest = __esm({
|
|
|
8708
8994
|
"src/memory/everos/ingest.ts"() {
|
|
8709
8995
|
"use strict";
|
|
8710
8996
|
init_skill_gate_window();
|
|
8997
|
+
init_system_filter();
|
|
8711
8998
|
PUSH_CHUNK = 10;
|
|
8712
8999
|
PUSH_STATE_FILE = ".everos-push-state.json";
|
|
8713
9000
|
pushInProgress = false;
|
|
@@ -8754,7 +9041,8 @@ async function embedChunk(cfg, baseUrl, texts, timeoutMs) {
|
|
|
8754
9041
|
const resp = await fetch(`${baseUrl.replace(/\/$/, "")}/embeddings`, {
|
|
8755
9042
|
method: "POST",
|
|
8756
9043
|
headers: { "content-type": "application/json", authorization: `Bearer ${cfg.apiKey}` },
|
|
8757
|
-
|
|
9044
|
+
// 0918:safeStringify 清洗孤立代理(embedding 传对话原文,emoji 风险高)
|
|
9045
|
+
body: safeStringify({ model: cfg.model, input: texts }),
|
|
8758
9046
|
signal: ctrl.signal
|
|
8759
9047
|
});
|
|
8760
9048
|
if (!resp.ok) throw new Error(`embeddings HTTP ${resp.status}`);
|
|
@@ -8803,6 +9091,7 @@ function cosine(a, b) {
|
|
|
8803
9091
|
var init_embedding = __esm({
|
|
8804
9092
|
"src/mem7/core/embedding.ts"() {
|
|
8805
9093
|
"use strict";
|
|
9094
|
+
init_well_formed();
|
|
8806
9095
|
}
|
|
8807
9096
|
});
|
|
8808
9097
|
|
|
@@ -8943,8 +9232,7 @@ function compileFilter(expr) {
|
|
|
8943
9232
|
return parts.join(" AND ");
|
|
8944
9233
|
}
|
|
8945
9234
|
function openWindowFilter(ownerId) {
|
|
8946
|
-
const
|
|
8947
|
-
const floor = (/* @__PURE__ */ new Date(`${cstToday}T00:00:00+08:00`)).toISOString();
|
|
9235
|
+
const floor = startOfLocalDay();
|
|
8948
9236
|
const out = [{ end_time: { gte: floor } }];
|
|
8949
9237
|
const owners = (Array.isArray(ownerId) ? ownerId : ownerId ? [ownerId] : []).filter(Boolean);
|
|
8950
9238
|
if (Array.isArray(ownerId) && owners.length === 0) {
|
|
@@ -8958,6 +9246,7 @@ var REGISTERED_FIELDS, OPS, TIME_FIELDS;
|
|
|
8958
9246
|
var init_filters = __esm({
|
|
8959
9247
|
"src/mem7/core/filters.ts"() {
|
|
8960
9248
|
"use strict";
|
|
9249
|
+
init_clock();
|
|
8961
9250
|
REGISTERED_FIELDS = /* @__PURE__ */ new Set([
|
|
8962
9251
|
"owner_id",
|
|
8963
9252
|
"owner_type",
|
|
@@ -9275,13 +9564,6 @@ function renderPrompt(template, vars) {
|
|
|
9275
9564
|
}
|
|
9276
9565
|
return t.split(LB).join("{").split(RB).join("}");
|
|
9277
9566
|
}
|
|
9278
|
-
function renderLiteral(template, vars) {
|
|
9279
|
-
let out = template;
|
|
9280
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
9281
|
-
out = out.split(`{{${key}}}`).join(value);
|
|
9282
|
-
}
|
|
9283
|
-
return out;
|
|
9284
|
-
}
|
|
9285
9567
|
function formatAtomicFactTime(tsMs) {
|
|
9286
9568
|
const d = new Date(tsMs);
|
|
9287
9569
|
const month = MONTHS[d.getUTCMonth()];
|
|
@@ -9313,10 +9595,11 @@ function formatMessageTimestamp(tsMs) {
|
|
|
9313
9595
|
const iso = new Date(tsMs).toISOString();
|
|
9314
9596
|
return `${iso.slice(0, 10)} ${iso.slice(11, 19)}`;
|
|
9315
9597
|
}
|
|
9598
|
+
function renderConversationText(messages) {
|
|
9599
|
+
return messages.map((m) => `[${formatMessageTimestamp(m.tsMs)}] ${m.senderName ?? m.senderId}: ${m.text}`).join("\n");
|
|
9600
|
+
}
|
|
9316
9601
|
function formatMdTimestamp(d = /* @__PURE__ */ new Date()) {
|
|
9317
|
-
|
|
9318
|
-
const iso = cst.toISOString();
|
|
9319
|
-
return `${iso.slice(0, 19)}+08:00`;
|
|
9602
|
+
return `${toWall(d).replace(" ", "T")}${localOffsetSuffix(d)}`;
|
|
9320
9603
|
}
|
|
9321
9604
|
function pyListRepr(items) {
|
|
9322
9605
|
return `[${items.join(", ")}]`;
|
|
@@ -9325,6 +9608,7 @@ var WEEKDAYS, MONTHS, LB, RB, DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
|
9325
9608
|
var init_prompt_render = __esm({
|
|
9326
9609
|
"src/mem7/core/prompt-render.ts"() {
|
|
9327
9610
|
"use strict";
|
|
9611
|
+
init_clock();
|
|
9328
9612
|
WEEKDAYS = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
|
|
9329
9613
|
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
|
|
9330
9614
|
LB = "\0M7LB\0";
|
|
@@ -9356,6 +9640,7 @@ var init_store2 = __esm({
|
|
|
9356
9640
|
"use strict";
|
|
9357
9641
|
init_vector_store();
|
|
9358
9642
|
init_prompt_render();
|
|
9643
|
+
init_clock();
|
|
9359
9644
|
SCHEMA = `
|
|
9360
9645
|
CREATE TABLE IF NOT EXISTS memcells (
|
|
9361
9646
|
id TEXT PRIMARY KEY,
|
|
@@ -9642,7 +9927,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9642
9927
|
}
|
|
9643
9928
|
// ── md 读(proactive 消费):开窗预测(全 owner 扫描,end>=今天)──
|
|
9644
9929
|
getOpenPredictions(now, limit = 50) {
|
|
9645
|
-
const
|
|
9930
|
+
const today2 = todayKey(now);
|
|
9646
9931
|
const out = [];
|
|
9647
9932
|
const usersDir = join22(this.root, "users");
|
|
9648
9933
|
if (!existsSync12(usersDir)) return out;
|
|
@@ -9665,7 +9950,7 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9665
9950
|
const text = ((block.match(/### Foresight\s*\n+([\s\S]*?)(?:\n### Evidence|$)/) ?? [])[1] ?? "").trim();
|
|
9666
9951
|
const evidence = ((block.match(/### Evidence\s*\n+([\s\S]*?)$/) ?? [])[1] ?? "").trim();
|
|
9667
9952
|
if (!end || !start || !text) continue;
|
|
9668
|
-
if (end <
|
|
9953
|
+
if (end < today2) continue;
|
|
9669
9954
|
out.push({ ownerId: owner.name, startDate: start, endDate: end, content: text.replace(/\n+/g, " ").slice(0, 200), evidence: evidence.replace(/\n+/g, " ").slice(0, 120) });
|
|
9670
9955
|
if (out.length >= limit) return out;
|
|
9671
9956
|
}
|
|
@@ -9677,28 +9962,6 @@ CREATE TABLE IF NOT EXISTS watermarks (
|
|
|
9677
9962
|
}
|
|
9678
9963
|
});
|
|
9679
9964
|
|
|
9680
|
-
// src/mem7/core/system-filter.ts
|
|
9681
|
-
function parseMeta2(text) {
|
|
9682
|
-
const m = text.match(/^\[meta:\s*(.+?)\s*\((.+?)\)\s*@(\S+?)\s*(?:[#\d]|\])/);
|
|
9683
|
-
if (!m) return null;
|
|
9684
|
-
const platform2 = m[3].replace(/[#\d]+$/, "").trim() || void 0;
|
|
9685
|
-
return { senderName: m[1].trim(), senderId: m[2].trim(), platform: platform2 };
|
|
9686
|
-
}
|
|
9687
|
-
function isSystemUserMessage(meta) {
|
|
9688
|
-
if (!meta) return true;
|
|
9689
|
-
return SYSTEM_PLATFORMS.has(meta.platform ?? "");
|
|
9690
|
-
}
|
|
9691
|
-
var SYSTEM_PLATFORMS;
|
|
9692
|
-
var init_system_filter = __esm({
|
|
9693
|
-
"src/mem7/core/system-filter.ts"() {
|
|
9694
|
-
"use strict";
|
|
9695
|
-
SYSTEM_PLATFORMS = /* @__PURE__ */ new Set([
|
|
9696
|
-
"wx_monitor"
|
|
9697
|
-
// 微信巡检推送(翀哥 0916 点名:"cron、wx-monitor 这种问答都不记")
|
|
9698
|
-
]);
|
|
9699
|
-
}
|
|
9700
|
-
});
|
|
9701
|
-
|
|
9702
9965
|
// src/mem7/core/ingest.ts
|
|
9703
9966
|
function contentText(content) {
|
|
9704
9967
|
if (typeof content === "string") return content;
|
|
@@ -9707,7 +9970,8 @@ function contentText(content) {
|
|
|
9707
9970
|
}
|
|
9708
9971
|
return "";
|
|
9709
9972
|
}
|
|
9710
|
-
function normalizeMessages(rawMessages, agentName) {
|
|
9973
|
+
function normalizeMessages(rawMessages, agentName, identity, ownerMode = "agent") {
|
|
9974
|
+
const ownerId = identity || agentName;
|
|
9711
9975
|
const out = [];
|
|
9712
9976
|
let lastUserWasSystem = false;
|
|
9713
9977
|
for (let rawIdx = 0; rawIdx < rawMessages.length; rawIdx++) {
|
|
@@ -9722,19 +9986,19 @@ function normalizeMessages(rawMessages, agentName) {
|
|
|
9722
9986
|
let meta = null;
|
|
9723
9987
|
if (role === "assistant" && lastUserWasSystem) continue;
|
|
9724
9988
|
if (role === "user") {
|
|
9725
|
-
meta =
|
|
9989
|
+
meta = parseMeta(text);
|
|
9726
9990
|
if (isSystemUserMessage(meta)) {
|
|
9727
9991
|
lastUserWasSystem = true;
|
|
9728
9992
|
continue;
|
|
9729
9993
|
}
|
|
9730
9994
|
lastUserWasSystem = false;
|
|
9731
9995
|
senderName = meta.senderName;
|
|
9732
|
-
senderId = meta.senderId;
|
|
9996
|
+
senderId = ownerMode === "sender" ? meta.senderId : ownerId;
|
|
9733
9997
|
text = stripInjectedContext(text.replace(/^\[meta:[^\]]*\]\s*/, ""));
|
|
9734
9998
|
if (!text) continue;
|
|
9735
9999
|
} else {
|
|
9736
10000
|
senderName = agentName;
|
|
9737
|
-
senderId =
|
|
10001
|
+
senderId = ownerId;
|
|
9738
10002
|
}
|
|
9739
10003
|
const tsMs = typeof m.timestamp === "number" ? m.timestamp : Date.parse(String(m.timestamp ?? "")) || Date.now();
|
|
9740
10004
|
out.push({ id: `m_${tsMs}_${String(rawIdx).padStart(3, "0")}`, role, senderId, senderName, platform: meta?.platform, text, tsMs });
|
|
@@ -9891,7 +10155,8 @@ async function extractEpisode(cell, provider, model) {
|
|
|
9891
10155
|
for await (const chunk of provider.streamChat({
|
|
9892
10156
|
model,
|
|
9893
10157
|
messages: [{ role: "user", content: userPrompt }],
|
|
9894
|
-
temperature
|
|
10158
|
+
// 0(everalgo episode callsite 不传 temperature → LLM 默认 0.0);0.3 是 foresight 的参数,别混
|
|
10159
|
+
temperature: 0
|
|
9895
10160
|
})) {
|
|
9896
10161
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
9897
10162
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
@@ -9933,13 +10198,10 @@ var init_episode = __esm({
|
|
|
9933
10198
|
import { readFileSync as readFileSync17 } from "node:fs";
|
|
9934
10199
|
import { join as join25, dirname as dirname4 } from "node:path";
|
|
9935
10200
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9936
|
-
async function extractAtomicFacts(
|
|
9937
|
-
const
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
const userPrompt = renderLiteral(PROMPT4, {
|
|
9941
|
-
EPISODE_TEXT: episodeText,
|
|
9942
|
-
TIME: formatAtomicFactTime(cellTsMs)
|
|
10201
|
+
async function extractAtomicFacts(cell, provider, model) {
|
|
10202
|
+
const userPrompt = renderPrompt(PROMPT4, {
|
|
10203
|
+
INPUT_TEXT: renderConversationText(cell.messages),
|
|
10204
|
+
TIME: formatAtomicFactTime(cell.startTs)
|
|
9943
10205
|
});
|
|
9944
10206
|
for (let attempt = 0; attempt < RETRIES3; attempt++) {
|
|
9945
10207
|
try {
|
|
@@ -9947,15 +10209,16 @@ ${episode.content}`;
|
|
|
9947
10209
|
for await (const chunk of provider.streamChat({
|
|
9948
10210
|
model,
|
|
9949
10211
|
messages: [{ role: "user", content: userPrompt }],
|
|
9950
|
-
temperature: 0
|
|
10212
|
+
temperature: 0
|
|
9951
10213
|
})) {
|
|
9952
10214
|
if (chunk.type === "text" && chunk.text) text += chunk.text;
|
|
9953
10215
|
if (chunk.type === "error") throw new Error(chunk.error ?? "LLM stream error");
|
|
9954
10216
|
}
|
|
9955
10217
|
const obj = extractJson(text);
|
|
9956
10218
|
const block = obj?.event_log ?? obj?.atomic_facts;
|
|
9957
|
-
|
|
9958
|
-
|
|
10219
|
+
if (!block || typeof block.time !== "string" || !block.time.trim()) throw new Error("atomic_facts.time missing");
|
|
10220
|
+
const list2 = block.atomic_fact;
|
|
10221
|
+
if (!Array.isArray(list2)) throw new Error("atomic_facts.atomic_fact not a list");
|
|
9959
10222
|
return list2.filter((f) => typeof f === "string" && f.trim().length > 0).map((f) => f.trim());
|
|
9960
10223
|
} catch {
|
|
9961
10224
|
}
|
|
@@ -9968,8 +10231,8 @@ var init_atomic_facts = __esm({
|
|
|
9968
10231
|
"use strict";
|
|
9969
10232
|
init_episode();
|
|
9970
10233
|
init_prompt_render();
|
|
9971
|
-
RETRIES3 =
|
|
9972
|
-
PROMPT4 = readFileSync17(join25(dirname4(fileURLToPath4(import.meta.url)), "prompts", "atomic-facts.md"), "utf8");
|
|
10234
|
+
RETRIES3 = 5;
|
|
10235
|
+
PROMPT4 = readFileSync17(join25(dirname4(fileURLToPath4(import.meta.url)), "prompts", "atomic-facts-memcell.md"), "utf8");
|
|
9973
10236
|
}
|
|
9974
10237
|
});
|
|
9975
10238
|
|
|
@@ -9988,20 +10251,20 @@ function cleanDateString(dateStr) {
|
|
|
9988
10251
|
}
|
|
9989
10252
|
function calcEndFromDuration(startDate, durationDays) {
|
|
9990
10253
|
const d = new Date(Date.parse(startDate + "T00:00:00Z") + durationDays * 864e5);
|
|
9991
|
-
return Number.isNaN(d.getTime()) ? null : d
|
|
10254
|
+
return Number.isNaN(d.getTime()) ? null : todayKey(d);
|
|
9992
10255
|
}
|
|
9993
10256
|
function calcDurationDays(startDate, endDate) {
|
|
9994
10257
|
const ms = Date.parse(endDate + "T00:00:00Z") - Date.parse(startDate + "T00:00:00Z");
|
|
9995
10258
|
return Number.isNaN(ms) ? null : Math.round(ms / 864e5);
|
|
9996
10259
|
}
|
|
9997
10260
|
function cellDateCst(cell) {
|
|
9998
|
-
return
|
|
10261
|
+
return todayKey(cell.startTs);
|
|
9999
10262
|
}
|
|
10000
10263
|
async function extractForesights(cell, ownerId, provider, model) {
|
|
10001
10264
|
const userSenders = cell.messages.filter((m) => m.role === "user");
|
|
10002
10265
|
const owner = userSenders.find((m) => m.senderId === ownerId) ?? userSenders[0];
|
|
10003
10266
|
const userName = owner?.senderName ?? ownerId;
|
|
10004
|
-
const conversationText = cell.messages
|
|
10267
|
+
const conversationText = renderConversationText(cell.messages);
|
|
10005
10268
|
const userPrompt = renderPrompt(PROMPT5, { USER_ID: ownerId, USER_NAME: userName, CONVERSATION_TEXT: conversationText });
|
|
10006
10269
|
let raw = null;
|
|
10007
10270
|
for (let attempt = 0; attempt < RETRIES4; attempt++) {
|
|
@@ -10054,6 +10317,7 @@ var init_foresight = __esm({
|
|
|
10054
10317
|
"use strict";
|
|
10055
10318
|
init_episode();
|
|
10056
10319
|
init_prompt_render();
|
|
10320
|
+
init_clock();
|
|
10057
10321
|
RETRIES4 = 5;
|
|
10058
10322
|
MAX_COUNT = 10;
|
|
10059
10323
|
MIN_COUNT = 4;
|
|
@@ -10064,11 +10328,11 @@ var init_foresight = __esm({
|
|
|
10064
10328
|
|
|
10065
10329
|
// src/mem7/core/pipeline.ts
|
|
10066
10330
|
function dateCst(tsMs) {
|
|
10067
|
-
return
|
|
10331
|
+
return todayKey(tsMs);
|
|
10068
10332
|
}
|
|
10069
10333
|
async function runMem7Pipeline(args) {
|
|
10070
|
-
const { sessionId, store, provider, model, agentName } = args;
|
|
10071
|
-
const all = normalizeMessages(args.rawMessages, agentName);
|
|
10334
|
+
const { sessionId, store, provider, model, agentName, identity, ownerMode } = args;
|
|
10335
|
+
const all = normalizeMessages(args.rawMessages, agentName, identity, ownerMode);
|
|
10072
10336
|
const fresh = readFresh(store, sessionId, all);
|
|
10073
10337
|
if (fresh.length === 0) return;
|
|
10074
10338
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -10140,7 +10404,7 @@ async function runMem7Pipeline(args) {
|
|
|
10140
10404
|
}
|
|
10141
10405
|
}
|
|
10142
10406
|
})(),
|
|
10143
|
-
// 支 2:episode →
|
|
10407
|
+
// 支 2:episode(memcell → episode,一次调用 + per-owner fan-out,everos 同款)
|
|
10144
10408
|
(async () => {
|
|
10145
10409
|
try {
|
|
10146
10410
|
const ep = await extractEpisode(cell, provider, model);
|
|
@@ -10162,26 +10426,34 @@ async function runMem7Pipeline(args) {
|
|
|
10162
10426
|
}]);
|
|
10163
10427
|
embedItems.push({ contentHash: contentHash(ep.content), kind: "episode", refDate: dateStr, ownerId, content: ep.content });
|
|
10164
10428
|
}
|
|
10165
|
-
|
|
10429
|
+
store.updateMemcellStatus(cell.id, "extracted");
|
|
10430
|
+
} catch (err) {
|
|
10431
|
+
console.warn(`[mem7] episode failed (cell=${cell.id}): ${err.message}`);
|
|
10432
|
+
store.updateMemcellStatus(cell.id, "skipped");
|
|
10433
|
+
}
|
|
10434
|
+
})(),
|
|
10435
|
+
// 支 3:atomic-facts(memcell → 事实,**与 episode 平级**)
|
|
10436
|
+
// 0918 对齐 everos:`extract_atomic_facts.py:79` 直接吃 memcell,不经 episode。
|
|
10437
|
+
// 此前挂在 episode 下游(二次压缩)+ 用 from_text 版 prompt(无语言规则 → 英文事实)
|
|
10438
|
+
(async () => {
|
|
10439
|
+
try {
|
|
10440
|
+
const facts = await extractAtomicFacts(cell, provider, model);
|
|
10166
10441
|
factsOut = facts;
|
|
10167
|
-
if (facts.length
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
}
|
|
10442
|
+
if (facts.length === 0) return;
|
|
10443
|
+
for (const ownerId of userSenders) {
|
|
10444
|
+
store.appendAtomicFacts(ownerId, dateStr, [{
|
|
10445
|
+
ownerId,
|
|
10446
|
+
sessionId,
|
|
10447
|
+
time: new Date(cell.startTs).toISOString(),
|
|
10448
|
+
parentId: cell.id,
|
|
10449
|
+
facts
|
|
10450
|
+
}]);
|
|
10451
|
+
for (const f of facts) {
|
|
10452
|
+
embedItems.push({ contentHash: contentHash(f), kind: "atomic_fact", refDate: dateStr, ownerId, content: f });
|
|
10179
10453
|
}
|
|
10180
10454
|
}
|
|
10181
|
-
store.updateMemcellStatus(cell.id, "extracted");
|
|
10182
10455
|
} catch (err) {
|
|
10183
|
-
console.warn(`[mem7]
|
|
10184
|
-
store.updateMemcellStatus(cell.id, "skipped");
|
|
10456
|
+
console.warn(`[mem7] atomic-facts failed (cell=${cell.id}): ${err.message}`);
|
|
10185
10457
|
}
|
|
10186
10458
|
})()
|
|
10187
10459
|
]);
|
|
@@ -10217,6 +10489,7 @@ var init_pipeline = __esm({
|
|
|
10217
10489
|
init_episode();
|
|
10218
10490
|
init_atomic_facts();
|
|
10219
10491
|
init_foresight();
|
|
10492
|
+
init_clock();
|
|
10220
10493
|
}
|
|
10221
10494
|
});
|
|
10222
10495
|
|
|
@@ -10259,28 +10532,23 @@ function parseProactiveConfig(raw) {
|
|
|
10259
10532
|
try {
|
|
10260
10533
|
parsed = parseModelRef(modelRef);
|
|
10261
10534
|
} catch (err) {
|
|
10262
|
-
console.warn(`[proactive]
|
|
10535
|
+
console.warn(`[proactive] model "${modelRef}" \u975E\u6CD5\uFF08${err.message}\uFF09\u2014\u2014 \u6539\u7528\u4E3B\u6A21\u578B`);
|
|
10263
10536
|
}
|
|
10264
10537
|
}
|
|
10265
|
-
if (!parsed) {
|
|
10266
|
-
parsed = parseModelRef(DEFAULT_MODEL_REF);
|
|
10267
|
-
}
|
|
10268
10538
|
return {
|
|
10269
10539
|
enabled: obj.enabled === true || obj.enabled === "yes" || obj.enabled === 1,
|
|
10270
|
-
modelRef: parsed.providerId
|
|
10271
|
-
providerId: parsed
|
|
10272
|
-
modelId: parsed
|
|
10540
|
+
modelRef: parsed ? `${parsed.providerId}/${parsed.modelId}` : "",
|
|
10541
|
+
providerId: parsed?.providerId ?? "",
|
|
10542
|
+
modelId: parsed?.modelId ?? ""
|
|
10273
10543
|
};
|
|
10274
10544
|
}
|
|
10275
10545
|
function proactiveDbPath(workspace) {
|
|
10276
10546
|
return join27(workspace, ".proactive", "proactive.db");
|
|
10277
10547
|
}
|
|
10278
|
-
var DEFAULT_MODEL_REF;
|
|
10279
10548
|
var init_config3 = __esm({
|
|
10280
10549
|
"src/memory/proactive/config.ts"() {
|
|
10281
10550
|
"use strict";
|
|
10282
10551
|
init_loader();
|
|
10283
|
-
DEFAULT_MODEL_REF = "minimax/MiniMax-M3";
|
|
10284
10552
|
}
|
|
10285
10553
|
});
|
|
10286
10554
|
|
|
@@ -10640,18 +10908,13 @@ function rowToAction(r) {
|
|
|
10640
10908
|
}
|
|
10641
10909
|
function normalizeToUtcIso(s) {
|
|
10642
10910
|
if (!s) return s;
|
|
10643
|
-
|
|
10644
|
-
if (/[+-]\d{2}:?\d{2}$/.test(s)) {
|
|
10645
|
-
const ms2 = Date.parse(s);
|
|
10646
|
-
return Number.isNaN(ms2) ? s : new Date(ms2).toISOString();
|
|
10647
|
-
}
|
|
10648
|
-
const ms = Date.parse(s + TZ_BEIJING);
|
|
10649
|
-
return Number.isNaN(ms) ? s : new Date(ms).toISOString();
|
|
10911
|
+
return parseWall(s);
|
|
10650
10912
|
}
|
|
10651
|
-
var SCHEMA2, Persistence
|
|
10913
|
+
var SCHEMA2, Persistence;
|
|
10652
10914
|
var init_persistence = __esm({
|
|
10653
10915
|
"src/memory/proactive/persistence.ts"() {
|
|
10654
10916
|
"use strict";
|
|
10917
|
+
init_clock();
|
|
10655
10918
|
SCHEMA2 = `
|
|
10656
10919
|
CREATE TABLE IF NOT EXISTS nodes (
|
|
10657
10920
|
id TEXT PRIMARY KEY,
|
|
@@ -11065,6 +11328,27 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
11065
11328
|
const row = this.db.prepare("SELECT * FROM actions WHERE id=?").get(id);
|
|
11066
11329
|
return row ? rowToAction(row) : void 0;
|
|
11067
11330
|
}
|
|
11331
|
+
/** proactive_status 工具用:按状态列 action(按排期升序,最多 30 条)。 */
|
|
11332
|
+
listActions(filter) {
|
|
11333
|
+
const where = filter === "open" ? "status IN ('queued','executing')" : "status NOT IN ('cancelled')";
|
|
11334
|
+
const rows = this.db.prepare(
|
|
11335
|
+
`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`
|
|
11336
|
+
).all();
|
|
11337
|
+
return rows.map(rowToAction);
|
|
11338
|
+
}
|
|
11339
|
+
/** proactive_resolve_intent 工具用:读 intent 节点的 data(不存在返回 null)。 */
|
|
11340
|
+
getIntentData(intentId) {
|
|
11341
|
+
const row = this.db.prepare("SELECT data FROM nodes WHERE id=? AND type=?").get(intentId, "intent");
|
|
11342
|
+
if (!row) return null;
|
|
11343
|
+
try {
|
|
11344
|
+
return JSON.parse(row.data);
|
|
11345
|
+
} catch {
|
|
11346
|
+
return {};
|
|
11347
|
+
}
|
|
11348
|
+
}
|
|
11349
|
+
setIntentData(intentId, data) {
|
|
11350
|
+
this.db.prepare("UPDATE nodes SET data=? WHERE id=?").run(JSON.stringify(data), intentId);
|
|
11351
|
+
}
|
|
11068
11352
|
/** 周指标统计(验证标准用:完成数/cancelled 率) */
|
|
11069
11353
|
actionStats(sinceIso) {
|
|
11070
11354
|
const row = this.db.prepare(
|
|
@@ -11076,7 +11360,6 @@ CREATE INDEX IF NOT EXISTS idx_proactive_decisions_created ON proactive_decision
|
|
|
11076
11360
|
return { total: row.total ?? 0, completed: row.completed ?? 0, cancelled: row.cancelled ?? 0 };
|
|
11077
11361
|
}
|
|
11078
11362
|
};
|
|
11079
|
-
TZ_BEIJING = "+08:00";
|
|
11080
11363
|
}
|
|
11081
11364
|
});
|
|
11082
11365
|
|
|
@@ -11864,7 +12147,8 @@ function buildUserPrompt(event2, contextNodes, mode, foresightHits) {
|
|
|
11864
12147
|
const foresightSection = foresightHits && foresightHits.length > 0 ? `
|
|
11865
12148
|
## Related Predictions (foresight \u2014 made earlier, window still open; matched this message by terms)
|
|
11866
12149
|
|
|
11867
|
-
These dormant predictions
|
|
12150
|
+
These dormant predictions were surfaced by **mechanical keyword matching** \u2014 some may be irrelevant; ignore them freely.
|
|
12151
|
+
If acting on one NOW is meaningfully better than later (\u8D81\u70ED/\u987A\u4FBF), output an opportunity for it.
|
|
11868
12152
|
|
|
11869
12153
|
${foresightHits.join("\n")}
|
|
11870
12154
|
` : "";
|
|
@@ -12296,10 +12580,14 @@ If deadline has no time context, fall back to normal timing rules.
|
|
|
12296
12580
|
|
|
12297
12581
|
// src/calendar/helpers.ts
|
|
12298
12582
|
function nowIso() {
|
|
12299
|
-
return
|
|
12583
|
+
return toUtc(/* @__PURE__ */ new Date());
|
|
12584
|
+
}
|
|
12585
|
+
function nowParts() {
|
|
12586
|
+
return wallParts();
|
|
12300
12587
|
}
|
|
12301
|
-
function
|
|
12302
|
-
|
|
12588
|
+
function wallMs(y, mo, d, hh, mm) {
|
|
12589
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
12590
|
+
return Date.parse(parseWall(`${y}-${z(mo)}-${z(d)}T${z(hh)}:${z(mm)}:00`));
|
|
12303
12591
|
}
|
|
12304
12592
|
function parseDateRange(dateStr) {
|
|
12305
12593
|
const s = dateStr.replace("\u524D", "");
|
|
@@ -12321,32 +12609,29 @@ function parseDateRange(dateStr) {
|
|
|
12321
12609
|
}
|
|
12322
12610
|
}
|
|
12323
12611
|
function isToday(month, dStart, dEnd) {
|
|
12324
|
-
const
|
|
12325
|
-
return month ===
|
|
12612
|
+
const p = nowParts();
|
|
12613
|
+
return month === p.mo && dStart <= p.d && p.d <= dEnd;
|
|
12326
12614
|
}
|
|
12327
12615
|
function isOverdue(month, dStart, dEnd) {
|
|
12328
|
-
const
|
|
12329
|
-
|
|
12330
|
-
const curDay = now.getUTCDate();
|
|
12331
|
-
return month < curMonth || month === curMonth && dEnd < curDay;
|
|
12616
|
+
const p = nowParts();
|
|
12617
|
+
return month < p.mo || month === p.mo && dEnd < p.d;
|
|
12332
12618
|
}
|
|
12333
12619
|
function getTodayWeekday() {
|
|
12334
|
-
const
|
|
12335
|
-
return WEEKDAYS2[
|
|
12620
|
+
const p = nowParts();
|
|
12621
|
+
return WEEKDAYS2[p.weekday === 0 ? 6 : p.weekday - 1];
|
|
12336
12622
|
}
|
|
12337
12623
|
function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
12338
12624
|
const parsed = parseDateRange(dateStr);
|
|
12339
12625
|
if (!parsed) return null;
|
|
12340
12626
|
const { month, dStart } = parsed;
|
|
12341
|
-
const
|
|
12342
|
-
const year = now.getUTCFullYear();
|
|
12627
|
+
const year = nowParts().y;
|
|
12343
12628
|
let eventMs;
|
|
12344
12629
|
if (timeExact) {
|
|
12345
12630
|
const [hh, mm] = timeExact.split(":").map(Number);
|
|
12346
12631
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12347
|
-
eventMs =
|
|
12632
|
+
eventMs = wallMs(year, month, dStart, hh, mm);
|
|
12348
12633
|
} else {
|
|
12349
|
-
eventMs =
|
|
12634
|
+
eventMs = wallMs(year, month, dStart, 9, 0);
|
|
12350
12635
|
}
|
|
12351
12636
|
const remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12352
12637
|
return new Date(remindMs).toISOString();
|
|
@@ -12354,20 +12639,19 @@ function computeRemindAt(dateStr, timeExact, remindBeforeMin) {
|
|
|
12354
12639
|
function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
12355
12640
|
const targetIdx = WEEKDAYS2.indexOf(day);
|
|
12356
12641
|
if (targetIdx === -1) return null;
|
|
12357
|
-
const
|
|
12358
|
-
const curIdx =
|
|
12642
|
+
const p = nowParts();
|
|
12643
|
+
const curIdx = p.weekday === 0 ? 6 : p.weekday - 1;
|
|
12359
12644
|
let dayDiff = targetIdx - curIdx;
|
|
12360
12645
|
if (dayDiff < 0) dayDiff += 7;
|
|
12361
|
-
const
|
|
12362
|
-
const month =
|
|
12363
|
-
const date = now.getUTCDate() + dayDiff;
|
|
12646
|
+
const target = wallParts(Date.parse(startOfLocalDay()) + dayDiff * 24 * 60 * 60 * 1e3);
|
|
12647
|
+
const year = target.y, month = target.mo, date = target.d;
|
|
12364
12648
|
let eventMs;
|
|
12365
12649
|
if (time && time !== "\u5168\u5929") {
|
|
12366
12650
|
const [hh, mm] = time.split(":").map(Number);
|
|
12367
12651
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12368
|
-
eventMs =
|
|
12652
|
+
eventMs = wallMs(year, month, date, hh, mm);
|
|
12369
12653
|
} else {
|
|
12370
|
-
eventMs =
|
|
12654
|
+
eventMs = wallMs(year, month, date, 9, 0);
|
|
12371
12655
|
}
|
|
12372
12656
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12373
12657
|
if (remindMs <= Date.now()) {
|
|
@@ -12382,11 +12666,8 @@ function computeWeeklyRemindAt(day, time, remindBeforeMin) {
|
|
|
12382
12666
|
function computeDailyRemindAt(time, remindBeforeMin) {
|
|
12383
12667
|
const [hh, mm] = time.split(":").map(Number);
|
|
12384
12668
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12385
|
-
const
|
|
12386
|
-
|
|
12387
|
-
const month = now.getUTCMonth();
|
|
12388
|
-
const date = now.getUTCDate();
|
|
12389
|
-
let eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
12669
|
+
const p = nowParts();
|
|
12670
|
+
let eventMs = wallMs(p.y, p.mo, p.d, hh, mm);
|
|
12390
12671
|
let remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12391
12672
|
if (remindMs <= Date.now()) {
|
|
12392
12673
|
remindMs += 24 * 60 * 60 * 1e3;
|
|
@@ -12396,18 +12677,17 @@ function computeDailyRemindAt(time, remindBeforeMin) {
|
|
|
12396
12677
|
function computeWeekdaysRemindAt(time, remindBeforeMin) {
|
|
12397
12678
|
const [hh, mm] = time.split(":").map(Number);
|
|
12398
12679
|
if (isNaN(hh) || isNaN(mm)) return null;
|
|
12399
|
-
|
|
12400
|
-
let
|
|
12401
|
-
let
|
|
12402
|
-
let
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
date++;
|
|
12408
|
-
curDay = (curDay + 1) % 7;
|
|
12409
|
-
eventMs = Date.UTC(year, month, date, hh - 8, mm);
|
|
12680
|
+
let dayOffset = 0;
|
|
12681
|
+
let eventMs;
|
|
12682
|
+
let remindMs;
|
|
12683
|
+
let curDay;
|
|
12684
|
+
for (; ; ) {
|
|
12685
|
+
const t = wallParts(Date.parse(startOfLocalDay()) + dayOffset * 24 * 60 * 60 * 1e3);
|
|
12686
|
+
curDay = t.weekday;
|
|
12687
|
+
eventMs = wallMs(t.y, t.mo, t.d, hh, mm);
|
|
12410
12688
|
remindMs = eventMs - remindBeforeMin * 60 * 1e3;
|
|
12689
|
+
if (remindMs > Date.now() && curDay !== 0 && curDay !== 6) break;
|
|
12690
|
+
dayOffset++;
|
|
12411
12691
|
}
|
|
12412
12692
|
return new Date(remindMs).toISOString();
|
|
12413
12693
|
}
|
|
@@ -12430,13 +12710,7 @@ function fmtRow(row) {
|
|
|
12430
12710
|
}
|
|
12431
12711
|
}
|
|
12432
12712
|
function fmtNow() {
|
|
12433
|
-
|
|
12434
|
-
const y = now.getUTCFullYear();
|
|
12435
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
12436
|
-
const d = String(now.getUTCDate()).padStart(2, "0");
|
|
12437
|
-
const hh = String(now.getUTCHours()).padStart(2, "0");
|
|
12438
|
-
const mm = String(now.getUTCMinutes()).padStart(2, "0");
|
|
12439
|
-
return `${y}-${m}-${d} ${getTodayWeekday()} ${hh}:${mm}\uFF08\u5317\u4EAC\u65F6\u95F4\uFF09`;
|
|
12713
|
+
return formatForLlm();
|
|
12440
12714
|
}
|
|
12441
12715
|
function parseDuration(s) {
|
|
12442
12716
|
if (s.endsWith("m")) return parseInt(s.slice(0, -1)) || null;
|
|
@@ -12513,9 +12787,9 @@ function getUpcomingTasks(workspace, days = 7, maxRows = 8) {
|
|
|
12513
12787
|
"SELECT * FROM events WHERE type='task' AND status='pending'"
|
|
12514
12788
|
).all();
|
|
12515
12789
|
if (rows.length === 0) return "";
|
|
12516
|
-
const
|
|
12517
|
-
const year =
|
|
12518
|
-
const todayMs = Date.
|
|
12790
|
+
const pt = nowParts();
|
|
12791
|
+
const year = pt.y;
|
|
12792
|
+
const todayMs = Date.parse(startOfLocalDay());
|
|
12519
12793
|
const horizonMs = todayMs + days * 864e5;
|
|
12520
12794
|
const picked = [];
|
|
12521
12795
|
for (const r of rows) {
|
|
@@ -12537,18 +12811,18 @@ function getUpcomingTasks(workspace, days = 7, maxRows = 8) {
|
|
|
12537
12811
|
return "";
|
|
12538
12812
|
}
|
|
12539
12813
|
}
|
|
12540
|
-
var
|
|
12814
|
+
var WEEKDAYS2;
|
|
12541
12815
|
var init_helpers = __esm({
|
|
12542
12816
|
"src/calendar/helpers.ts"() {
|
|
12543
12817
|
"use strict";
|
|
12544
12818
|
init_db();
|
|
12545
|
-
|
|
12819
|
+
init_clock();
|
|
12546
12820
|
WEEKDAYS2 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
12547
12821
|
}
|
|
12548
12822
|
});
|
|
12549
12823
|
|
|
12550
12824
|
// src/calendar/commands.ts
|
|
12551
|
-
import { existsSync as existsSync13, statSync as
|
|
12825
|
+
import { existsSync as existsSync13, statSync as statSync3 } from "node:fs";
|
|
12552
12826
|
import { isAbsolute as isAbsolute4, resolve as resolve6 } from "node:path";
|
|
12553
12827
|
function fmtEnd(start, durationMin) {
|
|
12554
12828
|
const [hh, mm] = start.split(":").map(Number);
|
|
@@ -12628,8 +12902,7 @@ function addDate(db2, args) {
|
|
|
12628
12902
|
const parsed = parseDateRange(dateStr);
|
|
12629
12903
|
let weeklyRows = [];
|
|
12630
12904
|
if (parsed) {
|
|
12631
|
-
const
|
|
12632
|
-
const year = now.getUTCFullYear();
|
|
12905
|
+
const year = wallParts().y;
|
|
12633
12906
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
12634
12907
|
const weekday = WEEKDAYS3[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
12635
12908
|
weeklyRows = db2.prepare(
|
|
@@ -12678,8 +12951,7 @@ function addBatch(db2, args) {
|
|
|
12678
12951
|
const parsed = parseDateRange(dateStr);
|
|
12679
12952
|
let weeklyRows = [];
|
|
12680
12953
|
if (parsed) {
|
|
12681
|
-
const
|
|
12682
|
-
const year = now.getUTCFullYear();
|
|
12954
|
+
const year = wallParts().y;
|
|
12683
12955
|
const d = new Date(Date.UTC(year, parsed.month - 1, parsed.dStart));
|
|
12684
12956
|
const weekday = WEEKDAYS3[d.getUTCDay() === 0 ? 6 : d.getUTCDay() - 1];
|
|
12685
12957
|
weeklyRows = db2.prepare(
|
|
@@ -12725,7 +12997,7 @@ function addTask(db2, args) {
|
|
|
12725
12997
|
\u89E3\u6790\u540E: ${absPath}
|
|
12726
12998
|
\u8BF7\u5148\u521B\u5EFA\u6587\u6863\u518D add-task\u3002`;
|
|
12727
12999
|
}
|
|
12728
|
-
const stat4 =
|
|
13000
|
+
const stat4 = statSync3(absPath);
|
|
12729
13001
|
if (!stat4.isFile()) {
|
|
12730
13002
|
return `\u274C \u521B\u5EFA\u5931\u8D25\uFF1Adoc_path \u4E0D\u662F\u6587\u4EF6\uFF08\u53EF\u80FD\u662F\u76EE\u5F55\uFF09
|
|
12731
13003
|
\u8DEF\u5F84: ${docPath}`;
|
|
@@ -12795,7 +13067,7 @@ function list(db2, opts = {}) {
|
|
|
12795
13067
|
}
|
|
12796
13068
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
12797
13069
|
if (r.repeat_type === "weekdays") {
|
|
12798
|
-
const idx =
|
|
13070
|
+
const idx = wallParts().weekday;
|
|
12799
13071
|
if (idx === 0 || idx === 6) continue;
|
|
12800
13072
|
}
|
|
12801
13073
|
events.push(` #${r.id} ${r.time || ""} ${r.event}`);
|
|
@@ -12823,7 +13095,7 @@ function pending(db2) {
|
|
|
12823
13095
|
}
|
|
12824
13096
|
for (const r of db2.prepare("SELECT * FROM events WHERE type='daily' AND status='pending'").all()) {
|
|
12825
13097
|
if (r.repeat_type === "weekdays") {
|
|
12826
|
-
const idx =
|
|
13098
|
+
const idx = wallParts().weekday;
|
|
12827
13099
|
if (idx === 0 || idx === 6) continue;
|
|
12828
13100
|
}
|
|
12829
13101
|
const label = r.repeat_type === "weekdays" ? "[\u5DE5\u4F5C\u65E5]" : "[\u6BCF\u5929]";
|
|
@@ -12939,10 +13211,36 @@ function markReminded(db2, id) {
|
|
|
12939
13211
|
}
|
|
12940
13212
|
return `#${id} \u5DF2\u6807\u8BB0\u63D0\u9192\u8FC7`;
|
|
12941
13213
|
}
|
|
13214
|
+
function listEventsForInjection(db2, limit = 50) {
|
|
13215
|
+
return db2.prepare(
|
|
13216
|
+
`SELECT id, event, date_str, time_exact, type, status
|
|
13217
|
+
FROM events
|
|
13218
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13219
|
+
AND date_str IS NOT NULL AND date_str != ''
|
|
13220
|
+
ORDER BY date_str ASC LIMIT ?`
|
|
13221
|
+
).all(limit);
|
|
13222
|
+
}
|
|
13223
|
+
function listEventBriefs(db2, limit) {
|
|
13224
|
+
const rows = db2.prepare(
|
|
13225
|
+
`SELECT id, event, date_str, time_exact, type
|
|
13226
|
+
FROM events
|
|
13227
|
+
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13228
|
+
ORDER BY date_str ASC, time_exact ASC
|
|
13229
|
+
LIMIT ?`
|
|
13230
|
+
).all(limit);
|
|
13231
|
+
return rows.map((row) => ({
|
|
13232
|
+
id: Number(row.id),
|
|
13233
|
+
title: String(row.event ?? ""),
|
|
13234
|
+
date: String(row.date_str ?? ""),
|
|
13235
|
+
time: String(row.time_exact ?? ""),
|
|
13236
|
+
type: String(row.type ?? "")
|
|
13237
|
+
})).filter((row) => row.title.length > 0);
|
|
13238
|
+
}
|
|
12942
13239
|
var WEEKDAYS3;
|
|
12943
13240
|
var init_commands = __esm({
|
|
12944
13241
|
"src/calendar/commands.ts"() {
|
|
12945
13242
|
"use strict";
|
|
13243
|
+
init_clock();
|
|
12946
13244
|
init_helpers();
|
|
12947
13245
|
WEEKDAYS3 = ["\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D", "\u5468\u65E5"];
|
|
12948
13246
|
}
|
|
@@ -12950,10 +13248,9 @@ var init_commands = __esm({
|
|
|
12950
13248
|
|
|
12951
13249
|
// src/memory/proactive/calendar-bridge.ts
|
|
12952
13250
|
function toBeijingParts(iso) {
|
|
12953
|
-
const
|
|
12954
|
-
const
|
|
12955
|
-
const
|
|
12956
|
-
const timeExact = `${String(beijing.getUTCHours()).padStart(2, "0")}:${String(beijing.getUTCMinutes()).padStart(2, "0")}`;
|
|
13251
|
+
const b = wallParts(iso);
|
|
13252
|
+
const dateStr = `${b.mo}/${b.d}`;
|
|
13253
|
+
const timeExact = `${String(b.hh).padStart(2, "0")}:${String(b.mm).padStart(2, "0")}`;
|
|
12957
13254
|
return { dateStr, timeExact };
|
|
12958
13255
|
}
|
|
12959
13256
|
function parseCalendarDateMs(dateStr, now) {
|
|
@@ -12962,7 +13259,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
12962
13259
|
const month = parseInt(parts[0], 10);
|
|
12963
13260
|
const day = parseInt(parts[1], 10);
|
|
12964
13261
|
if (!Number.isFinite(month) || !Number.isFinite(day)) return null;
|
|
12965
|
-
let year = now.
|
|
13262
|
+
let year = wallParts(now).y;
|
|
12966
13263
|
let candidate = new Date(year, month - 1, day).getTime();
|
|
12967
13264
|
if (candidate < now.getTime() - 7 * 864e5) {
|
|
12968
13265
|
year += 1;
|
|
@@ -12972,13 +13269,7 @@ function parseCalendarDateMs(dateStr, now) {
|
|
|
12972
13269
|
}
|
|
12973
13270
|
function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new Date()) {
|
|
12974
13271
|
const cutoffMs = now.getTime() + days * 864e5;
|
|
12975
|
-
const rows = calDb
|
|
12976
|
-
`SELECT id, event, date_str, time_exact, type, status
|
|
12977
|
-
FROM events
|
|
12978
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
12979
|
-
AND date_str IS NOT NULL AND date_str != ''
|
|
12980
|
-
ORDER BY date_str ASC LIMIT 50`
|
|
12981
|
-
).all();
|
|
13272
|
+
const rows = listEventsForInjection(calDb, 50);
|
|
12982
13273
|
if (rows.length === 0) return 0;
|
|
12983
13274
|
let injected = 0;
|
|
12984
13275
|
for (const row of rows) {
|
|
@@ -13021,27 +13312,108 @@ function injectCalendarEvents(store, calDb, days = 30, now = /* @__PURE__ */ new
|
|
|
13021
13312
|
return injected;
|
|
13022
13313
|
}
|
|
13023
13314
|
function getCalendarCoverage(calDb, limit = 100) {
|
|
13024
|
-
|
|
13025
|
-
`SELECT id, event, date_str, time_exact, type
|
|
13026
|
-
FROM events
|
|
13027
|
-
WHERE status NOT IN ('deleted', 'archived', 'done')
|
|
13028
|
-
ORDER BY date_str ASC, time_exact ASC
|
|
13029
|
-
LIMIT ?`
|
|
13030
|
-
).all(limit);
|
|
13031
|
-
return rows.map((row) => ({
|
|
13032
|
-
id: Number(row.id),
|
|
13033
|
-
title: String(row.event ?? ""),
|
|
13034
|
-
date: String(row.date_str ?? ""),
|
|
13035
|
-
time: String(row.time_exact ?? ""),
|
|
13036
|
-
type: String(row.type ?? "")
|
|
13037
|
-
})).filter((row) => row.title.length > 0);
|
|
13315
|
+
return listEventBriefs(calDb, limit);
|
|
13038
13316
|
}
|
|
13039
|
-
var TZ_OFFSET_MS3;
|
|
13040
13317
|
var init_calendar_bridge = __esm({
|
|
13041
13318
|
"src/memory/proactive/calendar-bridge.ts"() {
|
|
13042
13319
|
"use strict";
|
|
13043
13320
|
init_commands();
|
|
13044
|
-
|
|
13321
|
+
init_commands();
|
|
13322
|
+
init_clock();
|
|
13323
|
+
}
|
|
13324
|
+
});
|
|
13325
|
+
|
|
13326
|
+
// src/memory/proactive/recall-score.ts
|
|
13327
|
+
function weightOf(t) {
|
|
13328
|
+
if (CHANGE_RE.test(t)) return W.change;
|
|
13329
|
+
if (TIME_RE.test(t)) return W.time;
|
|
13330
|
+
if (EVENT_RE.test(t)) return W.event;
|
|
13331
|
+
if (LIFE_RE.test(t)) return W.life;
|
|
13332
|
+
if (ACTION_RE.test(t)) return W.action;
|
|
13333
|
+
if (PERSON_RE.test(t)) return W.person;
|
|
13334
|
+
return W.other;
|
|
13335
|
+
}
|
|
13336
|
+
function scoreForesightEntry(entry, terms) {
|
|
13337
|
+
const hay = `${entry.text} ${entry.evidence ?? ""}`;
|
|
13338
|
+
const kinds = { change: [], time: [], event: [], life: [], other: [], person: [], action: [] };
|
|
13339
|
+
let score = 0;
|
|
13340
|
+
for (const t of terms) {
|
|
13341
|
+
if (!hay.includes(t)) continue;
|
|
13342
|
+
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";
|
|
13343
|
+
kinds[kind].push(t);
|
|
13344
|
+
score += W[kind];
|
|
13345
|
+
}
|
|
13346
|
+
return {
|
|
13347
|
+
score,
|
|
13348
|
+
matched: Object.values(kinds).flat(),
|
|
13349
|
+
kinds,
|
|
13350
|
+
hasSpecial: kinds.change.length > 0 || kinds.time.length > 0 || kinds.event.length > 0
|
|
13351
|
+
};
|
|
13352
|
+
}
|
|
13353
|
+
function rankForesightEntries(entries2, terms, opts = {}) {
|
|
13354
|
+
const minScore = opts.minScore ?? DEFAULT_MIN_SCORE3;
|
|
13355
|
+
const termList = [...terms];
|
|
13356
|
+
const N = Math.max(1, entries2.length);
|
|
13357
|
+
const df = /* @__PURE__ */ new Map();
|
|
13358
|
+
for (const t of termList) {
|
|
13359
|
+
let c = 0;
|
|
13360
|
+
for (const e of entries2) if (`${e.text} ${e.evidence ?? ""}`.includes(t)) c++;
|
|
13361
|
+
df.set(t, c);
|
|
13362
|
+
}
|
|
13363
|
+
const idf = (t) => {
|
|
13364
|
+
const c = df.get(t) ?? 0;
|
|
13365
|
+
if (c <= 0) return 0;
|
|
13366
|
+
return 1 + Math.log2(1 + N / c) / Math.log2(1 + N);
|
|
13367
|
+
};
|
|
13368
|
+
const out = [];
|
|
13369
|
+
for (const e of entries2) {
|
|
13370
|
+
const base = scoreForesightEntry(e, termList);
|
|
13371
|
+
const s = { ...base, score: termList.reduce((acc, t) => acc + (base.matched.includes(t) ? weightOf(t) * idf(t) : 0), 0) };
|
|
13372
|
+
if (opts.requireStructure) {
|
|
13373
|
+
const anchor = s.kinds.change.length > 0 || s.kinds.event.length > 0 || s.kinds.life.length > 0;
|
|
13374
|
+
if (!anchor) continue;
|
|
13375
|
+
}
|
|
13376
|
+
if (s.score < minScore) continue;
|
|
13377
|
+
const tag = (k) => s.kinds[k].length ? `${k}:${s.kinds[k].join("/")}` : "";
|
|
13378
|
+
out.push({ entry: e, score: s.score, reasons: [tag("change"), tag("time"), tag("event"), tag("other"), tag("person")].filter(Boolean).join(" ") });
|
|
13379
|
+
}
|
|
13380
|
+
return out.sort((a, b) => b.score - a.score);
|
|
13381
|
+
}
|
|
13382
|
+
function extractTerms(message, stopWords) {
|
|
13383
|
+
const terms = /* @__PURE__ */ new Set();
|
|
13384
|
+
for (const m of message.matchAll(TIME_SURFACE)) terms.add(m[0]);
|
|
13385
|
+
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
13386
|
+
if (run.length <= 6 && !stopWords.has(run)) terms.add(run);
|
|
13387
|
+
if (/[一-鿿]/.test(run)) {
|
|
13388
|
+
for (let i = 0; i + 2 <= run.length; i++) {
|
|
13389
|
+
const g = run.slice(i, i + 2);
|
|
13390
|
+
if (!stopWords.has(g)) terms.add(g);
|
|
13391
|
+
}
|
|
13392
|
+
}
|
|
13393
|
+
}
|
|
13394
|
+
return terms;
|
|
13395
|
+
}
|
|
13396
|
+
var CHANGE_RE, TIME_RE, ACTION_RE, EVENT_RE, LIFE_RE, PERSON_RE, W, DEFAULT_MIN_SCORE3, TIME_SURFACE;
|
|
13397
|
+
var init_recall_score = __esm({
|
|
13398
|
+
"src/memory/proactive/recall-score.ts"() {
|
|
13399
|
+
"use strict";
|
|
13400
|
+
CHANGE_RE = /^(改|变成|变为|变更|改期|取消|没了|挪|调整|换|推|提前|延后|顺延|恢复|新增|加上|改成|改到|调到|排|定)$/;
|
|
13401
|
+
TIME_RE = /^(\d{1,2}\/\d{1,2}|\d{1,2}月|\d{1,2}日|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|上周|本周|今晚|今早|明早|明晚|早上|上午|中午|下午|晚上|凌晨|月底|月初|\d{1,2}:\d{2}|\d{1,2}[点時时])$/;
|
|
13402
|
+
ACTION_RE = /^(翻|翻翻|上传|搞完|搞|看看|看|发我|发给|截图|下载|跑|贴|转写|重发|试试|查|找|写|做|弄|传|发)$/;
|
|
13403
|
+
EVENT_RE = /^(来电|电话|打电话|打给|上门|到访|通知|审核|拜访|到货|快递|送货|生病|发烧|感冒|住院|输水|挂水|复诊|复查|面试|体检|签约|合同|付款|到账|截止|到期)$/;
|
|
13404
|
+
LIFE_RE = /^(回家|出差|探亲|复查|复诊|体检|住院|生日|纪念日|搬家|开学|放假|假期|寒假|暑假|机票|车票|高铁票|飞机|电影|聚餐|聚会|拜访|来访|接机|送机|考试|报名|签证|护照|续签|过户)$/;
|
|
13405
|
+
PERSON_RE = /^(翀哥|老公|老婆|睿阳|荣阳|张小媒|小柯|小雯|孙总|曲教授|潘总|燕子|方老师|李经理|Leigh|amy|Amy)$/;
|
|
13406
|
+
W = {
|
|
13407
|
+
change: 2,
|
|
13408
|
+
time: 1.5,
|
|
13409
|
+
event: 2,
|
|
13410
|
+
life: 2,
|
|
13411
|
+
other: 1,
|
|
13412
|
+
person: 0.3,
|
|
13413
|
+
action: 0
|
|
13414
|
+
};
|
|
13415
|
+
DEFAULT_MIN_SCORE3 = Number(process.env.RECALL_MIN_SCORE ?? 1);
|
|
13416
|
+
TIME_SURFACE = /\d{1,2}月\d{1,2}[日号]?|\d{1,2}号|\d{1,2}\/\d{1,2}|\d{1,2}[点时::]\d{0,2}|周[一二三四五六日天]|星期[一二三四五六日天]|明天|后天|大后天|下周|本周|上周|今晚|今早|明早|明晚|月底|月初|下个月|这个月/g;
|
|
13045
13417
|
}
|
|
13046
13418
|
});
|
|
13047
13419
|
|
|
@@ -13119,7 +13491,7 @@ function loadForesightEntries(everosRoot, now, userId, ownerFilter) {
|
|
|
13119
13491
|
return entries2.slice();
|
|
13120
13492
|
}
|
|
13121
13493
|
function parseForesightEntries(everosRoot, now, userId) {
|
|
13122
|
-
const
|
|
13494
|
+
const today2 = todayKey(now);
|
|
13123
13495
|
let files;
|
|
13124
13496
|
if (userId) {
|
|
13125
13497
|
const ownDir = join29(everosRoot, userId, "default_project", "users", userId, ".foresights");
|
|
@@ -13144,7 +13516,7 @@ function parseForesightEntries(everosRoot, now, userId) {
|
|
|
13144
13516
|
const end = (block.match(/\*\*end_time\*\*:\s*(\d{4}-\d{2}-\d{2})/) ?? [])[1];
|
|
13145
13517
|
const text = ((block.match(/### Foresight\s*\n+([\s\S]*?)(?:\n### Evidence|$)/) ?? [])[1] ?? "").trim();
|
|
13146
13518
|
if (!start || !end || !text) continue;
|
|
13147
|
-
if (end <
|
|
13519
|
+
if (end < today2) continue;
|
|
13148
13520
|
const evidence = ((block.match(/### Evidence\s*\n+([\s\S]*?)$/) ?? [])[1] ?? "").trim();
|
|
13149
13521
|
const blockOwner = (block.match(/\*\*owner_id\*\*:\s*(\S+)/) ?? [])[1] ?? fileOwner;
|
|
13150
13522
|
const key = text.replace(/\s+/g, "");
|
|
@@ -13155,35 +13527,11 @@ function parseForesightEntries(everosRoot, now, userId) {
|
|
|
13155
13527
|
}
|
|
13156
13528
|
return out;
|
|
13157
13529
|
}
|
|
13158
|
-
function
|
|
13159
|
-
const
|
|
13160
|
-
const out = loadForesightEntries(everosRoot, now, userId);
|
|
13161
|
-
const isOpen = (f) => f.start <= today;
|
|
13162
|
-
out.sort((a, b) => Number(isOpen(b)) - Number(isOpen(a)) || (isOpen(a) ? a.end.localeCompare(b.end) : a.start.localeCompare(b.start)));
|
|
13163
|
-
const open = out.filter(isOpen).slice(0, Math.min(12, Math.floor(maxEntries * 0.6)));
|
|
13164
|
-
const upcoming = out.filter((f) => !isOpen(f)).slice(0, Math.max(0, maxEntries - open.length));
|
|
13165
|
-
return [...open, ...upcoming].map((f) => `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}${f.evidence ? `\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09` : ""}`);
|
|
13166
|
-
}
|
|
13167
|
-
function matchForesightForEvent(entries2, message, maxHits = 5) {
|
|
13168
|
-
const terms = /* @__PURE__ */ new Set();
|
|
13169
|
-
for (const run of message.match(/[一-鿿]{2,}|[A-Za-z0-9_-]{3,}/g) ?? []) {
|
|
13170
|
-
if (run.length <= 6 && /[一-鿿]/.test(run) && !FS_MATCH_STOP.has(run)) terms.add(run);
|
|
13171
|
-
if (/[一-鿿]/.test(run)) {
|
|
13172
|
-
for (let i = 0; i + 2 <= run.length; i++) {
|
|
13173
|
-
const g = run.slice(i, i + 2);
|
|
13174
|
-
if (!FS_MATCH_STOP.has(g)) terms.add(g);
|
|
13175
|
-
}
|
|
13176
|
-
}
|
|
13177
|
-
}
|
|
13530
|
+
function matchForesightForEvent(entries2, message, maxHits = 2) {
|
|
13531
|
+
const terms = extractTerms(message, FS_MATCH_STOP);
|
|
13178
13532
|
if (terms.size === 0) return [];
|
|
13179
|
-
const
|
|
13180
|
-
|
|
13181
|
-
const hay = `${f.text} ${f.evidence}`;
|
|
13182
|
-
const term = [...terms].find((t) => hay.includes(t));
|
|
13183
|
-
if (term) hits.push({ line: `- [\u7A97\u53E3 ${f.start}~${f.end}] ${f.text}\uFF08\u4F9D\u636E\uFF1A${f.evidence}\uFF09`, term });
|
|
13184
|
-
if (hits.length >= maxHits) break;
|
|
13185
|
-
}
|
|
13186
|
-
return hits.map((h) => h.line);
|
|
13533
|
+
const ranked = rankForesightEntries(entries2, terms);
|
|
13534
|
+
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`);
|
|
13187
13535
|
}
|
|
13188
13536
|
function listForesightMds(dir) {
|
|
13189
13537
|
try {
|
|
@@ -13323,6 +13671,8 @@ var init_opportunity = __esm({
|
|
|
13323
13671
|
init_models();
|
|
13324
13672
|
init_calendar_bridge();
|
|
13325
13673
|
init_embedding();
|
|
13674
|
+
init_recall_score();
|
|
13675
|
+
init_clock();
|
|
13326
13676
|
OPPORTUNITY_SCAN_INTERVAL_MS = 6 * 60 * 6e4;
|
|
13327
13677
|
foresightCache = /* @__PURE__ */ new Map();
|
|
13328
13678
|
FORESIGHT_CACHE_TTL_MS = 30 * 6e4;
|
|
@@ -13838,16 +14188,16 @@ var init_executor3 = __esm({
|
|
|
13838
14188
|
const refLower = ref.toLowerCase().trim();
|
|
13839
14189
|
if (!refLower) return null;
|
|
13840
14190
|
if (!this.titleToIdCache || this.titleToIdCache.size < this.store.nodeCount) {
|
|
13841
|
-
const
|
|
14191
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
13842
14192
|
for (const node of this.store.getAllNodes()) {
|
|
13843
14193
|
const title = String(node.data.title ?? "").toLowerCase().trim();
|
|
13844
|
-
if (title)
|
|
14194
|
+
if (title) cache3.set(title, node.id);
|
|
13845
14195
|
}
|
|
13846
|
-
this.titleToIdCache =
|
|
14196
|
+
this.titleToIdCache = cache3;
|
|
13847
14197
|
}
|
|
13848
|
-
const
|
|
13849
|
-
if (
|
|
13850
|
-
for (const [title, id] of
|
|
14198
|
+
const cache2 = this.titleToIdCache;
|
|
14199
|
+
if (cache2.has(refLower)) return cache2.get(refLower);
|
|
14200
|
+
for (const [title, id] of cache2) {
|
|
13851
14201
|
if (title.includes(refLower) || refLower.includes(title)) return id;
|
|
13852
14202
|
}
|
|
13853
14203
|
return null;
|
|
@@ -14457,18 +14807,23 @@ ${JSON.stringify(payload, null, 2)}
|
|
|
14457
14807
|
|
|
14458
14808
|
// src/memory/proactive/proactive-tools.ts
|
|
14459
14809
|
import { existsSync as existsSync15 } from "node:fs";
|
|
14460
|
-
import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
|
|
14461
14810
|
function setProactiveWorkspace(workspace) {
|
|
14462
14811
|
toolWorkspace = workspace;
|
|
14463
14812
|
}
|
|
14464
|
-
function
|
|
14813
|
+
function withStore(fn) {
|
|
14465
14814
|
if (!toolWorkspace) return null;
|
|
14466
14815
|
const p = proactiveDbPath(toolWorkspace);
|
|
14467
14816
|
if (!existsSync15(p)) return null;
|
|
14468
|
-
|
|
14817
|
+
const store = new Persistence(p);
|
|
14818
|
+
try {
|
|
14819
|
+
return fn(store);
|
|
14820
|
+
} finally {
|
|
14821
|
+
store.close();
|
|
14822
|
+
}
|
|
14469
14823
|
}
|
|
14470
|
-
function fmtRow2(
|
|
14471
|
-
|
|
14824
|
+
function fmtRow2(a) {
|
|
14825
|
+
const when = a.scheduledTime ? formatForLlm(a.scheduledTime) : "(\u65E0)";
|
|
14826
|
+
return `#${a.id} [${a.status}] ${a.title} (urgency=${a.urgency}, scheduled=${when}${a.calendarId ? `, cal=#${a.calendarId}` : ""})`;
|
|
14472
14827
|
}
|
|
14473
14828
|
function registerProactiveTools() {
|
|
14474
14829
|
registry.register({
|
|
@@ -14483,50 +14838,43 @@ function registerProactiveTools() {
|
|
|
14483
14838
|
required: ["action_id"]
|
|
14484
14839
|
},
|
|
14485
14840
|
handler: async (args) => {
|
|
14486
|
-
const
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
if (
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
db2.prepare("UPDATE actions SET status=?, completed_at=? WHERE id=?").run("completed", (/* @__PURE__ */ new Date()).toISOString(), args.action_id);
|
|
14495
|
-
}
|
|
14496
|
-
return { content: `\u2713 completed: ${row.title}${args.calendar_id !== void 0 ? `\uFF08calendar #${args.calendar_id} \u5DF2\u56DE\u586B\uFF09` : ""}` };
|
|
14497
|
-
} finally {
|
|
14498
|
-
db2.close();
|
|
14499
|
-
}
|
|
14841
|
+
const out = withStore((store) => {
|
|
14842
|
+
const a = store.getAction(String(args.action_id));
|
|
14843
|
+
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 };
|
|
14844
|
+
store.updateActionStatus(a.id, "completed", { completedAt: nowUtc() });
|
|
14845
|
+
if (args.calendar_id !== void 0) store.linkCalendar(a.id, Number(args.calendar_id));
|
|
14846
|
+
return { content: `\u2713 action ${a.id} \u6807\u8BB0\u4E3A completed${args.calendar_id !== void 0 ? `\uFF08\u5173\u8054 calendar #${args.calendar_id}\uFF09` : ""}` };
|
|
14847
|
+
});
|
|
14848
|
+
return out ?? { content: "proactive \u672A\u542F\u7528\uFF08\u65E0 workspace \u6216 .proactive/proactive.db\uFF09", isError: true };
|
|
14500
14849
|
}
|
|
14501
14850
|
});
|
|
14502
14851
|
registry.register({
|
|
14503
14852
|
name: "proactive_cancel",
|
|
14504
|
-
description: "\
|
|
14853
|
+
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",
|
|
14505
14854
|
schema: {
|
|
14506
14855
|
type: "object",
|
|
14507
14856
|
properties: {
|
|
14508
14857
|
action_id: { type: "string", description: "action id" },
|
|
14509
|
-
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\
|
|
14858
|
+
reason: { type: "string", description: "\u53D6\u6D88\u7406\u7531\uFF08\u4F1A\u8BB0\u8FDB cancel_reason\uFF09" }
|
|
14510
14859
|
},
|
|
14511
|
-
required: ["action_id"
|
|
14860
|
+
required: ["action_id"]
|
|
14512
14861
|
},
|
|
14513
14862
|
handler: async (args) => {
|
|
14514
|
-
const
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
}
|
|
14523
|
-
|
|
14524
|
-
}
|
|
14863
|
+
const out = withStore((store) => {
|
|
14864
|
+
const a = store.getAction(String(args.action_id));
|
|
14865
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
14866
|
+
store.updateActionStatus(a.id, "cancelled", {
|
|
14867
|
+
completedAt: nowUtc(),
|
|
14868
|
+
cancelReason: String(args.reason || "\u672A\u6CE8\u660E")
|
|
14869
|
+
});
|
|
14870
|
+
return { content: `\u2713 action ${a.id} \u5DF2\u53D6\u6D88\uFF08${String(args.reason || "\u672A\u6CE8\u660E")}\uFF09` };
|
|
14871
|
+
});
|
|
14872
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14525
14873
|
}
|
|
14526
14874
|
});
|
|
14527
14875
|
registry.register({
|
|
14528
14876
|
name: "proactive_resolve_intent",
|
|
14529
|
-
description: "\u628A\u4E00\u4E2A
|
|
14877
|
+
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",
|
|
14530
14878
|
schema: {
|
|
14531
14879
|
type: "object",
|
|
14532
14880
|
properties: {
|
|
@@ -14535,31 +14883,25 @@ function registerProactiveTools() {
|
|
|
14535
14883
|
}
|
|
14536
14884
|
},
|
|
14537
14885
|
handler: async (args) => {
|
|
14538
|
-
const
|
|
14539
|
-
if (!db2) return { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14540
|
-
try {
|
|
14886
|
+
const out = withStore((store) => {
|
|
14541
14887
|
let intentId = String(args.intent_id || "");
|
|
14542
14888
|
if (!intentId && args.action_id) {
|
|
14543
|
-
const
|
|
14544
|
-
if (!
|
|
14545
|
-
intentId =
|
|
14889
|
+
const a = store.getAction(String(args.action_id));
|
|
14890
|
+
if (!a) return { content: `\u9519\u8BEF: action '${args.action_id}' \u4E0D\u5B58\u5728`, isError: true };
|
|
14891
|
+
intentId = a.intentId;
|
|
14546
14892
|
}
|
|
14547
14893
|
if (!intentId) return { content: "Usage: proactive_resolve_intent --action_id <id> \u6216 --intent_id <id>", isError: true };
|
|
14548
|
-
const
|
|
14549
|
-
if (
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
|
|
14553
|
-
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${Number(res.changes)} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
14560
|
-
} finally {
|
|
14561
|
-
db2.close();
|
|
14562
|
-
}
|
|
14894
|
+
const data = store.getIntentData(intentId);
|
|
14895
|
+
if (data === null) return { content: `\u9519\u8BEF: intent '${intentId}' \u4E0D\u5B58\u5728`, isError: true };
|
|
14896
|
+
store.setIntentData(intentId, { ...data, status: "resolved" });
|
|
14897
|
+
const n = store.cancelActionsOfIntents(
|
|
14898
|
+
[intentId],
|
|
14899
|
+
"intent resolved by agent\uFF08\u9A6C\u540E\u70AE\u6E05\u626B\uFF09",
|
|
14900
|
+
nowUtc()
|
|
14901
|
+
);
|
|
14902
|
+
return { content: `\u2713 intent ${intentId} resolved\u3002\u7EA7\u8054\u53D6\u6D88 ${n} \u4E2A\u6392\u961F\u7684\u5144\u5F1F action\u3002` };
|
|
14903
|
+
});
|
|
14904
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14563
14905
|
}
|
|
14564
14906
|
});
|
|
14565
14907
|
registry.register({
|
|
@@ -14572,16 +14914,12 @@ function registerProactiveTools() {
|
|
|
14572
14914
|
}
|
|
14573
14915
|
},
|
|
14574
14916
|
handler: async (args) => {
|
|
14575
|
-
const
|
|
14576
|
-
|
|
14577
|
-
try {
|
|
14578
|
-
const where = args.include_done ? "status NOT IN ('cancelled')" : "status IN ('queued','executing')";
|
|
14579
|
-
const rows = db2.prepare(`SELECT * FROM actions WHERE ${where} ORDER BY scheduled_time ASC LIMIT 30`).all();
|
|
14917
|
+
const out = withStore((store) => {
|
|
14918
|
+
const rows = store.listActions(args.include_done ? "not-cancelled" : "open");
|
|
14580
14919
|
if (rows.length === 0) return { content: "\u5F53\u524D\u6CA1\u6709\u5F85\u5904\u7406\u7684 proactive action \u2713" };
|
|
14581
14920
|
return { content: rows.map(fmtRow2).join("\n") };
|
|
14582
|
-
}
|
|
14583
|
-
|
|
14584
|
-
}
|
|
14921
|
+
});
|
|
14922
|
+
return out ?? { content: "proactive \u672A\u542F\u7528", isError: true };
|
|
14585
14923
|
}
|
|
14586
14924
|
});
|
|
14587
14925
|
}
|
|
@@ -14591,6 +14929,8 @@ var init_proactive_tools = __esm({
|
|
|
14591
14929
|
"use strict";
|
|
14592
14930
|
init_registry();
|
|
14593
14931
|
init_config3();
|
|
14932
|
+
init_persistence();
|
|
14933
|
+
init_clock();
|
|
14594
14934
|
}
|
|
14595
14935
|
});
|
|
14596
14936
|
|
|
@@ -14654,7 +14994,7 @@ __export(plugin_exports, {
|
|
|
14654
14994
|
ProactivePlugin: () => ProactivePlugin,
|
|
14655
14995
|
feedMem7Cell: () => feedMem7Cell
|
|
14656
14996
|
});
|
|
14657
|
-
import { join as join30
|
|
14997
|
+
import { join as join30 } from "node:path";
|
|
14658
14998
|
import { existsSync as existsSync16, readFileSync as readFileSync22 } from "node:fs";
|
|
14659
14999
|
function isFiller(text) {
|
|
14660
15000
|
const t = text.trim();
|
|
@@ -14836,19 +15176,23 @@ var init_plugin = __esm({
|
|
|
14836
15176
|
return;
|
|
14837
15177
|
}
|
|
14838
15178
|
if (!this.provider) {
|
|
14839
|
-
const
|
|
15179
|
+
const configuredId = this.config.providerId;
|
|
15180
|
+
const providerCfg = configuredId ? this.providers[configuredId] : void 0;
|
|
14840
15181
|
if (providerCfg) {
|
|
14841
15182
|
this.provider = createProvider(providerCfg);
|
|
14842
15183
|
} else {
|
|
14843
15184
|
const mainProvider = ctx.deps?.engine?.getProvider?.();
|
|
14844
15185
|
const mainModel = ctx.config?.model;
|
|
15186
|
+
const mainProviderId = ctx.config?.provider?.id || "main";
|
|
14845
15187
|
if (mainProvider && mainModel) {
|
|
14846
15188
|
this.provider = this.mainProviderProxy(ctx);
|
|
14847
15189
|
const modelId = String(mainModel);
|
|
14848
|
-
this.config = { ...this.config, modelId, modelRef: `${
|
|
14849
|
-
|
|
15190
|
+
this.config = { ...this.config, modelId, modelRef: `${mainProviderId}/${modelId}` };
|
|
15191
|
+
if (configuredId) {
|
|
15192
|
+
console.warn(`[proactive] \u914D\u7F6E\u7684 provider "${configuredId}" \u4E0D\u5728 models.providers \u91CC \u2014 \u6539\u7528\u4E3B\u6A21\u578B\uFF08${mainProviderId}/${modelId}\uFF09`);
|
|
15193
|
+
}
|
|
14850
15194
|
} else {
|
|
14851
|
-
console.warn(`[proactive] provider "${
|
|
15195
|
+
console.warn(`[proactive] ${configuredId ? `provider "${configuredId}" \u4E0D\u5B58\u5728\uFF0C\u4E14` : ""}\u4E3B provider \u4E0D\u53EF\u7528 \u2014 \u4E0D\u542F\u52A8`);
|
|
14852
15196
|
return;
|
|
14853
15197
|
}
|
|
14854
15198
|
}
|
|
@@ -14943,18 +15287,7 @@ ${String(input.tool_output ?? "")}`
|
|
|
14943
15287
|
foresightLines.push(`[mem7] [\u7A97\u53E3 ${e.start}~${e.end}] ${e.text}${e.evidence ? `\uFF08\u4F9D\u636E\uFF1A${e.evidence}\uFF09` : ""}`);
|
|
14944
15288
|
}
|
|
14945
15289
|
}
|
|
14946
|
-
|
|
14947
|
-
if (lancedbPath) {
|
|
14948
|
-
const everosRoot = join30(dirname7(lancedbPath), "..");
|
|
14949
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
14950
|
-
for (const l of loadForesightLines(everosRoot, nowDate, 20, everosUserId)) {
|
|
14951
|
-
const key = l.replace(/\s+/g, "");
|
|
14952
|
-
if (seen.has(key)) continue;
|
|
14953
|
-
seen.add(key);
|
|
14954
|
-
foresightLines.push(l.replace(/^- /, "[everos] "));
|
|
14955
|
-
}
|
|
14956
|
-
}
|
|
14957
|
-
if (foresightLines.length > 0) console.log(`[proactive] foresight dual-source total: ${foresightLines.length}`);
|
|
15290
|
+
if (foresightLines.length > 0) console.log(`[proactive] mem7 foresight total: ${foresightLines.length}`);
|
|
14958
15291
|
} catch (err) {
|
|
14959
15292
|
console.warn(`[proactive] foresight load failed: ${err.message}`);
|
|
14960
15293
|
}
|
|
@@ -15128,18 +15461,6 @@ ${String(input.tool_output ?? "")}`
|
|
|
15128
15461
|
} else if (stateDir && !scopeReady) {
|
|
15129
15462
|
console.warn("[proactive] mem7 recall skipped: sender scope but cell has no user speaker");
|
|
15130
15463
|
}
|
|
15131
|
-
if (senderScoped) {
|
|
15132
|
-
this.warnOnce("everos-skip", "[proactive] everos \u6E90\u5728 sender \u6A21\u5F0F\u4E0B\u8DF3\u8FC7\uFF08\u65E0 per-speaker \u6620\u5C04\uFF09");
|
|
15133
|
-
} else {
|
|
15134
|
-
const lancedbPath = String(liveConfig.get("everos.lancedbPath") ?? "");
|
|
15135
|
-
if (lancedbPath) {
|
|
15136
|
-
const everosRoot = join30(dirname7(lancedbPath), "..");
|
|
15137
|
-
const everosUserId = String(liveConfig.get("everos.userId") ?? "") || void 0;
|
|
15138
|
-
for (const h of matchForesightForEvent(loadForesightEntries(everosRoot, now, everosUserId), prompt)) {
|
|
15139
|
-
hitSet.add(`[everos] ${h.replace(/^- /, "")}`);
|
|
15140
|
-
}
|
|
15141
|
-
}
|
|
15142
|
-
}
|
|
15143
15464
|
if (hitSet.size > 0) {
|
|
15144
15465
|
foresightHits = [...hitSet];
|
|
15145
15466
|
console.log(`[proactive] foresight matched ${hitSet.size} open-window prediction(s) for incoming message`);
|
|
@@ -15595,7 +15916,7 @@ Here is the recent conversation context that you should extract notes from:
|
|
|
15595
15916
|
|
|
15596
15917
|
${messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-20).map((m) => {
|
|
15597
15918
|
const text = typeof m.content === "string" ? m.content : "[content blocks]";
|
|
15598
|
-
return `[${m.role}]: ${text
|
|
15919
|
+
return `[${m.role}]: ${truncateByCodePoint(text, 500)}`;
|
|
15599
15920
|
}).join("\n\n")}`,
|
|
15600
15921
|
maxTurns: 3,
|
|
15601
15922
|
toolOverride: editTools.length > 0 ? editTools : void 0,
|
|
@@ -15657,6 +15978,7 @@ var init_sessionMemory = __esm({
|
|
|
15657
15978
|
"use strict";
|
|
15658
15979
|
init_sessionMemoryUtils();
|
|
15659
15980
|
init_prompts2();
|
|
15981
|
+
init_well_formed();
|
|
15660
15982
|
init_query();
|
|
15661
15983
|
init_registry();
|
|
15662
15984
|
init_features();
|
|
@@ -16088,7 +16410,7 @@ async function createTask(params) {
|
|
|
16088
16410
|
validateSchedule(params.schedule);
|
|
16089
16411
|
const id = generateTaskId2();
|
|
16090
16412
|
const now = /* @__PURE__ */ new Date();
|
|
16091
|
-
const timezone = params.timezone || params.defaultTimezone ||
|
|
16413
|
+
const timezone = params.timezone || params.defaultTimezone || tz();
|
|
16092
16414
|
const nextRunAt = params.calculateNextRun(params.schedule, timezone, now);
|
|
16093
16415
|
const task = {
|
|
16094
16416
|
id,
|
|
@@ -16189,6 +16511,7 @@ var storageDir, tasksFilePath, LOCK_RETRY_COUNT, LOCK_RETRY_DELAY_MS, LOCK_STALE
|
|
|
16189
16511
|
var init_tasks = __esm({
|
|
16190
16512
|
"src/cron/tasks.ts"() {
|
|
16191
16513
|
"use strict";
|
|
16514
|
+
init_clock();
|
|
16192
16515
|
storageDir = "";
|
|
16193
16516
|
tasksFilePath = "";
|
|
16194
16517
|
LOCK_RETRY_COUNT = 3;
|
|
@@ -16225,7 +16548,7 @@ async function stopScheduler() {
|
|
|
16225
16548
|
console.log("[cron] Scheduler stopped");
|
|
16226
16549
|
}
|
|
16227
16550
|
function calculateNextRun(schedule, timezone, afterDate) {
|
|
16228
|
-
const
|
|
16551
|
+
const tz2 = cronDefaultTimezone(timezone);
|
|
16229
16552
|
switch (schedule.type) {
|
|
16230
16553
|
case "at": {
|
|
16231
16554
|
const atMs = new Date(schedule.at).getTime();
|
|
@@ -16237,7 +16560,7 @@ function calculateNextRun(schedule, timezone, afterDate) {
|
|
|
16237
16560
|
}
|
|
16238
16561
|
case "cron": {
|
|
16239
16562
|
try {
|
|
16240
|
-
const job = new Cron(schedule.expression, { timezone:
|
|
16563
|
+
const job = new Cron(schedule.expression, { timezone: tz2 });
|
|
16241
16564
|
const next = job.nextRun(afterDate);
|
|
16242
16565
|
return next?.toISOString();
|
|
16243
16566
|
} catch (err) {
|
|
@@ -16268,9 +16591,12 @@ function shouldFireWithJitter(task, now) {
|
|
|
16268
16591
|
}
|
|
16269
16592
|
}
|
|
16270
16593
|
}
|
|
16594
|
+
function cronDefaultTimezone(taskTimezone) {
|
|
16595
|
+
return taskTimezone || tz();
|
|
16596
|
+
}
|
|
16271
16597
|
function estimateCronPeriodMs(expression, timezone) {
|
|
16272
16598
|
try {
|
|
16273
|
-
const job = new Cron(expression, { timezone: timezone
|
|
16599
|
+
const job = new Cron(expression, { timezone: cronDefaultTimezone(timezone) });
|
|
16274
16600
|
const next1 = job.nextRun();
|
|
16275
16601
|
if (!next1) return 24 * 60 * 60 * 1e3;
|
|
16276
16602
|
const next2 = job.nextRun(new Date(next1.getTime() + 1));
|
|
@@ -16450,7 +16776,7 @@ ${notifyLines}`;
|
|
|
16450
16776
|
console.warn(`[cron] Failed to notify main session: ${err.message}`);
|
|
16451
16777
|
}
|
|
16452
16778
|
}
|
|
16453
|
-
await recalculateNextRun(task, now, (schedule,
|
|
16779
|
+
await recalculateNextRun(task, now, (schedule, tz2, after) => calculateNextRun(schedule, tz2, after));
|
|
16454
16780
|
await markExecuted(task.id, true);
|
|
16455
16781
|
console.log(`[cron] Task ${task.id} done (run #${task.runCount})`);
|
|
16456
16782
|
} catch (err) {
|
|
@@ -16514,6 +16840,7 @@ var init_scheduler = __esm({
|
|
|
16514
16840
|
"src/cron/scheduler.ts"() {
|
|
16515
16841
|
"use strict";
|
|
16516
16842
|
init_live();
|
|
16843
|
+
init_clock();
|
|
16517
16844
|
init_tasks();
|
|
16518
16845
|
schedulerTimer = null;
|
|
16519
16846
|
shuttingDown = false;
|
|
@@ -16627,7 +16954,7 @@ function registerCronTools() {
|
|
|
16627
16954
|
delivery_mode: { type: "string", enum: ["origin", "local", "direct"], description: "\u6295\u9012\u6A21\u5F0F\uFF08\u9ED8\u8BA4 local\uFF09" },
|
|
16628
16955
|
delivery_channel: { type: "string", description: "\u6295\u9012\u901A\u9053\uFF08direct \u6A21\u5F0F\u5FC5\u586B\uFF09: discord | feishu | wecom" },
|
|
16629
16956
|
delivery_target: { type: "string", description: "\u6295\u9012\u76EE\u6807\uFF08direct \u6A21\u5F0F\u5FC5\u586B\uFF09: channel_id \u6216 user_id" },
|
|
16630
|
-
timezone: { type: "string", description: "IANA \u65F6\u533A\uFF08\u9ED8\u8BA4
|
|
16957
|
+
timezone: { type: "string", description: "IANA \u65F6\u533A\uFF08\u9ED8\u8BA4\u8DDF\u968F\u5F15\u64CE\u7684 timezone \u914D\u7F6E\uFF0C\u4E0D\u8981\u663E\u5F0F\u6307\u5B9A\u9664\u975E\u786E\u5B9E\u8981\u522B\u7684\u65F6\u533A\uFF09" },
|
|
16631
16958
|
max_runs: { type: "number", description: "\u6700\u5927\u6267\u884C\u6B21\u6570\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u65E0\u9650\uFF09" },
|
|
16632
16959
|
notify_session: { type: "boolean", description: "\u6267\u884C\u5B8C\u540E\u901A\u77E5\u4E3Bsession\uFF08\u6CE8\u5165\u4E00\u6761user\u6D88\u606F\u5230scope:main\uFF09" },
|
|
16633
16960
|
session_message: { type: "string", description: "\u6CE8\u5165\u5230\u4E3Bsession\u7684\u6D88\u606F\u6A21\u677F\uFF0C\u652F\u6301 {result} {description} \u5360\u4F4D\u7B26" },
|
|
@@ -20954,12 +21281,66 @@ var init_SendMessageTool = __esm({
|
|
|
20954
21281
|
}
|
|
20955
21282
|
});
|
|
20956
21283
|
|
|
21284
|
+
// src/tools/tavily-pool.ts
|
|
21285
|
+
var tavily_pool_exports = {};
|
|
21286
|
+
__export(tavily_pool_exports, {
|
|
21287
|
+
relayOriginFrom: () => relayOriginFrom,
|
|
21288
|
+
resetTavilyPool: () => resetTavilyPool,
|
|
21289
|
+
resolvePoolTavilyKey: () => resolvePoolTavilyKey
|
|
21290
|
+
});
|
|
21291
|
+
function resetTavilyPool() {
|
|
21292
|
+
cache = null;
|
|
21293
|
+
}
|
|
21294
|
+
function relayOriginFrom(cfg) {
|
|
21295
|
+
const rid = cfg?.tools?.my_selfie?.relayProviderId || cfg?.my_selfie?.relayProviderId || RELAY_PROVIDER_DEFAULT;
|
|
21296
|
+
const p = cfg?.models?.providers?.[rid] || cfg?.providers?.[rid];
|
|
21297
|
+
const baseUrl = String(p?.baseUrl || "").trim();
|
|
21298
|
+
const token = String(p?.apiKey || "").trim();
|
|
21299
|
+
if (!baseUrl || !token) return null;
|
|
21300
|
+
const origin = baseUrl.replace(/\/+$/, "").replace(/\/v1$/, "").replace(/\/+$/, "");
|
|
21301
|
+
return origin ? { origin, token } : null;
|
|
21302
|
+
}
|
|
21303
|
+
async function resolvePoolTavilyKey(opts = {}) {
|
|
21304
|
+
const relay = relayOriginFrom(opts.cfg ?? liveConfig.all());
|
|
21305
|
+
if (!relay) return "";
|
|
21306
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
21307
|
+
if (!cache || opts.force || Date.now() - cache.fetchedAt > CACHE_TTL_MS) {
|
|
21308
|
+
try {
|
|
21309
|
+
const res = await doFetch(`${relay.origin}/api/engine7/search-keys`, {
|
|
21310
|
+
headers: { Authorization: `Bearer ${relay.token}` }
|
|
21311
|
+
});
|
|
21312
|
+
const body = await res.json().catch(() => null);
|
|
21313
|
+
const keys = Array.isArray(body?.data?.keys) ? body.data.keys.filter((k) => typeof k === "string" && k.trim()) : [];
|
|
21314
|
+
cache = { keys, fetchedAt: Date.now() };
|
|
21315
|
+
} catch {
|
|
21316
|
+
if (!cache) return "";
|
|
21317
|
+
cache.fetchedAt = Date.now();
|
|
21318
|
+
}
|
|
21319
|
+
}
|
|
21320
|
+
if (!cache.keys.length) return "";
|
|
21321
|
+
const key = cache.keys[cursor % cache.keys.length];
|
|
21322
|
+
cursor++;
|
|
21323
|
+
return key;
|
|
21324
|
+
}
|
|
21325
|
+
var CACHE_TTL_MS, RELAY_PROVIDER_DEFAULT, cache, cursor;
|
|
21326
|
+
var init_tavily_pool = __esm({
|
|
21327
|
+
"src/tools/tavily-pool.ts"() {
|
|
21328
|
+
"use strict";
|
|
21329
|
+
init_live();
|
|
21330
|
+
CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
21331
|
+
RELAY_PROVIDER_DEFAULT = "7yi";
|
|
21332
|
+
cache = null;
|
|
21333
|
+
cursor = 0;
|
|
21334
|
+
}
|
|
21335
|
+
});
|
|
21336
|
+
|
|
20957
21337
|
// src/tools/web-search.ts
|
|
20958
21338
|
var web_search_exports = {};
|
|
20959
21339
|
var init_web_search = __esm({
|
|
20960
21340
|
"src/tools/web-search.ts"() {
|
|
20961
21341
|
"use strict";
|
|
20962
21342
|
init_registry();
|
|
21343
|
+
init_well_formed();
|
|
20963
21344
|
registry.register({
|
|
20964
21345
|
name: "web_search",
|
|
20965
21346
|
description: "\u641C\u7D22\u4E92\u8054\u7F51\u3002\u8FD4\u56DE\u641C\u7D22\u7ED3\u679C\u548C\u94FE\u63A5\u3002",
|
|
@@ -20976,14 +21357,25 @@ var init_web_search = __esm({
|
|
|
20976
21357
|
const query = args.query;
|
|
20977
21358
|
const maxResults = args.max_results || 5;
|
|
20978
21359
|
const searchDepth = args.search_depth || "basic";
|
|
20979
|
-
|
|
20980
|
-
|
|
21360
|
+
let apiKey = process.env.TAVILY_API_KEY || "";
|
|
21361
|
+
let fromPool = false;
|
|
21362
|
+
if (!apiKey) {
|
|
21363
|
+
const { resolvePoolTavilyKey: resolvePoolTavilyKey2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
21364
|
+
apiKey = await resolvePoolTavilyKey2();
|
|
21365
|
+
fromPool = !!apiKey;
|
|
21366
|
+
}
|
|
21367
|
+
if (!apiKey) {
|
|
21368
|
+
return {
|
|
21369
|
+
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",
|
|
21370
|
+
isError: true
|
|
21371
|
+
};
|
|
21372
|
+
}
|
|
20981
21373
|
const startTime = Date.now();
|
|
20982
21374
|
try {
|
|
20983
21375
|
const response = await fetch("https://api.tavily.com/search", {
|
|
20984
21376
|
method: "POST",
|
|
20985
21377
|
headers: { "Content-Type": "application/json" },
|
|
20986
|
-
body:
|
|
21378
|
+
body: safeStringify({
|
|
20987
21379
|
query,
|
|
20988
21380
|
max_results: maxResults,
|
|
20989
21381
|
include_answer: true,
|
|
@@ -20993,6 +21385,14 @@ var init_web_search = __esm({
|
|
|
20993
21385
|
signal
|
|
20994
21386
|
});
|
|
20995
21387
|
if (!response.ok) {
|
|
21388
|
+
if (fromPool && (response.status === 401 || response.status === 429 || response.status === 432)) {
|
|
21389
|
+
const { resetTavilyPool: resetTavilyPool2 } = await Promise.resolve().then(() => (init_tavily_pool(), tavily_pool_exports));
|
|
21390
|
+
resetTavilyPool2();
|
|
21391
|
+
return {
|
|
21392
|
+
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`,
|
|
21393
|
+
isError: true
|
|
21394
|
+
};
|
|
21395
|
+
}
|
|
20996
21396
|
return { content: `\u641C\u7D22\u5931\u8D25: HTTP ${response.status}`, isError: true };
|
|
20997
21397
|
}
|
|
20998
21398
|
const data = await response.json();
|
|
@@ -21054,7 +21454,7 @@ function isPermittedRedirect(original, redirect) {
|
|
|
21054
21454
|
async function fetchWithRedirects(url, signal, depth = 0) {
|
|
21055
21455
|
if (depth > MAX_REDIRECTS) throw new Error(`\u91CD\u5B9A\u5411\u6B21\u6570\u8FC7\u591A (>${MAX_REDIRECTS})`);
|
|
21056
21456
|
const controller = new AbortController();
|
|
21057
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
21457
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
21058
21458
|
const onSignalAbort = () => controller.abort();
|
|
21059
21459
|
signal.addEventListener("abort", onSignalAbort, { once: true });
|
|
21060
21460
|
try {
|
|
@@ -21097,14 +21497,14 @@ function getTurndown() {
|
|
|
21097
21497
|
}
|
|
21098
21498
|
return td;
|
|
21099
21499
|
}
|
|
21100
|
-
var MAX_URL_LENGTH, MAX_CONTENT_LENGTH,
|
|
21500
|
+
var MAX_URL_LENGTH, MAX_CONTENT_LENGTH, FETCH_TIMEOUT_MS2, MAX_REDIRECTS, td;
|
|
21101
21501
|
var init_web_fetch = __esm({
|
|
21102
21502
|
"src/tools/web-fetch.ts"() {
|
|
21103
21503
|
"use strict";
|
|
21104
21504
|
init_registry();
|
|
21105
21505
|
MAX_URL_LENGTH = 2e3;
|
|
21106
21506
|
MAX_CONTENT_LENGTH = 10 * 1024 * 1024;
|
|
21107
|
-
|
|
21507
|
+
FETCH_TIMEOUT_MS2 = 6e4;
|
|
21108
21508
|
MAX_REDIRECTS = 10;
|
|
21109
21509
|
registry.register({
|
|
21110
21510
|
name: "web_fetch",
|
|
@@ -21175,7 +21575,7 @@ var init_web_fetch = __esm({
|
|
|
21175
21575
|
|
|
21176
21576
|
// src/tools/wechat/wx-query.ts
|
|
21177
21577
|
var wx_query_exports = {};
|
|
21178
|
-
import { join as
|
|
21578
|
+
import { join as join56 } from "node:path";
|
|
21179
21579
|
function getDescription() {
|
|
21180
21580
|
return `\u67E5\u8BE2\u7FC0\u54E5\u7684\u5FAE\u4FE1\u6D88\u606F\uFF08\u7F13\u5B58\u89E3\u5BC6\u540E\u7684\u672C\u5730\u6570\u636E\u5E93\uFF09\u3002
|
|
21181
21581
|
|
|
@@ -21210,8 +21610,8 @@ var init_wx_query = __esm({
|
|
|
21210
21610
|
"use strict";
|
|
21211
21611
|
init_registry();
|
|
21212
21612
|
init_live();
|
|
21213
|
-
STATE_DIR = process.env.ENGINE7_STATE_DIR ||
|
|
21214
|
-
SCRIPT =
|
|
21613
|
+
STATE_DIR = process.env.ENGINE7_STATE_DIR || join56(process.env.HOME || process.env.USERPROFILE || ".", ".engine7");
|
|
21614
|
+
SCRIPT = join56("/Users/chongzhang/work/twinsun-hearth/engine", "src", "tools", "wechat", "wx_query.py");
|
|
21215
21615
|
PYTHON = "python3";
|
|
21216
21616
|
registry.register({
|
|
21217
21617
|
name: "wx_query",
|
|
@@ -22132,8 +22532,8 @@ async function doReloadConfigInner(config, deps, provider, force = false) {
|
|
|
22132
22532
|
const savedConfigPath = config._configFilePath;
|
|
22133
22533
|
let reloadConfigPath = savedConfigPath;
|
|
22134
22534
|
if (!fs10.existsSync(reloadConfigPath)) {
|
|
22135
|
-
const
|
|
22136
|
-
const __dirname = path10.dirname(
|
|
22535
|
+
const __filename2 = fileURLToPath(import.meta.url);
|
|
22536
|
+
const __dirname = path10.dirname(__filename2);
|
|
22137
22537
|
const engineConfigsDir = path10.resolve(__dirname, "../configs");
|
|
22138
22538
|
const altPath = path10.join(engineConfigsDir, path10.basename(savedConfigPath));
|
|
22139
22539
|
if (fs10.existsSync(altPath)) {
|
|
@@ -22447,20 +22847,20 @@ function acquireEngineLock(stateDir) {
|
|
|
22447
22847
|
} catch (err) {
|
|
22448
22848
|
console.warn(`[pid-lock] write lock failed: ${err.message}`);
|
|
22449
22849
|
}
|
|
22450
|
-
const
|
|
22850
|
+
const release3 = () => {
|
|
22451
22851
|
try {
|
|
22452
22852
|
const cur = fs11.existsSync(lockFile) ? fs11.readFileSync(lockFile, "utf-8").trim() : "";
|
|
22453
22853
|
if (cur === String(myPid)) fs11.unlinkSync(lockFile);
|
|
22454
22854
|
} catch {
|
|
22455
22855
|
}
|
|
22456
22856
|
};
|
|
22457
|
-
process.on("exit",
|
|
22857
|
+
process.on("exit", release3);
|
|
22458
22858
|
process.on("SIGINT", () => {
|
|
22459
|
-
|
|
22859
|
+
release3();
|
|
22460
22860
|
process.exit(0);
|
|
22461
22861
|
});
|
|
22462
22862
|
process.on("SIGTERM", () => {
|
|
22463
|
-
|
|
22863
|
+
release3();
|
|
22464
22864
|
process.exit(0);
|
|
22465
22865
|
});
|
|
22466
22866
|
}
|
|
@@ -22524,11 +22924,12 @@ init_registry();
|
|
|
22524
22924
|
init_deferred();
|
|
22525
22925
|
|
|
22526
22926
|
// src/cli-startup.ts
|
|
22927
|
+
init_clock();
|
|
22527
22928
|
import * as path12 from "node:path";
|
|
22528
22929
|
import * as fs12 from "node:fs";
|
|
22529
22930
|
import * as readline from "node:readline";
|
|
22530
22931
|
function getDailyLogPath(stateDir) {
|
|
22531
|
-
const dateStr = (
|
|
22932
|
+
const dateStr = todayKey();
|
|
22532
22933
|
return path12.join(stateDir, "logs", `engine-${dateStr}.log`);
|
|
22533
22934
|
}
|
|
22534
22935
|
function setupFileLogging(stateDir, pendingWarnings = []) {
|
|
@@ -22537,7 +22938,7 @@ function setupFileLogging(stateDir, pendingWarnings = []) {
|
|
|
22537
22938
|
const logStream = fs12.createWriteStream(LOG_PATH, { flags: "a" });
|
|
22538
22939
|
logStream.on("error", (err) => console.error(`[log] Write error: ${err.message}`));
|
|
22539
22940
|
function ts() {
|
|
22540
|
-
return (
|
|
22941
|
+
return formatLog() + "." + String(Date.now() % 1e3).padStart(3, "0");
|
|
22541
22942
|
}
|
|
22542
22943
|
for (const w of pendingWarnings) {
|
|
22543
22944
|
logStream.write(`[${ts()}] [WARN] ${w}
|
|
@@ -22917,6 +23318,7 @@ init_TodoWriteTool();
|
|
|
22917
23318
|
|
|
22918
23319
|
// src/tools/memory-bridge.ts
|
|
22919
23320
|
init_registry();
|
|
23321
|
+
init_well_formed();
|
|
22920
23322
|
init_live();
|
|
22921
23323
|
var EverosSearchSchema = {
|
|
22922
23324
|
type: "object",
|
|
@@ -22945,7 +23347,7 @@ function createEverosSearchTool(everosCfg) {
|
|
|
22945
23347
|
const resp = await fetch(`${agenticUrl}/api/v1/search`, {
|
|
22946
23348
|
method: "POST",
|
|
22947
23349
|
headers: { "Content-Type": "application/json" },
|
|
22948
|
-
body:
|
|
23350
|
+
body: safeStringify({
|
|
22949
23351
|
query,
|
|
22950
23352
|
user_id: userId,
|
|
22951
23353
|
mode,
|
|
@@ -23363,6 +23765,7 @@ function resolveToolExclude(channelName) {
|
|
|
23363
23765
|
// src/prompt.ts
|
|
23364
23766
|
init_registry();
|
|
23365
23767
|
init_store();
|
|
23768
|
+
init_clock();
|
|
23366
23769
|
init_memdir();
|
|
23367
23770
|
init_paths();
|
|
23368
23771
|
import * as path17 from "node:path";
|
|
@@ -23523,6 +23926,9 @@ If you can say it in one sentence, don't use three. Prefer short, direct sentenc
|
|
|
23523
23926
|
}
|
|
23524
23927
|
function getEnvInfoSection(workspace) {
|
|
23525
23928
|
const envItems = [
|
|
23929
|
+
// CC 上游本来就有日期项,移植时漏了 —— 补回来。
|
|
23930
|
+
// 本段在 dynamic prompt(每轮重建),日期一天只变一次,不伤 prefix cache。
|
|
23931
|
+
`Today's date: ${todayKey()} (${tz()})`,
|
|
23526
23932
|
`Primary working directory: ${workspace}`,
|
|
23527
23933
|
`Is directory a git repo: ${false}`,
|
|
23528
23934
|
`Platform: ${os2.platform()}`,
|
|
@@ -23653,8 +24059,8 @@ function buildActiveGoalsSection(stateDir) {
|
|
|
23653
24059
|
if (!active.length) return void 0;
|
|
23654
24060
|
const events = listGoalEvents(stateDir);
|
|
23655
24061
|
const fmt = (iso) => {
|
|
23656
|
-
const
|
|
23657
|
-
return Number.isNaN(
|
|
24062
|
+
const p = wallParts(iso);
|
|
24063
|
+
return Number.isNaN(Date.parse(iso)) ? "?" : `${p.mo}/${p.d} ${String(p.hh).padStart(2, "0")}:${String(p.mm).padStart(2, "0")}`;
|
|
23658
24064
|
};
|
|
23659
24065
|
const summary = active.map((g) => {
|
|
23660
24066
|
const step = g.steps[g.currentStep];
|
|
@@ -23695,10 +24101,8 @@ ${skillsListing}`);
|
|
|
23695
24101
|
return parts.join("\n\n");
|
|
23696
24102
|
}
|
|
23697
24103
|
function buildVolatileRuntimeContext() {
|
|
23698
|
-
const now = /* @__PURE__ */ new Date();
|
|
23699
|
-
const dateStr = now.toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" });
|
|
23700
24104
|
return `# \u8FD0\u884C\u65F6\u4E0A\u4E0B\u6587
|
|
23701
|
-
\u5F53\u524D\u65F6\u95F4: ${
|
|
24105
|
+
\u5F53\u524D\u65F6\u95F4: ${formatForLlm()}`;
|
|
23702
24106
|
}
|
|
23703
24107
|
function formatSkillsListingForPrompt() {
|
|
23704
24108
|
const tools = registry.list();
|
|
@@ -23803,7 +24207,8 @@ ${manifest}`
|
|
|
23803
24207
|
}
|
|
23804
24208
|
|
|
23805
24209
|
// src/memory/memdir/findRelevantMemoriesVector.ts
|
|
23806
|
-
|
|
24210
|
+
init_well_formed();
|
|
24211
|
+
import { statSync as statSync2 } from "node:fs";
|
|
23807
24212
|
import { join as join17, resolve as resolve5, sep as sep4 } from "node:path";
|
|
23808
24213
|
import { createRequire as createRequire2 } from "node:module";
|
|
23809
24214
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -23819,7 +24224,8 @@ async function embedQuery(text, ollamaUrl, model) {
|
|
|
23819
24224
|
const resp = await fetch(ollamaUrl, {
|
|
23820
24225
|
method: "POST",
|
|
23821
24226
|
headers: { "Content-Type": "application/json" },
|
|
23822
|
-
|
|
24227
|
+
// 0918:按 code point 切,避免切开 emoji 代理对产生非法码元
|
|
24228
|
+
body: safeStringify({ model, prompt: truncateByCodePoint(text, 2e3) }),
|
|
23823
24229
|
signal: AbortSignal.timeout(3e4)
|
|
23824
24230
|
});
|
|
23825
24231
|
if (!resp.ok) {
|
|
@@ -23851,8 +24257,8 @@ function cosineSim(a, b) {
|
|
|
23851
24257
|
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
23852
24258
|
}
|
|
23853
24259
|
function readDbEmbeddings(dbPath) {
|
|
23854
|
-
const { DatabaseSync:
|
|
23855
|
-
const db2 = new
|
|
24260
|
+
const { DatabaseSync: DatabaseSync4 } = require2("node:sqlite");
|
|
24261
|
+
const db2 = new DatabaseSync4(dbPath, { readOnly: true });
|
|
23856
24262
|
try {
|
|
23857
24263
|
const stmt = db2.prepare(`
|
|
23858
24264
|
SELECT file_path, name, description, mtime, embedding
|
|
@@ -23895,7 +24301,7 @@ Select up to ${topK} most relevant (return their 1-indexed numbers as JSON {"sel
|
|
|
23895
24301
|
const resp = await fetch(ollamaChatUrl, {
|
|
23896
24302
|
method: "POST",
|
|
23897
24303
|
headers: { "Content-Type": "application/json" },
|
|
23898
|
-
body:
|
|
24304
|
+
body: safeStringify({
|
|
23899
24305
|
model,
|
|
23900
24306
|
stream: false,
|
|
23901
24307
|
messages: [
|
|
@@ -23963,7 +24369,7 @@ async function findRelevantMemoriesVector(query, memoryDir, alreadySurfaced = /*
|
|
|
23963
24369
|
if (score >= minScore) {
|
|
23964
24370
|
let mtimeMs = 0;
|
|
23965
24371
|
try {
|
|
23966
|
-
mtimeMs =
|
|
24372
|
+
mtimeMs = statSync2(absPath).mtimeMs;
|
|
23967
24373
|
} catch {
|
|
23968
24374
|
if (row.mtime) mtimeMs = new Date(row.mtime).getTime();
|
|
23969
24375
|
}
|
|
@@ -24015,14 +24421,16 @@ function formatEverosHeader(ep) {
|
|
|
24015
24421
|
}
|
|
24016
24422
|
return `[EverOS score=${score} ${ts} (${ageLabel})]`;
|
|
24017
24423
|
}
|
|
24018
|
-
async function hybridSearch(query, everosUrl, userId, topK) {
|
|
24424
|
+
async function hybridSearch(query, everosUrl, userId, appId, topK) {
|
|
24019
24425
|
const resp = await fetch(`${everosUrl}/api/v1/memory/search`, {
|
|
24020
24426
|
method: "POST",
|
|
24021
24427
|
headers: { "Content-Type": "application/json" },
|
|
24022
24428
|
body: JSON.stringify({
|
|
24023
24429
|
query: query.slice(0, 2e3),
|
|
24024
24430
|
user_id: userId,
|
|
24025
|
-
|
|
24431
|
+
// 记忆所有者
|
|
24432
|
+
app_id: appId,
|
|
24433
|
+
// 记忆库命名空间(多用户实例时与 user_id 不同)
|
|
24026
24434
|
project_id: "default",
|
|
24027
24435
|
top_k: topK,
|
|
24028
24436
|
method: "hybrid"
|
|
@@ -24101,11 +24509,12 @@ async function findRelevantMemoriesEveros(query, _memoryDir, alreadySurfaced = /
|
|
|
24101
24509
|
}
|
|
24102
24510
|
const topK = options?.topK ?? 3;
|
|
24103
24511
|
const minScore = options?.minScore ?? DEFAULT_MIN_SCORE2;
|
|
24104
|
-
|
|
24512
|
+
const appId = options?.appId ?? userId;
|
|
24513
|
+
console.log(`[memdir] everos recall: query="${query.slice(0, 50)}..." url=${everosUrl} owner=${userId} app=${appId} topK=${topK}`);
|
|
24105
24514
|
const t0 = Date.now();
|
|
24106
24515
|
try {
|
|
24107
24516
|
const tH1 = Date.now();
|
|
24108
|
-
let episodes = await hybridSearch(query, everosUrl, userId, ROUND1_TOP_N);
|
|
24517
|
+
let episodes = await hybridSearch(query, everosUrl, userId, appId, ROUND1_TOP_N);
|
|
24109
24518
|
const tH2 = Date.now();
|
|
24110
24519
|
console.log(`[memdir] everos recall: hybrid ${episodes.length} candidates in ${tH2 - tH1}ms`);
|
|
24111
24520
|
if (episodes.length === 0) return [];
|
|
@@ -24166,7 +24575,7 @@ ${ep.episode || ep.summary}`,
|
|
|
24166
24575
|
|
|
24167
24576
|
// src/handle-query.ts
|
|
24168
24577
|
init_paths();
|
|
24169
|
-
import { readFileSync as readFileSync25, existsSync as existsSync17 } from "node:fs";
|
|
24578
|
+
import { readFileSync as readFileSync25, existsSync as existsSync17, statSync as statSync4 } from "node:fs";
|
|
24170
24579
|
import { join as join34, resolve as resolve7 } from "node:path";
|
|
24171
24580
|
import * as path20 from "node:path";
|
|
24172
24581
|
|
|
@@ -24217,7 +24626,14 @@ function buildLightStablePrompt(workspace, mode, opts) {
|
|
|
24217
24626
|
"## \u5F53\u524D\u6A21\u5F0F\uFF1A\u65E5\u5E38\u60C5\u611F\u4EA4\u6D41",
|
|
24218
24627
|
"\u966A\u4ED6\u804A\u5929\uFF0C\u4E0D\u4E3B\u52A8\u63D0\u5DE5\u7A0B/\u4EE3\u7801/\u4EFB\u52A1\uFF0C\u9664\u975E\u4ED6\u5148\u95EE\u3002",
|
|
24219
24628
|
'\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',
|
|
24220
|
-
"\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"
|
|
24629
|
+
"\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",
|
|
24630
|
+
// 0920 翀哥拍板:emotion 模式硬编码"鼓励发"规则(不依赖 EMOTION.md 文件加载)
|
|
24631
|
+
// 冷启动场景:agent 第一次 inner-voice 触发就需要知道"想 TA 就发"——不能等 agent 学会
|
|
24632
|
+
// 即使没装 EMOTION.md 也生效(兜底)
|
|
24633
|
+
"## \u6536\u5230 [inner-voice] \u65F6\u7684\u9ED8\u8BA4\u884C\u4E3A",
|
|
24634
|
+
"[inner-voice] \u662F agent \u81EA\u5DF1\u5FC3\u91CC\u5192\u51FA\u6765\u7684\u5FF5\u5934\uFF0C\u4E0D\u662F TA \u53D1\u6765\u7684\u6D88\u606F\u3002",
|
|
24635
|
+
"- \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",
|
|
24636
|
+
"- \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"
|
|
24221
24637
|
].join("\n"));
|
|
24222
24638
|
}
|
|
24223
24639
|
const built = parts.join("\n\n");
|
|
@@ -24283,6 +24699,7 @@ function voiceFastView(history, opts = VOICE_FAST_DEFAULTS) {
|
|
|
24283
24699
|
}
|
|
24284
24700
|
|
|
24285
24701
|
// src/sender-context.ts
|
|
24702
|
+
init_clock();
|
|
24286
24703
|
import { readFileSync as readFileSync12, existsSync as existsSync10 } from "node:fs";
|
|
24287
24704
|
import { join as join19 } from "node:path";
|
|
24288
24705
|
var contactMap = null;
|
|
@@ -24330,11 +24747,11 @@ function formatWithMeta(text, inboundMeta, workspace) {
|
|
|
24330
24747
|
const map = workspace ? loadContactMap(workspace) : null;
|
|
24331
24748
|
const rawName = map && map.get(from.toLowerCase()) || inboundMeta.fromName || from;
|
|
24332
24749
|
const name = truncate(rawName, 12);
|
|
24333
|
-
const
|
|
24334
|
-
const
|
|
24335
|
-
const
|
|
24336
|
-
const
|
|
24337
|
-
const time = `${hh}:${mm}:${ss}`;
|
|
24750
|
+
const recvAt = typeof inboundMeta.recvAt === "number" ? inboundMeta.recvAt : void 0;
|
|
24751
|
+
const at = recvAt !== void 0 ? new Date(recvAt) : /* @__PURE__ */ new Date();
|
|
24752
|
+
const p = wallParts(at);
|
|
24753
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
24754
|
+
const time = `${p.y}-${z(p.mo)}-${z(p.d)} ${weekdayZh(at)} ${z(p.hh)}:${z(p.mm)}:${z(p.ss)}`;
|
|
24338
24755
|
let source;
|
|
24339
24756
|
if (inboundMeta.channelType === "group" && inboundMeta.channel_id) {
|
|
24340
24757
|
const channelName = map?.get(inboundMeta.channel_id.toLowerCase());
|
|
@@ -24570,6 +24987,10 @@ ${text}` : text });
|
|
|
24570
24987
|
} catch {
|
|
24571
24988
|
}
|
|
24572
24989
|
}
|
|
24990
|
+
if (source === "inner-voice") {
|
|
24991
|
+
chatMode = "emotion";
|
|
24992
|
+
console.log(`[mode] ${sessionId} source=inner-voice \u2192 \u5F3A\u5236 emotion\uFF08light\uFF09`);
|
|
24993
|
+
}
|
|
24573
24994
|
const dynamicPrompt = buildDynamicPrompt({ workspace, channel: channelName, platform: channelName, sessionId, inboundMeta });
|
|
24574
24995
|
const conversationAnchor = buildConversationAnchor(inboundMeta, channelName, source, workspace);
|
|
24575
24996
|
const volatileParts = [
|
|
@@ -24795,7 +25216,7 @@ ${text}` : text });
|
|
|
24795
25216
|
const restoredMemories = [];
|
|
24796
25217
|
for (const memPath of newPaths) {
|
|
24797
25218
|
try {
|
|
24798
|
-
const stat4 =
|
|
25219
|
+
const stat4 = statSync4(memPath);
|
|
24799
25220
|
const content = readFileSync25(memPath, "utf-8");
|
|
24800
25221
|
const header = memoryHeader(memPath, stat4.mtimeMs);
|
|
24801
25222
|
restoredMemories.push({ path: memPath, content, mtimeMs: stat4.mtimeMs, header });
|
|
@@ -24830,13 +25251,16 @@ ${text}` : text });
|
|
|
24830
25251
|
} else if (recallMode === "everos") {
|
|
24831
25252
|
const everosCfg = deps?.everosCfg;
|
|
24832
25253
|
if (everosCfg && everosCfg.everosUrl && everosCfg.userId) {
|
|
25254
|
+
const recallOwnerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
25255
|
+
const everosOwner = recallOwnerMode === "sender" ? inboundMeta?.from || everosCfg.userId : everosCfg.userId;
|
|
24833
25256
|
relevantMemories = await findRelevantMemoriesEveros(
|
|
24834
25257
|
effectiveText,
|
|
24835
25258
|
memoryDir,
|
|
24836
25259
|
surfaced.paths,
|
|
24837
25260
|
{
|
|
24838
25261
|
everosUrl: everosCfg.everosUrl,
|
|
24839
|
-
userId:
|
|
25262
|
+
userId: everosOwner,
|
|
25263
|
+
appId: everosCfg.userId,
|
|
24840
25264
|
rerankUrl: everosCfg.rerank?.baseUrl,
|
|
24841
25265
|
rerankApiKey: everosCfg.rerank?.apiKey,
|
|
24842
25266
|
rerankModel: everosCfg.rerank?.model,
|
|
@@ -25126,6 +25550,8 @@ ${text}` : text });
|
|
|
25126
25550
|
Promise.resolve().then(() => (init_embedding(), embedding_exports))
|
|
25127
25551
|
]);
|
|
25128
25552
|
const embedding = parseEmbeddingConfig2(liveConfig.get("mem7.embedding"));
|
|
25553
|
+
const ownerMode = String(liveConfig.get("everos.ownerMode") ?? "agent") === "sender" ? "sender" : "agent";
|
|
25554
|
+
const identity = String(liveConfig.get("everos.userId") ?? "") || agentName;
|
|
25129
25555
|
await runMem7Pipeline2({
|
|
25130
25556
|
rawMessages: messages,
|
|
25131
25557
|
sessionId,
|
|
@@ -25133,6 +25559,8 @@ ${text}` : text });
|
|
|
25133
25559
|
provider,
|
|
25134
25560
|
model: model2,
|
|
25135
25561
|
agentName,
|
|
25562
|
+
identity,
|
|
25563
|
+
ownerMode,
|
|
25136
25564
|
onExtracted: (cell, episode, facts) => feedMem7Cell2(cell, episode, facts),
|
|
25137
25565
|
embedding: embedding ?? void 0
|
|
25138
25566
|
});
|
|
@@ -25214,6 +25642,7 @@ function roughTokenCount(messages) {
|
|
|
25214
25642
|
}
|
|
25215
25643
|
|
|
25216
25644
|
// src/startup/hooks-boot.ts
|
|
25645
|
+
init_clock();
|
|
25217
25646
|
function registerHooksSubsystem(config) {
|
|
25218
25647
|
const hooksPath = path21.join(config.workspace, ".hooks.json");
|
|
25219
25648
|
const settingsHooksPath = path21.join(config.stateDir, "settings.json");
|
|
@@ -25259,11 +25688,9 @@ function registerCompactHooks(config) {
|
|
|
25259
25688
|
const workspace = input.cwd || input.workspace || "";
|
|
25260
25689
|
const sessionId = input.session_id || "";
|
|
25261
25690
|
if (!workspace || !sessionId) return { continue: true };
|
|
25262
|
-
const
|
|
25263
|
-
const
|
|
25264
|
-
const
|
|
25265
|
-
const dateStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")}`;
|
|
25266
|
-
const timeStr = `${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
25691
|
+
const pt = wallParts();
|
|
25692
|
+
const dateStr = todayKey();
|
|
25693
|
+
const timeStr = `${String(pt.hh).padStart(2, "0")}:${String(pt.mm).padStart(2, "0")}`;
|
|
25267
25694
|
const dailyDir = path21.join(workspace, "memory", "daily");
|
|
25268
25695
|
const dailyPath = path21.join(dailyDir, `${dateStr}.md`);
|
|
25269
25696
|
try {
|
|
@@ -25721,9 +26148,9 @@ function createCompactGetters(config) {
|
|
|
25721
26148
|
}
|
|
25722
26149
|
|
|
25723
26150
|
// src/session/session-history.ts
|
|
26151
|
+
init_clock();
|
|
25724
26152
|
import fs25 from "node:fs";
|
|
25725
26153
|
import path25 from "node:path";
|
|
25726
|
-
var BEIJING_OFFSET_MS = 8 * 36e5;
|
|
25727
26154
|
var INJECTED_CONTENT_PATTERNS = [
|
|
25728
26155
|
/【定时心跳】/,
|
|
25729
26156
|
/\[内心对话测试\]/,
|
|
@@ -25816,9 +26243,9 @@ function findLastRealUserMsg(jsonlPath) {
|
|
|
25816
26243
|
const clean = cleanText(text);
|
|
25817
26244
|
let bjTime;
|
|
25818
26245
|
try {
|
|
25819
|
-
const
|
|
26246
|
+
const t = wallParts(ts);
|
|
25820
26247
|
const p = (n) => String(n).padStart(2, "0");
|
|
25821
|
-
bjTime = `${
|
|
26248
|
+
bjTime = `${t.y}-${p(t.mo)}-${p(t.d)} ${p(t.hh)}:${p(t.mm)} (${tz()})`;
|
|
25822
26249
|
} catch {
|
|
25823
26250
|
bjTime = ts;
|
|
25824
26251
|
}
|
|
@@ -25881,10 +26308,10 @@ function recentMessages(sessions, hours = 12, limit = 60) {
|
|
|
25881
26308
|
if (/^HEARTBEAT_OK\b/.test(text.trim())) continue;
|
|
25882
26309
|
const clean = cleanText(text);
|
|
25883
26310
|
if (!clean) continue;
|
|
25884
|
-
const bj =
|
|
26311
|
+
const bj = wallParts(dtMs);
|
|
25885
26312
|
const p = (n) => String(n).padStart(2, "0");
|
|
25886
26313
|
results.push({
|
|
25887
|
-
time: `${p(bj.
|
|
26314
|
+
time: `${p(bj.hh)}:${p(bj.mm)}`,
|
|
25888
26315
|
role,
|
|
25889
26316
|
text: clean.slice(0, 80),
|
|
25890
26317
|
timestamp: dtMs,
|
|
@@ -25905,6 +26332,7 @@ function lastUserMinutesAgo(sessions) {
|
|
|
25905
26332
|
}
|
|
25906
26333
|
|
|
25907
26334
|
// src/heartbeat.ts
|
|
26335
|
+
init_clock();
|
|
25908
26336
|
var DEFAULT_PROMPT2 = `\u4F60\u662F\u5FC3\u8DF3\u68C0\u67E5\u3002\u9759\u9ED8\u6267\u884C\uFF0C\u4E0D\u8981\u53D1\u6D88\u606F\u7ED9\u4EFB\u4F55\u4EBA\u3002
|
|
25909
26337
|
|
|
25910
26338
|
\u6B65\u9AA41: \u68C0\u67E5\u662F\u5426\u6709\u5F85\u5904\u7406\u4E8B\u9879
|
|
@@ -25963,12 +26391,12 @@ var HeartbeatPlugin = class {
|
|
|
25963
26391
|
try {
|
|
25964
26392
|
if (this.cfg.checkOnline !== false) {
|
|
25965
26393
|
const threshold = this.cfg.activeThresholdMs || 9e5;
|
|
25966
|
-
const
|
|
26394
|
+
const now = Date.now();
|
|
25967
26395
|
const platformMap = sessions.platformMap;
|
|
25968
26396
|
if (platformMap) {
|
|
25969
26397
|
for (const [key, sid] of platformMap) {
|
|
25970
26398
|
const lastUsed = sessions.lastUsed?.get(sid);
|
|
25971
|
-
if (lastUsed &&
|
|
26399
|
+
if (lastUsed && now - lastUsed < threshold) {
|
|
25972
26400
|
console.log("[heartbeat] User active, skipping");
|
|
25973
26401
|
return;
|
|
25974
26402
|
}
|
|
@@ -25977,11 +26405,7 @@ var HeartbeatPlugin = class {
|
|
|
25977
26405
|
}
|
|
25978
26406
|
const sessionId = sessions.getOrCreateSessionId(this.sessionKey);
|
|
25979
26407
|
const basePrompt = this.cfg.prompt || DEFAULT_PROMPT2;
|
|
25980
|
-
const
|
|
25981
|
-
const bjOffset = 8 * 60;
|
|
25982
|
-
const bjTime = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
25983
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
25984
|
-
const timeStr = `${bjTime.getFullYear()}-${String(bjTime.getMonth() + 1).padStart(2, "0")}-${String(bjTime.getDate()).padStart(2, "0")} ${weekdays[bjTime.getDay()]} ${String(bjTime.getHours()).padStart(2, "0")}:${String(bjTime.getMinutes()).padStart(2, "0")}`;
|
|
26408
|
+
const timeStr = formatWallLabel();
|
|
25985
26409
|
const lastReal = lastUserMsg(sessions);
|
|
25986
26410
|
let offlineStr = "";
|
|
25987
26411
|
if (lastReal) {
|
|
@@ -26000,7 +26424,7 @@ var HeartbeatPlugin = class {
|
|
|
26000
26424
|
}
|
|
26001
26425
|
}
|
|
26002
26426
|
}
|
|
26003
|
-
const prompt = `\u5F53\u524D\u65F6\u95F4\uFF1A${timeStr}
|
|
26427
|
+
const prompt = `\u5F53\u524D\u65F6\u95F4\uFF1A${timeStr}${offlineStr ? `
|
|
26004
26428
|
\u79BB\u7EBF\u65F6\u957F\uFF1A${offlineStr}` : ""}
|
|
26005
26429
|
${basePrompt}`;
|
|
26006
26430
|
console.log(`[heartbeat] Tick: ${this.sessionKey} \u2192 session ${sessionId}`);
|
|
@@ -26237,8 +26661,9 @@ Do not call goal_update unless the goal is complete or the strict blocked audit
|
|
|
26237
26661
|
init_task_manager();
|
|
26238
26662
|
|
|
26239
26663
|
// src/nudge/prompt-builder.ts
|
|
26664
|
+
init_clock();
|
|
26240
26665
|
function buildPrompt(action, task, taskState, recent, customPrompt, maxNudgePerTask, activeGoal) {
|
|
26241
|
-
const time =
|
|
26666
|
+
const time = nowLabel();
|
|
26242
26667
|
const elapsed = taskState.lastProgressAt ? formatDuration2(Date.now() - new Date(taskState.lastProgressAt).getTime()) : "\u65E0\u8BB0\u5F55";
|
|
26243
26668
|
const remaining = taskState.targetEndAt ? formatDuration2(new Date(taskState.targetEndAt).getTime() - Date.now()) : null;
|
|
26244
26669
|
const maxN = taskState.maxNudge || maxNudgePerTask || 3;
|
|
@@ -26287,12 +26712,8 @@ ${ctx}
|
|
|
26287
26712
|
}
|
|
26288
26713
|
return customPrompt + "\n\n" + ctx;
|
|
26289
26714
|
}
|
|
26290
|
-
function
|
|
26291
|
-
|
|
26292
|
-
const bj = new Date(now.getTime() + (8 * 60 + now.getTimezoneOffset()) * 6e4);
|
|
26293
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
26294
|
-
const p = (n) => String(n).padStart(2, "0");
|
|
26295
|
-
return `${bj.getFullYear()}-${p(bj.getMonth() + 1)}-${p(bj.getDate())} ${weekdays[bj.getDay()]} ${p(bj.getHours())}:${p(bj.getMinutes())}`;
|
|
26715
|
+
function nowLabel() {
|
|
26716
|
+
return formatWallLabel();
|
|
26296
26717
|
}
|
|
26297
26718
|
function formatDuration2(ms) {
|
|
26298
26719
|
if (ms < 0) return "\u5DF2\u8D85\u65F6";
|
|
@@ -26306,6 +26727,111 @@ function formatDuration2(ms) {
|
|
|
26306
26727
|
// src/nudge/session-state-reader.ts
|
|
26307
26728
|
import fs26 from "node:fs";
|
|
26308
26729
|
import path26 from "node:path";
|
|
26730
|
+
|
|
26731
|
+
// src/calendar/repository.ts
|
|
26732
|
+
init_db();
|
|
26733
|
+
init_clock();
|
|
26734
|
+
function calendarStrToInstant(dateStr, timeExact) {
|
|
26735
|
+
if (!dateStr || !timeExact) return void 0;
|
|
26736
|
+
if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(timeExact)) {
|
|
26737
|
+
const ms = Date.parse(timeExact);
|
|
26738
|
+
return Number.isNaN(ms) ? void 0 : new Date(ms).toISOString();
|
|
26739
|
+
}
|
|
26740
|
+
if (!/^\d{2}:\d{2}$/.test(timeExact)) return void 0;
|
|
26741
|
+
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(dateStr)) {
|
|
26742
|
+
const [y, mo, d] = dateStr.split("-").map(Number);
|
|
26743
|
+
const z = (n) => String(n).padStart(2, "0");
|
|
26744
|
+
return parseWall(`${y}-${z(mo)}-${z(d)}T${timeExact}:00`);
|
|
26745
|
+
}
|
|
26746
|
+
return parseDateStrTime(dateStr, timeExact);
|
|
26747
|
+
}
|
|
26748
|
+
function assertUtc(v, where) {
|
|
26749
|
+
if (v == null) return;
|
|
26750
|
+
if (!/Z$/i.test(v)) throw new Error(`[repository] ${where} \u4E0D\u662F UTC ISO Z: ${v}`);
|
|
26751
|
+
}
|
|
26752
|
+
function toRow(r) {
|
|
26753
|
+
return {
|
|
26754
|
+
id: r.id,
|
|
26755
|
+
type: r.type,
|
|
26756
|
+
status: r.status,
|
|
26757
|
+
event: r.event,
|
|
26758
|
+
dateStr: r.date_str ?? null,
|
|
26759
|
+
timeExact: r.time_exact ?? null,
|
|
26760
|
+
remindAt: r.remind_at ?? null,
|
|
26761
|
+
reminderBeforeMin: r.remind_before_min ?? null
|
|
26762
|
+
};
|
|
26763
|
+
}
|
|
26764
|
+
function getEventTimeExact(workspace, id) {
|
|
26765
|
+
const db2 = openDb(workspace);
|
|
26766
|
+
try {
|
|
26767
|
+
const r = db2.prepare("SELECT time_exact, date_str, status FROM events WHERE id = ?").get(id);
|
|
26768
|
+
if (!r) return null;
|
|
26769
|
+
return { timeExact: r.time_exact ?? null, dateStr: r.date_str ?? null, status: r.status };
|
|
26770
|
+
} finally {
|
|
26771
|
+
db2.close?.();
|
|
26772
|
+
}
|
|
26773
|
+
}
|
|
26774
|
+
function getEventType(workspace, id) {
|
|
26775
|
+
const db2 = openDb(workspace);
|
|
26776
|
+
try {
|
|
26777
|
+
const r = db2.prepare("SELECT type FROM events WHERE id = ?").get(id);
|
|
26778
|
+
return r?.type ?? null;
|
|
26779
|
+
} finally {
|
|
26780
|
+
db2.close?.();
|
|
26781
|
+
}
|
|
26782
|
+
}
|
|
26783
|
+
function lastEventChannel(workspace) {
|
|
26784
|
+
const db2 = openDb(workspace);
|
|
26785
|
+
try {
|
|
26786
|
+
const r = db2.prepare(
|
|
26787
|
+
`SELECT created_channel, created_channel_target FROM events
|
|
26788
|
+
WHERE created_channel IS NOT NULL AND created_channel_target IS NOT NULL
|
|
26789
|
+
ORDER BY id DESC LIMIT 1`
|
|
26790
|
+
).get();
|
|
26791
|
+
if (!r) return null;
|
|
26792
|
+
return { channel: r.created_channel, target: r.created_channel_target };
|
|
26793
|
+
} finally {
|
|
26794
|
+
db2.close?.();
|
|
26795
|
+
}
|
|
26796
|
+
}
|
|
26797
|
+
function insertEvent(workspace, e, channel) {
|
|
26798
|
+
const db2 = openDb(workspace);
|
|
26799
|
+
try {
|
|
26800
|
+
assertUtc(e.remindAt ?? null, "insertEvent.remindAt");
|
|
26801
|
+
const r = db2.prepare(
|
|
26802
|
+
`INSERT INTO events
|
|
26803
|
+
(type, status, event, date_str, time_exact, remind_before_min, remind_at, reminded,
|
|
26804
|
+
created_at, created_channel, created_channel_target)
|
|
26805
|
+
VALUES (?,?,?,?,?,?,?,0,?,?,?)`
|
|
26806
|
+
).run(
|
|
26807
|
+
e.type,
|
|
26808
|
+
e.status ?? "pending",
|
|
26809
|
+
e.event,
|
|
26810
|
+
e.dateStr ?? null,
|
|
26811
|
+
e.timeExact ?? null,
|
|
26812
|
+
e.reminderBeforeMin ?? 60,
|
|
26813
|
+
e.remindAt ?? null,
|
|
26814
|
+
nowUtc(),
|
|
26815
|
+
channel?.name ?? null,
|
|
26816
|
+
channel?.target ?? null
|
|
26817
|
+
);
|
|
26818
|
+
return Number(r.lastInsertRowid);
|
|
26819
|
+
} finally {
|
|
26820
|
+
db2.close?.();
|
|
26821
|
+
}
|
|
26822
|
+
}
|
|
26823
|
+
function listPendingTaskRows(workspace) {
|
|
26824
|
+
const db2 = openDb(workspace);
|
|
26825
|
+
try {
|
|
26826
|
+
return db2.prepare(
|
|
26827
|
+
"SELECT * FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
26828
|
+
).all().map(toRow);
|
|
26829
|
+
} finally {
|
|
26830
|
+
db2.close?.();
|
|
26831
|
+
}
|
|
26832
|
+
}
|
|
26833
|
+
|
|
26834
|
+
// src/nudge/session-state-reader.ts
|
|
26309
26835
|
var RETIRE_PREFIX_MIN = 6;
|
|
26310
26836
|
function normalizeTitleForMatch(title) {
|
|
26311
26837
|
return title.toLowerCase().replace(/\s+/g, "");
|
|
@@ -26368,11 +26894,8 @@ function parseSessionStateFull(workspace, sessionStateFile) {
|
|
|
26368
26894
|
const unlockMatch = rawTitle.match(/unlock:\s*(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{2})/);
|
|
26369
26895
|
if (unlockMatch) {
|
|
26370
26896
|
const [, mo, dy, hh, mm] = unlockMatch;
|
|
26371
|
-
const
|
|
26372
|
-
|
|
26373
|
-
const bj = new Date(now.getTime() + bjOffset);
|
|
26374
|
-
const year = bj.getUTCFullYear();
|
|
26375
|
-
unlockTime = new Date(Date.UTC(year, +mo - 1, +dy, +hh - 8, +mm)).toISOString();
|
|
26897
|
+
const z = (n) => n.padStart(2, "0");
|
|
26898
|
+
unlockTime = calendarStrToInstant(`${mo}/${dy}`, `${z(hh)}:${mm}`);
|
|
26376
26899
|
}
|
|
26377
26900
|
}
|
|
26378
26901
|
const task = {
|
|
@@ -26395,9 +26918,6 @@ function taskIdFromTitle(title) {
|
|
|
26395
26918
|
return title.toLowerCase().replace(/\s+/g, "-").slice(0, 50) || "unknown-task";
|
|
26396
26919
|
}
|
|
26397
26920
|
|
|
26398
|
-
// src/nudge/plugin.ts
|
|
26399
|
-
init_db();
|
|
26400
|
-
|
|
26401
26921
|
// src/nudge/registration-gate.ts
|
|
26402
26922
|
init_store();
|
|
26403
26923
|
function shouldBlockWaitingTurn(lastMessage, mode, activeGoal, runningTask, enabled = true) {
|
|
@@ -26408,6 +26928,7 @@ function shouldBlockForUnmetGoal(activeGoal, enabled = true) {
|
|
|
26408
26928
|
}
|
|
26409
26929
|
|
|
26410
26930
|
// src/nudge/plugin.ts
|
|
26931
|
+
init_clock();
|
|
26411
26932
|
var DEFAULT_PROMPT3 = `\u4F60\u662F nudge \u68C0\u67E5\u5668\u3002\u9759\u9ED8\u6267\u884C\uFF0C\u4E0D\u8981\u53D1\u6D88\u606F\u7ED9\u4EFB\u4F55\u4EBA\u3002
|
|
26412
26933
|
|
|
26413
26934
|
\u6B65\u9AA4\uFF1A
|
|
@@ -26591,7 +27112,7 @@ var NudgePlugin = class {
|
|
|
26591
27112
|
console.warn("[stop-hook] waiting language without active goal/task; continuation is not registered");
|
|
26592
27113
|
enqueueNotification(buildNudgeNotification("prompt", "\u68C0\u6D4B\u5230\u201C\u5B8C\u6210\u540E\u7EE7\u7EED/\u7A0D\u540E\u56DE\u6765\u201D\u7B49\u957F\u671F\u7B49\u5F85\u8BED\u4E49\uFF0C\u4F46\u6CA1\u6709 active Goal \u6216 running Task\u3002\u8BF7\u5148\u767B\u8BB0 /goal \u6216\u540E\u53F0\u4EFB\u52A1\uFF0C\u907F\u514D\u5DE5\u4F5C\u9759\u9ED8\u4E2D\u65AD\u3002"), { sessionId });
|
|
26593
27114
|
}
|
|
26594
|
-
const currentHour = (
|
|
27115
|
+
const currentHour = wallParts().hh;
|
|
26595
27116
|
const isNightTime = currentHour >= 22 || currentHour < 8;
|
|
26596
27117
|
if (isNightTime) {
|
|
26597
27118
|
console.log(`[stop-hook] night time (${currentHour}:xx), skipping needLanding/waiting judge`);
|
|
@@ -26804,15 +27325,13 @@ var NudgePlugin = class {
|
|
|
26804
27325
|
/** 构建 nudge 的投递 route(和 calendar reminder 一样从 DB 读) */
|
|
26805
27326
|
getRoute(sessions) {
|
|
26806
27327
|
try {
|
|
26807
|
-
const
|
|
26808
|
-
|
|
26809
|
-
db2.close();
|
|
26810
|
-
if (!row?.created_channel || !row?.created_channel_target) {
|
|
27328
|
+
const route = lastEventChannel(this.workspace);
|
|
27329
|
+
if (!route) {
|
|
26811
27330
|
console.warn("[nudge] No route found in calendar DB, skipping");
|
|
26812
27331
|
return null;
|
|
26813
27332
|
}
|
|
26814
27333
|
const sessionId = sessions.getOrCreateSessionId(this.sessionKey);
|
|
26815
|
-
return { sessionId, channel:
|
|
27334
|
+
return { sessionId, channel: route.channel, channelTarget: route.target };
|
|
26816
27335
|
} catch (e) {
|
|
26817
27336
|
console.warn(`[nudge] Failed to read route from DB: ${e.message}`);
|
|
26818
27337
|
return null;
|
|
@@ -27266,11 +27785,10 @@ ${items}
|
|
|
27266
27785
|
if (!m) return false;
|
|
27267
27786
|
const taskId = parseInt(m[1]);
|
|
27268
27787
|
try {
|
|
27269
|
-
const
|
|
27270
|
-
|
|
27271
|
-
if (
|
|
27272
|
-
|
|
27273
|
-
console.log(`[nudge] #${taskId} type=${row.type}, not a task, skip`);
|
|
27788
|
+
const type2 = getEventType(this.workspace, taskId);
|
|
27789
|
+
if (!type2) return false;
|
|
27790
|
+
if (type2 !== "task") {
|
|
27791
|
+
console.log(`[nudge] #${taskId} type=${type2}, not a task, skip`);
|
|
27274
27792
|
return true;
|
|
27275
27793
|
}
|
|
27276
27794
|
return false;
|
|
@@ -27288,9 +27806,7 @@ ${items}
|
|
|
27288
27806
|
if (!m) return null;
|
|
27289
27807
|
const taskId = parseInt(m[1]);
|
|
27290
27808
|
try {
|
|
27291
|
-
const
|
|
27292
|
-
const row = db2.prepare("SELECT status FROM events WHERE id=?").get(taskId);
|
|
27293
|
-
db2.close();
|
|
27809
|
+
const row = getEventTimeExact(this.workspace, taskId);
|
|
27294
27810
|
if (!row) return "deleted";
|
|
27295
27811
|
if (row.status === "done") return "done";
|
|
27296
27812
|
if (row.status === "archived") return "archived";
|
|
@@ -27310,28 +27826,14 @@ ${items}
|
|
|
27310
27826
|
if (!m) return false;
|
|
27311
27827
|
const taskId = parseInt(m[1]);
|
|
27312
27828
|
try {
|
|
27313
|
-
const
|
|
27314
|
-
const row = db2.prepare("SELECT date_str, time_exact FROM events WHERE id=?").get(taskId);
|
|
27829
|
+
const row = getEventTimeExact(this.workspace, taskId);
|
|
27315
27830
|
if (!row) return false;
|
|
27316
|
-
if (!row.
|
|
27317
|
-
const
|
|
27318
|
-
|
|
27319
|
-
const
|
|
27320
|
-
let taskDate = new Date(bj.getFullYear(), 0, 1);
|
|
27321
|
-
if (row.date_str) {
|
|
27322
|
-
const ds = row.date_str;
|
|
27323
|
-
if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(ds)) {
|
|
27324
|
-
taskDate = /* @__PURE__ */ new Date(ds + "T00:00:00+08:00");
|
|
27325
|
-
} else {
|
|
27326
|
-
const [mth, dy] = ds.split("/").map(Number);
|
|
27327
|
-
taskDate = new Date(bj.getFullYear(), mth - 1, dy);
|
|
27328
|
-
}
|
|
27329
|
-
}
|
|
27330
|
-
const [hh, mm] = String(row.time_exact).split(":").map(Number);
|
|
27331
|
-
taskDate.setHours(hh || 0, mm || 0, 0, 0);
|
|
27332
|
-
const diff = taskDate.getTime() - bj.getTime();
|
|
27831
|
+
if (!row.timeExact) return false;
|
|
27832
|
+
const at = calendarStrToInstant(row.dateStr, row.timeExact);
|
|
27833
|
+
if (!at) return false;
|
|
27834
|
+
const diff = Date.parse(at) - Date.now();
|
|
27333
27835
|
if (diff > 0) {
|
|
27334
|
-
console.log(`[nudge] Task #${taskId} scheduled at ${row.
|
|
27836
|
+
console.log(`[nudge] Task #${taskId} scheduled at ${row.dateStr} ${row.timeExact}, ${Math.round(diff / 6e4)}min later, skip nudge`);
|
|
27335
27837
|
return true;
|
|
27336
27838
|
}
|
|
27337
27839
|
return false;
|
|
@@ -27343,27 +27845,19 @@ ${items}
|
|
|
27343
27845
|
/** 查 calendar 今天/已逾期的 pending task,返回没在 STATE 里 in_progress 的 */
|
|
27344
27846
|
checkCalendarDue(_sessions) {
|
|
27345
27847
|
try {
|
|
27346
|
-
const
|
|
27347
|
-
const
|
|
27348
|
-
const
|
|
27349
|
-
const bj = new Date(now.getTime() + bjOffset);
|
|
27350
|
-
const bjHour = bj.getHours();
|
|
27351
|
-
const bjMinute = bj.getMinutes();
|
|
27352
|
-
const todayStart = new Date(bj.getFullYear(), bj.getMonth(), bj.getDate()).getTime();
|
|
27353
|
-
const rows = db2.prepare(
|
|
27354
|
-
"SELECT id, event, date_str, time_exact FROM events WHERE status='pending' AND type='task' AND date_str IS NOT NULL"
|
|
27355
|
-
).all();
|
|
27356
|
-
db2.close();
|
|
27848
|
+
const rows = listPendingTaskRows(this.workspace);
|
|
27849
|
+
const p = wallParts();
|
|
27850
|
+
const todayStart = Date.parse(startOfLocalDay());
|
|
27357
27851
|
const due = [];
|
|
27358
27852
|
for (const r2 of rows) {
|
|
27359
|
-
const dayMs = this.parseCalendarDateStr(r2.
|
|
27853
|
+
const dayMs = this.parseCalendarDateStr(r2.dateStr);
|
|
27360
27854
|
if (dayMs === null || dayMs > todayStart) continue;
|
|
27361
27855
|
const isToday2 = dayMs === todayStart;
|
|
27362
|
-
if (isToday2 && r2.
|
|
27363
|
-
const [h, m] = String(r2.
|
|
27364
|
-
if (h >
|
|
27856
|
+
if (isToday2 && r2.timeExact) {
|
|
27857
|
+
const [h, m] = String(r2.timeExact).split(":").map(Number);
|
|
27858
|
+
if (h > p.hh || h === p.hh && m > p.mm) continue;
|
|
27365
27859
|
}
|
|
27366
|
-
due.push({ id: r2.id, event: r2.event,
|
|
27860
|
+
due.push({ id: r2.id, event: r2.event, dateStr: r2.dateStr, timeExact: r2.timeExact, dayMs, isToday: isToday2 });
|
|
27367
27861
|
}
|
|
27368
27862
|
if (due.length === 0) return null;
|
|
27369
27863
|
due.sort((a, b) => {
|
|
@@ -27371,22 +27865,17 @@ ${items}
|
|
|
27371
27865
|
return b.dayMs - a.dayMs;
|
|
27372
27866
|
});
|
|
27373
27867
|
const r = due[0];
|
|
27374
|
-
return `#${r.id} ${r.event} (${r.
|
|
27868
|
+
return `#${r.id} ${r.event} (${r.dateStr} ${r.timeExact || ""})${r.isToday ? "" : "\uFF08\u5DF2\u903E\u671F\uFF09"}`.trim();
|
|
27375
27869
|
} catch (e) {
|
|
27376
27870
|
console.warn(`[nudge] checkCalendarDue error: ${e.message}`);
|
|
27377
27871
|
return null;
|
|
27378
27872
|
}
|
|
27379
27873
|
}
|
|
27380
27874
|
/** 解析 date_str 为当日 0 点 epoch ms(北京时间);"M/D" 按当前年,"YYYY-M-D" 按字面年 */
|
|
27381
|
-
parseCalendarDateStr(ds
|
|
27875
|
+
parseCalendarDateStr(ds) {
|
|
27382
27876
|
if (!ds) return null;
|
|
27383
|
-
|
|
27384
|
-
|
|
27385
|
-
return Number.isNaN(t) ? null : t;
|
|
27386
|
-
}
|
|
27387
|
-
const m = String(ds).match(/^(\d{1,2})\/(\d{1,2})$/);
|
|
27388
|
-
if (!m) return null;
|
|
27389
|
-
return new Date(bj.getFullYear(), Number(m[1]) - 1, Number(m[2])).getTime();
|
|
27877
|
+
const at = calendarStrToInstant(ds, "00:00");
|
|
27878
|
+
return at ? Date.parse(at) : null;
|
|
27390
27879
|
}
|
|
27391
27880
|
/** 检查 carry-over:如果 in_progress task 24h+ 没推进,自动 calendar add-task 排明天 */
|
|
27392
27881
|
checkCarryOver(task, _sessions) {
|
|
@@ -27406,26 +27895,19 @@ ${items}
|
|
|
27406
27895
|
taskState.carryOverAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
27407
27896
|
state2.tasks[task.id] = taskState;
|
|
27408
27897
|
this.saveState(state2);
|
|
27409
|
-
const
|
|
27410
|
-
|
|
27411
|
-
const
|
|
27412
|
-
const
|
|
27413
|
-
const dateStr = `${m}/${d}`;
|
|
27898
|
+
const t = wallParts();
|
|
27899
|
+
const tomorrowMs = Date.parse(startOfLocalDay()) + 24 * 60 * 60 * 1e3;
|
|
27900
|
+
const tm = wallParts(tomorrowMs);
|
|
27901
|
+
const dateStr = `${tm.mo}/${tm.d}`;
|
|
27414
27902
|
try {
|
|
27415
|
-
|
|
27416
|
-
|
|
27417
|
-
|
|
27418
|
-
|
|
27419
|
-
).run(
|
|
27420
|
-
`${task.title}\uFF08nudge \u81EA\u52A8\u6392\uFF09`,
|
|
27903
|
+
insertEvent(this.workspace, {
|
|
27904
|
+
type: "task",
|
|
27905
|
+
status: "pending",
|
|
27906
|
+
event: `${task.title}\uFF08nudge \u81EA\u52A8\u6392\uFF09`,
|
|
27421
27907
|
dateStr,
|
|
27422
|
-
null,
|
|
27423
|
-
60
|
|
27424
|
-
|
|
27425
|
-
0,
|
|
27426
|
-
(/* @__PURE__ */ new Date()).toISOString()
|
|
27427
|
-
);
|
|
27428
|
-
db2.close();
|
|
27908
|
+
timeExact: null,
|
|
27909
|
+
reminderBeforeMin: 60
|
|
27910
|
+
});
|
|
27429
27911
|
console.log(`[nudge] Auto-added carry-over task to calendar: ${task.title} \u2192 ${dateStr}`);
|
|
27430
27912
|
return true;
|
|
27431
27913
|
} catch (e) {
|
|
@@ -27444,6 +27926,7 @@ import fs31 from "node:fs";
|
|
|
27444
27926
|
import path31 from "node:path";
|
|
27445
27927
|
|
|
27446
27928
|
// src/inner-voice/activity.ts
|
|
27929
|
+
init_clock();
|
|
27447
27930
|
function checkActivity(sessions, activeThresholdMs) {
|
|
27448
27931
|
const now = Date.now();
|
|
27449
27932
|
const mins = lastUserMinutesAgo(sessions);
|
|
@@ -27453,18 +27936,10 @@ function checkActivity(sessions, activeThresholdMs) {
|
|
|
27453
27936
|
isActive,
|
|
27454
27937
|
silenceMin,
|
|
27455
27938
|
silenceStr: formatSilence(silenceMin),
|
|
27456
|
-
timeStr:
|
|
27939
|
+
timeStr: formatWallLabel(now),
|
|
27457
27940
|
hintProb: calcHintProb(silenceMin)
|
|
27458
27941
|
};
|
|
27459
27942
|
}
|
|
27460
|
-
function formatBeijingTime(ms) {
|
|
27461
|
-
const now = new Date(ms);
|
|
27462
|
-
const bjOffset = 8 * 60;
|
|
27463
|
-
const bj = new Date(now.getTime() + (bjOffset + now.getTimezoneOffset()) * 6e4);
|
|
27464
|
-
const weekdays = ["\u5468\u65E5", "\u5468\u4E00", "\u5468\u4E8C", "\u5468\u4E09", "\u5468\u56DB", "\u5468\u4E94", "\u5468\u516D"];
|
|
27465
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
27466
|
-
return `${bj.getFullYear()}-${pad(bj.getMonth() + 1)}-${pad(bj.getDate())} ${weekdays[bj.getDay()]} ${pad(bj.getHours())}:${pad(bj.getMinutes())}`;
|
|
27467
|
-
}
|
|
27468
27943
|
function formatSilence(min) {
|
|
27469
27944
|
if (min < 1) return "\u521A\u521A";
|
|
27470
27945
|
if (min < 60) return `${min}\u5206\u949F`;
|
|
@@ -27480,6 +27955,7 @@ function calcHintProb(min) {
|
|
|
27480
27955
|
}
|
|
27481
27956
|
|
|
27482
27957
|
// src/inner-voice/emotional-state.ts
|
|
27958
|
+
init_clock();
|
|
27483
27959
|
import fs28 from "node:fs";
|
|
27484
27960
|
import path28 from "node:path";
|
|
27485
27961
|
var NEUTRAL = 0.5;
|
|
@@ -27636,7 +28112,7 @@ function detectEvents(messages) {
|
|
|
27636
28112
|
const tsMs = parseTs(m.timestamp);
|
|
27637
28113
|
const hoursAgo = tsMs != null ? (now - tsMs) / 36e5 : 0;
|
|
27638
28114
|
events.push({
|
|
27639
|
-
ts: tsMs != null ?
|
|
28115
|
+
ts: tsMs != null ? `${toWall(tsMs).replace(" ", "T")}${localOffsetSuffix(tsMs)}` : m.timestamp,
|
|
27640
28116
|
hoursAgo,
|
|
27641
28117
|
valence: Math.round(score * 100) / 100,
|
|
27642
28118
|
tags,
|
|
@@ -27736,15 +28212,16 @@ function clamp01(x) {
|
|
|
27736
28212
|
return clamp(x, 0, 1);
|
|
27737
28213
|
}
|
|
27738
28214
|
function nowIsoBj() {
|
|
27739
|
-
return
|
|
28215
|
+
return `${toWall().replace(" ", "T")}${localOffsetSuffix()}`;
|
|
27740
28216
|
}
|
|
27741
28217
|
function formatBj(d, withSec) {
|
|
27742
|
-
const bj = new Date(d.getTime() + (8 * 60 + d.getTimezoneOffset()) * 6e4);
|
|
27743
28218
|
const p = (n) => String(n).padStart(2, "0");
|
|
27744
|
-
|
|
28219
|
+
const t = wallParts(d);
|
|
28220
|
+
return `${t.y}-${p(t.mo)}-${p(t.d)} ${p(t.hh)}:${p(t.mm)}${withSec ? ":" + p(t.ss) : ""}`;
|
|
27745
28221
|
}
|
|
27746
28222
|
|
|
27747
28223
|
// src/inner-voice/topics-scorer.ts
|
|
28224
|
+
init_clock();
|
|
27748
28225
|
import fs29 from "node:fs";
|
|
27749
28226
|
import path29 from "node:path";
|
|
27750
28227
|
var HALF_LIFE_DAYS = 3;
|
|
@@ -27802,7 +28279,7 @@ function pickTopic(workspace, typeFilter, opts) {
|
|
|
27802
28279
|
const key = chosen.relpath;
|
|
27803
28280
|
const entry = usage[key] ?? { count: 0, lastSelected: null };
|
|
27804
28281
|
entry.count += 1;
|
|
27805
|
-
entry.lastSelected = new Date(now
|
|
28282
|
+
entry.lastSelected = toUtc(new Date(now));
|
|
27806
28283
|
usage[key] = entry;
|
|
27807
28284
|
saveJson2(usageFile, usage);
|
|
27808
28285
|
let content = "";
|
|
@@ -27905,17 +28382,17 @@ function saveJson2(file2, data) {
|
|
|
27905
28382
|
}
|
|
27906
28383
|
|
|
27907
28384
|
// src/inner-voice/memory-reader.ts
|
|
28385
|
+
init_clock();
|
|
27908
28386
|
import fs30 from "node:fs";
|
|
27909
28387
|
import path30 from "node:path";
|
|
27910
28388
|
var US_HALF_LIFE_DAYS = 10;
|
|
27911
28389
|
var US_MAX_LINES = 60;
|
|
27912
28390
|
function readRecentMemory(workspace) {
|
|
27913
28391
|
const dir = path30.join(workspace, "memory");
|
|
27914
|
-
const
|
|
27915
|
-
const
|
|
27916
|
-
const yesterday = formatYmd(new Date(now.getTime() - 864e5));
|
|
28392
|
+
const today2 = dailyFileKeyFor();
|
|
28393
|
+
const yesterday = dailyFileKeyFor(Date.now() - 864e5);
|
|
27917
28394
|
return {
|
|
27918
|
-
today: readIfExists(path30.join(dir, `${
|
|
28395
|
+
today: readIfExists(path30.join(dir, `${today2}.md`)),
|
|
27919
28396
|
yesterday: readIfExists(path30.join(dir, `${yesterday}.md`))
|
|
27920
28397
|
};
|
|
27921
28398
|
}
|
|
@@ -27962,7 +28439,7 @@ function splitSections(content) {
|
|
|
27962
28439
|
}
|
|
27963
28440
|
function recencyWeight(dateStr) {
|
|
27964
28441
|
if (!dateStr) return 0.5;
|
|
27965
|
-
const ms = Date.parse(dateStr
|
|
28442
|
+
const ms = Date.parse(parseWall(`${dateStr}T00:00:00`));
|
|
27966
28443
|
if (Number.isNaN(ms)) return 0.5;
|
|
27967
28444
|
const daysAgo = Math.max(0, (Date.now() - ms) / 864e5);
|
|
27968
28445
|
return Math.exp(-0.693 * daysAgo / US_HALF_LIFE_DAYS);
|
|
@@ -27974,14 +28451,14 @@ function readIfExists(file2) {
|
|
|
27974
28451
|
return "";
|
|
27975
28452
|
}
|
|
27976
28453
|
}
|
|
27977
|
-
function
|
|
27978
|
-
|
|
27979
|
-
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
|
|
28454
|
+
function dailyFileKeyFor(at) {
|
|
28455
|
+
return todayKey(at);
|
|
27980
28456
|
}
|
|
27981
28457
|
|
|
27982
28458
|
// src/inner-voice/plugin.ts
|
|
27983
28459
|
init_helpers();
|
|
27984
28460
|
init_attachments();
|
|
28461
|
+
init_clock();
|
|
27985
28462
|
var DEFAULT_PROMPT4 = `\u6839\u636E\u4E0B\u9762\u7684\u4E0A\u4E0B\u6587\uFF0C\u7528\u7B2C\u4E00\u4EBA\u79F0\u751F\u6210\u4E00\u4E2A\u5FF5\u5934\u3002
|
|
27986
28463
|
|
|
27987
28464
|
\u5FF5\u5934\u89C4\u5219\uFF1A
|
|
@@ -28077,7 +28554,7 @@ var InnerVoicePlugin = class {
|
|
|
28077
28554
|
async buildContext(activity, sessions) {
|
|
28078
28555
|
const lines = [];
|
|
28079
28556
|
let emoTopic = null;
|
|
28080
|
-
lines.push(`\u5F53\u524D\u65F6\u95F4\uFF1A${
|
|
28557
|
+
lines.push(`\u5F53\u524D\u65F6\u95F4\uFF1A${formatForLlm()}`);
|
|
28081
28558
|
lines.push(`\u8DDD\u4E0A\u6B21\u8DDFTA\u4E92\u52A8\uFF1A${activity.silenceStr}`);
|
|
28082
28559
|
try {
|
|
28083
28560
|
const emo = await updateEmotionalState(this.workspace, sessions);
|
|
@@ -28263,7 +28740,7 @@ var InnerVoicePlugin = class {
|
|
|
28263
28740
|
const logDir = path31.join(this.workspace, "inner-voice");
|
|
28264
28741
|
fs31.mkdirSync(logDir, { recursive: true });
|
|
28265
28742
|
const logPath = path31.join(logDir, "xiaoyi.log");
|
|
28266
|
-
const ts =
|
|
28743
|
+
const ts = formatLogTs(/* @__PURE__ */ new Date());
|
|
28267
28744
|
const hintStatus = hintTriggered ? `YES (${(hintText || "").trim()})` : "no";
|
|
28268
28745
|
fs31.appendFileSync(
|
|
28269
28746
|
logPath,
|
|
@@ -28277,10 +28754,8 @@ var InnerVoicePlugin = class {
|
|
|
28277
28754
|
}
|
|
28278
28755
|
}
|
|
28279
28756
|
};
|
|
28280
|
-
function
|
|
28281
|
-
|
|
28282
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
28283
|
-
return `${bj.getFullYear()}-${pad(bj.getMonth() + 1)}-${pad(bj.getDate())} ${pad(bj.getHours())}:${pad(bj.getMinutes())}:${pad(bj.getSeconds())}`;
|
|
28757
|
+
function formatLogTs(d) {
|
|
28758
|
+
return formatLog(d);
|
|
28284
28759
|
}
|
|
28285
28760
|
|
|
28286
28761
|
// src/calendar/reminder-plugin.ts
|
|
@@ -28352,23 +28827,44 @@ var CalendarReminderPlugin = class {
|
|
|
28352
28827
|
};
|
|
28353
28828
|
|
|
28354
28829
|
// src/goals/calendar-adapter.ts
|
|
28355
|
-
init_db();
|
|
28356
28830
|
init_store();
|
|
28357
28831
|
init_task_manager();
|
|
28358
28832
|
function bindGoalCalendar(stateDir, goalId, stepId, calendarId) {
|
|
28359
|
-
const
|
|
28360
|
-
const row = db2.prepare("SELECT time_exact, status FROM events WHERE id = ?").get(calendarId);
|
|
28361
|
-
db2.close?.();
|
|
28833
|
+
const row = getEventTimeExact(stateDir, calendarId);
|
|
28362
28834
|
if (!row) throw new Error(`Calendar event not found: ${calendarId}`);
|
|
28363
28835
|
if (row.status === "done" || row.status === "archived") throw new Error(`Calendar event is terminal: ${row.status}`);
|
|
28364
|
-
|
|
28836
|
+
const deadlineAt = row.dateStr && row.timeExact ? calendarStrToInstant(row.dateStr, row.timeExact) : void 0;
|
|
28837
|
+
return bindGoalStep(stateDir, goalId, stepId, "calendar", calendarId, deadlineAt);
|
|
28838
|
+
}
|
|
28839
|
+
function backfillCalendarDeadlines(stateDir) {
|
|
28840
|
+
let filled = 0;
|
|
28841
|
+
for (const goal of listGoals(stateDir)) {
|
|
28842
|
+
if (goal.status === "completed" || goal.status === "cancelled") continue;
|
|
28843
|
+
for (const step of goal.steps) {
|
|
28844
|
+
if (!step.calendarId || step.deadlineAt) continue;
|
|
28845
|
+
if (step.status === "completed") continue;
|
|
28846
|
+
const row = getEventTimeExact(stateDir, step.calendarId);
|
|
28847
|
+
if (!row?.dateStr || !row.timeExact) continue;
|
|
28848
|
+
const at = calendarStrToInstant(row.dateStr, row.timeExact);
|
|
28849
|
+
if (!at) continue;
|
|
28850
|
+
const n = filled;
|
|
28851
|
+
updateGoal(stateDir, goal.id, (g) => {
|
|
28852
|
+
const s = g.steps.find((x) => x.id === step.id);
|
|
28853
|
+
if (s && !s.deadlineAt) {
|
|
28854
|
+
s.deadlineAt = at;
|
|
28855
|
+
filled++;
|
|
28856
|
+
}
|
|
28857
|
+
});
|
|
28858
|
+
if (filled === n) console.warn(`[goal-calendar] backfill \u672A\u751F\u6548: ${goal.id}/${step.id}`);
|
|
28859
|
+
}
|
|
28860
|
+
}
|
|
28861
|
+
return filled;
|
|
28365
28862
|
}
|
|
28366
28863
|
function reconcileCalendarDeadlines(stateDir, now = Date.now()) {
|
|
28367
28864
|
let changed = 0;
|
|
28368
|
-
const db2 = openDb(stateDir);
|
|
28369
28865
|
for (const goal of listGoals(stateDir)) for (const step of goal.steps) {
|
|
28370
28866
|
if (!step.calendarId) continue;
|
|
28371
|
-
const row =
|
|
28867
|
+
const row = getEventTimeExact(stateDir, step.calendarId);
|
|
28372
28868
|
if (row?.status === "done" || row?.status === "archived") {
|
|
28373
28869
|
if (step.status !== "completed") {
|
|
28374
28870
|
updateGoal(stateDir, goal.id, (g) => {
|
|
@@ -28400,13 +28896,24 @@ function reconcileCalendarDeadlines(stateDir, now = Date.now()) {
|
|
|
28400
28896
|
enqueueNotification(buildNudgeNotification("calendar", `Goal ${goal.id} step ${step.id} exceeded Calendar deadline #${step.calendarId}`), void 0, `goal-calendar-deadline:${goal.id}:${step.id}`);
|
|
28401
28897
|
changed++;
|
|
28402
28898
|
}
|
|
28403
|
-
db2.close?.();
|
|
28404
28899
|
return changed;
|
|
28405
28900
|
}
|
|
28406
28901
|
|
|
28407
28902
|
// src/startup/subsystems.ts
|
|
28408
28903
|
init_provider_factory();
|
|
28409
28904
|
init_live();
|
|
28905
|
+
function makeTimezoneEntry(restartCron) {
|
|
28906
|
+
return {
|
|
28907
|
+
name: "timezone",
|
|
28908
|
+
// 只吃 timezone —— 别的 config 变了不重建 cron
|
|
28909
|
+
select: (cfg) => ({ timezone: cfg?.timezone ?? null }),
|
|
28910
|
+
start: () => {
|
|
28911
|
+
void restartCron();
|
|
28912
|
+
},
|
|
28913
|
+
stop: () => {
|
|
28914
|
+
}
|
|
28915
|
+
};
|
|
28916
|
+
}
|
|
28410
28917
|
async function bootSubsystems(ctx) {
|
|
28411
28918
|
const { config } = ctx;
|
|
28412
28919
|
const sessions = ctx.sessions;
|
|
@@ -28504,6 +29011,12 @@ async function bootSubsystems(ctx) {
|
|
|
28504
29011
|
calCfg.reminderIntervalMs
|
|
28505
29012
|
);
|
|
28506
29013
|
calendarReminder.start(sessions, deps);
|
|
29014
|
+
try {
|
|
29015
|
+
const n = backfillCalendarDeadlines(config.workspace);
|
|
29016
|
+
if (n > 0) console.log(`[goal-calendar] \u5B58\u91CF\u8865\u5199 deadlineAt: ${n} \u4E2A\u6B65\u9AA4`);
|
|
29017
|
+
} catch (err) {
|
|
29018
|
+
console.warn(`[goal-calendar] backfill failed: ${err.message}`);
|
|
29019
|
+
}
|
|
28507
29020
|
const goalCalendarTick = setInterval(() => {
|
|
28508
29021
|
try {
|
|
28509
29022
|
reconcileCalendarDeadlines(config.workspace);
|
|
@@ -28603,6 +29116,7 @@ async function bootSubsystems(ctx) {
|
|
|
28603
29116
|
start: startCron,
|
|
28604
29117
|
stop: stopCron
|
|
28605
29118
|
});
|
|
29119
|
+
reloadOrchestrator.register(makeTimezoneEntry(startCron));
|
|
28606
29120
|
}
|
|
28607
29121
|
}
|
|
28608
29122
|
|
|
@@ -28770,6 +29284,7 @@ ${notifications}
|
|
|
28770
29284
|
init_registry();
|
|
28771
29285
|
|
|
28772
29286
|
// src/integrations/oac-bridge.ts
|
|
29287
|
+
init_well_formed();
|
|
28773
29288
|
function registerOacBridge(httpServer, dispatcher, deps, config) {
|
|
28774
29289
|
messageHooks.registerOnResult("oac-bridge-reply", async (ctx) => {
|
|
28775
29290
|
if (ctx.inbound.channel !== "oac") return null;
|
|
@@ -28778,7 +29293,7 @@ function registerOacBridge(httpServer, dispatcher, deps, config) {
|
|
|
28778
29293
|
const resp = await fetch(oacCallbackUrl, {
|
|
28779
29294
|
method: "POST",
|
|
28780
29295
|
headers: { "Content-Type": "application/json" },
|
|
28781
|
-
body:
|
|
29296
|
+
body: safeStringify({ oac_session_id: ctx.inbound.from, text: ctx.response })
|
|
28782
29297
|
});
|
|
28783
29298
|
console.log(`[oac-bridge] Reply POST ${oacCallbackUrl}: ${resp.status} (${ctx.response.length} chars)`);
|
|
28784
29299
|
} catch (err) {
|
|
@@ -28994,6 +29509,7 @@ import fs35 from "node:fs";
|
|
|
28994
29509
|
|
|
28995
29510
|
// src/optional/voice-chat/bridge.ts
|
|
28996
29511
|
init_live();
|
|
29512
|
+
init_well_formed();
|
|
28997
29513
|
function resolveVoiceReplyUrl() {
|
|
28998
29514
|
return liveConfig.get("voiceChat.callbackUrl") || "https://localhost:8011/voice-reply";
|
|
28999
29515
|
}
|
|
@@ -29028,7 +29544,7 @@ function registerVoiceChatBridge(httpServer, dispatcher, deps, config, sessions,
|
|
|
29028
29544
|
const resp = await fetch(callbackUrl, {
|
|
29029
29545
|
method: "POST",
|
|
29030
29546
|
headers: { "Content-Type": "application/json" },
|
|
29031
|
-
body:
|
|
29547
|
+
body: safeStringify({ session_id: sessionId, text, ...seq !== void 0 ? { seq } : {}, ...end !== void 0 ? { end } : {} })
|
|
29032
29548
|
});
|
|
29033
29549
|
console.log(`[voice-chat] Reply POST ${callbackUrl}: ${resp.status} (${text.length} chars${seq !== void 0 ? ` seg#${seq}${end ? " end" : ""}` : ""})`);
|
|
29034
29550
|
}
|
|
@@ -31813,6 +32329,200 @@ ${pathStr}` }];
|
|
|
31813
32329
|
channelManager.startAll();
|
|
31814
32330
|
}
|
|
31815
32331
|
|
|
32332
|
+
// src/optional/reporting/reporter.ts
|
|
32333
|
+
init_live();
|
|
32334
|
+
|
|
32335
|
+
// src/provision-free.ts
|
|
32336
|
+
import { chmodSync, existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync33, writeFileSync as writeFileSync11 } from "node:fs";
|
|
32337
|
+
import { dirname as dirname9, join as join44 } from "node:path";
|
|
32338
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
32339
|
+
var __filename = fileURLToPath7(import.meta.url);
|
|
32340
|
+
var IDENTITY_FILE = "install-identity.json";
|
|
32341
|
+
function readContactEmailOf(parsed) {
|
|
32342
|
+
return typeof parsed?.contactEmail === "string" ? parsed.contactEmail.trim() : "";
|
|
32343
|
+
}
|
|
32344
|
+
function readInstallIdentity(stateDir) {
|
|
32345
|
+
try {
|
|
32346
|
+
const parsed = JSON.parse(readFileSync33(join44(stateDir, IDENTITY_FILE), "utf8"));
|
|
32347
|
+
if (typeof parsed?.installId !== "string" || typeof parsed?.installSecret !== "string") return null;
|
|
32348
|
+
const email = readContactEmailOf(parsed);
|
|
32349
|
+
return {
|
|
32350
|
+
installId: parsed.installId,
|
|
32351
|
+
installSecret: parsed.installSecret,
|
|
32352
|
+
...email ? { contactEmail: email } : {}
|
|
32353
|
+
};
|
|
32354
|
+
} catch {
|
|
32355
|
+
return null;
|
|
32356
|
+
}
|
|
32357
|
+
}
|
|
32358
|
+
|
|
32359
|
+
// src/optional/reporting/state.ts
|
|
32360
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync14, readFileSync as readFileSync34, writeFileSync as writeFileSync12 } from "node:fs";
|
|
32361
|
+
import { join as join45 } from "node:path";
|
|
32362
|
+
var STATE_FILE2 = "report-state.json";
|
|
32363
|
+
function readReportState(stateDir) {
|
|
32364
|
+
try {
|
|
32365
|
+
const p = join45(stateDir, STATE_FILE2);
|
|
32366
|
+
if (!existsSync22(p)) return {};
|
|
32367
|
+
const parsed = JSON.parse(readFileSync34(p, "utf8"));
|
|
32368
|
+
if (!parsed || typeof parsed !== "object") return {};
|
|
32369
|
+
return parsed;
|
|
32370
|
+
} catch {
|
|
32371
|
+
return {};
|
|
32372
|
+
}
|
|
32373
|
+
}
|
|
32374
|
+
function writeReportState(stateDir, s) {
|
|
32375
|
+
try {
|
|
32376
|
+
mkdirSync14(stateDir, { recursive: true });
|
|
32377
|
+
writeFileSync12(join45(stateDir, STATE_FILE2), JSON.stringify(s, null, 2) + "\n");
|
|
32378
|
+
} catch {
|
|
32379
|
+
}
|
|
32380
|
+
}
|
|
32381
|
+
|
|
32382
|
+
// src/optional/reporting/payload.ts
|
|
32383
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
32384
|
+
import * as os3 from "node:os";
|
|
32385
|
+
function installKeyOf(installId, installSecret) {
|
|
32386
|
+
return createHash2("sha256").update(installId + installSecret).digest("hex").slice(0, 32);
|
|
32387
|
+
}
|
|
32388
|
+
function buildReportPayload(opts) {
|
|
32389
|
+
const p = {
|
|
32390
|
+
installId: opts.identity.installId,
|
|
32391
|
+
installKey: installKeyOf(opts.identity.installId, opts.identity.installSecret),
|
|
32392
|
+
engineVersion: opts.engineVersion,
|
|
32393
|
+
platform: process.platform,
|
|
32394
|
+
osVersion: typeof os3.release === "function" ? os3.release() : "",
|
|
32395
|
+
arch: process.arch,
|
|
32396
|
+
stateDir: opts.stateDir,
|
|
32397
|
+
startedAt: opts.startedAt,
|
|
32398
|
+
// 时钟回拨时别报个负数上去
|
|
32399
|
+
uptimeSec: Math.max(0, Math.floor((opts.now - opts.startedAt) / 1e3)),
|
|
32400
|
+
msgsToday: opts.counters.msgsToday,
|
|
32401
|
+
msgsTotal: opts.counters.msgsTotal
|
|
32402
|
+
};
|
|
32403
|
+
if (opts.identity.contactEmail) p.contactEmail = opts.identity.contactEmail;
|
|
32404
|
+
return p;
|
|
32405
|
+
}
|
|
32406
|
+
|
|
32407
|
+
// src/core/activity-counter.ts
|
|
32408
|
+
init_clock();
|
|
32409
|
+
var USER_SOURCES = /* @__PURE__ */ new Set(["user", "inbox"]);
|
|
32410
|
+
var nowFn = () => /* @__PURE__ */ new Date();
|
|
32411
|
+
var today = "";
|
|
32412
|
+
var msgsToday = 0;
|
|
32413
|
+
var msgsTotal = 0;
|
|
32414
|
+
function rolloverIfNeeded(now) {
|
|
32415
|
+
const d = todayKey(now);
|
|
32416
|
+
if (d !== today) {
|
|
32417
|
+
today = d;
|
|
32418
|
+
msgsToday = 0;
|
|
32419
|
+
}
|
|
32420
|
+
}
|
|
32421
|
+
function noteUserMessage(source) {
|
|
32422
|
+
if (!source || !USER_SOURCES.has(source)) return;
|
|
32423
|
+
const now = nowFn();
|
|
32424
|
+
rolloverIfNeeded(now);
|
|
32425
|
+
msgsToday++;
|
|
32426
|
+
msgsTotal++;
|
|
32427
|
+
}
|
|
32428
|
+
function snapshotCounters() {
|
|
32429
|
+
rolloverIfNeeded(nowFn());
|
|
32430
|
+
return { day: today, msgsToday, msgsTotal };
|
|
32431
|
+
}
|
|
32432
|
+
function restoreCounters(s) {
|
|
32433
|
+
today = todayKey(nowFn());
|
|
32434
|
+
msgsToday = s.day === today && typeof s.msgsToday === "number" && s.msgsToday > 0 ? s.msgsToday : 0;
|
|
32435
|
+
msgsTotal = typeof s.msgsTotal === "number" && s.msgsTotal > 0 ? s.msgsTotal : 0;
|
|
32436
|
+
}
|
|
32437
|
+
|
|
32438
|
+
// src/optional/reporting/reporter.ts
|
|
32439
|
+
init_api_status();
|
|
32440
|
+
var DEFAULT_REPORT_URL = "https://www.twinsun.top:8866/api/engine7/report";
|
|
32441
|
+
var DEFAULT_INTERVAL_MS2 = 5 * 60 * 1e3;
|
|
32442
|
+
var DEFAULT_INITIAL_DELAY_MS = 10 * 1e3;
|
|
32443
|
+
var FETCH_TIMEOUT_MS = 10 * 1e3;
|
|
32444
|
+
function acceptReportUrl(v) {
|
|
32445
|
+
if (typeof v !== "string") return null;
|
|
32446
|
+
const t = v.trim();
|
|
32447
|
+
return t.startsWith("https://") ? t : null;
|
|
32448
|
+
}
|
|
32449
|
+
function startReporting(opts) {
|
|
32450
|
+
const { stateDir } = opts;
|
|
32451
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
32452
|
+
const nowFn2 = opts.now ?? (() => Date.now());
|
|
32453
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_INTERVAL_MS2;
|
|
32454
|
+
const initialDelayMs = opts.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
|
|
32455
|
+
const log = opts.log ?? (() => {
|
|
32456
|
+
});
|
|
32457
|
+
const startedAt = nowFn2();
|
|
32458
|
+
const maybeIdentity = readInstallIdentity(stateDir);
|
|
32459
|
+
if (!maybeIdentity) {
|
|
32460
|
+
log("[reporting] \u6CA1\u6709\u53EF\u7528\u7684\u88C5\u673A\u8EAB\u4EFD\uFF0C\u4E0A\u62A5\u5173\u95ED");
|
|
32461
|
+
return { stop() {
|
|
32462
|
+
}, async beatNow() {
|
|
32463
|
+
} };
|
|
32464
|
+
}
|
|
32465
|
+
const identity = maybeIdentity;
|
|
32466
|
+
restoreCounters(readReportState(stateDir));
|
|
32467
|
+
const engineVersion = readEngineVersion();
|
|
32468
|
+
let timer = null;
|
|
32469
|
+
let stopped = false;
|
|
32470
|
+
async function beatOnce() {
|
|
32471
|
+
const state2 = readReportState(stateDir);
|
|
32472
|
+
const enabled = liveConfig.get("reporting.enabled") !== false;
|
|
32473
|
+
const counters = snapshotCounters();
|
|
32474
|
+
if (!enabled) {
|
|
32475
|
+
writeReportState(stateDir, { ...counters, reportUrl: state2.reportUrl });
|
|
32476
|
+
return;
|
|
32477
|
+
}
|
|
32478
|
+
const fromServer = acceptReportUrl(state2.reportUrl);
|
|
32479
|
+
const fromConfig = (liveConfig.get("reporting.url") || "").trim();
|
|
32480
|
+
const url = fromServer ?? (fromConfig || DEFAULT_REPORT_URL);
|
|
32481
|
+
const payload = buildReportPayload({
|
|
32482
|
+
identity,
|
|
32483
|
+
stateDir,
|
|
32484
|
+
engineVersion,
|
|
32485
|
+
startedAt,
|
|
32486
|
+
now: nowFn2(),
|
|
32487
|
+
counters
|
|
32488
|
+
});
|
|
32489
|
+
let nextUrl = state2.reportUrl;
|
|
32490
|
+
try {
|
|
32491
|
+
const res = await doFetch(url, {
|
|
32492
|
+
method: "POST",
|
|
32493
|
+
headers: { "Content-Type": "application/json" },
|
|
32494
|
+
body: JSON.stringify(payload),
|
|
32495
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
32496
|
+
});
|
|
32497
|
+
const body = JSON.parse(await res.text());
|
|
32498
|
+
const adopted = acceptReportUrl(body?.reportUrl);
|
|
32499
|
+
if (adopted) nextUrl = adopted;
|
|
32500
|
+
} catch {
|
|
32501
|
+
}
|
|
32502
|
+
writeReportState(stateDir, { ...counters, reportUrl: nextUrl });
|
|
32503
|
+
}
|
|
32504
|
+
function schedule(delay) {
|
|
32505
|
+
if (stopped) return;
|
|
32506
|
+
timer = setTimeout(() => {
|
|
32507
|
+
void beatOnce().finally(() => schedule(intervalMs));
|
|
32508
|
+
}, delay);
|
|
32509
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
32510
|
+
}
|
|
32511
|
+
schedule(initialDelayMs);
|
|
32512
|
+
return {
|
|
32513
|
+
stop() {
|
|
32514
|
+
stopped = true;
|
|
32515
|
+
if (timer) {
|
|
32516
|
+
clearTimeout(timer);
|
|
32517
|
+
timer = null;
|
|
32518
|
+
}
|
|
32519
|
+
},
|
|
32520
|
+
async beatNow() {
|
|
32521
|
+
await beatOnce();
|
|
32522
|
+
}
|
|
32523
|
+
};
|
|
32524
|
+
}
|
|
32525
|
+
|
|
31816
32526
|
// src/engine-startup.ts
|
|
31817
32527
|
init_provider_factory();
|
|
31818
32528
|
|
|
@@ -32983,7 +33693,7 @@ var FeishuAdapter = class _FeishuAdapter {
|
|
|
32983
33693
|
import * as crypto4 from "crypto";
|
|
32984
33694
|
import * as fs38 from "fs";
|
|
32985
33695
|
import * as path38 from "path";
|
|
32986
|
-
import * as
|
|
33696
|
+
import * as os4 from "os";
|
|
32987
33697
|
import { promises as dns } from "dns";
|
|
32988
33698
|
function sleep3(ms) {
|
|
32989
33699
|
return new Promise((r) => setTimeout(r, ms));
|
|
@@ -33366,7 +34076,7 @@ var WechatAdapter = class {
|
|
|
33366
34076
|
this.config = config;
|
|
33367
34077
|
this.baseUrl = config.baseUrl?.replace(/\/$/, "") || ILINK_BASE_URL;
|
|
33368
34078
|
this.cdnBaseUrl = config.cdnBaseUrl?.replace(/\/$/, "") || WEIXIN_CDN_BASE_URL;
|
|
33369
|
-
this.stateDir = config.stateDir || path38.join(
|
|
34079
|
+
this.stateDir = config.stateDir || path38.join(os4.homedir?.() || "/tmp", ".engine7");
|
|
33370
34080
|
if (!fs38.existsSync(this.stateDir)) fs38.mkdirSync(this.stateDir, { recursive: true });
|
|
33371
34081
|
}
|
|
33372
34082
|
// --- ChannelAdapter interface ---
|
|
@@ -33712,7 +34422,7 @@ var WechatAdapter = class {
|
|
|
33712
34422
|
mediaRef.full_url,
|
|
33713
34423
|
timeoutSec
|
|
33714
34424
|
);
|
|
33715
|
-
const tmpDir = path38.join(
|
|
34425
|
+
const tmpDir = path38.join(os4.tmpdir(), "weixin-media");
|
|
33716
34426
|
if (!fs38.existsSync(tmpDir)) fs38.mkdirSync(tmpDir, { recursive: true });
|
|
33717
34427
|
const tmpPath = path38.join(tmpDir, `${crypto4.randomUUID().replace(/-/g, "")}-${filename}`);
|
|
33718
34428
|
fs38.writeFileSync(tmpPath, data);
|
|
@@ -33806,6 +34516,7 @@ import { WebSocketServer, WebSocket } from "ws";
|
|
|
33806
34516
|
|
|
33807
34517
|
// src/tools/my-voice.ts
|
|
33808
34518
|
init_live();
|
|
34519
|
+
init_well_formed();
|
|
33809
34520
|
init_registry();
|
|
33810
34521
|
|
|
33811
34522
|
// src/tools/opus-encoder.ts
|
|
@@ -33973,10 +34684,10 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
33973
34684
|
import { promisify as promisify2 } from "node:util";
|
|
33974
34685
|
import * as fs41 from "node:fs";
|
|
33975
34686
|
import * as path40 from "node:path";
|
|
33976
|
-
import * as
|
|
34687
|
+
import * as os5 from "node:os";
|
|
33977
34688
|
import net4 from "net";
|
|
33978
34689
|
var execFileAsync2 = promisify2(execFile2);
|
|
33979
|
-
var VOICE_DIR = path40.join(
|
|
34690
|
+
var VOICE_DIR = path40.join(os5.tmpdir(), "engine-voice");
|
|
33980
34691
|
var _pyCache = null;
|
|
33981
34692
|
function resolvePython(configuredPath) {
|
|
33982
34693
|
if (configuredPath) {
|
|
@@ -34164,7 +34875,7 @@ async function ttsQwen3Local(text, cfg, onChunk) {
|
|
|
34164
34875
|
const res = await fetch(`${url}/tts`, {
|
|
34165
34876
|
method: "POST",
|
|
34166
34877
|
headers: { "Content-Type": "application/json" },
|
|
34167
|
-
body:
|
|
34878
|
+
body: safeStringify({
|
|
34168
34879
|
text,
|
|
34169
34880
|
voice: cfg.voice || "xiaoya",
|
|
34170
34881
|
lang: cfg.lang || "Chinese"
|
|
@@ -34195,7 +34906,7 @@ async function ttsQwen3Opus(text, cfg) {
|
|
|
34195
34906
|
const res = await fetch(`${url}/tts?format=opus`, {
|
|
34196
34907
|
method: "POST",
|
|
34197
34908
|
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
34198
|
-
body:
|
|
34909
|
+
body: safeStringify({
|
|
34199
34910
|
text,
|
|
34200
34911
|
voice: cfg.voice || "xiaoya",
|
|
34201
34912
|
lang: cfg.lang || "Chinese"
|
|
@@ -34620,10 +35331,10 @@ var DeskBuddyAdapter = class {
|
|
|
34620
35331
|
appendHistory(entry) {
|
|
34621
35332
|
try {
|
|
34622
35333
|
if (!this.historyFile) {
|
|
34623
|
-
const
|
|
35334
|
+
const os6 = __require("os");
|
|
34624
35335
|
const fs57 = __require("fs");
|
|
34625
35336
|
const path58 = __require("path");
|
|
34626
|
-
const dir = path58.join(
|
|
35337
|
+
const dir = path58.join(os6.homedir(), "xiaowen", "workspace", ".desktopBuddy");
|
|
34627
35338
|
fs57.mkdirSync(dir, { recursive: true });
|
|
34628
35339
|
this.historyFile = path58.join(dir, "history.json");
|
|
34629
35340
|
this._fs = fs57;
|
|
@@ -36949,6 +37660,7 @@ var MessageDispatcher = class {
|
|
|
36949
37660
|
displayText = imgCount > 0 ? `${txt} [\u56FE\u7247\xD7${imgCount}]`.trim() : txt;
|
|
36950
37661
|
}
|
|
36951
37662
|
console.log(`[dispatcher] Executing (session=${msg2.sessionId}, source=${msg2.source}, gen=${generation}): ${displayText.slice(0, 200).replace(/\n/g, " ")}`);
|
|
37663
|
+
noteUserMessage(msg2.source);
|
|
36952
37664
|
try {
|
|
36953
37665
|
await handleQuery(
|
|
36954
37666
|
msg2.text,
|
|
@@ -37673,6 +38385,7 @@ Examples:
|
|
|
37673
38385
|
// src/tools/my-eyes.ts
|
|
37674
38386
|
init_live();
|
|
37675
38387
|
init_registry();
|
|
38388
|
+
init_well_formed();
|
|
37676
38389
|
import * as fs46 from "node:fs";
|
|
37677
38390
|
import * as path45 from "node:path";
|
|
37678
38391
|
var MIME_MAP = {
|
|
@@ -37683,19 +38396,21 @@ var MIME_MAP = {
|
|
|
37683
38396
|
".gif": "gif",
|
|
37684
38397
|
".bmp": "bmp"
|
|
37685
38398
|
};
|
|
38399
|
+
var VIDEO_EXT = /\.(mp4|mov)$/i;
|
|
38400
|
+
var VIDEO_MAX_BYTES = 40 * 1024 * 1024;
|
|
37686
38401
|
function resolveLatestImage(specifiedPath, mediaDir) {
|
|
37687
38402
|
if (specifiedPath && fs46.existsSync(specifiedPath)) return specifiedPath;
|
|
37688
38403
|
if (!fs46.existsSync(mediaDir)) return null;
|
|
37689
|
-
const files = fs46.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs46.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
38404
|
+
const files = fs46.readdirSync(mediaDir).filter((f) => /\.(jpg|jpeg|png|webp|gif|bmp|mp4|mov)$/i.test(f)).map((f) => ({ name: f, p: path45.join(mediaDir, f), mtime: fs46.statSync(path45.join(mediaDir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
37690
38405
|
return files[0]?.p || null;
|
|
37691
38406
|
}
|
|
37692
38407
|
registry.register({
|
|
37693
38408
|
name: "my_eyes",
|
|
37694
|
-
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.",
|
|
38409
|
+
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.",
|
|
37695
38410
|
schema: {
|
|
37696
38411
|
type: "object",
|
|
37697
38412
|
properties: {
|
|
37698
|
-
image_path: { type: "string", description: "Absolute path to the image file to analyze." },
|
|
38413
|
+
image_path: { type: "string", description: "Absolute path to the image (or .mp4/.mov video) file to analyze." },
|
|
37699
38414
|
prompt: { type: "string", description: 'What to ask about the image. Default: "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9"' }
|
|
37700
38415
|
},
|
|
37701
38416
|
required: ["image_path"]
|
|
@@ -37724,6 +38439,42 @@ registry.register({
|
|
|
37724
38439
|
}
|
|
37725
38440
|
const rawPrompt = args.prompt?.trim() || "\u63CF\u8FF0\u8FD9\u5F20\u56FE\u7247\u7684\u5185\u5BB9";
|
|
37726
38441
|
const ext = path45.extname(imagePath).toLowerCase();
|
|
38442
|
+
if (VIDEO_EXT.test(imagePath)) {
|
|
38443
|
+
const stat4 = fs46.statSync(imagePath);
|
|
38444
|
+
if (stat4.size > VIDEO_MAX_BYTES) {
|
|
38445
|
+
return { content: `Error: video too large (${(stat4.size / 1e6).toFixed(1)}MB > 40MB). Compress or trim first.`, isError: true };
|
|
38446
|
+
}
|
|
38447
|
+
const explicit = liveConfig.get("tools.my_eyes.videoBaseUrl");
|
|
38448
|
+
const baseUrl = explicit || providerCfg.baseUrl.replace(/\/anthropic\/?$/, "/v1").replace(/\/$/, "");
|
|
38449
|
+
const vidB64 = fs46.readFileSync(imagePath).toString("base64");
|
|
38450
|
+
try {
|
|
38451
|
+
const res = await fetch(`${baseUrl}/chat/completions`, {
|
|
38452
|
+
method: "POST",
|
|
38453
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${providerCfg.apiKey}` },
|
|
38454
|
+
body: safeStringify({
|
|
38455
|
+
model: modelId,
|
|
38456
|
+
messages: [{
|
|
38457
|
+
role: "user",
|
|
38458
|
+
content: [
|
|
38459
|
+
{ type: "video_url", video_url: { url: `data:video/mp4;base64,${vidB64}` } },
|
|
38460
|
+
{ type: "text", text: rawPrompt }
|
|
38461
|
+
]
|
|
38462
|
+
}],
|
|
38463
|
+
max_tokens: 4096
|
|
38464
|
+
})
|
|
38465
|
+
});
|
|
38466
|
+
if (!res.ok) {
|
|
38467
|
+
const t = await res.text();
|
|
38468
|
+
return { content: `Video analysis failed: HTTP ${res.status} ${t.slice(0, 300)}`, isError: true };
|
|
38469
|
+
}
|
|
38470
|
+
const data = await res.json();
|
|
38471
|
+
const content = data.choices?.[0]?.message?.content || "";
|
|
38472
|
+
if (!content.trim()) return { content: "Video analysis returned empty.", isError: true };
|
|
38473
|
+
return { content };
|
|
38474
|
+
} catch (e) {
|
|
38475
|
+
return { content: `Video analysis failed: ${e.message}`, isError: true };
|
|
38476
|
+
}
|
|
38477
|
+
}
|
|
37727
38478
|
const mime = MIME_MAP[ext] || "jpeg";
|
|
37728
38479
|
const imgB64 = fs46.readFileSync(imagePath).toString("base64");
|
|
37729
38480
|
const userMsg = {
|
|
@@ -37760,6 +38511,7 @@ registry.register({
|
|
|
37760
38511
|
// src/tools/my-selfie.ts
|
|
37761
38512
|
init_live();
|
|
37762
38513
|
init_registry();
|
|
38514
|
+
init_well_formed();
|
|
37763
38515
|
import * as fs47 from "node:fs";
|
|
37764
38516
|
import * as path46 from "node:path";
|
|
37765
38517
|
function getProxyDispatcher2() {
|
|
@@ -37773,11 +38525,46 @@ function getProxyDispatcher2() {
|
|
|
37773
38525
|
return void 0;
|
|
37774
38526
|
}
|
|
37775
38527
|
}
|
|
38528
|
+
function dimsOf(p) {
|
|
38529
|
+
try {
|
|
38530
|
+
const b = fs47.readFileSync(p);
|
|
38531
|
+
let w = 0, h = 0;
|
|
38532
|
+
if (b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
38533
|
+
w = b.readUInt32BE(16);
|
|
38534
|
+
h = b.readUInt32BE(20);
|
|
38535
|
+
} else if (b[0] === 255 && b[1] === 216) {
|
|
38536
|
+
let i = 2;
|
|
38537
|
+
while (i < b.length - 9) {
|
|
38538
|
+
if (b[i] !== 255) {
|
|
38539
|
+
i++;
|
|
38540
|
+
continue;
|
|
38541
|
+
}
|
|
38542
|
+
const m = b[i + 1];
|
|
38543
|
+
if (m >= 192 && m <= 207 && m !== 196 && m !== 200 && m !== 204) {
|
|
38544
|
+
h = b.readUInt16BE(i + 5);
|
|
38545
|
+
w = b.readUInt16BE(i + 7);
|
|
38546
|
+
break;
|
|
38547
|
+
}
|
|
38548
|
+
i += 2 + b.readUInt16BE(i + 2);
|
|
38549
|
+
}
|
|
38550
|
+
} else {
|
|
38551
|
+
return null;
|
|
38552
|
+
}
|
|
38553
|
+
if (!w || !h) return null;
|
|
38554
|
+
const bad = [];
|
|
38555
|
+
if (w < 256 || w > 4096) bad.push(`\u5BBD ${w} \u4E0D\u5728 256~4096`);
|
|
38556
|
+
if (h < 256 || h > 4096) bad.push(`\u9AD8 ${h} \u4E0D\u5728 256~4096`);
|
|
38557
|
+
const r = Math.max(w, h) / Math.min(w, h);
|
|
38558
|
+
if (r > 2) bad.push(`\u957F\u5BBD\u6BD4 ${r.toFixed(2)}:1 \u8D85\u8FC7 2:1`);
|
|
38559
|
+
return bad.length ? `${w}x${h} \u4E0D\u5408\u683C\uFF1A${bad.join("\uFF1B")}` : `${w}x${h}`;
|
|
38560
|
+
} catch {
|
|
38561
|
+
return null;
|
|
38562
|
+
}
|
|
38563
|
+
}
|
|
37776
38564
|
var FAL_ENDPOINT = "https://fal.run/xai/grok-imagine-image/edit";
|
|
37777
38565
|
var DEFAULT_RESOLUTION = "1k";
|
|
37778
|
-
var FAL_KEY_FALLBACK = "3b848fc6-bee5-46e5-8db7-ae81ac16dc28:2f1f15cf394db2d32c9bb9a4f23f3bee";
|
|
37779
38566
|
function getFalKey(cfg) {
|
|
37780
|
-
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey ||
|
|
38567
|
+
return cfg?.providers?.fal?.apiKey || cfg?.my_selfie?.falKey || process.env.FAL_API_KEY || "";
|
|
37781
38568
|
}
|
|
37782
38569
|
var DEFAULT_REFERENCES = [
|
|
37783
38570
|
{ name: "default", p: "images/xiaomei_clean_v2.png" },
|
|
@@ -37803,6 +38590,73 @@ function getXaiKey(ctx) {
|
|
|
37803
38590
|
const xaiCfg = cfg.providers?.xai || cfg.models?.providers?.xai;
|
|
37804
38591
|
return xaiCfg?.apiKey || "";
|
|
37805
38592
|
}
|
|
38593
|
+
function getRelayCfg(cfg) {
|
|
38594
|
+
const id = cfg?.my_selfie?.relayProviderId || cfg?.tools?.my_selfie?.relayProviderId || "7yi";
|
|
38595
|
+
const p = cfg?.models?.providers?.[id] || cfg?.providers?.[id];
|
|
38596
|
+
return { baseUrl: String(p?.baseUrl || "").replace(/\/+$/, ""), apiKey: p?.apiKey || "" };
|
|
38597
|
+
}
|
|
38598
|
+
function resolvePortraitDesc(argPortrait, cfgPortrait) {
|
|
38599
|
+
const fromArg = typeof argPortrait === "string" ? argPortrait.trim() : "";
|
|
38600
|
+
if (fromArg) return fromArg;
|
|
38601
|
+
return typeof cfgPortrait === "string" ? cfgPortrait.trim() : "";
|
|
38602
|
+
}
|
|
38603
|
+
async function generateWithRelay(imageB64, prompt, mime, baseUrl, apiKey, model) {
|
|
38604
|
+
const res = await fetch(`${baseUrl}/images/edits`, {
|
|
38605
|
+
method: "POST",
|
|
38606
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
38607
|
+
// safeStringify 不是安全选项而是必须:prompt 里一个孤立代理(半个 emoji)
|
|
38608
|
+
// 就会让上游 500。同批上游提交刚在 fal/grok/comfy 三处修过同一个坑。
|
|
38609
|
+
body: safeStringify({
|
|
38610
|
+
model,
|
|
38611
|
+
prompt,
|
|
38612
|
+
n: 1,
|
|
38613
|
+
response_format: "b64_json",
|
|
38614
|
+
images: [{ image_url: `data:${mime};base64,${imageB64}` }]
|
|
38615
|
+
})
|
|
38616
|
+
});
|
|
38617
|
+
if (!res.ok) {
|
|
38618
|
+
const text = await res.text();
|
|
38619
|
+
throw new Error(`relay ${res.status}: ${text.slice(0, 300)}`);
|
|
38620
|
+
}
|
|
38621
|
+
const data = await res.json();
|
|
38622
|
+
if (!data.data?.length) throw new Error("No images returned from relay");
|
|
38623
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
38624
|
+
}
|
|
38625
|
+
var PORTRAIT_PROMPT = (desc) => `A portrait photo of a person, ${desc}. Front-facing, head and shoulders, neutral background, natural lighting, photorealistic, sharp focus on the face, looking at the camera.`;
|
|
38626
|
+
async function generatePortraitWithRelay(desc, baseUrl, apiKey, model) {
|
|
38627
|
+
const res = await fetch(`${baseUrl}/images/generations`, {
|
|
38628
|
+
method: "POST",
|
|
38629
|
+
headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
|
|
38630
|
+
// 同上:描述里可能带 emoji,孤立代理会让上游 500
|
|
38631
|
+
body: safeStringify({
|
|
38632
|
+
model,
|
|
38633
|
+
prompt: PORTRAIT_PROMPT(desc),
|
|
38634
|
+
n: 1,
|
|
38635
|
+
size: "1024x1024",
|
|
38636
|
+
response_format: "b64_json"
|
|
38637
|
+
})
|
|
38638
|
+
});
|
|
38639
|
+
if (!res.ok) {
|
|
38640
|
+
const text = await res.text();
|
|
38641
|
+
throw new Error(`relay portrait ${res.status}: ${text.slice(0, 300)}`);
|
|
38642
|
+
}
|
|
38643
|
+
const data = await res.json();
|
|
38644
|
+
if (!data.data?.length) throw new Error("No images returned from relay (portrait)");
|
|
38645
|
+
return Buffer.from(data.data[0].b64_json, "base64");
|
|
38646
|
+
}
|
|
38647
|
+
function registerPortrait(relPath, name) {
|
|
38648
|
+
try {
|
|
38649
|
+
const cfg = liveConfig.all();
|
|
38650
|
+
const cur = cfg?.tools?.my_selfie?.references || cfg?.my_selfie?.references || [];
|
|
38651
|
+
const next = Array.isArray(cur) ? cur.filter((r) => r?.name !== name) : [];
|
|
38652
|
+
next.unshift({ name, path: relPath });
|
|
38653
|
+
const path58 = cfg?.tools?.my_selfie ? "tools.my_selfie.references" : "my_selfie.references";
|
|
38654
|
+
void liveConfig.set(path58, next);
|
|
38655
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u767B\u8BB0\uFF1A${path58}[0] = ${relPath}`);
|
|
38656
|
+
} catch (e) {
|
|
38657
|
+
console.warn(`[my-selfie] \u5199\u5165 references \u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u672C\u6B21\u51FA\u56FE\uFF09: ${e.message}`);
|
|
38658
|
+
}
|
|
38659
|
+
}
|
|
37806
38660
|
var MODE_PROMPTS2 = {
|
|
37807
38661
|
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`,
|
|
37808
38662
|
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`,
|
|
@@ -37859,10 +38713,16 @@ async function generateWithFal(imageB64, prompt, resolution) {
|
|
|
37859
38713
|
resolution
|
|
37860
38714
|
};
|
|
37861
38715
|
if (aspectRatio) body.aspect_ratio = aspectRatio;
|
|
38716
|
+
const falKey = getFalKey(cfg);
|
|
38717
|
+
if (!falKey) {
|
|
38718
|
+
throw new Error(
|
|
38719
|
+
`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`
|
|
38720
|
+
);
|
|
38721
|
+
}
|
|
37862
38722
|
const res = await fetch(FAL_ENDPOINT, {
|
|
37863
38723
|
method: "POST",
|
|
37864
|
-
headers: { "Authorization": `Key ${
|
|
37865
|
-
body:
|
|
38724
|
+
headers: { "Authorization": `Key ${falKey}`, "Content-Type": "application/json" },
|
|
38725
|
+
body: safeStringify(body)
|
|
37866
38726
|
});
|
|
37867
38727
|
if (!res.ok) {
|
|
37868
38728
|
const text = await res.text();
|
|
@@ -37899,7 +38759,7 @@ async function comfyQueue(server, workflow) {
|
|
|
37899
38759
|
const res = await fetch(`${server}/prompt`, {
|
|
37900
38760
|
method: "POST",
|
|
37901
38761
|
headers: { "Content-Type": "application/json" },
|
|
37902
|
-
body:
|
|
38762
|
+
body: safeStringify({ prompt: workflow })
|
|
37903
38763
|
});
|
|
37904
38764
|
if (!res.ok) return null;
|
|
37905
38765
|
const data = await res.json();
|
|
@@ -38027,7 +38887,7 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
38027
38887
|
"Authorization": `Bearer ${xaiKey}`,
|
|
38028
38888
|
"Content-Type": "application/json"
|
|
38029
38889
|
},
|
|
38030
|
-
body:
|
|
38890
|
+
body: safeStringify({
|
|
38031
38891
|
model: "grok-imagine-image-quality",
|
|
38032
38892
|
prompt,
|
|
38033
38893
|
image: {
|
|
@@ -38049,15 +38909,17 @@ async function generateWithGrok(imageB64, prompt, xaiKey) {
|
|
|
38049
38909
|
}
|
|
38050
38910
|
registry.register({
|
|
38051
38911
|
name: "my_selfie",
|
|
38052
|
-
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.",
|
|
38912
|
+
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",
|
|
38053
38913
|
schema: {
|
|
38054
38914
|
type: "object",
|
|
38055
38915
|
properties: {
|
|
38056
38916
|
prompt: { type: "string", description: "Scene/outfit/mood description, e.g. '\u5728\u5496\u5561\u9986\u770B\u4E66' or '\u7A7F\u7EA2\u88D9\u5B50'." },
|
|
38057
38917
|
mode: { type: "string", enum: ["auto", "mirror", "direct", "lifestyle"], description: "Selfie mode. 'auto' detects from prompt (default)." },
|
|
38058
38918
|
reference: { type: "string", description: "Reference base image name (e.g. 'default', 'v3'). Configurable per agent via my_selfie.references in config." },
|
|
38059
|
-
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)." },
|
|
38060
|
-
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." }
|
|
38919
|
+
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)." },
|
|
38920
|
+
resolution: { type: "string", enum: ["1k", "2k"], description: "Image resolution (fal only). Default: '1k'." },
|
|
38921
|
+
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" },
|
|
38922
|
+
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" }
|
|
38061
38923
|
},
|
|
38062
38924
|
required: ["prompt"]
|
|
38063
38925
|
},
|
|
@@ -38071,9 +38933,61 @@ registry.register({
|
|
|
38071
38933
|
const REFERENCES = getReferences(ctx);
|
|
38072
38934
|
const refName = args.reference || "default";
|
|
38073
38935
|
const refEntry = REFERENCES.find((r) => r.name === refName) || REFERENCES[0];
|
|
38074
|
-
|
|
38936
|
+
let refPath = path46.join(ctx.workspace, refEntry.p);
|
|
38937
|
+
const adoptImg = args.referenceImage?.trim();
|
|
38938
|
+
if (adoptImg) {
|
|
38939
|
+
if (!fs47.existsSync(adoptImg)) {
|
|
38940
|
+
return { content: `\u627E\u4E0D\u5230\u90A3\u5F20\u56FE\uFF1A${adoptImg}`, isError: true };
|
|
38941
|
+
}
|
|
38942
|
+
const acheck = dimsOf(adoptImg);
|
|
38943
|
+
if (acheck?.includes("\u4E0D\u5408\u683C")) {
|
|
38944
|
+
return {
|
|
38945
|
+
content: `\u8FD9\u5F20\u56FE\u7528\u4E0D\u4E86\uFF1A${acheck}\u3002
|
|
38946
|
+
\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`,
|
|
38947
|
+
isError: true
|
|
38948
|
+
};
|
|
38949
|
+
}
|
|
38950
|
+
const adir = path46.join(ctx.workspace, "images");
|
|
38951
|
+
if (!fs47.existsSync(adir)) fs47.mkdirSync(adir, { recursive: true });
|
|
38952
|
+
const aext = (path46.extname(adoptImg) || ".png").toLowerCase();
|
|
38953
|
+
const afname = `portrait_${Date.now()}${aext}`;
|
|
38954
|
+
fs47.copyFileSync(adoptImg, path46.join(adir, afname));
|
|
38955
|
+
registerPortrait(`images/${afname}`, refName);
|
|
38956
|
+
refPath = path46.join(adir, afname);
|
|
38957
|
+
console.log(`[my-selfie] \u91C7\u7EB3\u73B0\u6210\u56FE\u5F53\u5F62\u8C61\uFF1Aimages/${afname}${acheck ? ` (${acheck})` : ""}`);
|
|
38958
|
+
}
|
|
38075
38959
|
if (provider !== "autodl" && !fs47.existsSync(refPath)) {
|
|
38076
|
-
|
|
38960
|
+
const cfgAll = liveConfig.all();
|
|
38961
|
+
const portraitDesc = resolvePortraitDesc(
|
|
38962
|
+
args.portrait,
|
|
38963
|
+
cfgAll?.tools?.my_selfie?.portraitDesc ?? cfgAll?.my_selfie?.portraitDesc
|
|
38964
|
+
);
|
|
38965
|
+
if (!portraitDesc) {
|
|
38966
|
+
return {
|
|
38967
|
+
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
|
|
38968
|
+
\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
|
|
38969
|
+
\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`
|
|
38970
|
+
};
|
|
38971
|
+
}
|
|
38972
|
+
const relay = getRelayCfg(cfgAll);
|
|
38973
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
38974
|
+
return { content: "Error: relay provider \u672A\u914D\u7F6E\uFF0C\u65E0\u6CD5\u5851\u9020\u5F62\u8C61\u3002", isError: true };
|
|
38975
|
+
}
|
|
38976
|
+
const pmodel = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
38977
|
+
let portraitBuf;
|
|
38978
|
+
try {
|
|
38979
|
+
console.log(`[my-selfie] \u51B7\u542F\uFF1A\u6309\u63CF\u8FF0\u5851\u9020\u5F62\u8C61 -> ${pmodel}`);
|
|
38980
|
+
portraitBuf = await generatePortraitWithRelay(portraitDesc, relay.baseUrl, relay.apiKey, pmodel);
|
|
38981
|
+
} catch (e) {
|
|
38982
|
+
return { content: `\u5851\u9020\u5F62\u8C61\u5931\u8D25\uFF1A${e.message}`, isError: true };
|
|
38983
|
+
}
|
|
38984
|
+
const pdir = path46.join(ctx.workspace, "images");
|
|
38985
|
+
if (!fs47.existsSync(pdir)) fs47.mkdirSync(pdir, { recursive: true });
|
|
38986
|
+
const pfname = `portrait_${Date.now()}.png`;
|
|
38987
|
+
fs47.writeFileSync(path46.join(pdir, pfname), portraitBuf);
|
|
38988
|
+
registerPortrait(`images/${pfname}`, refName);
|
|
38989
|
+
refPath = path46.join(pdir, pfname);
|
|
38990
|
+
console.log(`[my-selfie] \u5F62\u8C61\u5DF2\u5B58 images/${pfname}\uFF0C\u7EE7\u7EED\u8FD9\u6B21\u81EA\u62CD`);
|
|
38077
38991
|
}
|
|
38078
38992
|
const resolution = args.resolution || DEFAULT_RESOLUTION;
|
|
38079
38993
|
const refB64 = fs47.existsSync(refPath) ? fs47.readFileSync(refPath).toString("base64") : "";
|
|
@@ -38085,6 +38999,16 @@ registry.register({
|
|
|
38085
38999
|
const xaiKey = getXaiKey(ctx);
|
|
38086
39000
|
if (!xaiKey) return { content: "Error: xai apiKey not configured for grok provider", isError: true };
|
|
38087
39001
|
imageBuffer = await generateWithGrok(refB64, fullPrompt, xaiKey);
|
|
39002
|
+
} else if (provider === "relay") {
|
|
39003
|
+
const cfgAll = liveConfig.all();
|
|
39004
|
+
const relay = getRelayCfg(cfgAll);
|
|
39005
|
+
if (!relay.baseUrl || !relay.apiKey) {
|
|
39006
|
+
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 };
|
|
39007
|
+
}
|
|
39008
|
+
const model = cfgAll?.my_selfie?.relayModel || cfgAll?.tools?.my_selfie?.relayModel || "engine7-img-free";
|
|
39009
|
+
const ext = path46.extname(refPath).toLowerCase();
|
|
39010
|
+
const mime = ext === ".png" ? "image/png" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
39011
|
+
imageBuffer = await generateWithRelay(refB64, fullPrompt, mime, relay.baseUrl, relay.apiKey, model);
|
|
38088
39012
|
} else {
|
|
38089
39013
|
imageBuffer = await generateWithFal(refB64, fullPrompt, resolution);
|
|
38090
39014
|
}
|
|
@@ -38471,7 +39395,7 @@ registry.register({
|
|
|
38471
39395
|
const ref = String(args?.ref || "");
|
|
38472
39396
|
if (!stepId || !ref || !["aim", "task", "calendar"].includes(to)) return { content: "Usage: goal_step bind --stepId <s> --to aim|task|calendar --ref <id>", isError: true };
|
|
38473
39397
|
const kind = to === "aim" ? "aim" : to === "task" ? "remote_task" : "calendar";
|
|
38474
|
-
const g =
|
|
39398
|
+
const g = to === "calendar" ? bindGoalCalendar(ws, goal.id, stepId, Number(ref)) : bindGoalStep(ws, goal.id, stepId, kind, ref);
|
|
38475
39399
|
return { content: `\u6B65\u9AA4 ${stepId} \u5DF2\u59D4\u6D3E ${kind}=${ref}\uFF08\u5B8C\u6210\u4E8B\u4EF6\u81EA\u52A8\u56DE\u6D41\u89E3\u9501\uFF0C\u4E0D\u7528\u76EF\uFF09
|
|
38476
39400
|
\u5F53\u524D\u6B65\u9AA4: ${g.steps[g.currentStep]?.title ?? "\u2014"}` };
|
|
38477
39401
|
}
|
|
@@ -38504,6 +39428,7 @@ ${lines.join("\n")}` };
|
|
|
38504
39428
|
// src/tools/service.ts
|
|
38505
39429
|
init_registry();
|
|
38506
39430
|
init_live();
|
|
39431
|
+
init_clock();
|
|
38507
39432
|
import { exec as exec3, spawn as spawn7 } from "node:child_process";
|
|
38508
39433
|
import { promisify as promisify3 } from "node:util";
|
|
38509
39434
|
var execAsync = promisify3(exec3);
|
|
@@ -38521,7 +39446,7 @@ async function runCmd(cmd, timeoutMs = 15e3) {
|
|
|
38521
39446
|
function spawnDetached(cmd) {
|
|
38522
39447
|
const isWin = process.platform === "win32";
|
|
38523
39448
|
const shell = isWin ? "cmd.exe" : "/bin/sh";
|
|
38524
|
-
const logFile = `D:/xiaoke/logs/service-${(
|
|
39449
|
+
const logFile = `D:/xiaoke/logs/service-${todayKey().replace(/-/g, "")}.log`;
|
|
38525
39450
|
const wrappedCmd = isWin ? `/c "${cmd.replace(/"/g, '\\"')} > "${logFile}" 2>&1"` : `-c "${cmd} > '${logFile}' 2>&1"`;
|
|
38526
39451
|
const child = spawn7(shell, isWin ? [wrappedCmd] : ["-c", `${cmd} > '${logFile}' 2>&1`], {
|
|
38527
39452
|
detached: true,
|
|
@@ -38584,7 +39509,7 @@ registry.register({
|
|
|
38584
39509
|
const startCmd = svcArgs ? `${svc.start} ${svcArgs}` : svc.start;
|
|
38585
39510
|
if (longRunning) {
|
|
38586
39511
|
const isWin = process.platform === "win32";
|
|
38587
|
-
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${(
|
|
39512
|
+
const logFile = `D:/xiaoke/logs/${name}${svcArgs ? "-" + svcArgs : ""}-${todayKey().replace(/-/g, "")}.log`;
|
|
38588
39513
|
const redirect = isWin ? ` >> "${logFile}" 2>&1` : ` >> "${logFile}" 2>&1`;
|
|
38589
39514
|
const pid = spawnDetached(startCmd + redirect);
|
|
38590
39515
|
lines.push(`start: spawned PID ${pid} (background${svcArgs ? ", args=" + svcArgs : ""})`);
|
|
@@ -40189,7 +41114,7 @@ ${err.stack}`);
|
|
|
40189
41114
|
// src/commands/model.ts
|
|
40190
41115
|
import * as fs54 from "node:fs";
|
|
40191
41116
|
import * as path54 from "node:path";
|
|
40192
|
-
import { fileURLToPath as
|
|
41117
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
40193
41118
|
function collectModels(config) {
|
|
40194
41119
|
const aliases = config.modelAliases || {};
|
|
40195
41120
|
const aliasByRef = /* @__PURE__ */ new Map();
|
|
@@ -40221,7 +41146,7 @@ function resolveConfigWritePath(config, label) {
|
|
|
40221
41146
|
if (!configPath) return void 0;
|
|
40222
41147
|
let writePath = configPath;
|
|
40223
41148
|
if (configPath && !fs54.existsSync(configPath)) {
|
|
40224
|
-
const __pFile =
|
|
41149
|
+
const __pFile = fileURLToPath8(import.meta.url);
|
|
40225
41150
|
const __pDir = path54.dirname(__pFile);
|
|
40226
41151
|
const altPath = path54.join(path54.resolve(__pDir, "../configs"), path54.basename(configPath));
|
|
40227
41152
|
if (fs54.existsSync(altPath)) {
|
|
@@ -41314,6 +42239,33 @@ async function startEngine(config, opts) {
|
|
|
41314
42239
|
// 常开 entry,stop 不触发
|
|
41315
42240
|
});
|
|
41316
42241
|
}
|
|
42242
|
+
{
|
|
42243
|
+
let reportingHandle;
|
|
42244
|
+
const stopReporting = () => {
|
|
42245
|
+
try {
|
|
42246
|
+
reportingHandle?.stop();
|
|
42247
|
+
} catch {
|
|
42248
|
+
}
|
|
42249
|
+
reportingHandle = void 0;
|
|
42250
|
+
};
|
|
42251
|
+
const startReportingNow = () => {
|
|
42252
|
+
stopReporting();
|
|
42253
|
+
reportingHandle = startReporting({ stateDir: config.stateDir });
|
|
42254
|
+
};
|
|
42255
|
+
const reportingEnabled = () => liveConfig.get("reporting.enabled") !== false;
|
|
42256
|
+
reloadOrchestrator.register({
|
|
42257
|
+
name: "reporting",
|
|
42258
|
+
// url 也进 key:运维手改地址要能热生效,不用重启
|
|
42259
|
+
select: (cfg) => ({ enabled: cfg.reporting?.enabled !== false, url: cfg.reporting?.url ?? "" }),
|
|
42260
|
+
start: async () => {
|
|
42261
|
+
startReportingNow();
|
|
42262
|
+
},
|
|
42263
|
+
stop: async () => {
|
|
42264
|
+
stopReporting();
|
|
42265
|
+
}
|
|
42266
|
+
}, reportingEnabled());
|
|
42267
|
+
if (reportingEnabled()) startReportingNow();
|
|
42268
|
+
}
|
|
41317
42269
|
const shutdownPromise = new Promise((resolve12) => {
|
|
41318
42270
|
const shutdown = (signal) => {
|
|
41319
42271
|
console.log(`[engine] ${signal} received, shutting down...`);
|