@kanadego/dsh-heartbeat 1.5.1 → 1.6.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 +21 -5
- package/assets/screenpulse.ps1 +13 -18
- package/client.js +1 -0
- package/dist/{chunk-2M35HRL6.js → chunk-5TNGUHIR.js} +90 -279
- package/dist/chunk-5TNGUHIR.js.map +1 -0
- package/dist/chunk-SVP2NDRF.js +262 -0
- package/dist/chunk-SVP2NDRF.js.map +1 -0
- package/dist/cli/index.js +11 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +528 -101
- package/dist/index.js.map +1 -1
- package/dist/pool-YAIBE2L5.js +40 -0
- package/dist/pool-YAIBE2L5.js.map +1 -0
- package/package.json +76 -74
- package/dist/chunk-2M35HRL6.js.map +0 -1
- package/dist/chunk-AISZRA4C.js +0 -235
- package/dist/chunk-AISZRA4C.js.map +0 -1
- package/dist/lib-FJP7J4T6.js +0 -2281
- package/dist/lib-FJP7J4T6.js.map +0 -1
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
activeSeeds,
|
|
3
|
+
loadPool,
|
|
4
|
+
normalizeCategory,
|
|
5
|
+
seedsFilePath
|
|
6
|
+
} from "./chunk-SVP2NDRF.js";
|
|
7
|
+
import {
|
|
3
8
|
loadJson,
|
|
4
9
|
readText,
|
|
5
|
-
saveEncryptedText,
|
|
6
10
|
saveJson,
|
|
7
11
|
writeText
|
|
8
12
|
} from "./chunk-LLD7LUNN.js";
|
|
@@ -244,229 +248,12 @@ function loadPolicy(guard, configDir, settingsDir) {
|
|
|
244
248
|
return merged;
|
|
245
249
|
}
|
|
246
250
|
|
|
247
|
-
// src/seeds/pool.ts
|
|
248
|
-
import path5 from "path";
|
|
249
|
-
|
|
250
|
-
// src/seeds/types.ts
|
|
251
|
-
function emptySeedDb() {
|
|
252
|
-
return { seq: 0, seeds: [] };
|
|
253
|
-
}
|
|
254
|
-
var SEED_SOURCE_DEFAULT_CONFIDENCE = {
|
|
255
|
-
hand: 1,
|
|
256
|
-
profile: 0.7,
|
|
257
|
-
chat: 0.6,
|
|
258
|
-
screen: 0.4,
|
|
259
|
-
browse: 0.4
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
// src/seeds/pool.ts
|
|
263
|
-
var DAY_MS = 864e5;
|
|
264
|
-
var TTL_KEYS = ["news", "fandom", "scene", "promise"];
|
|
265
|
-
function normalizeTag(tag) {
|
|
266
|
-
if (tag && TTL_KEYS.includes(tag)) return tag;
|
|
267
|
-
return "scene";
|
|
268
|
-
}
|
|
269
|
-
function parseIso(v) {
|
|
270
|
-
const t = Date.parse(v);
|
|
271
|
-
return Number.isFinite(t) ? t : 0;
|
|
272
|
-
}
|
|
273
|
-
function seedsFilePath(dataDir) {
|
|
274
|
-
return path5.join(dataDir, "seeds.jsonl");
|
|
275
|
-
}
|
|
276
|
-
function loadPool(guard, file) {
|
|
277
|
-
const raw = loadEncryptedText(guard, file);
|
|
278
|
-
if (raw === null) return emptySeedDb();
|
|
279
|
-
const db = emptySeedDb();
|
|
280
|
-
for (const line of raw.split("\n")) {
|
|
281
|
-
const trimmed = line.trim();
|
|
282
|
-
if (!trimmed) continue;
|
|
283
|
-
try {
|
|
284
|
-
const obj = JSON.parse(trimmed);
|
|
285
|
-
if (typeof obj.id === "string" && obj.id.startsWith("s")) {
|
|
286
|
-
db.seeds.push(obj);
|
|
287
|
-
const n = Number(obj.id.slice(1));
|
|
288
|
-
if (Number.isFinite(n) && n > db.seq) db.seq = n;
|
|
289
|
-
}
|
|
290
|
-
} catch {
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
return db;
|
|
294
|
-
}
|
|
295
|
-
function savePool(guard, file, db) {
|
|
296
|
-
const body = db.seeds.map((s) => JSON.stringify(s)).join("\n");
|
|
297
|
-
saveEncryptedText(guard, file, body ? body + "\n" : "");
|
|
298
|
-
}
|
|
299
|
-
var activeSeeds = (db) => db.seeds.filter((s) => s.status === "active");
|
|
300
|
-
var archivedSeeds = (db) => db.seeds.filter((s) => s.status === "archived");
|
|
301
|
-
function evictionScore(seed, policy, now) {
|
|
302
|
-
const ttlDays = policy.seeds.ttlDays[seed.tag] || 14;
|
|
303
|
-
const daysStale = Math.max(0, (now - parseIso(seed.lastEvidenceAt)) / DAY_MS);
|
|
304
|
-
const freshness = Math.max(0, 1 - daysStale / ttlDays);
|
|
305
|
-
const unused = seed.used === 0 ? 1 : 1 / seed.used;
|
|
306
|
-
const w = policy.seeds.scoreWeights;
|
|
307
|
-
return freshness * w.freshness + unused * w.unused + seed.confidence * w.confidence;
|
|
308
|
-
}
|
|
309
|
-
function pickEvictionVictim(db, policy, now) {
|
|
310
|
-
const actives = activeSeeds(db);
|
|
311
|
-
if (actives.length === 0) return null;
|
|
312
|
-
const unprotected = actives.filter((s) => !s.protected);
|
|
313
|
-
const candidates = unprotected.length > 0 ? unprotected : actives;
|
|
314
|
-
let worst = null;
|
|
315
|
-
let worstScore = Number.POSITIVE_INFINITY;
|
|
316
|
-
for (const s of candidates) {
|
|
317
|
-
const score = evictionScore(s, policy, now);
|
|
318
|
-
if (score < worstScore) {
|
|
319
|
-
worstScore = score;
|
|
320
|
-
worst = s;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
return worst;
|
|
324
|
-
}
|
|
325
|
-
function archiveSeed(seed, reason, now) {
|
|
326
|
-
seed.status = "archived";
|
|
327
|
-
seed.retireReason = reason;
|
|
328
|
-
seed.retiredAt = new Date(now).toISOString();
|
|
329
|
-
}
|
|
330
|
-
function addSeed(guard, file, policy, input, now = Date.now()) {
|
|
331
|
-
const db = loadPool(guard, file);
|
|
332
|
-
const text = input.text.trim();
|
|
333
|
-
if (!text) throw new Error("seed text must not be empty");
|
|
334
|
-
const tag = normalizeTag(input.tag);
|
|
335
|
-
const source = input.source ?? "chat";
|
|
336
|
-
const confidence = input.confidence ?? SEED_SOURCE_DEFAULT_CONFIDENCE[source];
|
|
337
|
-
const dup = activeSeeds(db).find((s) => s.text === text);
|
|
338
|
-
if (dup) return { kind: "duplicate", seed: dup };
|
|
339
|
-
const nowIso = new Date(now).toISOString();
|
|
340
|
-
const ttlMs = (policy.seeds.ttlDays[tag] || 14) * DAY_MS;
|
|
341
|
-
const sameTopic = activeSeeds(db).filter((s) => s.topic === (input.topic ?? text.slice(0, 24)));
|
|
342
|
-
let seed;
|
|
343
|
-
if (sameTopic.length > 0) {
|
|
344
|
-
const kept = sameTopic[0];
|
|
345
|
-
kept.text = text;
|
|
346
|
-
kept.tag = tag;
|
|
347
|
-
kept.source = source;
|
|
348
|
-
kept.confidence = Math.max(kept.confidence, confidence);
|
|
349
|
-
kept.used = sameTopic.reduce((acc, s) => acc + s.used, 0);
|
|
350
|
-
kept.lastEvidenceAt = [kept.lastEvidenceAt, nowIso, ...sameTopic.map((s) => s.lastEvidenceAt)].reduce((a, b) => parseIso(b) > parseIso(a) ? b : a);
|
|
351
|
-
kept.expiresAt = new Date(Math.max(parseIso(kept.expiresAt), now + ttlMs)).toISOString();
|
|
352
|
-
kept.protected = kept.protected || source === "hand" || source === "profile" && confidence >= 0.7;
|
|
353
|
-
for (const extra of sameTopic.slice(1)) {
|
|
354
|
-
extra.status = "archived";
|
|
355
|
-
extra.retireReason = "completed";
|
|
356
|
-
extra.retiredAt = nowIso;
|
|
357
|
-
}
|
|
358
|
-
seed = kept;
|
|
359
|
-
if (activeSeeds(db).length > policy.seeds.maxActive) {
|
|
360
|
-
const victim = pickEvictionVictim(db, policy, now);
|
|
361
|
-
if (victim && victim.id !== kept.id) {
|
|
362
|
-
archiveSeed(victim, "pool_cap", now);
|
|
363
|
-
savePool(guard, file, db);
|
|
364
|
-
return { kind: "merged", seed: kept, evicted: victim };
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
savePool(guard, file, db);
|
|
368
|
-
return { kind: "merged", seed: kept };
|
|
369
|
-
}
|
|
370
|
-
let evicted;
|
|
371
|
-
if (activeSeeds(db).length >= policy.seeds.maxActive) {
|
|
372
|
-
const victim = pickEvictionVictim(db, policy, now);
|
|
373
|
-
if (victim) {
|
|
374
|
-
archiveSeed(victim, "pool_cap", now);
|
|
375
|
-
evicted = victim;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
db.seq += 1;
|
|
379
|
-
seed = {
|
|
380
|
-
id: `s${db.seq}`,
|
|
381
|
-
text,
|
|
382
|
-
topic: input.topic ?? text.slice(0, 24),
|
|
383
|
-
tag,
|
|
384
|
-
source,
|
|
385
|
-
confidence,
|
|
386
|
-
protected: source === "hand" || source === "profile" && confidence >= 0.7,
|
|
387
|
-
used: 0,
|
|
388
|
-
bornAt: nowIso,
|
|
389
|
-
expiresAt: new Date(now + ttlMs).toISOString(),
|
|
390
|
-
lastUsedAt: null,
|
|
391
|
-
lastEvidenceAt: nowIso,
|
|
392
|
-
status: "active"
|
|
393
|
-
};
|
|
394
|
-
db.seeds.push(seed);
|
|
395
|
-
savePool(guard, file, db);
|
|
396
|
-
return { kind: "added", seed, evicted };
|
|
397
|
-
}
|
|
398
|
-
function gcPool(guard, file, policy, now = Date.now()) {
|
|
399
|
-
const db = loadPool(guard, file);
|
|
400
|
-
const report = { consumed: 0, expired: 0, coldBench: 0, activeAfter: 0 };
|
|
401
|
-
for (const s of activeSeeds(db)) {
|
|
402
|
-
const ageMs = now - parseIso(s.bornAt);
|
|
403
|
-
const sinceEvidence = parseIso(s.lastEvidenceAt);
|
|
404
|
-
const sinceUsed = s.lastUsedAt ? parseIso(s.lastUsedAt) : 0;
|
|
405
|
-
if (s.used >= policy.seeds.retireAfterUsed && sinceEvidence <= sinceUsed) {
|
|
406
|
-
archiveSeed(s, "consumed", now);
|
|
407
|
-
report.consumed += 1;
|
|
408
|
-
} else if (now > parseIso(s.expiresAt)) {
|
|
409
|
-
archiveSeed(s, "expired", now);
|
|
410
|
-
report.expired += 1;
|
|
411
|
-
} else if (s.used === 0 && ageMs >= policy.seeds.coldBenchDays * DAY_MS) {
|
|
412
|
-
archiveSeed(s, "cold_bench", now);
|
|
413
|
-
report.coldBench += 1;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
report.activeAfter = activeSeeds(db).length;
|
|
417
|
-
savePool(guard, file, db);
|
|
418
|
-
return report;
|
|
419
|
-
}
|
|
420
|
-
function surfaceSeed(guard, file, policy, id, now = Date.now()) {
|
|
421
|
-
const db = loadPool(guard, file);
|
|
422
|
-
const s = db.seeds.find((x) => x.id === id && x.status === "active");
|
|
423
|
-
if (!s) return null;
|
|
424
|
-
s.used += 1;
|
|
425
|
-
s.lastUsedAt = new Date(now).toISOString();
|
|
426
|
-
if (s.used >= policy.seeds.retireAfterUsed && parseIso(s.lastEvidenceAt) <= parseIso(s.lastUsedAt)) {
|
|
427
|
-
archiveSeed(s, "consumed", now);
|
|
428
|
-
}
|
|
429
|
-
savePool(guard, file, db);
|
|
430
|
-
return s;
|
|
431
|
-
}
|
|
432
|
-
function archiveSeedById(guard, file, id, reason = "completed", now = Date.now()) {
|
|
433
|
-
const db = loadPool(guard, file);
|
|
434
|
-
const s = db.seeds.find((x) => x.id === id && x.status === "active");
|
|
435
|
-
if (!s) return null;
|
|
436
|
-
archiveSeed(s, reason, now);
|
|
437
|
-
savePool(guard, file, db);
|
|
438
|
-
return s;
|
|
439
|
-
}
|
|
440
|
-
function restoreSeed(guard, file, policy, id, now = Date.now()) {
|
|
441
|
-
const db = loadPool(guard, file);
|
|
442
|
-
const s = db.seeds.find((x) => x.id === id && x.status === "archived");
|
|
443
|
-
if (!s) return { ok: false, reason: "archived seed not found" };
|
|
444
|
-
if (activeSeeds(db).length >= policy.seeds.maxActive) {
|
|
445
|
-
return { ok: false, reason: `pool full (${policy.seeds.maxActive}); archive something first` };
|
|
446
|
-
}
|
|
447
|
-
s.status = "active";
|
|
448
|
-
s.retireReason = void 0;
|
|
449
|
-
s.retiredAt = void 0;
|
|
450
|
-
s.expiresAt = new Date(now + (policy.seeds.ttlDays[s.tag] || 14) * DAY_MS).toISOString();
|
|
451
|
-
s.lastEvidenceAt = new Date(now).toISOString();
|
|
452
|
-
savePool(guard, file, db);
|
|
453
|
-
return { ok: true, seed: s };
|
|
454
|
-
}
|
|
455
|
-
function deleteSeed(guard, file, id) {
|
|
456
|
-
const db = loadPool(guard, file);
|
|
457
|
-
const before = db.seeds.length;
|
|
458
|
-
db.seeds = db.seeds.filter((x) => x.id !== id);
|
|
459
|
-
if (db.seeds.length === before) return false;
|
|
460
|
-
savePool(guard, file, db);
|
|
461
|
-
return true;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
251
|
// src/ledger/ledger.ts
|
|
465
|
-
import
|
|
252
|
+
import path5 from "path";
|
|
466
253
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
467
|
-
var
|
|
254
|
+
var DAY_MS = 864e5;
|
|
468
255
|
function ledgerFilePath(dataDir) {
|
|
469
|
-
return
|
|
256
|
+
return path5.join(dataDir, "ledger.md");
|
|
470
257
|
}
|
|
471
258
|
var LINE_RE = /^- \[(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2})\]\[(open|done)\]\[#([0-9a-f]{6})\] (.*)$/;
|
|
472
259
|
function renderEntry(e) {
|
|
@@ -523,20 +310,20 @@ function scanPending(guard, file, now = Date.now()) {
|
|
|
523
310
|
return entries.filter((e) => e.status === "open").sort((a, b) => a.date < b.date ? -1 : 1);
|
|
524
311
|
}
|
|
525
312
|
function pendingOlderThan(guard, file, days, now = Date.now()) {
|
|
526
|
-
const cutoff = new Date(now - days *
|
|
313
|
+
const cutoff = new Date(now - days * DAY_MS).toISOString().slice(0, 10);
|
|
527
314
|
return scanPending(guard, file, now).filter((e) => e.date <= cutoff);
|
|
528
315
|
}
|
|
529
316
|
|
|
530
317
|
// src/browse/browse.ts
|
|
531
318
|
import fs5 from "fs";
|
|
532
|
-
import
|
|
319
|
+
import path6 from "path";
|
|
533
320
|
var WATCH_THROTTLE_MS = 6 * 36e5;
|
|
534
321
|
var UA = { "User-Agent": "dsh-heartbeat/2.0 (+local; personal companion)" };
|
|
535
322
|
function emptyBrowseState() {
|
|
536
|
-
return { targets: {}, last_check_at: 0, wander: { focusHistory: {}, focusCount: {}, last_wander_at: 0 } };
|
|
323
|
+
return { targets: {}, last_check_at: 0, wander: { focusHistory: {}, focusCount: {}, last_wander_at: 0, refillCount: {} } };
|
|
537
324
|
}
|
|
538
325
|
function browseStatePath(paths) {
|
|
539
|
-
return
|
|
326
|
+
return path6.join(paths.dataDir, "browse.json");
|
|
540
327
|
}
|
|
541
328
|
function readJsonFile(file, fallback) {
|
|
542
329
|
try {
|
|
@@ -546,14 +333,14 @@ function readJsonFile(file, fallback) {
|
|
|
546
333
|
}
|
|
547
334
|
}
|
|
548
335
|
function loadInterests(paths) {
|
|
549
|
-
const userPath =
|
|
336
|
+
const userPath = path6.join(paths.settingsDir, "interests.json");
|
|
550
337
|
if (fs5.existsSync(userPath)) return readJsonFile(userPath, { interests: [], _schedule: {} });
|
|
551
|
-
return readJsonFile(
|
|
338
|
+
return readJsonFile(path6.join(paths.configDir, "interests.json"), { interests: [], _schedule: {} });
|
|
552
339
|
}
|
|
553
340
|
function loadWatchlist(paths) {
|
|
554
|
-
const userPath =
|
|
341
|
+
const userPath = path6.join(paths.settingsDir, "watchlist.json");
|
|
555
342
|
if (fs5.existsSync(userPath)) return readJsonFile(userPath, { targets: [] });
|
|
556
|
-
return readJsonFile(
|
|
343
|
+
return readJsonFile(path6.join(paths.configDir, "watchlist.json"), { targets: [] });
|
|
557
344
|
}
|
|
558
345
|
function loadState(guard, paths) {
|
|
559
346
|
return loadJson(guard, browseStatePath(paths)) ?? emptyBrowseState();
|
|
@@ -646,20 +433,53 @@ function adviseWander(guard, paths, policy, now = /* @__PURE__ */ new Date()) {
|
|
|
646
433
|
if (!focus) return { focus: null, query: null, skipped: "no-focus" };
|
|
647
434
|
return { focus, query: `${focus} 2026 \u6700\u65B0`, skipped: null };
|
|
648
435
|
}
|
|
649
|
-
function completeWander(guard, paths, focus, now = Date.now()) {
|
|
436
|
+
function completeWander(guard, paths, focus, now = Date.now(), opts = {}) {
|
|
650
437
|
const state = loadState(guard, paths);
|
|
651
438
|
state.wander.focusHistory[focus] = now;
|
|
652
439
|
state.wander.focusCount[focus] = (state.wander.focusCount[focus] ?? 0) + 1;
|
|
653
440
|
state.wander.last_wander_at = now;
|
|
441
|
+
let refillsToday;
|
|
442
|
+
if (opts.refill) {
|
|
443
|
+
const today = localDayKey(new Date(now));
|
|
444
|
+
state.wander.refillCount = state.wander.refillCount ?? {};
|
|
445
|
+
state.wander.refillCount[today] = (state.wander.refillCount[today] ?? 0) + 1;
|
|
446
|
+
refillsToday = state.wander.refillCount[today];
|
|
447
|
+
}
|
|
654
448
|
saveJson(guard, browseStatePath(paths), state);
|
|
655
|
-
return { focus, count: state.wander.focusCount[focus] };
|
|
449
|
+
return { focus, count: state.wander.focusCount[focus], ...refillsToday === void 0 ? {} : { refillsToday } };
|
|
450
|
+
}
|
|
451
|
+
function localDayKey(d) {
|
|
452
|
+
const y = d.getFullYear();
|
|
453
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
454
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
455
|
+
return `${y}-${m}-${day}`;
|
|
456
|
+
}
|
|
457
|
+
var REFILL_TOPIC_THRESHOLD = 4;
|
|
458
|
+
var REFILL_MAX_PER_DAY = 2;
|
|
459
|
+
function adviseRefillWander(guard, paths, policy, now = /* @__PURE__ */ new Date()) {
|
|
460
|
+
const state = loadState(guard, paths);
|
|
461
|
+
const today = localDayKey(now);
|
|
462
|
+
const refillsToday = state.wander.refillCount?.[today] ?? 0;
|
|
463
|
+
const topicCount = activeSeeds(loadPool(guard, seedsFilePath(paths.dataDir))).filter((s) => normalizeCategory(s.category) === "topic").length;
|
|
464
|
+
if (refillsToday >= REFILL_MAX_PER_DAY) {
|
|
465
|
+
return { focus: null, query: null, skipped: `refill-daily-cap(${refillsToday})`, topicCount, refillsToday };
|
|
466
|
+
}
|
|
467
|
+
if (topicCount > REFILL_TOPIC_THRESHOLD) {
|
|
468
|
+
return { focus: null, query: null, skipped: `topic-stock-ok(${topicCount})`, topicCount, refillsToday };
|
|
469
|
+
}
|
|
470
|
+
const interests = loadInterests(paths);
|
|
471
|
+
const focus = pickFocus(state, interests, now.getTime());
|
|
472
|
+
if (!focus) {
|
|
473
|
+
return { focus: null, query: null, skipped: "no-focus", topicCount, refillsToday };
|
|
474
|
+
}
|
|
475
|
+
return { focus, query: `${focus} 2026 \u6700\u65B0`, skipped: null, topicCount, refillsToday };
|
|
656
476
|
}
|
|
657
477
|
function browseStatus(guard, paths) {
|
|
658
478
|
return loadState(guard, paths);
|
|
659
479
|
}
|
|
660
480
|
|
|
661
481
|
// src/profile/store.ts
|
|
662
|
-
import
|
|
482
|
+
import path8 from "path";
|
|
663
483
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
664
484
|
import fs7 from "fs";
|
|
665
485
|
|
|
@@ -681,10 +501,10 @@ function emptyProfile() {
|
|
|
681
501
|
|
|
682
502
|
// src/profile/schema.ts
|
|
683
503
|
import fs6 from "fs";
|
|
684
|
-
import
|
|
504
|
+
import path7 from "path";
|
|
685
505
|
function loadProfileSchema(paths) {
|
|
686
|
-
const userPath =
|
|
687
|
-
const file = fs6.existsSync(userPath) ? userPath :
|
|
506
|
+
const userPath = path7.join(paths.settingsDir, "profile-schema.json");
|
|
507
|
+
const file = fs6.existsSync(userPath) ? userPath : path7.join(paths.configDir, "profile-schema.json");
|
|
688
508
|
try {
|
|
689
509
|
const raw = JSON.parse(fs6.readFileSync(file, "utf8"));
|
|
690
510
|
if (!raw.partitions) throw new Error("partitions missing");
|
|
@@ -714,12 +534,12 @@ function checkAddAgainstSchema(schema, partition, topic, subTopic, nominated) {
|
|
|
714
534
|
}
|
|
715
535
|
|
|
716
536
|
// src/profile/store.ts
|
|
717
|
-
var
|
|
537
|
+
var DAY_MS2 = 864e5;
|
|
718
538
|
function profileFilePath(dataDir) {
|
|
719
|
-
return
|
|
539
|
+
return path8.join(dataDir, "profile.json");
|
|
720
540
|
}
|
|
721
541
|
function journalFilePath(dataDir) {
|
|
722
|
-
return
|
|
542
|
+
return path8.join(dataDir, "profile_journal.jsonl");
|
|
723
543
|
}
|
|
724
544
|
function loadProfile(guard, file) {
|
|
725
545
|
const doc = loadJson(guard, file);
|
|
@@ -729,14 +549,14 @@ function loadProfile(guard, file) {
|
|
|
729
549
|
}
|
|
730
550
|
return doc;
|
|
731
551
|
}
|
|
732
|
-
function
|
|
552
|
+
function parseIso(v) {
|
|
733
553
|
const t = Date.parse(v);
|
|
734
554
|
return Number.isFinite(t) ? t : 0;
|
|
735
555
|
}
|
|
736
556
|
function refExists(guard, dataDir, ref) {
|
|
737
557
|
const base = ref.split("#")[0] ?? "";
|
|
738
558
|
if (!base) return false;
|
|
739
|
-
const target =
|
|
559
|
+
const target = path8.join(dataDir, base);
|
|
740
560
|
try {
|
|
741
561
|
return fs7.existsSync(guard.assert(target));
|
|
742
562
|
} catch {
|
|
@@ -840,7 +660,7 @@ function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
|
|
|
840
660
|
rejected.push({ op, reason: "volatile expiry is code-owned (time-driven), not LLM-nominated" });
|
|
841
661
|
continue;
|
|
842
662
|
}
|
|
843
|
-
const hasNewObservation = (op.evidence ?? []).length > 0 && (op.evidence ?? []).some((e) =>
|
|
663
|
+
const hasNewObservation = (op.evidence ?? []).length > 0 && (op.evidence ?? []).some((e) => parseIso(e.at) > parseIso(entry.evidence[entry.evidence.length - 1]?.at ?? ""));
|
|
844
664
|
if (!hasNewObservation) {
|
|
845
665
|
rejected.push({ op, reason: "stable INVALIDATE requires a newer contradicting observation" });
|
|
846
666
|
continue;
|
|
@@ -864,15 +684,15 @@ function runDeterministicAging(doc, policy, now) {
|
|
|
864
684
|
for (const p of PARTITIONS) {
|
|
865
685
|
for (const e of doc.partitions[p].entries) {
|
|
866
686
|
if (e.validTo !== null) continue;
|
|
867
|
-
const lastEvidence = Math.max(...e.evidence.map((x) =>
|
|
687
|
+
const lastEvidence = Math.max(...e.evidence.map((x) => parseIso(x.at)), parseIso(e.updatedAt));
|
|
868
688
|
if (e.temporal === "volatile") {
|
|
869
|
-
if (now - lastEvidence > policy.profile.volatileDays *
|
|
689
|
+
if (now - lastEvidence > policy.profile.volatileDays * DAY_MS2) {
|
|
870
690
|
e.validTo = nowIso;
|
|
871
691
|
e.updatedAt = nowIso;
|
|
872
692
|
e.updateCount += 1;
|
|
873
693
|
volatileExpired += 1;
|
|
874
694
|
}
|
|
875
|
-
} else if (!e.lowActivity && now - lastEvidence > policy.profile.stableLowActivityDays *
|
|
695
|
+
} else if (!e.lowActivity && now - lastEvidence > policy.profile.stableLowActivityDays * DAY_MS2) {
|
|
876
696
|
e.lowActivity = true;
|
|
877
697
|
lowActivityMarked += 1;
|
|
878
698
|
}
|
|
@@ -979,7 +799,7 @@ function verifyProfile(guard, dataDir) {
|
|
|
979
799
|
function rebuildProfile(guard, dataDir, opts = {}) {
|
|
980
800
|
const replayed = replayJournal(guard, dataDir);
|
|
981
801
|
const target = profileFilePath(dataDir);
|
|
982
|
-
const tmp =
|
|
802
|
+
const tmp = path8.join(dataDir, `.profile.rebuild.${Date.now()}.tmp`);
|
|
983
803
|
atomicWriteFileSync(tmp, JSON.stringify(replayed.doc, null, 2));
|
|
984
804
|
if (opts.check) {
|
|
985
805
|
const onDisk = loadProfile(guard, target);
|
|
@@ -997,7 +817,7 @@ function rebuildProfile(guard, dataDir, opts = {}) {
|
|
|
997
817
|
if (replayed.truncatedTail > 0) {
|
|
998
818
|
writeText(
|
|
999
819
|
guard,
|
|
1000
|
-
|
|
820
|
+
path8.join(dataDir, "logs", "rebuild-report.txt"),
|
|
1001
821
|
`rebuild truncated ${replayed.truncatedTail} torn line(s) at journal tail; ${replayed.records} records applied
|
|
1002
822
|
`
|
|
1003
823
|
);
|
|
@@ -1007,11 +827,11 @@ function rebuildProfile(guard, dataDir, opts = {}) {
|
|
|
1007
827
|
|
|
1008
828
|
// src/notify/notify.ts
|
|
1009
829
|
import { spawnSync } from "child_process";
|
|
1010
|
-
import
|
|
830
|
+
import path9 from "path";
|
|
1011
831
|
function runNotify(paths, args) {
|
|
1012
832
|
const r = spawnSync(
|
|
1013
833
|
"powershell.exe",
|
|
1014
|
-
["-NoProfile", "-ExecutionPolicy", "Bypass", "-File",
|
|
834
|
+
["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", path9.join(paths.assetsDir, "notify.ps1"), ...args],
|
|
1015
835
|
{ timeout: 2e4, encoding: "utf8" }
|
|
1016
836
|
);
|
|
1017
837
|
return { status: r.status ?? -1, out: `${r.stdout ?? ""}${r.stderr ?? ""}`.trim() };
|
|
@@ -1030,7 +850,7 @@ function sendNewMessageHint(paths) {
|
|
|
1030
850
|
// src/core/preset-install.ts
|
|
1031
851
|
import fs8 from "fs";
|
|
1032
852
|
import os from "os";
|
|
1033
|
-
import
|
|
853
|
+
import path10 from "path";
|
|
1034
854
|
import { fileURLToPath } from "url";
|
|
1035
855
|
var COMPOSITION_FILE = "agent.cordis.yml";
|
|
1036
856
|
var METADATA_FILE = "preset.yml";
|
|
@@ -1038,14 +858,14 @@ var BUNDLED_PRESET_ID = "heartbeat";
|
|
|
1038
858
|
function bundledPresetDir(moduleUrl, id = BUNDLED_PRESET_ID) {
|
|
1039
859
|
let dir;
|
|
1040
860
|
try {
|
|
1041
|
-
dir =
|
|
861
|
+
dir = path10.dirname(fileURLToPath(moduleUrl));
|
|
1042
862
|
} catch {
|
|
1043
863
|
return void 0;
|
|
1044
864
|
}
|
|
1045
865
|
for (let depth = 0; depth < 5; depth += 1) {
|
|
1046
|
-
const candidate =
|
|
1047
|
-
if (fs8.existsSync(
|
|
1048
|
-
const parent =
|
|
866
|
+
const candidate = path10.join(dir, "assets", "presets", id);
|
|
867
|
+
if (fs8.existsSync(path10.join(candidate, COMPOSITION_FILE))) return candidate;
|
|
868
|
+
const parent = path10.dirname(dir);
|
|
1049
869
|
if (parent === dir) break;
|
|
1050
870
|
dir = parent;
|
|
1051
871
|
}
|
|
@@ -1055,12 +875,12 @@ function userPresetRoot(roots) {
|
|
|
1055
875
|
const found = roots?.find(
|
|
1056
876
|
(root) => root?.trust === "user" && typeof root.path === "string" && root.path.length > 0
|
|
1057
877
|
);
|
|
1058
|
-
return found?.path === void 0 ? void 0 :
|
|
878
|
+
return found?.path === void 0 ? void 0 : path10.resolve(found.path);
|
|
1059
879
|
}
|
|
1060
880
|
function conventionalUserPresetRoot(env = process.env, home = os.homedir()) {
|
|
1061
881
|
const override = env.DSH_HOME?.trim();
|
|
1062
|
-
const root = override && override.length > 0 ? override :
|
|
1063
|
-
return
|
|
882
|
+
const root = override && override.length > 0 ? override : path10.join(home, ".dsh");
|
|
883
|
+
return path10.join(root, ".agent-presets");
|
|
1064
884
|
}
|
|
1065
885
|
function installBundledPreset(options) {
|
|
1066
886
|
const id = options.id && options.id.length > 0 ? options.id : BUNDLED_PRESET_ID;
|
|
@@ -1092,12 +912,12 @@ function installBundledPreset(options) {
|
|
|
1092
912
|
detail: "the roster mounts no user preset root (includeUserRoot=false)"
|
|
1093
913
|
};
|
|
1094
914
|
}
|
|
1095
|
-
const dir =
|
|
1096
|
-
const composition =
|
|
915
|
+
const dir = path10.join(root, id);
|
|
916
|
+
const composition = path10.join(dir, COMPOSITION_FILE);
|
|
1097
917
|
try {
|
|
1098
918
|
if (fs8.existsSync(composition)) {
|
|
1099
919
|
if (options.force !== true) {
|
|
1100
|
-
const drifted = !sameBytes(composition,
|
|
920
|
+
const drifted = !sameBytes(composition, path10.join(bundledDir, COMPOSITION_FILE));
|
|
1101
921
|
return {
|
|
1102
922
|
action: "exists",
|
|
1103
923
|
id,
|
|
@@ -1106,7 +926,7 @@ function installBundledPreset(options) {
|
|
|
1106
926
|
detail: drifted ? "kept as-is (differs from the bundled template)" : "kept as-is"
|
|
1107
927
|
};
|
|
1108
928
|
}
|
|
1109
|
-
fs8.copyFileSync(
|
|
929
|
+
fs8.copyFileSync(path10.join(bundledDir, COMPOSITION_FILE), composition);
|
|
1110
930
|
return {
|
|
1111
931
|
action: "restored",
|
|
1112
932
|
id,
|
|
@@ -1117,9 +937,9 @@ function installBundledPreset(options) {
|
|
|
1117
937
|
}
|
|
1118
938
|
const existed = fs8.existsSync(dir);
|
|
1119
939
|
fs8.mkdirSync(dir, { recursive: true });
|
|
1120
|
-
fs8.copyFileSync(
|
|
1121
|
-
const metadata =
|
|
1122
|
-
if (!fs8.existsSync(metadata)) fs8.copyFileSync(
|
|
940
|
+
fs8.copyFileSync(path10.join(bundledDir, COMPOSITION_FILE), composition);
|
|
941
|
+
const metadata = path10.join(dir, METADATA_FILE);
|
|
942
|
+
if (!fs8.existsSync(metadata)) fs8.copyFileSync(path10.join(bundledDir, METADATA_FILE), metadata);
|
|
1123
943
|
return {
|
|
1124
944
|
action: existed ? "repaired" : "created",
|
|
1125
945
|
id,
|
|
@@ -1137,16 +957,16 @@ function describeInstall(result) {
|
|
|
1137
957
|
return `preset ${result.id} ${result.action}${where}${why}`;
|
|
1138
958
|
}
|
|
1139
959
|
function presetStatus(moduleUrl, id = BUNDLED_PRESET_ID, root = conventionalUserPresetRoot()) {
|
|
1140
|
-
const dir =
|
|
960
|
+
const dir = path10.join(root, id);
|
|
1141
961
|
const bundledDir = bundledPresetDir(moduleUrl, id);
|
|
1142
|
-
const installed = fs8.existsSync(
|
|
962
|
+
const installed = fs8.existsSync(path10.join(dir, COMPOSITION_FILE));
|
|
1143
963
|
return {
|
|
1144
964
|
id,
|
|
1145
965
|
dir,
|
|
1146
966
|
...bundledDir === void 0 ? {} : { bundledDir },
|
|
1147
967
|
installed,
|
|
1148
|
-
compositionMatches: installed && bundledDir !== void 0 && sameBytes(
|
|
1149
|
-
metadataMatches: bundledDir !== void 0 && fs8.existsSync(
|
|
968
|
+
compositionMatches: installed && bundledDir !== void 0 && sameBytes(path10.join(dir, COMPOSITION_FILE), path10.join(bundledDir, COMPOSITION_FILE)),
|
|
969
|
+
metadataMatches: bundledDir !== void 0 && fs8.existsSync(path10.join(dir, METADATA_FILE)) && sameBytes(path10.join(dir, METADATA_FILE), path10.join(bundledDir, METADATA_FILE))
|
|
1150
970
|
};
|
|
1151
971
|
}
|
|
1152
972
|
function sameBytes(left, right) {
|
|
@@ -1165,16 +985,6 @@ export {
|
|
|
1165
985
|
pruneAuditFile,
|
|
1166
986
|
deepMerge,
|
|
1167
987
|
loadPolicy,
|
|
1168
|
-
seedsFilePath,
|
|
1169
|
-
loadPool,
|
|
1170
|
-
activeSeeds,
|
|
1171
|
-
archivedSeeds,
|
|
1172
|
-
addSeed,
|
|
1173
|
-
gcPool,
|
|
1174
|
-
surfaceSeed,
|
|
1175
|
-
archiveSeedById,
|
|
1176
|
-
restoreSeed,
|
|
1177
|
-
deleteSeed,
|
|
1178
988
|
ledgerFilePath,
|
|
1179
989
|
readLedger,
|
|
1180
990
|
appendEntry,
|
|
@@ -1186,6 +996,7 @@ export {
|
|
|
1186
996
|
checkWatchlist,
|
|
1187
997
|
adviseWander,
|
|
1188
998
|
completeWander,
|
|
999
|
+
adviseRefillWander,
|
|
1189
1000
|
browseStatus,
|
|
1190
1001
|
loadProfileSchema,
|
|
1191
1002
|
profileFilePath,
|
|
@@ -1204,4 +1015,4 @@ export {
|
|
|
1204
1015
|
describeInstall,
|
|
1205
1016
|
presetStatus
|
|
1206
1017
|
};
|
|
1207
|
-
//# sourceMappingURL=chunk-
|
|
1018
|
+
//# sourceMappingURL=chunk-5TNGUHIR.js.map
|