@kanadego/dsh-heartbeat 1.9.2 → 2.0.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.
Files changed (57) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +344 -396
  3. package/client.js +108 -20
  4. package/config/policy.json +74 -73
  5. package/config/profile-schema.json +306 -306
  6. package/dist/{bindings-225SI7KC.js → bindings-U2M3CO2D.js} +3 -3
  7. package/dist/{chunk-TY6UP74X.js → chunk-3DNGY4DS.js} +2 -2
  8. package/dist/{chunk-CUOSICYJ.js → chunk-544QOJN5.js} +397 -23
  9. package/dist/chunk-544QOJN5.js.map +1 -0
  10. package/dist/{chunk-AJ5HLAL7.js → chunk-6MVKWICX.js} +708 -154
  11. package/dist/chunk-6MVKWICX.js.map +1 -0
  12. package/dist/{chunk-IG6YMUHW.js → chunk-6XM6KY7M.js} +43 -12
  13. package/dist/chunk-6XM6KY7M.js.map +1 -0
  14. package/dist/chunk-AKIJ4LS2.js +680 -0
  15. package/dist/chunk-AKIJ4LS2.js.map +1 -0
  16. package/dist/{chunk-KB5SMG3F.js → chunk-DC5HMCJC.js} +3 -3
  17. package/dist/{chunk-3MDBU6WY.js → chunk-DZ7YCFGX.js} +37 -7
  18. package/dist/chunk-DZ7YCFGX.js.map +1 -0
  19. package/dist/{chunk-WRUTATW4.js → chunk-IBBAISSW.js} +13 -2
  20. package/dist/chunk-IBBAISSW.js.map +1 -0
  21. package/dist/{chunk-ZLV4NMFX.js → chunk-IFRMKVIC.js} +85 -21
  22. package/dist/chunk-IFRMKVIC.js.map +1 -0
  23. package/dist/{chunk-XFNCTOW7.js → chunk-NCPKOWS7.js} +52 -4
  24. package/dist/chunk-NCPKOWS7.js.map +1 -0
  25. package/dist/{chunk-JDH2LDFS.js → chunk-TVX4HV4Y.js} +4 -6
  26. package/dist/{chunk-JDH2LDFS.js.map → chunk-TVX4HV4Y.js.map} +1 -1
  27. package/dist/cli/index.js +149 -26
  28. package/dist/cli/index.js.map +1 -1
  29. package/dist/{inbox-P26DDMWC.js → inbox-OWBK6PE2.js} +8 -7
  30. package/dist/index.d.ts +50 -315
  31. package/dist/index.js +288 -920
  32. package/dist/index.js.map +1 -1
  33. package/dist/migrate-TQ5PB2DI.js +27 -0
  34. package/dist/{orchestrator-EUTZ3KG6.js → orchestrator-P7AEWVYR.js} +27 -12
  35. package/dist/{pool-UTXQJJ5C.js → pool-2T6TVS2L.js} +4 -5
  36. package/dist/{snapshot-K5VHETTK.js → snapshot-F5B2DD56.js} +5 -5
  37. package/package.json +84 -83
  38. package/dist/chunk-3MDBU6WY.js.map +0 -1
  39. package/dist/chunk-AJ5HLAL7.js.map +0 -1
  40. package/dist/chunk-CUOSICYJ.js.map +0 -1
  41. package/dist/chunk-IG6YMUHW.js.map +0 -1
  42. package/dist/chunk-IV2ZWQA3.js +0 -56
  43. package/dist/chunk-IV2ZWQA3.js.map +0 -1
  44. package/dist/chunk-K5Y6JP2B.js +0 -399
  45. package/dist/chunk-K5Y6JP2B.js.map +0 -1
  46. package/dist/chunk-WRUTATW4.js.map +0 -1
  47. package/dist/chunk-XFNCTOW7.js.map +0 -1
  48. package/dist/chunk-ZLV4NMFX.js.map +0 -1
  49. package/dist/migrate-D4VKAKYT.js +0 -21
  50. /package/dist/{bindings-225SI7KC.js.map → bindings-U2M3CO2D.js.map} +0 -0
  51. /package/dist/{chunk-TY6UP74X.js.map → chunk-3DNGY4DS.js.map} +0 -0
  52. /package/dist/{chunk-KB5SMG3F.js.map → chunk-DC5HMCJC.js.map} +0 -0
  53. /package/dist/{inbox-P26DDMWC.js.map → inbox-OWBK6PE2.js.map} +0 -0
  54. /package/dist/{migrate-D4VKAKYT.js.map → migrate-TQ5PB2DI.js.map} +0 -0
  55. /package/dist/{orchestrator-EUTZ3KG6.js.map → orchestrator-P7AEWVYR.js.map} +0 -0
  56. /package/dist/{pool-UTXQJJ5C.js.map → pool-2T6TVS2L.js.map} +0 -0
  57. /package/dist/{snapshot-K5VHETTK.js.map → snapshot-F5B2DD56.js.map} +0 -0
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  loadJson,
3
+ matchFilesRecursive,
3
4
  readText,
