@kanadego/dsh-heartbeat 1.9.1 → 1.9.3
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 +351 -374
- package/client.js +96 -20
- package/config/policy.json +4 -3
- package/cordis.patch.yml +18 -39
- package/dist/{chunk-BQCXYE7N.js → chunk-3MDBU6WY.js} +2 -2
- package/dist/{chunk-OYVNWB5G.js → chunk-CUOSICYJ.js} +2 -2
- package/dist/{chunk-KOOQOMQY.js → chunk-JDH2LDFS.js} +2 -2
- package/dist/chunk-K5Y6JP2B.js +399 -0
- package/dist/chunk-K5Y6JP2B.js.map +1 -0
- package/dist/{chunk-QOQ7EPZZ.js → chunk-KB5SMG3F.js} +3 -3
- package/dist/{chunk-HT57FOEF.js → chunk-TRO4E6WG.js} +164 -35
- package/dist/chunk-TRO4E6WG.js.map +1 -0
- package/dist/{chunk-WRJCINZ7.js → chunk-WLZSABBH.js} +20 -6
- package/dist/chunk-WLZSABBH.js.map +1 -0
- package/dist/{chunk-2NB4QILF.js → chunk-XFNCTOW7.js} +2 -2
- package/dist/{chunk-ZLV4NMFX.js → chunk-XYYEJK3W.js} +4 -1
- package/dist/chunk-XYYEJK3W.js.map +1 -0
- package/dist/cli/index.js +10 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/{inbox-JLT5G7TL.js → inbox-P26DDMWC.js} +3 -3
- package/dist/index.d.ts +35 -0
- package/dist/index.js +137 -26
- package/dist/index.js.map +1 -1
- package/dist/{migrate-WDVFHUP7.js → migrate-D4VKAKYT.js} +3 -3
- package/dist/{orchestrator-2ZLU7RGE.js → orchestrator-ZPRGVDCB.js} +16 -10
- package/dist/{pool-B34NTH6D.js → pool-UTXQJJ5C.js} +3 -3
- package/dist/{snapshot-JQDJ346Z.js → snapshot-K5VHETTK.js} +4 -4
- package/package.json +83 -83
- package/dist/chunk-7TW6DD6Q.js +0 -208
- package/dist/chunk-7TW6DD6Q.js.map +0 -1
- package/dist/chunk-HT57FOEF.js.map +0 -1
- package/dist/chunk-WRJCINZ7.js.map +0 -1
- package/dist/chunk-ZLV4NMFX.js.map +0 -1
- /package/dist/{chunk-BQCXYE7N.js.map → chunk-3MDBU6WY.js.map} +0 -0
- /package/dist/{chunk-OYVNWB5G.js.map → chunk-CUOSICYJ.js.map} +0 -0
- /package/dist/{chunk-KOOQOMQY.js.map → chunk-JDH2LDFS.js.map} +0 -0
- /package/dist/{chunk-QOQ7EPZZ.js.map → chunk-KB5SMG3F.js.map} +0 -0
- /package/dist/{chunk-2NB4QILF.js.map → chunk-XFNCTOW7.js.map} +0 -0
- /package/dist/{inbox-JLT5G7TL.js.map → inbox-P26DDMWC.js.map} +0 -0
- /package/dist/{migrate-WDVFHUP7.js.map → migrate-D4VKAKYT.js.map} +0 -0
- /package/dist/{orchestrator-2ZLU7RGE.js.map → orchestrator-ZPRGVDCB.js.map} +0 -0
- /package/dist/{pool-B34NTH6D.js.map → pool-UTXQJJ5C.js.map} +0 -0
- /package/dist/{snapshot-JQDJ346Z.js.map → snapshot-K5VHETTK.js.map} +0 -0
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
minutesOfDay,
|
|
11
11
|
pendingOlderThan,
|
|
12
12
|
preferenceFilePath,
|
|
13
|
+
pruneWeeklyReports,
|
|
13
14
|
readLedger,
|
|
14
15
|
recordDelivery,
|
|
15
16
|
renderTemplateReport,
|
|
@@ -20,7 +21,7 @@ import {
|
|
|
20
21
|
sendNewMessageHint,
|
|
21
22
|
sendWeeklyReadyHint,
|
|
22
23
|
weeklyDue
|
|
23
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WLZSABBH.js";
|
|
24
25
|
import {
|
|
25
26
|
getRuntime
|
|
26
27
|
} from "./chunk-3QJJRXIR.js";
|
|
@@ -31,17 +32,17 @@ import {
|
|
|
31
32
|
loadPool,
|
|
32
33
|
seedsFilePath,
|
|
33
34
|
surfaceSeed
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-JDH2LDFS.js";
|
|
35
36
|
import {
|
|
36
37
|
dedupeItems,
|
|
37
38
|
inboxClear,
|
|
38
39
|
inboxCount,
|
|
39
40
|
inboxDrain,
|
|
40
41
|
inboxFilePath
|
|
41
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-3MDBU6WY.js";
|
|
42
43
|
import {
|
|
43
44
|
snapshotIfDue
|
|
44
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-KB5SMG3F.js";
|
|
45
46
|
import {
|
|
46
47
|
appendAuditLine,
|
|
47
48
|
applyOpsToDoc,
|
|
@@ -53,7 +54,7 @@ import {
|
|
|
53
54
|
readAuditLines,
|
|
54
55
|
readJournalTexts,
|
|
55
56
|
runDeterministicAging
|
|
56
|
-
} from "./chunk-
|
|
57
|
+
} from "./chunk-CUOSICYJ.js";
|
|
57
58
|
import {
|
|
58
59
|
decryptFile,
|
|
59
60
|
encryptFile,
|
|
@@ -63,7 +64,7 @@ import {
|
|
|
63
64
|
saveEncryptedText,
|
|
64
65
|
saveJson,
|
|
65
66
|
writeText
|
|
66
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-K5Y6JP2B.js";
|
|
67
68
|
import {
|
|
68
69
|
atomicWriteJsonSync,
|
|
69
70
|
burnFileSync
|
|
@@ -560,7 +561,8 @@ function assembleCandidates(seeds, opts = {}) {
|
|
|
560
561
|
if (topic.length < 4) chat.push(...chatPool.slice(2, 2 + (4 - topic.length)));
|
|
561
562
|
return [...topic, ...chat].slice(0, 6);
|
|
562
563
|
}
|
|
563
|
-
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";
|
|
564
|
+
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\u5F00\u53E3:\u8BE5\u8BF4\u5C31\u9009\u4E00\u6761\u8BF4,\u4E0D\u8BE5\u8BF4\u5C31\u7559\u767D";
|
|
565
|
+
var SILENCE_DECLARE = '(\u51B3\u5B9A\u4E0D\u8BF4\u7684\u65F6\u5019:\u56DE\u590D\u91CC\u4E0D\u8981\u5199\u4EFB\u4F55\u6B63\u6587,\u4E5F\u4E0D\u8981\u5199"\u55EF""\u5728\u7684"\u8FD9\u79CD\u5BA2\u5957,\u53EA\u8C03\u4E00\u6B21 seed_report \u62A5 spoken=silent \u5E76\u5199\u4E0A reason;\u7A7A\u7740\u5C31\u662F\u771F\u7684\u6C89\u9ED8)';
|
|
564
566
|
function materialLines(materials) {
|
|
565
567
|
return materials.map((m) => `- [${m.id}] ${m.text.trim()}`);
|
|
566
568
|
}
|
|
@@ -578,13 +580,14 @@ function buildMaterialPrompt(materials, opts = {}) {
|
|
|
578
580
|
if (wantHonestOption(materials, opts.threshold ?? 2)) {
|
|
579
581
|
lines.push("(\u6216\u8005\u4E5F\u53EF\u4EE5\u8BF4\u4E00\u53E5\u771F\u5FC3\u8BDD,\u4E0D\u5E26\u7D20\u6750)");
|
|
580
582
|
}
|
|
583
|
+
lines.push(SILENCE_DECLARE);
|
|
581
584
|
if (opts.deliveryId) {
|
|
582
585
|
lines.push(`(\u672C\u6B21\u6295\u9012\u7F16\u53F7:${opts.deliveryId},\u62A5\u8D26\u65F6\u539F\u6837\u586B\u8FDB delivery_id)`);
|
|
583
586
|
}
|
|
584
587
|
lines.push(REPORT_DECLARE);
|
|
585
588
|
return lines.join("\n");
|
|
586
589
|
}
|
|
587
|
-
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";
|
|
590
|
+
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(\u4E0D\u8BF4\u8BDD\u65F6\u6B63\u6587\u5C31\u7559\u7A7A):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";
|
|
588
591
|
function explicitIds(candidates, ids = []) {
|
|
589
592
|
const known = new Set(candidates.map((c) => c.id));
|
|
590
593
|
return [...new Set(ids)].filter((id) => known.has(id));
|
|
@@ -990,7 +993,7 @@ async function runConsolidation(guard, paths, policy, llm, now = Date.now()) {
|
|
|
990
993
|
const report = applyOpsToDoc(guard, paths.dataDir, doc, profileOpsCast, schema, policy, now);
|
|
991
994
|
const aged = runDeterministicAging(doc, policy, now);
|
|
992
995
|
persistWithJournal(guard, paths.dataDir, doc, { runId, applied: report.applied, rejected: report.rejected });
|
|
993
|
-
const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-
|
|
996
|
+
const { addSeed: addSeed2, seedsFilePath: seedsFilePath2 } = await import("./pool-UTXQJJ5C.js");
|
|
994
997
|
const seedAudit = (entry) => {
|
|
995
998
|
try {
|
|
996
999
|
appendAuditLine(paths.dataDir + "/logs/heartbeat.jsonl", entry);
|
|
@@ -1100,7 +1103,11 @@ function summarizeRhythm(paths) {
|
|
|
1100
1103
|
}
|
|
1101
1104
|
|
|
1102
1105
|
// src/profile/digest.ts
|
|
1103
|
-
var BUDGET_CHARS =
|
|
1106
|
+
var BUDGET_CHARS = 6e3;
|
|
1107
|
+
var TACT_MAX_CHARS = 2500;
|
|
1108
|
+
var WANDER_MAX_CHARS = 850;
|
|
1109
|
+
var TOPIC_MIN_CHARS = 2400;
|
|
1110
|
+
var TOPN_DEFAULT = 12;
|
|
1104
1111
|
function fmtEntry(prefix, content, opts) {
|
|
1105
1112
|
const flag = opts.low ? "\uFF08\u4E45\u672A\u9A8C\u8BC1\uFF09" : "";
|
|
1106
1113
|
return `${prefix}${content}${flag} [conf ${opts.confidence.toFixed(2)}]`;
|
|
@@ -1109,9 +1116,25 @@ function profileTopicEntries(doc, topN) {
|
|
|
1109
1116
|
const score = (e) => e.confidence * 0.7 + 1 / (1 + Math.max(0, Date.now() - Date.parse(e.updatedAt)) / 864e5) * 0.3;
|
|
1110
1117
|
return [...doc.partitions.interest.entries, ...doc.partitions.projects.entries].filter((e) => e.validTo === null).sort((a, b) => score(b) - score(a)).slice(0, topN);
|
|
1111
1118
|
}
|
|
1119
|
+
function capSection(entries, cap) {
|
|
1120
|
+
const kept = [];
|
|
1121
|
+
let len = 0;
|
|
1122
|
+
let omitted = 0;
|
|
1123
|
+
for (const line of entries) {
|
|
1124
|
+
const add = kept.length === 0 ? line.length : line.length + 1;
|
|
1125
|
+
if (len + add > cap) {
|
|
1126
|
+
omitted += 1;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
kept.push(line);
|
|
1130
|
+
len += add;
|
|
1131
|
+
}
|
|
1132
|
+
if (kept.length === 0 && omitted > 0) return { text: `\uFF08\u672C\u8282 ${omitted} \u6761\u8D85\u51FA\u9884\u7B97\uFF0C\u5DF2\u7701\u7565\uFF09`, omitted };
|
|
1133
|
+
return { text: kept.join("\n"), omitted };
|
|
1134
|
+
}
|
|
1112
1135
|
function buildDigest(guard, paths, policy, input = {}) {
|
|
1113
1136
|
const doc = loadProfile(guard, profileFilePath(paths.dataDir));
|
|
1114
|
-
const topN = input.topN ??
|
|
1137
|
+
const topN = input.topN ?? TOPN_DEFAULT;
|
|
1115
1138
|
const summary = summarizeRhythm(paths);
|
|
1116
1139
|
let rhythmLine = "\u4F5C\u606F\u672A\u77E5\uFF08\u6837\u672C\u4E0D\u8DB3\uFF09";
|
|
1117
1140
|
if (summary.daysSampled > 0 && summary.peakHours.length > 0) {
|
|
@@ -1127,24 +1150,25 @@ function buildDigest(guard, paths, policy, input = {}) {
|
|
|
1127
1150
|
return fmtEntry(prefix, `${e.topic}/${e.subTopic}: ${e.content}`, { low: e.lowActivity, confidence: e.confidence });
|
|
1128
1151
|
});
|
|
1129
1152
|
const wanderEntries = doc.partitions.interest.entries.filter((e) => e.validTo === null && e.confidence >= 0.6 && e.subTopic === "preference").slice(0, 5).map((e) => `- ${e.content} [conf ${e.confidence.toFixed(2)}]`);
|
|
1130
|
-
const
|
|
1131
|
-
const
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
}
|
|
1153
|
+
const ledgerBlock = pendingOlderThan(guard, ledgerFilePath(paths.dataDir), 3).slice(0, 3).map((e) => `- ${e.text}\uFF08${e.date}\uFF09`);
|
|
1154
|
+
const ledgerText = ledgerBlock.length ? ["[\u8D26\u672C\u5F85\u8DDF\u8FDB] ", ...ledgerBlock].join("\n") : "";
|
|
1155
|
+
const tact = capSection(tactParts, TACT_MAX_CHARS);
|
|
1156
|
+
const wander = capSection(wanderEntries.length ? wanderEntries : ["(\u65E0\u9AD8\u7F6E\u4FE1\u5174\u8DA3)"], WANDER_MAX_CHARS);
|
|
1157
|
+
const topicBudget = Math.max(
|
|
1158
|
+
TOPIC_MIN_CHARS,
|
|
1159
|
+
BUDGET_CHARS - tact.text.length - wander.text.length - ledgerText.length - 40
|
|
1160
|
+
);
|
|
1161
|
+
const topicCapped = capSection(topicEntries, topicBudget);
|
|
1162
|
+
const topic = [topicCapped.text, ledgerText].filter((s) => s.length > 0).join("\n");
|
|
1163
|
+
const totalChars = tact.text.length + topic.length + wander.text.length;
|
|
1142
1164
|
return {
|
|
1143
|
-
tact,
|
|
1144
|
-
topic
|
|
1145
|
-
wander,
|
|
1146
|
-
totalChars
|
|
1147
|
-
withinBudget:
|
|
1165
|
+
tact: tact.text,
|
|
1166
|
+
topic,
|
|
1167
|
+
wander: wander.text,
|
|
1168
|
+
totalChars,
|
|
1169
|
+
withinBudget: totalChars <= BUDGET_CHARS,
|
|
1170
|
+
omitted: { tact: tact.omitted, topic: topicCapped.omitted, wander: wander.omitted },
|
|
1171
|
+
budgetChars: BUDGET_CHARS
|
|
1148
1172
|
};
|
|
1149
1173
|
}
|
|
1150
1174
|
|
|
@@ -1277,6 +1301,13 @@ function applyHeartbeatInterval(deps, intervalMin) {
|
|
|
1277
1301
|
reschedule?.(v);
|
|
1278
1302
|
appendAuditLine(deps.paths.logsDir + "/heartbeat.jsonl", { event: "interval_changed", intervalMin: v });
|
|
1279
1303
|
}
|
|
1304
|
+
function looksPermanentResumeFailure(error) {
|
|
1305
|
+
return [
|
|
1306
|
+
/SessionFormatUnsupportedError/i,
|
|
1307
|
+
/refuses the transformed artifact/i,
|
|
1308
|
+
/session migration[^]*refus/i
|
|
1309
|
+
].some((re) => re.test(error));
|
|
1310
|
+
}
|
|
1280
1311
|
function sessionEvents(session, fromSeq) {
|
|
1281
1312
|
if (!session) return [];
|
|
1282
1313
|
if (typeof session.snapshotEvents === "function") {
|
|
@@ -1398,6 +1429,11 @@ function makeHomeSetup(deps) {
|
|
|
1398
1429
|
return async (agentCtx) => {
|
|
1399
1430
|
const notes = [];
|
|
1400
1431
|
const presetId = deps.agentPreset ?? "heartbeat";
|
|
1432
|
+
try {
|
|
1433
|
+
await withTimeout(deps.presetRegistration?.() ?? Promise.resolve(), 3e4, "preset registration timeout (30s)");
|
|
1434
|
+
} catch (e) {
|
|
1435
|
+
notes.push(`preset-register=threw(${String(e).slice(0, 120)})`);
|
|
1436
|
+
}
|
|
1401
1437
|
try {
|
|
1402
1438
|
const presets = agentCtx.get("agentPresets");
|
|
1403
1439
|
if (typeof presets?.mount !== "function") {
|
|
@@ -1539,12 +1575,15 @@ async function ensureAgent(deps) {
|
|
|
1539
1575
|
agent = unwrap(handle);
|
|
1540
1576
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "agent_resume_ok", sessionId: savedId, model: agent.options?.model ?? "(none)" });
|
|
1541
1577
|
} catch (resumeErr) {
|
|
1578
|
+
const resumeMessage = String(resumeErr);
|
|
1542
1579
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
1543
1580
|
event: "agent_resume_failed",
|
|
1544
1581
|
sessionId: savedId,
|
|
1545
|
-
error:
|
|
1582
|
+
error: resumeMessage.slice(0, 160)
|
|
1546
1583
|
});
|
|
1547
|
-
|
|
1584
|
+
const gone = /not found/i.test(resumeMessage);
|
|
1585
|
+
const permanent = !gone && looksPermanentResumeFailure(resumeMessage);
|
|
1586
|
+
if (!gone && !permanent) {
|
|
1548
1587
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
1549
1588
|
event: "agent_deferred",
|
|
1550
1589
|
sessionId: savedId,
|
|
@@ -1553,6 +1592,13 @@ async function ensureAgent(deps) {
|
|
|
1553
1592
|
agentPromise = null;
|
|
1554
1593
|
return null;
|
|
1555
1594
|
}
|
|
1595
|
+
if (permanent) {
|
|
1596
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
1597
|
+
event: "agent_resume_permanent",
|
|
1598
|
+
sessionId: savedId,
|
|
1599
|
+
error: resumeMessage.slice(0, 160)
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1556
1602
|
try {
|
|
1557
1603
|
const handle = await withTimeout(Promise.resolve(ctx.agents.create({ sessionId: savedId, meta: { cwd: paths.dataDir }, ...agentOptions ? { agentOptions } : {}, setup })), 3e4, "agents.create (self-heal) timeout");
|
|
1558
1604
|
agent = unwrap(handle);
|
|
@@ -1760,7 +1806,7 @@ async function observeBoundSessions(bc) {
|
|
|
1760
1806
|
const { deps, now } = bc;
|
|
1761
1807
|
const { guard, paths } = deps;
|
|
1762
1808
|
const { loadBindings, observeTargets } = await import("./bindings-225SI7KC.js");
|
|
1763
|
-
const { inboxAppend, inboxFilePath: inboxFilePath2 } = await import("./inbox-
|
|
1809
|
+
const { inboxAppend, inboxFilePath: inboxFilePath2 } = await import("./inbox-P26DDMWC.js");
|
|
1764
1810
|
const data = loadBindings(guard, paths.settingsDir);
|
|
1765
1811
|
const targets = observeTargets(data);
|
|
1766
1812
|
if (targets.length === 0) return;
|
|
@@ -1847,6 +1893,10 @@ async function weeklyPhase(bc) {
|
|
|
1847
1893
|
text
|
|
1848
1894
|
});
|
|
1849
1895
|
audit({ event: "weekly_generated", source, chars: text.length });
|
|
1896
|
+
const pruned = pruneWeeklyReports(guard, paths.dataDir, policy.weekly.keepReports);
|
|
1897
|
+
if (pruned.length > 0) {
|
|
1898
|
+
audit({ event: "weekly_pruned", keep: policy.weekly.keepReports, removed: pruned });
|
|
1899
|
+
}
|
|
1850
1900
|
if (!inQuietHours(policy, now)) {
|
|
1851
1901
|
try {
|
|
1852
1902
|
await sendWeeklyReadyHint(paths);
|
|
@@ -2240,13 +2290,13 @@ function writeBeatStatus(deps, info) {
|
|
|
2240
2290
|
const pulse = readPulse(guard, paths);
|
|
2241
2291
|
const last = getLastBeat();
|
|
2242
2292
|
const spokeThisBeat = last?.verdict === "spoke" && !!last.at && last.at >= info.beatStart;
|
|
2243
|
-
const scene = deriveScene({
|
|
2293
|
+
const scene = info.away ? "away" : deriveScene({
|
|
2244
2294
|
quietHours: inQuietHours(policy, Date.now()),
|
|
2245
2295
|
spokeThisBeat,
|
|
2246
2296
|
wanderedThisBeat: info.wandered,
|
|
2247
2297
|
presence: pulse?.presence ?? "unknown"
|
|
2248
2298
|
});
|
|
2249
|
-
const note = last?.verdict === "spoke" ? clampNote(last.text) : void 0;
|
|
2299
|
+
const note = info.away ? "\u4F60\u4E0D\u5728\uFF0C\u5FC3\u8DF3\u6302\u8D77" : last?.verdict === "spoke" ? clampNote(last.text) : void 0;
|
|
2250
2300
|
writeStatus(guard, paths, {
|
|
2251
2301
|
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2252
2302
|
scene,
|
|
@@ -2269,12 +2319,72 @@ function scheduleDeferredRetry(deps) {
|
|
|
2269
2319
|
}, delayMs);
|
|
2270
2320
|
}
|
|
2271
2321
|
var TOKEN_SAVER_IDLE_SECONDS = 1800;
|
|
2322
|
+
var TOKEN_SAVER_WAKE_MS = 3 * 6e4;
|
|
2323
|
+
var TOKEN_SAVER_ACTIVITY_SCAN = 200;
|
|
2324
|
+
var wakeTimer;
|
|
2325
|
+
var pausedSince = 0;
|
|
2326
|
+
var heldChecks = 0;
|
|
2327
|
+
function clearTokenSaverWake() {
|
|
2328
|
+
if (wakeTimer) {
|
|
2329
|
+
clearTimeout(wakeTimer);
|
|
2330
|
+
wakeTimer = void 0;
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
function scheduleTokenSaverWake(deps) {
|
|
2334
|
+
if (!pausedSince) pausedSince = Date.now();
|
|
2335
|
+
if (wakeTimer) return;
|
|
2336
|
+
wakeTimer = setTimeout(() => {
|
|
2337
|
+
wakeTimer = void 0;
|
|
2338
|
+
void (async () => {
|
|
2339
|
+
try {
|
|
2340
|
+
if (await tokenSaverActive(deps)) {
|
|
2341
|
+
heldChecks += 1;
|
|
2342
|
+
scheduleTokenSaverWake(deps);
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
const heldMin = Math.round((Date.now() - pausedSince) / 6e4);
|
|
2346
|
+
const checks = heldChecks;
|
|
2347
|
+
clearTokenSaverWake();
|
|
2348
|
+
pausedSince = 0;
|
|
2349
|
+
heldChecks = 0;
|
|
2350
|
+
appendAuditLine(deps.paths.logsDir + "/heartbeat.jsonl", { event: "token_saver_resumed", via: "wake", heldMin, checks });
|
|
2351
|
+
void beat(deps);
|
|
2352
|
+
} catch {
|
|
2353
|
+
}
|
|
2354
|
+
})();
|
|
2355
|
+
}, TOKEN_SAVER_WAKE_MS);
|
|
2356
|
+
wakeTimer.unref?.();
|
|
2357
|
+
}
|
|
2272
2358
|
async function tokenSaverActive(deps) {
|
|
2273
2359
|
if (!getRuntime().flags.tokenSaver()) return false;
|
|
2360
|
+
if (await userActiveInBoundSessions(deps)) return false;
|
|
2274
2361
|
if (await probeWorkstationLocked()) return true;
|
|
2275
2362
|
const idle = await probeIdleSeconds(deps.guard, deps.paths);
|
|
2276
2363
|
return idle >= TOKEN_SAVER_IDLE_SECONDS;
|
|
2277
2364
|
}
|
|
2365
|
+
async function userActiveInBoundSessions(deps) {
|
|
2366
|
+
try {
|
|
2367
|
+
const { loadBindings, observeTargets } = await import("./bindings-225SI7KC.js");
|
|
2368
|
+
const targets = observeTargets(loadBindings(deps.guard, deps.paths.settingsDir));
|
|
2369
|
+
if (targets.length === 0) return false;
|
|
2370
|
+
const cursors = loadCursors(path9.join(deps.paths.dataDir, "cursors.json")).sessions;
|
|
2371
|
+
for (const b of targets) {
|
|
2372
|
+
const agent = ctx_getAgent(deps, b.sessionId);
|
|
2373
|
+
if (!agent) continue;
|
|
2374
|
+
const count = sessionEventCount(agent.session);
|
|
2375
|
+
const from = Math.max(cursors[b.sessionId] ?? 0, count - TOKEN_SAVER_ACTIVITY_SCAN);
|
|
2376
|
+
for (const e of sessionEvents(agent.session, from)) {
|
|
2377
|
+
if (e.type !== "user/message") continue;
|
|
2378
|
+
const d = e.data;
|
|
2379
|
+
if (d?.source && (d.source.kind === "heartbeat" || d.source.kind === "plugin" || d.source.plugin === "heartbeat")) continue;
|
|
2380
|
+
return true;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
return false;
|
|
2384
|
+
} catch {
|
|
2385
|
+
return false;
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2278
2388
|
async function beat(deps) {
|
|
2279
2389
|
if (beating) return;
|
|
2280
2390
|
beating = true;
|
|
@@ -2318,13 +2428,26 @@ async function beat(deps) {
|
|
|
2318
2428
|
return;
|
|
2319
2429
|
}
|
|
2320
2430
|
let wandered = false;
|
|
2431
|
+
let pausedAway = false;
|
|
2321
2432
|
if (agent) {
|
|
2322
2433
|
deferredRetries = 0;
|
|
2323
2434
|
const bc = { deps, agent, now };
|
|
2324
2435
|
if (await tokenSaverActive(deps)) {
|
|
2325
2436
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "silent", reason: "token-saver" });
|
|
2326
2437
|
noteBeat("silent", { reason: "token-saver\uFF08\u4F60\u4E0D\u5728\uFF0C\u5FC3\u8DF3\u6302\u8D77\uFF09" });
|
|
2438
|
+
pausedAway = true;
|
|
2439
|
+
scheduleTokenSaverWake(deps);
|
|
2327
2440
|
} else {
|
|
2441
|
+
if (pausedSince) {
|
|
2442
|
+
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", {
|
|
2443
|
+
event: "token_saver_resumed",
|
|
2444
|
+
via: "interval",
|
|
2445
|
+
heldMin: Math.round((Date.now() - pausedSince) / 6e4)
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
clearTokenSaverWake();
|
|
2449
|
+
pausedSince = 0;
|
|
2450
|
+
heldChecks = 0;
|
|
2328
2451
|
await withTimeout(maintenancePhase(bc), MAINTENANCE_TIMEOUT_MS, "maintenance");
|
|
2329
2452
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "phase_done", phase: "maintenance" });
|
|
2330
2453
|
await weeklyPhase(bc);
|
|
@@ -2346,7 +2469,7 @@ async function beat(deps) {
|
|
|
2346
2469
|
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "beat_agentless" });
|
|
2347
2470
|
scheduleDeferredRetry(deps);
|
|
2348
2471
|
}
|
|
2349
|
-
writeBeatStatus(deps, { beatStart, wandered });
|
|
2472
|
+
writeBeatStatus(deps, { beatStart, wandered, away: pausedAway });
|
|
2350
2473
|
} catch (e) {
|
|
2351
2474
|
deps.ctx.logger.error("heartbeat: beat failed: %s", String(e).slice(0, 200));
|
|
2352
2475
|
markHomeRotateIfOverflow(e);
|
|
@@ -2409,6 +2532,9 @@ function startOrchestrator(deps) {
|
|
|
2409
2532
|
lastBeat = null;
|
|
2410
2533
|
droppedStreak = 0;
|
|
2411
2534
|
deferredRetries = 0;
|
|
2535
|
+
clearTokenSaverWake();
|
|
2536
|
+
pausedSince = 0;
|
|
2537
|
+
heldChecks = 0;
|
|
2412
2538
|
if (retryTimer) {
|
|
2413
2539
|
clearTimeout(retryTimer);
|
|
2414
2540
|
retryTimer = void 0;
|
|
@@ -2428,6 +2554,7 @@ export {
|
|
|
2428
2554
|
StatusReader,
|
|
2429
2555
|
buildDigest,
|
|
2430
2556
|
applyHeartbeatInterval,
|
|
2557
|
+
looksPermanentResumeFailure,
|
|
2431
2558
|
sessionEvents,
|
|
2432
2559
|
sessionEventCount,
|
|
2433
2560
|
HOME_ROTATE_EVENT_COUNT,
|
|
@@ -2448,7 +2575,9 @@ export {
|
|
|
2448
2575
|
pickSpokenLine,
|
|
2449
2576
|
spokeTextSince,
|
|
2450
2577
|
TOKEN_SAVER_IDLE_SECONDS,
|
|
2578
|
+
TOKEN_SAVER_WAKE_MS,
|
|
2579
|
+
userActiveInBoundSessions,
|
|
2451
2580
|
beat,
|
|
2452
2581
|
startOrchestrator
|
|
2453
2582
|
};
|
|
2454
|
-
//# sourceMappingURL=chunk-
|
|
2583
|
+
//# sourceMappingURL=chunk-TRO4E6WG.js.map
|