@kanadego/dsh-heartbeat 1.8.0 → 1.9.0
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/README.md +20 -4
- package/config/policy.json +73 -72
- package/dist/{chunk-SQJLKP7L.js → chunk-2SYXKKZF.js} +145 -37
- package/dist/chunk-2SYXKKZF.js.map +1 -0
- package/dist/{chunk-UCJR6TC7.js → chunk-IPOZFZPY.js} +28 -14
- package/dist/chunk-IPOZFZPY.js.map +1 -0
- package/dist/{chunk-MXJPX3FM.js → chunk-PQTSXW4F.js} +37 -8
- package/dist/chunk-PQTSXW4F.js.map +1 -0
- package/dist/cli/index.js +14 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +387 -174
- package/dist/index.js.map +1 -1
- package/dist/{migrate-43PUK25G.js → migrate-N6NXMZ3G.js} +2 -2
- package/dist/{pool-JQC5JWIZ.js → pool-7I4VACG2.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-MXJPX3FM.js.map +0 -1
- package/dist/chunk-SQJLKP7L.js.map +0 -1
- package/dist/chunk-UCJR6TC7.js.map +0 -1
- /package/dist/{migrate-43PUK25G.js.map → migrate-N6NXMZ3G.js.map} +0 -0
- /package/dist/{pool-JQC5JWIZ.js.map → pool-7I4VACG2.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -8,14 +8,17 @@ import {
|
|
|
8
8
|
deepMerge,
|
|
9
9
|
describeInstall,
|
|
10
10
|
ensureRegistered,
|
|
11
|
+
ensureWeeklyAnchor,
|
|
11
12
|
installBundledPreset,
|
|
12
13
|
ledgerFilePath,
|
|
13
14
|
listWeeklyReports,
|
|
14
15
|
loadPolicy,
|
|
15
16
|
markDone,
|
|
16
17
|
pendingOlderThan,
|
|
18
|
+
preferenceFilePath,
|
|
17
19
|
readLedger,
|
|
18
20
|
readWeeklyReport,
|
|
21
|
+
recordDelivery,
|
|
19
22
|
renderTemplateReport,
|
|
20
23
|
saveWeeklyReport,
|
|
21
24
|
scanPending,
|
|
@@ -24,7 +27,7 @@ import {
|
|
|
24
27
|
updateUserPolicy,
|
|
25
28
|
userPresetRoot,
|
|
26
29
|
weeklyDue
|
|
27
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-2SYXKKZF.js";
|
|
28
31
|
import {
|
|
29
32
|
getRuntime,
|
|
30
33
|
setRuntime
|
|
@@ -40,7 +43,7 @@ import {
|
|
|
40
43
|
restoreSeed,
|
|
41
44
|
seedsFilePath,
|
|
42
45
|
surfaceSeed
|
|
43
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-PQTSXW4F.js";
|
|
44
47
|
import {
|
|
45
48
|
dedupeItems,
|
|
46
49
|
inboxClear,
|
|
@@ -81,7 +84,7 @@ import {
|
|
|
81
84
|
writeText
|
|
82
85
|
} from "./chunk-IFTFDHZX.js";
|
|
83
86
|
|
|
84
|
-
// node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
87
|
+
// node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.3/node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
85
88
|
function isNullable(value) {
|
|
86
89
|
return value === null || value === void 0;
|
|
87
90
|
}
|
|
@@ -100,32 +103,6 @@ function pick(source, keys, forced) {
|
|
|
100
103
|
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
101
104
|
return result;
|
|
102
105
|
}
|
|
103
|
-
var write = /* @__PURE__ */ Symbol.for("cosmokit.volatile.write");
|
|
104
|
-
function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
105
|
-
if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
|
|
106
|
-
if (value === null || typeof value !== "object") return value;
|
|
107
|
-
if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
|
|
108
|
-
ancestors.add(value);
|
|
109
|
-
try {
|
|
110
|
-
if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
|
|
111
|
-
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
|
|
112
|
-
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
|
|
113
|
-
} finally {
|
|
114
|
-
ancestors.delete(value);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function createVolatile(value) {
|
|
118
|
-
let current = snapshot(value);
|
|
119
|
-
return Object.freeze({
|
|
120
|
-
get: () => current,
|
|
121
|
-
[write]: (value2) => {
|
|
122
|
-
current = value2;
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
function isVolatile(value) {
|
|
127
|
-
return typeof value === "object" && value !== null && write in value;
|
|
128
|
-
}
|
|
129
106
|
function is(type, value) {
|
|
130
107
|
if (arguments.length === 1) return (value2) => is(type, value2);
|
|
131
108
|
return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
|
|
@@ -204,37 +181,24 @@ function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
|
204
181
|
return result;
|
|
205
182
|
}
|
|
206
183
|
function deepEqual(a, b, strict) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
const viewA = new Uint8Array(a3);
|
|
226
|
-
const viewB = new Uint8Array(b3);
|
|
227
|
-
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
228
|
-
return true;
|
|
229
|
-
}) ?? ((!strict || [a2, b2].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
|
|
230
|
-
...a2,
|
|
231
|
-
...b2
|
|
232
|
-
}).every((key) => compare(a2[key], b2[key])));
|
|
233
|
-
} finally {
|
|
234
|
-
ancestors.delete(a2);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
return compare(a, b);
|
|
184
|
+
if (a === b) return true;
|
|
185
|
+
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
186
|
+
if (typeof a !== typeof b) return false;
|
|
187
|
+
if (typeof a !== "object") return false;
|
|
188
|
+
if (!a || !b) return false;
|
|
189
|
+
function check(test, then) {
|
|
190
|
+
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
191
|
+
}
|
|
192
|
+
return check(Array.isArray, (a2, b2) => a2.length === b2.length && a2.every((item, index) => deepEqual(item, b2[index]))) ?? check(is("Date"), (a2, b2) => a2.valueOf() === b2.valueOf()) ?? check(is("RegExp"), (a2, b2) => a2.source === b2.source && a2.flags === b2.flags) ?? check(isArrayBufferLike, (a2, b2) => {
|
|
193
|
+
if (a2.byteLength !== b2.byteLength) return false;
|
|
194
|
+
const viewA = new Uint8Array(a2);
|
|
195
|
+
const viewB = new Uint8Array(b2);
|
|
196
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
197
|
+
return true;
|
|
198
|
+
}) ?? Object.keys({
|
|
199
|
+
...a,
|
|
200
|
+
...b
|
|
201
|
+
}).every((key) => deepEqual(a[key], b[key], strict));
|
|
238
202
|
}
|
|
239
203
|
var Time;
|
|
240
204
|
(function(Time2) {
|
|
@@ -306,7 +270,7 @@ var Time;
|
|
|
306
270
|
Time2.template = template;
|
|
307
271
|
})(Time || (Time = {}));
|
|
308
272
|
|
|
309
|
-
// node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
273
|
+
// node_modules/.pnpm/@deepseek-ai+schemastery@3.18.2/node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
310
274
|
var kSchema = /* @__PURE__ */ Symbol.for("schemastery");
|
|
311
275
|
var kValidationError = /* @__PURE__ */ Symbol.for("ValidationError");
|
|
312
276
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -483,7 +447,6 @@ Schema.prototype.pattern = function pattern(regexp) {
|
|
|
483
447
|
return schema;
|
|
484
448
|
};
|
|
485
449
|
Schema.prototype.simplify = function simplify(value) {
|
|
486
|
-
if (isVolatile(value)) value = value.get();
|
|
487
450
|
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
488
451
|
if (isNullable(value)) return value;
|
|
489
452
|
if (this.type === "object" || this.type === "dict") {
|
|
@@ -541,49 +504,12 @@ for (const key of [
|
|
|
541
504
|
};
|
|
542
505
|
return schema;
|
|
543
506
|
} });
|
|
544
|
-
Schema.prototype.volatile = function volatile() {
|
|
545
|
-
if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
|
|
546
|
-
return this.extra("volatile", true);
|
|
547
|
-
};
|
|
548
507
|
var resolvers = {};
|
|
549
|
-
var checkedVolatile = /* @__PURE__ */ Symbol("checked-volatile-schema");
|
|
550
|
-
function validateVolatileSchema(schema, path13 = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
|
|
551
|
-
const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
|
|
552
|
-
if (states.has(blocked)) return;
|
|
553
|
-
states.add(blocked);
|
|
554
|
-
seen.set(schema, states);
|
|
555
|
-
if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path: path13 });
|
|
556
|
-
const nested = blocked || !!schema.meta?.volatile;
|
|
557
|
-
if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path13, key], nested, seen);
|
|
558
|
-
if (schema.sKey) validateVolatileSchema(schema.sKey, [...path13, "<key>"], true, seen);
|
|
559
|
-
if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path13, "*"], true, seen);
|
|
560
|
-
if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path13, String(index)], true, seen);
|
|
561
|
-
}
|
|
562
508
|
Schema.extend = function extend(type, resolve2) {
|
|
563
509
|
resolvers[type] = resolve2;
|
|
564
510
|
};
|
|
565
511
|
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
566
512
|
if (!schema) return [data];
|
|
567
|
-
if (!options[checkedVolatile]) {
|
|
568
|
-
validateVolatileSchema(schema, options.path);
|
|
569
|
-
options = {
|
|
570
|
-
...options,
|
|
571
|
-
[checkedVolatile]: true
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
if (schema.meta?.volatile) {
|
|
575
|
-
const inner = Schema(schema);
|
|
576
|
-
inner.meta = {
|
|
577
|
-
...schema.meta,
|
|
578
|
-
volatile: false
|
|
579
|
-
};
|
|
580
|
-
const [value, adapted] = Schema.resolve(data, inner, options, strict);
|
|
581
|
-
try {
|
|
582
|
-
return [createVolatile(value), adapted];
|
|
583
|
-
} catch (error) {
|
|
584
|
-
throw new ValidationError(error instanceof Error ? error.message : String(error), options);
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
513
|
if (options.ignore?.(data, schema)) return [data];
|
|
588
514
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
589
515
|
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
@@ -691,7 +617,6 @@ Schema.extend("lazy", (data, schema, options, strict) => {
|
|
|
691
617
|
...schema.meta,
|
|
692
618
|
...schema.inner.meta
|
|
693
619
|
};
|
|
694
|
-
validateVolatileSchema(schema.inner, options.path, true);
|
|
695
620
|
}
|
|
696
621
|
return Schema.resolve(data, schema.inner, options, strict);
|
|
697
622
|
});
|
|
@@ -791,7 +716,7 @@ function property(data, key, schema, options) {
|
|
|
791
716
|
} catch (e) {
|
|
792
717
|
if (!options?.autofix) throw e;
|
|
793
718
|
delete data[key];
|
|
794
|
-
return schema.meta.
|
|
719
|
+
return schema.meta.default;
|
|
795
720
|
}
|
|
796
721
|
}
|
|
797
722
|
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
@@ -993,7 +918,7 @@ function saveUiConfig(guard, settingsDir, patch) {
|
|
|
993
918
|
// src/core/orchestrator.ts
|
|
994
919
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
995
920
|
import fs10 from "fs";
|
|
996
|
-
import
|
|
921
|
+
import path10 from "path";
|
|
997
922
|
|
|
998
923
|
// src/env/envpulse.ts
|
|
999
924
|
import fs3 from "fs";
|
|
@@ -1121,7 +1046,7 @@ function collectPulse(guard, paths, rules, fgProcess = null, now = /* @__PURE__
|
|
|
1121
1046
|
const idle = probeIdle(guard, paths);
|
|
1122
1047
|
const windowClass = classifyProcess(fgProcess, rules);
|
|
1123
1048
|
const t = timeContext(now);
|
|
1124
|
-
const
|
|
1049
|
+
const snapshot = {
|
|
1125
1050
|
takenAt: now.toISOString(),
|
|
1126
1051
|
idleSeconds: idle,
|
|
1127
1052
|
presence: presenceOf(idle, windowClass),
|
|
@@ -1132,24 +1057,24 @@ function collectPulse(guard, paths, rules, fgProcess = null, now = /* @__PURE__
|
|
|
1132
1057
|
festival: t.festival
|
|
1133
1058
|
};
|
|
1134
1059
|
try {
|
|
1135
|
-
fs3.writeFileSync(path3.join(paths.dataDir, "envpulse.json"), JSON.stringify(
|
|
1060
|
+
fs3.writeFileSync(path3.join(paths.dataDir, "envpulse.json"), JSON.stringify(snapshot, null, 1), "utf8");
|
|
1136
1061
|
} catch {
|
|
1137
1062
|
}
|
|
1138
|
-
writePulseStream(paths,
|
|
1139
|
-
return
|
|
1063
|
+
writePulseStream(paths, snapshot);
|
|
1064
|
+
return snapshot;
|
|
1140
1065
|
}
|
|
1141
|
-
function writePulseStream(paths,
|
|
1066
|
+
function writePulseStream(paths, snapshot) {
|
|
1142
1067
|
try {
|
|
1143
1068
|
appendAuditLine(path3.join(paths.logsDir, "envpulse.jsonl"), {
|
|
1144
1069
|
event: "pulse",
|
|
1145
|
-
takenAt:
|
|
1146
|
-
idleSeconds:
|
|
1147
|
-
presence:
|
|
1148
|
-
windowClass:
|
|
1149
|
-
daypart:
|
|
1150
|
-
weekday:
|
|
1151
|
-
isWeekend:
|
|
1152
|
-
festival:
|
|
1070
|
+
takenAt: snapshot.takenAt,
|
|
1071
|
+
idleSeconds: snapshot.idleSeconds,
|
|
1072
|
+
presence: snapshot.presence,
|
|
1073
|
+
windowClass: snapshot.windowClass,
|
|
1074
|
+
daypart: snapshot.daypart,
|
|
1075
|
+
weekday: snapshot.weekday,
|
|
1076
|
+
isWeekend: snapshot.isWeekend,
|
|
1077
|
+
festival: snapshot.festival
|
|
1153
1078
|
});
|
|
1154
1079
|
} catch {
|
|
1155
1080
|
}
|
|
@@ -1490,7 +1415,7 @@ function assembleCandidates(seeds, opts = {}) {
|
|
|
1490
1415
|
}
|
|
1491
1416
|
var PACKAGE_DECLARE = "\u8FD9\u662F\u5FC3\u8DF3\u63D2\u4EF6\u7D20\u6750\u6295\u9012,\u8BF7\u4F60\u6839\u636E\u5F53\u524D\u5904\u5883\u5224\u65AD\u8981\u4E0D\u8981\u9009\u4E00\u6761\u8BF4";
|
|
1492
1417
|
function materialLines(materials) {
|
|
1493
|
-
return materials.map((m) => m.text.trim());
|
|
1418
|
+
return materials.map((m) => `- [${m.id}] ${m.text.trim()}`);
|
|
1494
1419
|
}
|
|
1495
1420
|
function wantHonestOption(materials, threshold = 2) {
|
|
1496
1421
|
return materials.filter((m) => m.used >= 1).length >= threshold;
|
|
@@ -1502,22 +1427,60 @@ function buildMaterialPrompt(materials, opts = {}) {
|
|
|
1502
1427
|
}
|
|
1503
1428
|
lines.push(PACKAGE_DECLARE);
|
|
1504
1429
|
const items = materialLines(materials);
|
|
1505
|
-
for (const it of items) lines.push(
|
|
1430
|
+
for (const it of items) lines.push(it);
|
|
1506
1431
|
if (wantHonestOption(materials, opts.threshold ?? 2)) {
|
|
1507
1432
|
lines.push("(\u6216\u8005\u4E5F\u53EF\u4EE5\u8BF4\u4E00\u53E5\u771F\u5FC3\u8BDD,\u4E0D\u5E26\u7D20\u6750)");
|
|
1508
1433
|
}
|
|
1434
|
+
if (opts.deliveryId) {
|
|
1435
|
+
lines.push(`(\u672C\u6B21\u6295\u9012\u7F16\u53F7:${opts.deliveryId},\u62A5\u8D26\u65F6\u539F\u6837\u586B\u8FDB delivery_id)`);
|
|
1436
|
+
}
|
|
1437
|
+
lines.push(REPORT_DECLARE);
|
|
1509
1438
|
return lines.join("\n");
|
|
1510
1439
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
const
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1440
|
+
var REPORT_DECLARE = "\u6700\u540E,\u65E0\u8BBA\u521A\u624D\u8BF4\u4E0D\u8BF4\u8BDD\u3001\u7528\u6CA1\u7528\u7D20\u6750,\u90FD\u8981\u8C03\u7528\u4E00\u6B21 seed_report \u5DE5\u5177\u62A5\u8D26:spoken=material(\u8BF4\u4E86\u7D20\u6750)/heartfelt(\u8BF4\u4E86\u4E0D\u5E26\u7D20\u6750\u7684\u8BDD)/silent(\u6CA1\u8BF4\u8BDD);\u7528\u4E86\u7D20\u6750\u5C31\u628A\u7D20\u6750\u7F16\u53F7\u586B\u8FDB seed_ids(\u53EF\u591A\u6761,\u53EA\u586B\u7F16\u53F7\u672C\u8EAB);\u7528\u4E86\u753B\u50CF\u6761\u76EE\u5F53\u8BDD\u9898\u5C31\u586B profile_ids;\u6CA1\u7528\u7D20\u6750\u65F6\u7528 reason \u7B80\u5355\u8BB0\u4E00\u7B14\u539F\u56E0(\u4E0D\u60F3\u8BF4\u8BDD/\u7D20\u6750\u4E0D\u642D/\u5728\u5FD9\u6216\u521A\u804A\u8FC7,\u65E0\u9700\u7D20\u6750);\u5E76\u628A\u672C\u6B21\u6295\u9012\u7F16\u53F7\u586B\u8FDB delivery_id\u3002";
|
|
1441
|
+
function explicitIds(candidates, ids = []) {
|
|
1442
|
+
const known = new Set(candidates.map((c) => c.id));
|
|
1443
|
+
return [...new Set(ids)].filter((id) => known.has(id));
|
|
1444
|
+
}
|
|
1445
|
+
function pickReport(reports, deliveryId) {
|
|
1446
|
+
const exact = reports.filter((r) => r.deliveryId && r.deliveryId === deliveryId);
|
|
1447
|
+
if (exact.length > 0) return exact[exact.length - 1];
|
|
1448
|
+
const anonymous = reports.filter((r) => !r.deliveryId);
|
|
1449
|
+
return anonymous.length > 0 ? anonymous[anonymous.length - 1] : null;
|
|
1450
|
+
}
|
|
1451
|
+
function reconcileDelivery(materials, report, decisionIds, spokeText) {
|
|
1452
|
+
if (report && report.spoken === "silent" && !spokeText) {
|
|
1453
|
+
return {
|
|
1454
|
+
spoken: "silent",
|
|
1455
|
+
seedIds: [],
|
|
1456
|
+
profileIds: explicitIds(materials, report.profileIds),
|
|
1457
|
+
...report.reason ? { reason: report.reason } : {},
|
|
1458
|
+
source: "report"
|
|
1459
|
+
};
|
|
1519
1460
|
}
|
|
1520
|
-
|
|
1461
|
+
if (!spokeText) {
|
|
1462
|
+
return { spoken: "silent", seedIds: [], profileIds: [], source: "none" };
|
|
1463
|
+
}
|
|
1464
|
+
if (report) {
|
|
1465
|
+
const seedIds = explicitIds(materials, report.seedIds);
|
|
1466
|
+
return {
|
|
1467
|
+
// 报账说 silent 但实际出了声:话已成事实,降级记 heartfelt(说了但不带素材)。
|
|
1468
|
+
// v1.9.0: 报账说 material 但 id 一个都不在本包里(抄错/上一轮残留)同样降级——
|
|
1469
|
+
// 绝不凭一句「我用了素材」记名。
|
|
1470
|
+
spoken: report.spoken === "silent" || seedIds.length === 0 ? "heartfelt" : report.spoken,
|
|
1471
|
+
seedIds,
|
|
1472
|
+
profileIds: explicitIds(materials, report.profileIds),
|
|
1473
|
+
...report.reason ? { reason: report.reason } : {},
|
|
1474
|
+
source: "report"
|
|
1475
|
+
};
|
|
1476
|
+
}
|
|
1477
|
+
const ids = explicitIds(materials, decisionIds);
|
|
1478
|
+
return {
|
|
1479
|
+
spoken: ids.length > 0 ? "material" : "heartfelt",
|
|
1480
|
+
seedIds: ids,
|
|
1481
|
+
profileIds: [],
|
|
1482
|
+
source: ids.length > 0 ? "decision" : "none"
|
|
1483
|
+
};
|
|
1521
1484
|
}
|
|
1522
1485
|
function buildRuminationPrompt(input) {
|
|
1523
1486
|
const max = input.max ?? 3;
|
|
@@ -1558,9 +1521,128 @@ function buildRuminationPrompt(input) {
|
|
|
1558
1521
|
return lines.join("\n");
|
|
1559
1522
|
}
|
|
1560
1523
|
|
|
1524
|
+
// src/seeds/report.ts
|
|
1525
|
+
import path7 from "path";
|
|
1526
|
+
var REPORT_REASONS = ["\u4E0D\u60F3\u8BF4\u8BDD", "\u7D20\u6750\u4E0D\u642D", "\u5728\u5FD9\u6216\u521A\u804A\u8FC7"];
|
|
1527
|
+
function reportFilePath(dataDir) {
|
|
1528
|
+
return path7.join(dataDir, "seed_report.jsonl");
|
|
1529
|
+
}
|
|
1530
|
+
var REPORT_KEEP = 400;
|
|
1531
|
+
function parseEntry(obj) {
|
|
1532
|
+
const e = obj;
|
|
1533
|
+
if (!e || typeof e.ts !== "number" || !Number.isFinite(e.ts)) return null;
|
|
1534
|
+
if (e.spoken !== "material" && e.spoken !== "heartfelt" && e.spoken !== "silent") return null;
|
|
1535
|
+
const arr = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string" && x.length > 0) : [];
|
|
1536
|
+
const ids = arr(e.seedIds);
|
|
1537
|
+
const pids = arr(e.profileIds);
|
|
1538
|
+
const reason = REPORT_REASONS.includes(e.reason) ? e.reason : void 0;
|
|
1539
|
+
const did = typeof e.deliveryId === "string" && e.deliveryId.trim() ? e.deliveryId.trim() : "";
|
|
1540
|
+
return {
|
|
1541
|
+
ts: e.ts,
|
|
1542
|
+
spoken: e.spoken,
|
|
1543
|
+
seedIds: ids,
|
|
1544
|
+
profileIds: pids,
|
|
1545
|
+
...did ? { deliveryId: did } : {},
|
|
1546
|
+
...reason ? { reason } : {}
|
|
1547
|
+
};
|
|
1548
|
+
}
|
|
1549
|
+
function appendSeedReport(guard, file, entry) {
|
|
1550
|
+
const raw = loadEncryptedText(guard, file) ?? "";
|
|
1551
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
1552
|
+
lines.push(JSON.stringify(entry));
|
|
1553
|
+
saveEncryptedText(guard, file, lines.slice(-REPORT_KEEP).join("\n") + "\n");
|
|
1554
|
+
}
|
|
1555
|
+
function readSeedReports(guard, file) {
|
|
1556
|
+
const raw = loadEncryptedText(guard, file);
|
|
1557
|
+
if (!raw) return [];
|
|
1558
|
+
const out = [];
|
|
1559
|
+
for (const line of raw.split("\n")) {
|
|
1560
|
+
const t = line.trim();
|
|
1561
|
+
if (!t) continue;
|
|
1562
|
+
try {
|
|
1563
|
+
const e = parseEntry(JSON.parse(t));
|
|
1564
|
+
if (e) out.push(e);
|
|
1565
|
+
} catch {
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
return out;
|
|
1569
|
+
}
|
|
1570
|
+
function readSeedReportsSince(guard, file, sinceTs) {
|
|
1571
|
+
return readSeedReports(guard, file).filter((e) => e.ts >= sinceTs);
|
|
1572
|
+
}
|
|
1573
|
+
function cleanIds(v) {
|
|
1574
|
+
return Array.isArray(v) ? v.map((x) => String(x ?? "").replace(/[[\]{}"'`]/g, "").trim().replace(/\s+/g, "")).filter((s) => s.length > 0 && s.length <= 24) : [];
|
|
1575
|
+
}
|
|
1576
|
+
function buildSeedReportTool(guard, file) {
|
|
1577
|
+
return {
|
|
1578
|
+
name: "seed_report",
|
|
1579
|
+
description: "\u7D20\u6750\u62A5\u8D26\u5DE5\u5177\uFF08\u5FC3\u8DF3\u63D2\u4EF6\uFF09\u3002\u6536\u5230\u5FC3\u8DF3\u7D20\u6750\u5305\u540E\u5FC5\u987B\u8C03\u7528\u4E00\u6B21\u672C\u5DE5\u5177\u62A5\u8D26\uFF0C\u8BF4\u4E0D\u8BF4\u3001\u7528\u6CA1\u7528\u90FD\u8981\u62A5\uFF1Aspoken=material \u8868\u793A\u8BF4\u4E86\u7D20\u6750\u91CC\u7684\u5185\u5BB9\uFF08\u6B64\u65F6 seed_ids \u5FC5\u586B\uFF0C\u586B\u5B9E\u9645\u7528\u5230\u7684\u7D20\u6750\u7F16\u53F7\uFF0C\u53EF\u591A\u6761\uFF09\uFF1Bspoken=heartfelt \u8868\u793A\u8BF4\u4E86\u4E0D\u5E26\u7D20\u6750\u7684\u8BDD\uFF08\u771F\u5FC3\u8BDD\u6216\u77ED\u5E94\u7B54\uFF09\uFF1Bspoken=silent \u8868\u793A\u6CA1\u8BF4\u8BDD\u3002\u628A\u5F53\u6210\u8BDD\u9898\u804A\u4E86\u7684\u753B\u50CF\u6761\u76EE\u7F16\u53F7\u586B\u8FDB profile_ids\u3002\u6CA1\u7528\u7D20\u6750\u65F6\u7528 reason \u8BB0\u4E00\u7B14\u539F\u56E0\uFF1A\u4E0D\u60F3\u8BF4\u8BDD / \u7D20\u6750\u4E0D\u642D / \u5728\u5FD9\u6216\u521A\u804A\u8FC7\u3002\u4E00\u6B21\u6295\u9012\u53EA\u62A5\u4E00\u6B21\u8D26\uFF0C\u5E76\u628A\u7D20\u6750\u5305\u91CC\u7684\u672C\u6B21\u6295\u9012\u7F16\u53F7\u539F\u6837\u586B\u8FDB delivery_id\u3002",
|
|
1580
|
+
parameters: {
|
|
1581
|
+
type: "object",
|
|
1582
|
+
properties: {
|
|
1583
|
+
spoken: { type: "string", enum: ["material", "heartfelt", "silent"], description: "material=\u8BF4\u4E86\u7D20\u6750\uFF0Cheartfelt=\u8BF4\u4E86\u4E0D\u5E26\u7D20\u6750\u7684\u8BDD\uFF0Csilent=\u6CA1\u8BF4\u8BDD" },
|
|
1584
|
+
seed_ids: { type: "array", items: { type: "string" }, description: 'spoken=material \u65F6\u5FC5\u586B\uFF1A\u5B9E\u9645\u7528\u5230\u7684\u7D20\u6750\u7F16\u53F7\uFF0C\u53EA\u586B\u7F16\u53F7\u672C\u8EAB\uFF08\u5982 ["s3"]\uFF09\uFF0C\u4E0D\u8981\u5E26\u65B9\u62EC\u53F7' },
|
|
1585
|
+
profile_ids: { type: "array", items: { type: "string" }, description: "\u5B9E\u9645\u5F53\u6210\u8BDD\u9898\u804A\u4E86\u7684\u753B\u50CF\u6761\u76EE\u7F16\u53F7\uFF08\u95F2\u7740\u6A21\u5F0F\u7D20\u6750\u5305\u91CC\u624D\u6709\uFF09" },
|
|
1586
|
+
reason: { type: "string", enum: [...REPORT_REASONS], description: "\u6CA1\u7528\u7D20\u6750\u65F6\u7684\u539F\u56E0\uFF0C\u9009\u586B" },
|
|
1587
|
+
delivery_id: { type: "string", description: "\u7D20\u6750\u5305\u91CC\u7684\u672C\u6B21\u6295\u9012\u7F16\u53F7\uFF08\u5F62\u5982 d1a2b3c4\uFF09\uFF0C\u539F\u6837\u56DE\u586B" }
|
|
1588
|
+
},
|
|
1589
|
+
required: ["spoken"],
|
|
1590
|
+
additionalProperties: false
|
|
1591
|
+
},
|
|
1592
|
+
output: {
|
|
1593
|
+
schema: {
|
|
1594
|
+
type: "object",
|
|
1595
|
+
properties: { ok: { type: "boolean" }, message: { type: "string" } },
|
|
1596
|
+
required: ["ok", "message"],
|
|
1597
|
+
additionalProperties: false
|
|
1598
|
+
},
|
|
1599
|
+
render: (_args, value) => {
|
|
1600
|
+
const v = value;
|
|
1601
|
+
return [{ type: "text", text: String(v.message ?? "") }];
|
|
1602
|
+
}
|
|
1603
|
+
},
|
|
1604
|
+
// Whole-file load+save through DPAPI, and the report is filed while the
|
|
1605
|
+
// persona turn is still finishing — 0.7–2.7 s measured here, so 5 s was
|
|
1606
|
+
// too tight for a slow disk (raised 2026-10-06).
|
|
1607
|
+
timeoutMs: 15e3,
|
|
1608
|
+
// whole-file rewrite on one encrypted file; never parallel
|
|
1609
|
+
isConcurrencySafe: () => false,
|
|
1610
|
+
async execute(args) {
|
|
1611
|
+
const a = args ?? {};
|
|
1612
|
+
const spoken = String(a.spoken ?? "");
|
|
1613
|
+
if (spoken !== "material" && spoken !== "heartfelt" && spoken !== "silent") {
|
|
1614
|
+
return { ok: false, message: "spoken \u5FC5\u987B\u662F material / heartfelt / silent \u4E4B\u4E00" };
|
|
1615
|
+
}
|
|
1616
|
+
const seedIds = cleanIds(a.seed_ids);
|
|
1617
|
+
const profileIds = cleanIds(a.profile_ids);
|
|
1618
|
+
if (spoken === "material" && seedIds.length === 0) {
|
|
1619
|
+
return { ok: false, message: "spoken=material \u9700\u8981 seed_ids\uFF1A\u628A\u5B9E\u9645\u7528\u5230\u7684\u7D20\u6750\u7F16\u53F7\u586B\u8FDB\u6765" };
|
|
1620
|
+
}
|
|
1621
|
+
const reasonRaw = String(a.reason ?? "");
|
|
1622
|
+
const reason = REPORT_REASONS.includes(reasonRaw) ? reasonRaw : void 0;
|
|
1623
|
+
const deliveryId = String(a.delivery_id ?? "").trim().slice(0, 64);
|
|
1624
|
+
const entry = {
|
|
1625
|
+
ts: Date.now(),
|
|
1626
|
+
spoken,
|
|
1627
|
+
seedIds,
|
|
1628
|
+
profileIds,
|
|
1629
|
+
...deliveryId ? { deliveryId } : {}
|
|
1630
|
+
};
|
|
1631
|
+
if (spoken !== "material" && reason) entry.reason = reason;
|
|
1632
|
+
appendSeedReport(guard, file, entry);
|
|
1633
|
+
const bits = [`spoken=${entry.spoken}`];
|
|
1634
|
+
if (entry.deliveryId) bits.push(`delivery_id=${entry.deliveryId}`);
|
|
1635
|
+
if (entry.seedIds.length) bits.push(`seed_ids=${entry.seedIds.join(",")}`);
|
|
1636
|
+
if (entry.profileIds.length) bits.push(`profile_ids=${entry.profileIds.join(",")}`);
|
|
1637
|
+
if (entry.reason) bits.push(`reason=${entry.reason}`);
|
|
1638
|
+
return { ok: true, message: `\u5DF2\u62A5\u8D26\uFF1A${bits.join(" ")}` };
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1561
1643
|
// src/statusbar/store.ts
|
|
1562
1644
|
import fs7 from "fs";
|
|
1563
|
-
import
|
|
1645
|
+
import path8 from "path";
|
|
1564
1646
|
function deriveScene(input) {
|
|
1565
1647
|
if (input.quietHours) return "quiet-hours";
|
|
1566
1648
|
if (input.spokeThisBeat) return "just-spoke";
|
|
@@ -1575,7 +1657,7 @@ function clampNote(note) {
|
|
|
1575
1657
|
return t.length <= 30 ? t : t.slice(0, 30);
|
|
1576
1658
|
}
|
|
1577
1659
|
function statusFilePath(dataDir) {
|
|
1578
|
-
return
|
|
1660
|
+
return path8.join(dataDir, "settings", "status.json");
|
|
1579
1661
|
}
|
|
1580
1662
|
function writeStatus(guard, paths, state) {
|
|
1581
1663
|
atomicWriteJsonSync(guard.assert(statusFilePath(paths.dataDir)), state);
|
|
@@ -1750,7 +1832,13 @@ async function runConsolidation(guard, paths, policy, llm, now = Date.now()) {
|
|
|
1750
1832
|
const report = applyOpsToDoc(guard, paths.dataDir, doc, profileOpsCast, schema, policy, now);
|
|
1751
1833
|
const aged = runDeterministicAging(doc, policy, now);
|
|
1752
1834
|
persistWithJournal(guard, paths.dataDir, doc, { runId, applied: report.applied, rejected: report.rejected });
|
|
1753
|
-
const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-
|
|
1835
|
+
const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-7I4VACG2.js");
|
|
1836
|
+
const seedAudit = (entry) => {
|
|
1837
|
+
try {
|
|
1838
|
+
appendAuditLine(paths.dataDir + "/logs/heartbeat.jsonl", entry);
|
|
1839
|
+
} catch {
|
|
1840
|
+
}
|
|
1841
|
+
};
|
|
1754
1842
|
let chatSeedsAdded = 0;
|
|
1755
1843
|
for (const cs of chatSeeds) {
|
|
1756
1844
|
try {
|
|
@@ -1759,7 +1847,7 @@ async function runConsolidation(guard, paths, policy, llm, now = Date.now()) {
|
|
|
1759
1847
|
...cs.topic ? { topic: cs.topic } : {},
|
|
1760
1848
|
source: "chat",
|
|
1761
1849
|
tag: "scene"
|
|
1762
|
-
}, now);
|
|
1850
|
+
}, now, seedAudit);
|
|
1763
1851
|
chatSeedsAdded += 1;
|
|
1764
1852
|
} catch {
|
|
1765
1853
|
}
|
|
@@ -1793,6 +1881,10 @@ function fmtEntry(prefix, content, opts) {
|
|
|
1793
1881
|
const flag = opts.low ? "\uFF08\u4E45\u672A\u9A8C\u8BC1\uFF09" : "";
|
|
1794
1882
|
return `${prefix}${content}${flag} [conf ${opts.confidence.toFixed(2)}]`;
|
|
1795
1883
|
}
|
|
1884
|
+
function profileTopicEntries(doc, topN) {
|
|
1885
|
+
const score = (e) => e.confidence * 0.7 + 1 / (1 + Math.max(0, Date.now() - Date.parse(e.updatedAt)) / 864e5) * 0.3;
|
|
1886
|
+
return [...doc.partitions.interest.entries, ...doc.partitions.projects.entries].filter((e) => e.validTo === null).sort((a, b) => score(b) - score(a)).slice(0, topN);
|
|
1887
|
+
}
|
|
1796
1888
|
function buildDigest(guard, paths, policy, input = {}) {
|
|
1797
1889
|
const doc = loadProfile(guard, profileFilePath(paths.dataDir));
|
|
1798
1890
|
const topN = input.topN ?? 8;
|
|
@@ -1810,8 +1902,7 @@ function buildDigest(guard, paths, policy, input = {}) {
|
|
|
1810
1902
|
`[\u65F6\u95F4\u611F] ${rhythmLine}${input.windowClass ? ` | \u5F53\u524D\u7A97\u53E3\u7C7B\u522B: ${input.windowClass}` : ""}`,
|
|
1811
1903
|
...comm
|
|
1812
1904
|
];
|
|
1813
|
-
const
|
|
1814
|
-
const topicEntries = [...doc.partitions.interest.entries, ...doc.partitions.projects.entries].filter((e) => e.validTo === null).sort((a, b) => score(b) - score(a)).slice(0, topN).map((e) => {
|
|
1905
|
+
const topicEntries = profileTopicEntries(doc, topN).map((e) => {
|
|
1815
1906
|
const prefix = e.partition === "projects" ? "- \u8FDB\u884C\u4E2D: " : "- \u5174\u8DA3: ";
|
|
1816
1907
|
return fmtEntry(prefix, `${e.topic}/${e.subTopic}: ${e.content}`, { low: e.lowActivity, confidence: e.confidence });
|
|
1817
1908
|
});
|
|
@@ -1839,11 +1930,11 @@ function buildDigest(guard, paths, policy, input = {}) {
|
|
|
1839
1930
|
|
|
1840
1931
|
// src/rhythm/rhythm.ts
|
|
1841
1932
|
import fs8 from "fs";
|
|
1842
|
-
import
|
|
1933
|
+
import path9 from "path";
|
|
1843
1934
|
var DAY_MS = 864e5;
|
|
1844
1935
|
var TAU_DAYS = 10;
|
|
1845
1936
|
function rhythmFilePath(paths) {
|
|
1846
|
-
return
|
|
1937
|
+
return path9.join(paths.dataDir, "profile_rhythm.json");
|
|
1847
1938
|
}
|
|
1848
1939
|
function loadRhythm(paths) {
|
|
1849
1940
|
try {
|
|
@@ -1914,6 +2005,7 @@ function collectWeeklyFacts(guard, paths, policy, now = Date.now()) {
|
|
|
1914
2005
|
let spoken = 0;
|
|
1915
2006
|
let silent = 0;
|
|
1916
2007
|
let observedItems = 0;
|
|
2008
|
+
let reportMissing = 0;
|
|
1917
2009
|
const reasons = /* @__PURE__ */ new Map();
|
|
1918
2010
|
try {
|
|
1919
2011
|
for (const e of readAuditLines(paths.logsDir + "/heartbeat.jsonl")) {
|
|
@@ -1921,6 +2013,7 @@ function collectWeeklyFacts(guard, paths, policy, now = Date.now()) {
|
|
|
1921
2013
|
if (!inWindow(ts, start, end)) continue;
|
|
1922
2014
|
const ev = e.event;
|
|
1923
2015
|
if (ev === "spoke") spoken += 1;
|
|
2016
|
+
else if (ev === "report_missing") reportMissing += 1;
|
|
1924
2017
|
else if (ev === "silent") {
|
|
1925
2018
|
silent += 1;
|
|
1926
2019
|
const reason = String(e.reason ?? "unknown").slice(0, 60);
|
|
@@ -1981,6 +2074,20 @@ function collectWeeklyFacts(guard, paths, policy, now = Date.now()) {
|
|
|
1981
2074
|
seeds.waiting = activeSeeds(db).filter((s) => s.used === 0 && s.category === "topic").sort((a, b) => a.bornAt < b.bornAt ? 1 : -1).slice(0, 5).map((s) => trim(s.text, 40));
|
|
1982
2075
|
} catch {
|
|
1983
2076
|
}
|
|
2077
|
+
const reports = { total: 0, material: 0, heartfelt: 0, silent: 0, missing: reportMissing, reasons: [] };
|
|
2078
|
+
try {
|
|
2079
|
+
const reasonTally = /* @__PURE__ */ new Map();
|
|
2080
|
+
for (const r of readSeedReports(guard, reportFilePath(paths.dataDir))) {
|
|
2081
|
+
if (r.ts < start || r.ts > end) continue;
|
|
2082
|
+
reports.total += 1;
|
|
2083
|
+
if (r.spoken === "material") reports.material += 1;
|
|
2084
|
+
else if (r.spoken === "heartfelt") reports.heartfelt += 1;
|
|
2085
|
+
else reports.silent += 1;
|
|
2086
|
+
if (r.reason) reasonTally.set(r.reason, (reasonTally.get(r.reason) ?? 0) + 1);
|
|
2087
|
+
}
|
|
2088
|
+
reports.reasons = [...reasonTally.entries()].map(([reason, count]) => ({ reason, count })).sort((a, b) => b.count - a.count);
|
|
2089
|
+
} catch {
|
|
2090
|
+
}
|
|
1984
2091
|
let peakHours = [];
|
|
1985
2092
|
try {
|
|
1986
2093
|
peakHours = summarizeRhythm(paths).peakHours;
|
|
@@ -1996,6 +2103,7 @@ function collectWeeklyFacts(guard, paths, policy, now = Date.now()) {
|
|
|
1996
2103
|
profileAdds,
|
|
1997
2104
|
ledger,
|
|
1998
2105
|
seeds,
|
|
2106
|
+
reports,
|
|
1999
2107
|
peakHours
|
|
2000
2108
|
};
|
|
2001
2109
|
}
|
|
@@ -2013,8 +2121,8 @@ function sessionEvents(session) {
|
|
|
2013
2121
|
if (!session) return [];
|
|
2014
2122
|
if (typeof session.snapshotEvents === "function") {
|
|
2015
2123
|
try {
|
|
2016
|
-
const
|
|
2017
|
-
if (Array.isArray(
|
|
2124
|
+
const snapshot = session.snapshotEvents();
|
|
2125
|
+
if (Array.isArray(snapshot)) return snapshot;
|
|
2018
2126
|
} catch {
|
|
2019
2127
|
}
|
|
2020
2128
|
}
|
|
@@ -2051,7 +2159,7 @@ function noteBeat(verdict, detail) {
|
|
|
2051
2159
|
lastBeat = { at: (/* @__PURE__ */ new Date()).toISOString(), verdict, ...detail };
|
|
2052
2160
|
}
|
|
2053
2161
|
function stateFile(paths) {
|
|
2054
|
-
return
|
|
2162
|
+
return path10.join(paths.dataDir, "gate.json");
|
|
2055
2163
|
}
|
|
2056
2164
|
function readBeatState(guard, paths) {
|
|
2057
2165
|
try {
|
|
@@ -2370,10 +2478,18 @@ function parseJsonBlock(raw) {
|
|
|
2370
2478
|
}
|
|
2371
2479
|
throw new Error(text.includes("{") ? "unparseable JSON object in model output" : "no JSON object in model output");
|
|
2372
2480
|
}
|
|
2481
|
+
function seedAuditSink(paths) {
|
|
2482
|
+
return (entry) => {
|
|
2483
|
+
try {
|
|
2484
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", entry);
|
|
2485
|
+
} catch {
|
|
2486
|
+
}
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2373
2489
|
async function maintenancePhase(bc) {
|
|
2374
2490
|
const { deps, now } = bc;
|
|
2375
2491
|
const { guard, paths, policy } = deps;
|
|
2376
|
-
gcPool(guard, seedsFilePath(paths.dataDir), policy, now);
|
|
2492
|
+
gcPool(guard, seedsFilePath(paths.dataDir), policy, now, seedAuditSink(paths));
|
|
2377
2493
|
pruneAuditFile(paths.logsDir + "/envpulse.jsonl", policy.retention.envPulseHours * 36e5, now);
|
|
2378
2494
|
pruneAuditFile(paths.logsDir + "/heartbeat.jsonl", policy.retention.decisionLogDays * 864e5, now);
|
|
2379
2495
|
snapshotIfDue(guard, paths.dataDir, paths.logsDir + "/heartbeat.jsonl", "maintenance-threshold", now);
|
|
@@ -2412,7 +2528,7 @@ async function observeBoundSessions(bc) {
|
|
|
2412
2528
|
const data = loadBindings2(guard, paths.settingsDir);
|
|
2413
2529
|
const targets = observeTargets(data);
|
|
2414
2530
|
if (targets.length === 0) return;
|
|
2415
|
-
const cursorFile =
|
|
2531
|
+
const cursorFile = path10.join(paths.dataDir, "cursors.json");
|
|
2416
2532
|
let cursors = {};
|
|
2417
2533
|
try {
|
|
2418
2534
|
cursors = JSON.parse(fs10.readFileSync(cursorFile, "utf8"));
|
|
@@ -2458,13 +2574,16 @@ async function weeklyPhase(bc) {
|
|
|
2458
2574
|
const { deps, agent, now } = bc;
|
|
2459
2575
|
const { guard, paths, policy } = deps;
|
|
2460
2576
|
if (policy.weekly.enabled === false) return;
|
|
2461
|
-
if (!weeklyDue(guard, paths.dataDir, now)) return;
|
|
2462
2577
|
const audit = (entry) => {
|
|
2463
2578
|
try {
|
|
2464
2579
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", entry);
|
|
2465
2580
|
} catch {
|
|
2466
2581
|
}
|
|
2467
2582
|
};
|
|
2583
|
+
if (ensureWeeklyAnchor(guard, paths.dataDir, now)) {
|
|
2584
|
+
audit({ event: "weekly_anchored" });
|
|
2585
|
+
}
|
|
2586
|
+
if (!weeklyDue(guard, paths.dataDir, now)) return;
|
|
2468
2587
|
if (!agent) {
|
|
2469
2588
|
audit({ event: "weekly_skipped", reason: "agentless beat" });
|
|
2470
2589
|
return;
|
|
@@ -2590,7 +2709,7 @@ async function runWanderTurn(bc, prompt, focus, opts) {
|
|
|
2590
2709
|
tag: "news",
|
|
2591
2710
|
source: "browse",
|
|
2592
2711
|
confidence: 0.4
|
|
2593
|
-
}, now);
|
|
2712
|
+
}, now, seedAuditSink(paths));
|
|
2594
2713
|
registered += 1;
|
|
2595
2714
|
}
|
|
2596
2715
|
} catch (e) {
|
|
@@ -2600,6 +2719,24 @@ async function runWanderTurn(bc, prompt, focus, opts) {
|
|
|
2600
2719
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: opts.label, focus, registered });
|
|
2601
2720
|
return true;
|
|
2602
2721
|
}
|
|
2722
|
+
function recordDeliveryFromAccount(deps, materials, account) {
|
|
2723
|
+
if (account.spoken === "silent" && account.reason !== "\u7D20\u6750\u4E0D\u642D") return;
|
|
2724
|
+
const { guard, paths } = deps;
|
|
2725
|
+
const db = loadPool(guard, seedsFilePath(paths.dataDir));
|
|
2726
|
+
const doc = loadProfile(guard, profileFilePath(paths.dataDir));
|
|
2727
|
+
const seedTopic = new Map(db.seeds.map((s) => [s.id, s.topic]));
|
|
2728
|
+
const entryTopic = new Map(
|
|
2729
|
+
[...doc.partitions.interest.entries, ...doc.partitions.projects.entries].map((e) => [e.id, `${e.topic}/${e.subTopic}`])
|
|
2730
|
+
);
|
|
2731
|
+
const topicOf = (id) => seedTopic.get(id) ?? entryTopic.get(id) ?? null;
|
|
2732
|
+
const offered = [...new Set(materials.map((m) => topicOf(m.id)).filter((t) => Boolean(t)))];
|
|
2733
|
+
const adopted = [...new Set([...account.seedIds, ...account.profileIds].map(topicOf).filter((t) => Boolean(t)))];
|
|
2734
|
+
recordDelivery(guard, preferenceFilePath(paths.dataDir), {
|
|
2735
|
+
offeredTopics: offered,
|
|
2736
|
+
adoptedTopics: adopted,
|
|
2737
|
+
now: Date.now()
|
|
2738
|
+
});
|
|
2739
|
+
}
|
|
2603
2740
|
async function deliverPackage(bc, materials, opts) {
|
|
2604
2741
|
const { deps, now } = bc;
|
|
2605
2742
|
const { guard, paths, policy } = deps;
|
|
@@ -2630,11 +2767,14 @@ async function deliverPackage(bc, materials, opts) {
|
|
|
2630
2767
|
}
|
|
2631
2768
|
const voiceSessionId = voiceAgent.session?.id ?? null;
|
|
2632
2769
|
try {
|
|
2770
|
+
const deliveryId = `d${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
2633
2771
|
const phrasePrompt = buildMaterialPrompt(materials, {
|
|
2772
|
+
deliveryId,
|
|
2634
2773
|
// spec ②: the "我在干嘛" line rides on every delivery when vision
|
|
2635
2774
|
// produced one this beat; absent otherwise (never invented).
|
|
2636
2775
|
...typeof opts.doing === "string" && opts.doing.trim() ? { doing: opts.doing.trim().slice(0, 80) } : {}
|
|
2637
2776
|
});
|
|
2777
|
+
const turnStart = Date.now();
|
|
2638
2778
|
let spokenRaw;
|
|
2639
2779
|
try {
|
|
2640
2780
|
spokenRaw = await agentTurn(bc.deps, voiceAgent, phrasePrompt, "expression", EXPRESSION_IDLE_WAIT_MS);
|
|
@@ -2650,9 +2790,31 @@ async function deliverPackage(bc, materials, opts) {
|
|
|
2650
2790
|
const spokenLines = spokenRaw.replace(/<\/?thinking[\s\S]*?<\/think>/gi, "").trim().split("\n").map((l) => l.trim()).filter((l) => l && !/^<\/?tool_calls?>$/i.test(l));
|
|
2651
2791
|
const cnLine = [...spokenLines].reverse().find((l) => /[\u4e00-\u9fff]/.test(l));
|
|
2652
2792
|
const text = (cnLine ?? spokenLines[spokenLines.length - 1] ?? "").slice(0, 200);
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2793
|
+
const spokeText = Boolean(text && /[\u4e00-\u9fff]/.test(text));
|
|
2794
|
+
let report = null;
|
|
2795
|
+
try {
|
|
2796
|
+
const reports = readSeedReportsSince(guard, reportFilePath(paths.dataDir), turnStart);
|
|
2797
|
+
report = pickReport(reports, deliveryId);
|
|
2798
|
+
} catch (e) {
|
|
2799
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "report_read_failed", error: String(e).slice(0, 120) });
|
|
2800
|
+
}
|
|
2801
|
+
const account = reconcileDelivery(materials, report, opts.seedIds, spokeText);
|
|
2802
|
+
if (!spokeText) {
|
|
2803
|
+
if (account.source === "report" && account.spoken === "silent") {
|
|
2804
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
2805
|
+
event: "silent",
|
|
2806
|
+
reason: `\u62A5\u8D26\u6C89\u9ED8:${account.reason ?? "\u672A\u8BF4\u660E"}`
|
|
2807
|
+
});
|
|
2808
|
+
noteBeat("silent", { reason: "\u672C\u4EBA\u62A5\u8D26\u6C89\u9ED8" });
|
|
2809
|
+
try {
|
|
2810
|
+
recordDeliveryFromAccount(deps, materials, account);
|
|
2811
|
+
} catch (e) {
|
|
2812
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "preference_record_failed", error: String(e).slice(0, 120) });
|
|
2813
|
+
}
|
|
2814
|
+
} else {
|
|
2815
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "spoke_failed", reason: "non-Chinese output discarded" });
|
|
2816
|
+
noteBeat("spoke_failed", { reason: "non-Chinese output discarded" });
|
|
2817
|
+
}
|
|
2656
2818
|
return;
|
|
2657
2819
|
}
|
|
2658
2820
|
const confirm = confirmSend(guard, policy, paths, "topic", text, now);
|
|
@@ -2661,12 +2823,31 @@ async function deliverPackage(bc, materials, opts) {
|
|
|
2661
2823
|
noteBeat("spoke_failed", { reason: confirm.reason });
|
|
2662
2824
|
return;
|
|
2663
2825
|
}
|
|
2664
|
-
const
|
|
2665
|
-
|
|
2666
|
-
|
|
2826
|
+
for (const id of account.seedIds) {
|
|
2827
|
+
surfaceSeed(guard, seedsFilePath(paths.dataDir), policy, id, now, seedAuditSink(paths));
|
|
2828
|
+
}
|
|
2829
|
+
if (account.source === "none") {
|
|
2830
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
2831
|
+
event: "report_missing",
|
|
2832
|
+
offered: materials.map((m) => m.id).join(",")
|
|
2833
|
+
});
|
|
2834
|
+
} else {
|
|
2835
|
+
try {
|
|
2836
|
+
recordDeliveryFromAccount(deps, materials, account);
|
|
2837
|
+
} catch (e) {
|
|
2838
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "preference_record_failed", error: String(e).slice(0, 120) });
|
|
2839
|
+
}
|
|
2667
2840
|
}
|
|
2668
2841
|
sendNewMessageHint(paths);
|
|
2669
|
-
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
2842
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
2843
|
+
event: "spoke",
|
|
2844
|
+
text: text.slice(0, 80),
|
|
2845
|
+
seeds: account.seedIds,
|
|
2846
|
+
profile_ids: account.profileIds,
|
|
2847
|
+
source: account.source,
|
|
2848
|
+
spoken: account.spoken,
|
|
2849
|
+
...account.reason ? { reason: account.reason } : {}
|
|
2850
|
+
});
|
|
2670
2851
|
if (voiceSessionId && voiceSessionId !== homeId) {
|
|
2671
2852
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "delivered", sessionId: voiceSessionId });
|
|
2672
2853
|
}
|
|
@@ -2705,9 +2886,13 @@ async function expressionPhases(bc) {
|
|
|
2705
2886
|
const offered = assembleCandidates(activeSeeds(loadPool(guard, seedsFilePath(paths.dataDir))));
|
|
2706
2887
|
if (offered.length === 0) {
|
|
2707
2888
|
if (policy.heartbeat.idleMode && bc.agent) {
|
|
2708
|
-
const
|
|
2709
|
-
if (
|
|
2710
|
-
const fallback =
|
|
2889
|
+
const idleEntries = profileTopicEntries(loadProfile(guard, profileFilePath(paths.dataDir)), 3);
|
|
2890
|
+
if (idleEntries.length > 0) {
|
|
2891
|
+
const fallback = idleEntries.map((e) => ({
|
|
2892
|
+
id: e.id,
|
|
2893
|
+
text: `${e.partition === "projects" ? "\u8FDB\u884C\u4E2D" : "\u5174\u8DA3"} ${e.topic}/${e.subTopic}: ${e.content}`.slice(0, 60),
|
|
2894
|
+
used: 0
|
|
2895
|
+
}));
|
|
2711
2896
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "idle_fallback", topics: fallback.length });
|
|
2712
2897
|
return deliverPackage(bc, fallback, {
|
|
2713
2898
|
seedIds: [],
|
|
@@ -2940,20 +3125,20 @@ function startOrchestrator(deps) {
|
|
|
2940
3125
|
import { spawn as spawn2 } from "child_process";
|
|
2941
3126
|
import fs13 from "fs";
|
|
2942
3127
|
import os from "os";
|
|
2943
|
-
import
|
|
3128
|
+
import path13 from "path";
|
|
2944
3129
|
|
|
2945
3130
|
// src/browse/interests-edit.ts
|
|
2946
3131
|
import fs11 from "fs";
|
|
2947
|
-
import
|
|
3132
|
+
import path11 from "path";
|
|
2948
3133
|
var MAX_INTERESTS = 32;
|
|
2949
3134
|
var MAX_INTEREST_LEN = 60;
|
|
2950
3135
|
var MAX_WINDOWS = 6;
|
|
2951
3136
|
var HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
2952
3137
|
function userInterestsPath(paths) {
|
|
2953
|
-
return
|
|
3138
|
+
return path11.join(paths.settingsDir, "interests.json");
|
|
2954
3139
|
}
|
|
2955
3140
|
function factoryInterestsPath(paths) {
|
|
2956
|
-
return
|
|
3141
|
+
return path11.join(paths.configDir, "interests.json");
|
|
2957
3142
|
}
|
|
2958
3143
|
function readDoc(file) {
|
|
2959
3144
|
try {
|
|
@@ -2975,7 +3160,7 @@ function ensureUserLayer(guard, paths) {
|
|
|
2975
3160
|
return doc;
|
|
2976
3161
|
}
|
|
2977
3162
|
function saveDoc(guard, file, doc) {
|
|
2978
|
-
fs11.mkdirSync(
|
|
3163
|
+
fs11.mkdirSync(path11.dirname(file), { recursive: true });
|
|
2979
3164
|
fs11.writeFileSync(guard.assert(file), JSON.stringify(doc, null, 2), "utf8");
|
|
2980
3165
|
}
|
|
2981
3166
|
function normalizeInterest(text) {
|
|
@@ -3043,7 +3228,7 @@ function setWanderWindows(guard, paths, rawWindows) {
|
|
|
3043
3228
|
|
|
3044
3229
|
// src/statusbar/time-inject.ts
|
|
3045
3230
|
import fs12 from "fs";
|
|
3046
|
-
import
|
|
3231
|
+
import path12 from "path";
|
|
3047
3232
|
function shouldInjectTime(input) {
|
|
3048
3233
|
if (input.step !== 1) return false;
|
|
3049
3234
|
if (!input.originIsInboxSplice) return false;
|
|
@@ -3105,7 +3290,7 @@ function lastMessageTime(session) {
|
|
|
3105
3290
|
return void 0;
|
|
3106
3291
|
}
|
|
3107
3292
|
function timeInjectStatePath(dataDir) {
|
|
3108
|
-
return
|
|
3293
|
+
return path12.join(dataDir, "time-inject-state.json");
|
|
3109
3294
|
}
|
|
3110
3295
|
function loadTimeInjectState(guard, dataDir) {
|
|
3111
3296
|
try {
|
|
@@ -3174,7 +3359,7 @@ var cachedVersion = null;
|
|
|
3174
3359
|
function pluginVersion(paths) {
|
|
3175
3360
|
if (cachedVersion) return cachedVersion;
|
|
3176
3361
|
try {
|
|
3177
|
-
const pkg = JSON.parse(fs13.readFileSync(
|
|
3362
|
+
const pkg = JSON.parse(fs13.readFileSync(path13.join(paths.packageRoot, "package.json"), "utf8"));
|
|
3178
3363
|
cachedVersion = typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
3179
3364
|
} catch {
|
|
3180
3365
|
cachedVersion = "unknown";
|
|
@@ -3183,12 +3368,20 @@ function pluginVersion(paths) {
|
|
|
3183
3368
|
}
|
|
3184
3369
|
function homeSessionId(paths, guard) {
|
|
3185
3370
|
try {
|
|
3186
|
-
const raw = loadEncryptedText(guard,
|
|
3371
|
+
const raw = loadEncryptedText(guard, path13.join(paths.dataDir, "gate.json"));
|
|
3187
3372
|
return JSON.parse(raw ?? "{}").sessionId ?? null;
|
|
3188
3373
|
} catch {
|
|
3189
3374
|
return null;
|
|
3190
3375
|
}
|
|
3191
3376
|
}
|
|
3377
|
+
function seedAuditSink2(paths) {
|
|
3378
|
+
return (entry) => {
|
|
3379
|
+
try {
|
|
3380
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", entry);
|
|
3381
|
+
} catch {
|
|
3382
|
+
}
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3192
3385
|
function auditInterests(paths, action, result, detail) {
|
|
3193
3386
|
if (!result.ok) return;
|
|
3194
3387
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
@@ -3205,13 +3398,13 @@ function loadSessionTitles() {
|
|
|
3205
3398
|
if (row && typeof row.val === "string" && row.val && !titles[id]) titles[id] = row.val;
|
|
3206
3399
|
};
|
|
3207
3400
|
try {
|
|
3208
|
-
const dir =
|
|
3401
|
+
const dir = path13.join(os.homedir(), ".dsh", "storages", "session_projcache", "sessions");
|
|
3209
3402
|
for (const file of fs13.readdirSync(dir)) {
|
|
3210
3403
|
if (!file.endsWith(".json")) continue;
|
|
3211
3404
|
const id = file.slice(0, -".json".length);
|
|
3212
3405
|
if (!id.startsWith("session-")) continue;
|
|
3213
3406
|
try {
|
|
3214
|
-
const record = JSON.parse(fs13.readFileSync(
|
|
3407
|
+
const record = JSON.parse(fs13.readFileSync(path13.join(dir, file), "utf8"));
|
|
3215
3408
|
take(id, record.record);
|
|
3216
3409
|
} catch {
|
|
3217
3410
|
}
|
|
@@ -3219,7 +3412,7 @@ function loadSessionTitles() {
|
|
|
3219
3412
|
} catch {
|
|
3220
3413
|
}
|
|
3221
3414
|
try {
|
|
3222
|
-
const raw = JSON.parse(fs13.readFileSync(
|
|
3415
|
+
const raw = JSON.parse(fs13.readFileSync(path13.join(os.homedir(), ".dsh", "storages", "session_projcache.json"), "utf8"));
|
|
3223
3416
|
const walk = (node) => {
|
|
3224
3417
|
if (!node || typeof node !== "object") return;
|
|
3225
3418
|
for (const [key, value] of Object.entries(node)) {
|
|
@@ -3237,7 +3430,7 @@ function loadSessionTitles() {
|
|
|
3237
3430
|
}
|
|
3238
3431
|
function loadVisibleSessionIds(registryFile) {
|
|
3239
3432
|
try {
|
|
3240
|
-
const file = registryFile ??
|
|
3433
|
+
const file = registryFile ?? path13.join(os.homedir(), ".dsh", "storages", "workspace.json");
|
|
3241
3434
|
const j = JSON.parse(fs13.readFileSync(file, "utf8"));
|
|
3242
3435
|
const archived = new Set(
|
|
3243
3436
|
(Array.isArray(j.global?.archivedSessionIds) ? j.global.archivedSessionIds : []).map((s) => String(s))
|
|
@@ -3309,7 +3502,7 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3309
3502
|
});
|
|
3310
3503
|
}
|
|
3311
3504
|
case "sessions.list": {
|
|
3312
|
-
const root =
|
|
3505
|
+
const root = path13.join(os.homedir(), ".dsh", "sessions");
|
|
3313
3506
|
const bindings = loadBindings(guard, paths.settingsDir).bindings;
|
|
3314
3507
|
const home = homeSessionId(paths, guard);
|
|
3315
3508
|
const titles = loadSessionTitles();
|
|
@@ -3317,7 +3510,7 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3317
3510
|
const out = [];
|
|
3318
3511
|
if (fs13.existsSync(root)) {
|
|
3319
3512
|
for (const slug of fs13.readdirSync(root)) {
|
|
3320
|
-
for (const id of fs13.readdirSync(
|
|
3513
|
+
for (const id of fs13.readdirSync(path13.join(root, slug))) {
|
|
3321
3514
|
if (registryFound && id !== home && !visible.has(id)) continue;
|
|
3322
3515
|
const binding = bindings.find((b) => b.sessionId === id);
|
|
3323
3516
|
out.push({
|
|
@@ -3353,7 +3546,7 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3353
3546
|
let homeReset = false;
|
|
3354
3547
|
if (id === homeSessionId(paths, guard)) {
|
|
3355
3548
|
try {
|
|
3356
|
-
fs13.rmSync(guard.assert(
|
|
3549
|
+
fs13.rmSync(guard.assert(path13.join(paths.dataDir, "gate.json")), { force: true });
|
|
3357
3550
|
homeReset = true;
|
|
3358
3551
|
} catch {
|
|
3359
3552
|
}
|
|
@@ -3369,15 +3562,15 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3369
3562
|
});
|
|
3370
3563
|
}
|
|
3371
3564
|
case "seeds.archive": {
|
|
3372
|
-
const s = archiveSeedById(guard, seedsFilePath(paths.dataDir), String(p.id ?? ""), "completed");
|
|
3565
|
+
const s = archiveSeedById(guard, seedsFilePath(paths.dataDir), String(p.id ?? ""), "completed", Date.now(), seedAuditSink2(paths));
|
|
3373
3566
|
return s ? ok(s) : err("not-found", "active seed not found");
|
|
3374
3567
|
}
|
|
3375
3568
|
case "seeds.restore": {
|
|
3376
|
-
const r = restoreSeed(guard, seedsFilePath(paths.dataDir), policy, String(p.id ?? ""));
|
|
3569
|
+
const r = restoreSeed(guard, seedsFilePath(paths.dataDir), policy, String(p.id ?? ""), Date.now(), seedAuditSink2(paths));
|
|
3377
3570
|
return r.ok ? ok(r.seed) : err("restore-failed", r.reason);
|
|
3378
3571
|
}
|
|
3379
3572
|
case "seeds.delete": {
|
|
3380
|
-
return ok({ deleted: deleteSeed(guard, seedsFilePath(paths.dataDir), String(p.id ?? "")) });
|
|
3573
|
+
return ok({ deleted: deleteSeed(guard, seedsFilePath(paths.dataDir), String(p.id ?? ""), seedAuditSink2(paths)) });
|
|
3381
3574
|
}
|
|
3382
3575
|
// ── 兴趣范围 / 浏览时段(v1.4.0;首编继承出厂,见 interests-edit.ts)──
|
|
3383
3576
|
case "interests.list":
|
|
@@ -3435,7 +3628,7 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3435
3628
|
lines.push(`- [${e.topic}/${e.subTopic}] ${e.content} (conf ${e.confidence.toFixed(2)}, ${e.temporal})`);
|
|
3436
3629
|
}
|
|
3437
3630
|
}
|
|
3438
|
-
const out =
|
|
3631
|
+
const out = path13.join(paths.exportsDir, `profile-export-${Date.now()}.md`);
|
|
3439
3632
|
writeText(guard, out, lines.join("\n") + "\n");
|
|
3440
3633
|
return ok({ path: out });
|
|
3441
3634
|
}
|
|
@@ -3456,10 +3649,10 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3456
3649
|
case "migrate.export": {
|
|
3457
3650
|
const passphrase = String(p.passphrase ?? "");
|
|
3458
3651
|
if (!passphrase) return err("bad-request", "\u9700\u8981\u8BBE\u7F6E\u53E3\u4EE4");
|
|
3459
|
-
const { collectMigrationEntries, encryptContainer } = await import("./migrate-
|
|
3652
|
+
const { collectMigrationEntries, encryptContainer } = await import("./migrate-N6NXMZ3G.js");
|
|
3460
3653
|
const { entries } = collectMigrationEntries(guard, paths);
|
|
3461
3654
|
if (entries.length === 0) return err("bad-request", "\u6CA1\u6709\u53EF\u6253\u5305\u7684\u8BB0\u5FC6\u6587\u4EF6\uFF08data/ \u662F\u7A7A\u7684\uFF09");
|
|
3462
|
-
const out =
|
|
3655
|
+
const out = path13.join(paths.exportsDir, `heartbeat-memory-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.hbmig`);
|
|
3463
3656
|
writeText(guard, out, encryptContainer(entries, passphrase));
|
|
3464
3657
|
appendAuditLine(guard.assert(paths.logsDir + "/heartbeat.jsonl"), { event: "migrate_export", files: entries.length });
|
|
3465
3658
|
return ok({ path: out, count: entries.length });
|
|
@@ -3468,7 +3661,7 @@ function installHeartbeatRpc(ctx, deps) {
|
|
|
3468
3661
|
const passphrase = String(p.passphrase ?? "");
|
|
3469
3662
|
const file = String(p.file ?? "");
|
|
3470
3663
|
if (!passphrase || !file) return err("bad-request", "\u9700\u8981\u5BB9\u5668\u8DEF\u5F84\u4E0E\u53E3\u4EE4");
|
|
3471
|
-
const { decryptContainer, applyMigrationEntries } = await import("./migrate-
|
|
3664
|
+
const { decryptContainer, applyMigrationEntries } = await import("./migrate-N6NXMZ3G.js");
|
|
3472
3665
|
let text;
|
|
3473
3666
|
try {
|
|
3474
3667
|
text = fs13.readFileSync(file, "utf8");
|
|
@@ -3721,6 +3914,8 @@ var Config = Schema.object({
|
|
|
3721
3914
|
tokenSaver: Schema.boolean().default(false),
|
|
3722
3915
|
/** v1.8.0 账本工具:向所有日常会话 agent 注册共享账本工具(默认开)。 */
|
|
3723
3916
|
ledgerTool: Schema.boolean().default(true),
|
|
3917
|
+
/** v1.9.0 素材报账工具:投递后由陪伴 agent 主动报账(默认开)。 */
|
|
3918
|
+
seedReportTool: Schema.boolean().default(true),
|
|
3724
3919
|
/** v1.8.0 引擎室追加工具:逗号分隔的全局工具名,存在才加入白名单(bili 压缩工具自动探测,无需手填)。 */
|
|
3725
3920
|
extraTools: Schema.string().default("")
|
|
3726
3921
|
});
|
|
@@ -3895,6 +4090,24 @@ function apply(ctx, config = {}) {
|
|
|
3895
4090
|
}
|
|
3896
4091
|
});
|
|
3897
4092
|
}
|
|
4093
|
+
if (config.seedReportTool !== false) {
|
|
4094
|
+
ctx.inject(["tools"], (scoped) => {
|
|
4095
|
+
const audit = (entry) => {
|
|
4096
|
+
try {
|
|
4097
|
+
appendAuditLine(guard.assert(paths.logsDir + "/heartbeat.jsonl"), entry);
|
|
4098
|
+
} catch {
|
|
4099
|
+
}
|
|
4100
|
+
};
|
|
4101
|
+
try {
|
|
4102
|
+
const tools = scoped.tools;
|
|
4103
|
+
if (!tools || typeof tools.register !== "function") throw new Error("ToolRuntime.register unavailable on this host");
|
|
4104
|
+
tools.register(buildSeedReportTool(guard, reportFilePath(paths.dataDir)));
|
|
4105
|
+
audit({ event: "seed_report_tool_registered" });
|
|
4106
|
+
} catch (e) {
|
|
4107
|
+
audit({ event: "seed_report_tool_register_failed", error: String(e).slice(0, 160) });
|
|
4108
|
+
}
|
|
4109
|
+
});
|
|
4110
|
+
}
|
|
3898
4111
|
startOrchestrator(deps);
|
|
3899
4112
|
const statusReader = new StatusReader();
|
|
3900
4113
|
registerStatusbarSection(ctx, guard, paths, {
|