4
5
  saveJson,
5
6
  writeText
6
- } from "./chunk-K5Y6JP2B.js";
7
+ } from "./chunk-AKIJ4LS2.js";
7
8
  import {
8
9
  atomicWriteFileSync
9
- } from "./chunk-WRUTATW4.js";
10
+ } from "./chunk-IBBAISSW.js";
10
11
 
11
12
  // src/core/audit-log.ts
12
13
  import fs from "fs";
@@ -58,6 +59,7 @@ import { randomUUID } from "crypto";
58
59
  import fs3 from "fs";
59
60
 
60
61
  // src/profile/types.ts
62
+ var JOURNAL_VERSION = 2;
61
63
  var PARTITIONS = ["interest", "projects", "comm", "psy"];
62
64
  var CONFIDENCE_CAP = {
63
65
  chat: 0.6,
@@ -123,6 +125,290 @@ function checkAddAgainstSchema(schema, partition, topic, subTopic, nominated) {
123
125
  return { ok: true, temporal: nominated };
124
126
  }
125
127
 
128
+ // src/profile/shapes.ts
129
+ import z from "@deepseek-ai/schemastery";
130
+ var ShapeError = class extends Error {
131
+ constructor(message) {
132
+ super(message);
133
+ this.name = "ShapeError";
134
+ }
135
+ };
136
+ function isPlainObject(v) {
137
+ return typeof v === "object" && v !== null && !Array.isArray(v);
138
+ }
139
+ function describe(v) {
140
+ if (v === null) return "null";
141
+ if (Array.isArray(v)) return "an array";
142
+ return typeof v;
143
+ }
144
+ function closed(schema, keys, required = keys) {
145
+ return { schema, keys, required };
146
+ }
147
+ function check(shape, value, label) {
148
+ if (!isPlainObject(value)) throw new ShapeError(`${label}: expected an object, got ${describe(value)}`);
149
+ for (const req of shape.required) {
150
+ const key = typeof req === "string" ? req : req.key;
151
+ const nullable = typeof req !== "string" && req.nullable;
152
+ if (value[key] === void 0 || value[key] === null && !nullable) {
153
+ throw new ShapeError(`${label}.${key}: missing required value`);
154
+ }
155
+ }
156
+ for (const k of Object.keys(value)) {
157
+ if (!shape.keys.includes(k)) throw new ShapeError(`${label}.${k}: unknown field`);
158
+ }
159
+ try {
160
+ return shape.schema(value);
161
+ } catch (e) {
162
+ throw new ShapeError(`${label}: ${e instanceof Error ? e.message : String(e)}`);
163
+ }
164
+ }
165
+ var Str = z.string().required();
166
+ var Num = z.number().required();
167
+ var NullableStr = z.union([z.string(), z.const(null)]);
168
+ var PartitionEnum = z.union(PARTITIONS.map((p) => z.const(p))).required();
169
+ var TemporalEnum = z.union([z.const("stable"), z.const("volatile")]).required();
170
+ var OptionalTemporal = z.union([z.const("stable"), z.const("volatile")]);
171
+ var KindEnum = z.union([
172
+ z.const("chat"),
173
+ z.const("screen"),
174
+ z.const("browse"),
175
+ z.const("hand"),
176
+ z.const("ledger")
177
+ ]).required();
178
+ var ConfidenceNum = z.number().min(0).max(1).required();
179
+ var EvidenceShape = closed(
180
+ z.object({ kind: KindEnum, at: Str, ref: Str, quote: z.string() }),
181
+ ["kind", "at", "ref", "quote"],
182
+ ["kind", "at", "ref"]
183
+ );
184
+ function parseEvidence(value, label = "evidence") {
185
+ const o = check(EvidenceShape, value, label);
186
+ return {
187
+ kind: o.kind,
188
+ at: String(o.at),
189
+ ref: String(o.ref),
190
+ ...o.quote === void 0 ? {} : { quote: String(o.quote) }
191
+ };
192
+ }
193
+ function parseEvidenceList(value, label) {
194
+ if (!Array.isArray(value)) throw new ShapeError(`${label}: expected an array`);
195
+ return value.map((v, i) => parseEvidence(v, `${label}[${i}]`));
196
+ }
197
+ var EntryShape = closed(
198
+ z.object({
199
+ id: Str,
200
+ partition: PartitionEnum,
201
+ topic: Str,
202
+ subTopic: Str,
203
+ content: Str,
204
+ confidence: ConfidenceNum,
205
+ temporal: TemporalEnum,
206
+ validFrom: Str,
207
+ validTo: NullableStr,
208
+ supersededBy: NullableStr,
209
+ evidence: z.array(z.any()).required(),
210
+ createdAt: Str,
211
+ updatedAt: Str,
212
+ updateCount: Num,
213
+ lowActivity: z.boolean()
214
+ }),
215
+ [
216
+ "id",
217
+ "partition",
218
+ "topic",
219
+ "subTopic",
220
+ "content",
221
+ "confidence",
222
+ "temporal",
223
+ "validFrom",
224
+ "validTo",
225
+ "supersededBy",
226
+ "evidence",
227
+ "createdAt",
228
+ "updatedAt",
229
+ "updateCount",
230
+ "lowActivity"
231
+ ],
232
+ [
233
+ "id",
234
+ "partition",
235
+ "topic",
236
+ "subTopic",
237
+ "content",
238
+ "confidence",
239
+ "temporal",
240
+ "validFrom",
241
+ { key: "validTo", nullable: true },
242
+ { key: "supersededBy", nullable: true },
243
+ "evidence",
244
+ "createdAt",
245
+ "updatedAt",
246
+ "updateCount"
247
+ ]
248
+ );
249
+ function parseProfileEntry(value, label = "entry") {
250
+ const o = check(EntryShape, value, label);
251
+ return {
252
+ id: String(o.id),
253
+ partition: o.partition,
254
+ topic: String(o.topic),
255
+ subTopic: String(o.subTopic),
256
+ content: String(o.content),
257
+ confidence: Number(o.confidence),
258
+ temporal: o.temporal,
259
+ validFrom: String(o.validFrom),
260
+ validTo: o.validTo === null ? null : String(o.validTo),
261
+ supersededBy: o.supersededBy === null ? null : String(o.supersededBy),
262
+ evidence: parseEvidenceList(o.evidence, `${label}.evidence`),
263
+ createdAt: String(o.createdAt),
264
+ updatedAt: String(o.updatedAt),
265
+ updateCount: Number(o.updateCount),
266
+ ...o.lowActivity === void 0 ? {} : { lowActivity: Boolean(o.lowActivity) }
267
+ };
268
+ }
269
+ var InboxShape = closed(
270
+ z.object({ id: z.string(), kind: KindEnum, at: Str, ref: Str, note: z.string() }),
271
+ ["id", "kind", "at", "ref", "note"],
272
+ ["kind", "at", "ref"]
273
+ );
274
+ var AddShape = closed(
275
+ z.object({
276
+ op: z.const("ADD").required(),
277
+ partition: PartitionEnum,
278
+ topic: Str,
279
+ subTopic: Str,
280
+ content: Str,
281
+ temporal: OptionalTemporal,
282
+ confidence: z.number().min(0).max(1),
283
+ why: z.string(),
284
+ evidence: z.array(z.any()).required()
285
+ }),
286
+ ["op", "partition", "topic", "subTopic", "content", "temporal", "confidence", "why", "evidence"],
287
+ ["op", "partition", "topic", "subTopic", "content", "evidence"]
288
+ );
289
+ var UpdateShape = closed(
290
+ z.object({
291
+ op: z.const("UPDATE").required(),
292
+ id: Str,
293
+ changes: z.object({ content: z.string(), confidence: z.number().min(0).max(1) }),
294
+ why: z.string()
295
+ }),
296
+ ["op", "id", "changes", "why"],
297
+ ["op", "id", "changes"]
298
+ );
299
+ var InvalidateShape = closed(
300
+ z.object({
301
+ op: z.const("INVALIDATE").required(),
302
+ id: Str,
303
+ why: z.string(),
304
+ evidence: z.array(z.any())
305
+ }),
306
+ ["op", "id", "why", "evidence"],
307
+ ["op", "id"]
308
+ );
309
+ var NoopShape = closed(
310
+ z.object({ op: z.const("NOOP").required(), why: z.string() }),
311
+ ["op", "why"],
312
+ ["op"]
313
+ );
314
+ function parseObservationRefs(value, label) {
315
+ if (value === void 0) return [];
316
+ if (!Array.isArray(value)) throw new ShapeError(`${label}: expected an array of observation ids`);
317
+ return value.map((v, i) => {
318
+ if (typeof v !== "string" || !v.trim()) {
319
+ throw new ShapeError(`${label}[${i}]: expected an observation id like "o1" (refs, timestamps and kinds are code-generated)`);
320
+ }
321
+ return v.trim();
322
+ });
323
+ }
324
+ function parseProposedOp(raw, label) {
325
+ if (!isPlainObject(raw)) throw new ShapeError(`${label}: expected an object, got ${describe(raw)}`);
326
+ const tag = raw.op;
327
+ if (tag === "ADD") {
328
+ const o = check(AddShape, raw, label);
329
+ const evidenceRefs = parseObservationRefs(o.evidence, `${label}.evidence`);
330
+ if (evidenceRefs.length === 0) {
331
+ throw new ShapeError(`${label}.evidence: ADD needs at least one observation id (axiom 1: no provenance, no memory)`);
332
+ }
333
+ return {
334
+ op: "ADD",
335
+ partition: o.partition,
336
+ topic: String(o.topic),
337
+ subTopic: String(o.subTopic),
338
+ content: String(o.content),
339
+ ...o.temporal === void 0 ? {} : { temporal: o.temporal },
340
+ ...o.confidence === void 0 ? {} : { confidence: Number(o.confidence) },
341
+ why: typeof o.why === "string" ? o.why : "",
342
+ evidenceRefs
343
+ };
344
+ }
345
+ if (tag === "UPDATE") {
346
+ const o = check(UpdateShape, raw, label);
347
+ const changes = o.changes;
348
+ const out = {};
349
+ if (changes.content !== void 0) out.content = String(changes.content);
350
+ if (changes.confidence !== void 0) out.confidence = Number(changes.confidence);
351
+ if (out.content === void 0 && out.confidence === void 0) {
352
+ throw new ShapeError(`${label}.changes: needs content or confidence`);
353
+ }
354
+ return { op: "UPDATE", id: String(o.id), changes: out, why: typeof o.why === "string" ? o.why : "" };
355
+ }
356
+ if (tag === "INVALIDATE") {
357
+ const o = check(InvalidateShape, raw, label);
358
+ return {
359
+ op: "INVALIDATE",
360
+ id: String(o.id),
361
+ why: typeof o.why === "string" ? o.why : "",
362
+ evidenceRefs: parseObservationRefs(o.evidence, `${label}.evidence`)
363
+ };
364
+ }
365
+ if (tag === "NOOP") {
366
+ const o = check(NoopShape, raw, label);
367
+ return { op: "NOOP", why: typeof o.why === "string" ? o.why : "" };
368
+ }
369
+ throw new ShapeError(`${label}.op: unknown op ${JSON.stringify(tag)}`);
370
+ }
371
+ function parseProposedOps(list) {
372
+ const ops = [];
373
+ const rejected = [];
374
+ list.forEach((raw, i) => {
375
+ try {
376
+ ops.push(parseProposedOp(raw, `op[${i}]`));
377
+ } catch (e) {
378
+ rejected.push({ op: raw, reason: e instanceof Error ? e.message : String(e) });
379
+ }
380
+ });
381
+ return { ops, rejected };
382
+ }
383
+ function materializeEvidence(refs, observations, label) {
384
+ const evidence = [];
385
+ for (const ref of refs) {
386
+ const item = observations.get(ref);
387
+ if (!item) return { evidence: [], reason: `${label}: ${ref} does not match any observation in this batch` };
388
+ evidence.push({ kind: item.kind, at: item.at, ref: item.ref });
389
+ }
390
+ return { evidence };
391
+ }
392
+ function tryParseJournalRecord(value) {
393
+ try {
394
+ if (!isPlainObject(value)) return null;
395
+ const rec = {
396
+ ts: typeof value.ts === "string" ? value.ts : "",
397
+ runId: typeof value.runId === "string" ? value.runId : "",
398
+ applied: Array.isArray(value.applied) ? value.applied : [],
399
+ rejected: Array.isArray(value.rejected) ? value.rejected : []
400
+ };
401
+ if (value.v === JOURNAL_VERSION) {
402
+ if (!Array.isArray(value.entries)) return null;
403
+ rec.v = JOURNAL_VERSION;
404
+ rec.entries = value.entries.map((e, i) => parseProfileEntry(e, `entries[${i}]`));
405
+ }
406
+ return rec;
407
+ } catch {
408
+ return null;
409
+ }
410
+ }
411
+
126
412
  // src/profile/store.ts
127
413
  var DAY_MS = 864e5;
128
414
  function profileFilePath(dataDir) {
@@ -153,34 +439,77 @@ function replayText(doc, text, baselineTs, stats) {
153
439
  for (let i = 0; i < lines.length; i++) {
154
440
  const trimmed = lines[i].trim();
155
441
  if (!trimmed) continue;
156
- try {
157
- const rec = JSON.parse(trimmed);
158
- if (baselineTs && rec.ts && String(rec.ts) <= baselineTs) continue;
159
- for (const op of rec.applied ?? []) applyOpPermissive(doc, op, rec.ts);
160
- stats.records += 1;
161
- } catch {
442
+ const tornTail = () => {
162
443
  const isLast = lines.slice(i + 1).every((l) => !l.trim());
163
444
  if (stats.isLive && isLast) {
164
445
  stats.truncatedTail = lines.length - i;
165
- break;
446
+ return true;
166
447
  }
448
+ return false;
449
+ };
450
+ let rec = null;
451
+ try {
452
+ rec = tryParseJournalRecord(JSON.parse(trimmed));
453
+ } catch {
454
+ rec = null;
455
+ }
456
+ if (!rec) {
457
+ if (tornTail()) break;
458
+ stats.badRecords += 1;
459
+ continue;
460
+ }
461
+ if (baselineTs && rec.ts && String(rec.ts) <= baselineTs) continue;
462
+ try {
463
+ if (rec.v === JOURNAL_VERSION && rec.entries) {
464
+ for (const entry of rec.entries) upsertEntry(doc, entry);
465
+ } else {
466
+ for (const op of rec.applied ?? []) applyOpPermissive(doc, op, rec.ts);
467
+ }
468
+ stats.records += 1;
469
+ } catch {
470
+ stats.badRecords += 1;
471
+ }
472
+ }
473
+ }
474
+ function bucketFor(doc, partition) {
475
+ let bucket = doc.partitions[partition];
476
+ if (!bucket) {
477
+ bucket = { entries: [] };
478
+ doc.partitions[partition] = bucket;
479
+ }
480
+ return bucket;
481
+ }
482
+ function upsertEntry(doc, entry) {
483
+ const next = structuredClone(entry);
484
+ for (const p of PARTITIONS) {
485
+ const list = doc.partitions[p]?.entries;
486
+ if (!list) continue;
487
+ const at = list.findIndex((e) => e.id === next.id);
488
+ if (at < 0) continue;
489
+ if (p === next.partition) {
490
+ list[at] = next;
491
+ } else {
492
+ list.splice(at, 1);
493
+ bucketFor(doc, next.partition).entries.push(next);
167
494
  }
495
+ return;
168
496
  }
497
+ bucketFor(doc, next.partition).entries.push(next);
169
498
  }
170
499
  function replayJournal(guard, dataDir) {
171
500
  const snap = loadSnapshot(guard, dataDir);
172
501
  const doc = snap ? snap.doc : emptyProfile();
173
502
  const baselineTs = snap?.baselineTs ?? "";
174
- const stats = { records: snap?.recordsFolded ?? 0, truncatedTail: 0, isLive: false };
503
+ const stats = { records: snap?.recordsFolded ?? 0, truncatedTail: 0, badRecords: 0, isLive: false };
175
504
  for (const file of listArchiveFiles(guard, dataDir)) {
176
505
  try {
177
- replayText(doc, fs3.readFileSync(path3.join(dataDir, file), "utf8"), baselineTs, { records: 0, truncatedTail: 0, isLive: false });
506
+ replayText(doc, fs3.readFileSync(path3.join(dataDir, file), "utf8"), baselineTs, { records: 0, truncatedTail: 0, badRecords: 0, isLive: false });
178
507
  } catch {
179
508
  }
180
509
  }
181
510
  stats.isLive = true;
182
511
  replayText(doc, readText(guard, journalFilePath(dataDir), ""), baselineTs, stats);
183
- return { doc, truncatedTail: stats.truncatedTail, records: stats.records };
512
+ return { doc, truncatedTail: stats.truncatedTail, records: stats.records, badRecords: stats.badRecords };
184
513
  }
185
514
  function readJournalTexts(guard, dataDir) {
186
515
  const texts = [];
@@ -229,6 +558,7 @@ function findActive(doc, id) {
229
558
  function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
230
559
  const applied = [];
231
560
  const rejected = [];
561
+ const entries = [];
232
562
  const nowIso = new Date(now).toISOString();
233
563
  for (const op of ops) {
234
564
  if (op.op === "NOOP") {
@@ -242,12 +572,12 @@ function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
242
572
  }
243
573
  let temporal = "stable";
244
574
  if (schema) {
245
- const check = checkAddAgainstSchema(schema, op.partition, op.topic, op.subTopic, op.temporal);
246
- if (!check.ok) {
247
- rejected.push({ op, reason: check.reason });
575
+ const check2 = checkAddAgainstSchema(schema, op.partition, op.topic, op.subTopic, op.temporal);
576
+ if (!check2.ok) {
577
+ rejected.push({ op, reason: check2.reason });
248
578
  continue;
249
579
  }
250
- temporal = check.temporal;
580
+ temporal = check2.temporal;
251
581
  } else if (!(op.partition in doc.partitions)) {
252
582
  rejected.push({ op, reason: `unknown partition: ${op.partition}` });
253
583
  continue;
@@ -288,6 +618,7 @@ function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
288
618
  };
289
619
  op.assignedId = entry.id;
290
620
  doc.partitions[op.partition].entries.push(entry);
621
+ entries.push(structuredClone(entry));
291
622
  applied.push(op);
292
623
  continue;
293
624
  }
@@ -297,17 +628,20 @@ function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
297
628
  rejected.push({ op, reason: `unknown or inactive entry: ${op.id}` });
298
629
  continue;
299
630
  }
300
- if (op.changes.content !== void 0) entry.content = op.changes.content.trim();
631
+ let nextConfidence = null;
301
632
  if (op.changes.confidence !== void 0) {
302
633
  const cap = capForKinds(entry.evidence.map((e) => e.kind));
303
634
  if (op.changes.confidence > entry.confidence && entry.evidence.length < 2) {
304
635
  rejected.push({ op, reason: "confidence upgrade requires a second confirming observation" });
305
636
  continue;
306
637
  }
307
- entry.confidence = Math.min(op.changes.confidence, cap);
638
+ nextConfidence = Math.min(op.changes.confidence, cap);
308
639
  }
640
+ if (op.changes.content !== void 0) entry.content = op.changes.content.trim();
641
+ if (nextConfidence !== null) entry.confidence = nextConfidence;
309
642
  entry.updatedAt = nowIso;
310
643
  entry.updateCount += 1;
644
+ entries.push(structuredClone(entry));
311
645
  applied.push(op);
312
646
  continue;
313
647
  }
@@ -331,15 +665,17 @@ function applyOpsToDoc(guard, dataDir, doc, ops, schema, policy, now) {
331
665
  entry.updatedAt = nowIso;
332
666
  entry.updateCount += 1;
333
667
  if (op.evidence) entry.evidence.push(...op.evidence);
668
+ entries.push(structuredClone(entry));
334
669
  applied.push(op);
335
670
  continue;
336
671
  }
337
672
  }
338
- return { applied, rejected };
673
+ return { applied, rejected, entries };
339
674
  }
340
675
  var dbSeq = 0;
341
676
  function runDeterministicAging(doc, policy, now) {
342
677
  const nowIso = new Date(now).toISOString();
678
+ const entries = [];
343
679
  let volatileExpired = 0;
344
680
  let lowActivityMarked = 0;
345
681
  for (const p of PARTITIONS) {
@@ -352,18 +688,21 @@ function runDeterministicAging(doc, policy, now) {
352
688
  e.updatedAt = nowIso;
353
689
  e.updateCount += 1;
354
690
  volatileExpired += 1;
691
+ entries.push(structuredClone(e));
355
692
  }
356
693
  } else if (!e.lowActivity && now - lastEvidence > policy.profile.stableLowActivityDays * DAY_MS) {
357
694
  e.lowActivity = true;
358
695
  lowActivityMarked += 1;
696
+ entries.push(structuredClone(e));
359
697
  }
360
698
  }
361
699
  }
362
- return { volatileExpired, lowActivityMarked };
700
+ return { volatileExpired, lowActivityMarked, entries };
363
701
  }
364
702
  function persistWithJournal(guard, dataDir, doc, record) {
365
703
  saveJson(guard, profileFilePath(dataDir), doc);
366
- const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...record });
704
+ const v = record.entries === void 0 ? void 0 : JOURNAL_VERSION;
705
+ const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ...record, v });
367
706
  const journal = journalFilePath(dataDir);
368
707
  try {
369
708
  fs3.appendFileSync(guard.assert(journal), line + "\n", "utf8");
@@ -458,12 +797,46 @@ function rebuildProfile(guard, dataDir, opts = {}) {
458
797
  }
459
798
  return { ok: true, truncatedTail: replayed.truncatedTail, records: replayed.records, wrote: true };
460
799
  }
800
+ var WIPE_FILES = ["profile.json", "profile_inbox.jsonl", "profile_journal.jsonl", SNAPSHOT_FILE];
801
+ var WIPE_PATTERNS = [
802
+ /^profile_journal\.archive-/,
803
+ /\.bak-migrate-\d{14}$/,
804
+ /\.incoming-\d{14}$/
805
+ ];
806
+ function wipeProfile(guard, dataDir) {
807
+ const removed = [];
808
+ for (const f of WIPE_FILES) {
809
+ const abs = guard.assert(path3.join(dataDir, f));
810
+ if (fs3.existsSync(abs)) {
811
+ fs3.rmSync(abs, { force: true });
812
+ removed.push(f);
813
+ }
814
+ }
815
+ for (const re of WIPE_PATTERNS) {
816
+ let rels = [];
817
+ try {
818
+ rels = matchFilesRecursive(guard.assert(dataDir), re);
819
+ } catch {
820
+ rels = [];
821
+ }
822
+ for (const rel of rels) {
823
+ try {
824
+ fs3.rmSync(guard.assert(path3.join(dataDir, rel)), { force: true });
825
+ removed.push(rel);
826
+ } catch {
827
+ }
828
+ }
829
+ }
830
+ return removed;
831
+ }
461
832
 
462
833
  export {
463
834
  appendAuditLine,
464
835
  readAuditLines,
465
836
  pruneAuditFile,
466
837
  loadProfileSchema,
838
+ parseProposedOps,
839
+ materializeEvidence,
467
840
  profileFilePath,
468
841
  journalFilePath,
469
842
  ARCHIVE_PREFIX,
@@ -475,6 +848,7 @@ export {
475
848
  runDeterministicAging,
476
849
  persistWithJournal,
477
850
  verifyProfile,
478
- rebuildProfile
851
+ rebuildProfile,
852
+ wipeProfile
479
853
  };
480
- //# sourceMappingURL=chunk-CUOSICYJ.js.map
854
+ //# sourceMappingURL=chunk-544QOJN5.js.map