@remnic/core 9.69.53 → 9.69.54

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.
@@ -231,7 +231,7 @@ import {
231
231
  runTimelinePublishCli,
232
232
  screenCandidateFact,
233
233
  summarizeVaultPublish
234
- } from "./chunk-TS66MUUJ.js";
234
+ } from "./chunk-ZHV6FGVF.js";
235
235
  import {
236
236
  normalizeReplaySessionKey
237
237
  } from "./chunk-AJVLDJU6.js";
@@ -261,7 +261,7 @@ import {
261
261
  import {
262
262
  runNamespaceMaintenanceFanout,
263
263
  summarizeNamespaceMaintenanceHealth
264
- } from "./chunk-SEQ6DWXD.js";
264
+ } from "./chunk-5IKW2VP6.js";
265
265
  import {
266
266
  planNamespaceMaintenance
267
267
  } from "./chunk-7RKAE6Y4.js";
@@ -325,13 +325,14 @@ import {
325
325
  import {
326
326
  REFUSED_MERGE_CATEGORIES,
327
327
  applyNoveltyGate,
328
+ buildMergeFrontmatterUpdate,
328
329
  connectorMatchesScope,
329
330
  decideSemanticMerge,
330
331
  detectMeetings,
331
332
  embeddingsFromCosineHits,
332
333
  normalizeConnectorScope,
333
334
  runSeedGraduationPass
334
- } from "./chunk-UVZBDIJX.js";
335
+ } from "./chunk-KGY6F4VJ.js";
335
336
  import {
336
337
  compileCorrectionRule,
337
338
  correctionsFilePath,
@@ -666,9 +667,6 @@ import {
666
667
  import {
667
668
  fallbackLlmRuntimeContextFromConfig
668
669
  } from "./chunk-EP6BA7VV.js";
669
- import {
670
- normalizeRecallTokenSet
671
- } from "./chunk-YCVB7X56.js";
672
670
  import {
673
671
  graphemeUnits,
674
672
  truncateGraphemeSafe
@@ -2413,7 +2411,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
2413
2411
  namespaces,
2414
2412
  {
2415
2413
  readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
2416
- readMemoryFrontmatter: async (path42, preferredNamespace) => {
2414
+ readMemoryFrontmatter: async (path41, preferredNamespace) => {
2417
2415
  if (!fallbackReader) {
2418
2416
  for (const ns of namespaces) {
2419
2417
  try {
@@ -2425,7 +2423,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
2425
2423
  }
2426
2424
  if (!fallbackReader) return null;
2427
2425
  const memory = await this.readQmdResultMemory(
2428
- path42,
2426
+ path41,
2429
2427
  fallbackReader,
2430
2428
  namespaces,
2431
2429
  preferredNamespace
@@ -5605,93 +5603,6 @@ async function persistRepairedContentHash(storage, targetId, committedContent, c
5605
5603
  }
5606
5604
  }
5607
5605
 
5608
- // src/dedup/merge-provenance.ts
5609
- var MERGE_DERIVED_VIA = "merge";
5610
- var ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
5611
- function assertIsoInstant(value, field) {
5612
- if (typeof value !== "string" || !ISO_TIMESTAMP.test(value)) {
5613
- throw new RangeError(`${field} must be a full ISO 8601 instant, got ${JSON.stringify(value)}`);
5614
- }
5615
- const ms = Date.parse(value);
5616
- if (Number.isNaN(ms)) {
5617
- throw new RangeError(`${field} must be a full ISO 8601 instant, got ${JSON.stringify(value)}`);
5618
- }
5619
- if (!new Date(ms).toISOString().startsWith(value.slice(0, 10))) {
5620
- throw new RangeError(`${field} is not a real calendar date: ${JSON.stringify(value)}`);
5621
- }
5622
- return value;
5623
- }
5624
- function assertOffset(value, field) {
5625
- if (value === void 0) return;
5626
- if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
5627
- throw new RangeError(`${field} must be a non-negative integer when present, got ${String(value)}`);
5628
- }
5629
- }
5630
- function normalizeSource(source, index) {
5631
- const label = `sources[${index}]`;
5632
- for (const field of ["sessionKey", "quote"]) {
5633
- const value = source[field];
5634
- if (typeof value !== "string" || value.trim() === "") {
5635
- throw new RangeError(`${label}.${field} must be a non-blank string`);
5636
- }
5637
- }
5638
- assertIsoInstant(source.observedAt, `${label}.observedAt`);
5639
- if (source.turnId !== void 0 && (typeof source.turnId !== "string" || source.turnId.trim() === "")) {
5640
- throw new RangeError(`${label}.turnId must be a non-blank string when present`);
5641
- }
5642
- assertOffset(source.charStart, `${label}.charStart`);
5643
- assertOffset(source.charEnd, `${label}.charEnd`);
5644
- if (source.charStart !== void 0 && source.charEnd !== void 0 && source.charEnd < source.charStart) {
5645
- throw new RangeError(`${label}.charEnd must be >= charStart`);
5646
- }
5647
- const copy = {
5648
- sessionKey: source.sessionKey,
5649
- observedAt: source.observedAt,
5650
- quote: source.quote
5651
- };
5652
- if (source.turnId !== void 0) copy.turnId = source.turnId;
5653
- if (source.charStart !== void 0) copy.charStart = source.charStart;
5654
- if (source.charEnd !== void 0) copy.charEnd = source.charEnd;
5655
- return copy;
5656
- }
5657
- function sourceKey(source) {
5658
- return JSON.stringify([
5659
- source.sessionKey,
5660
- source.turnId ?? null,
5661
- source.observedAt,
5662
- source.quote,
5663
- source.charStart ?? null,
5664
- source.charEnd ?? null
5665
- ]);
5666
- }
5667
- function buildMergeFrontmatterUpdate(input) {
5668
- const updated = assertIsoInstant(input.nowIso, "nowIso");
5669
- const previous = input.targetReinforcementCount;
5670
- if (previous !== void 0 && (typeof previous !== "number" || !Number.isInteger(previous) || previous < 0)) {
5671
- throw new RangeError(
5672
- `reinforcement_count must be a non-negative integer when supplied, got ${String(previous)}`
5673
- );
5674
- }
5675
- const seen = /* @__PURE__ */ new Set();
5676
- const sources = [];
5677
- const append = (source, index) => {
5678
- const copy = normalizeSource(source, index);
5679
- const key = sourceKey(copy);
5680
- if (seen.has(key)) return;
5681
- seen.add(key);
5682
- sources.push(copy);
5683
- };
5684
- const target = input.targetSources ?? [];
5685
- target.forEach(append);
5686
- input.incomingSources.forEach((source, i) => append(source, target.length + i));
5687
- return {
5688
- updated,
5689
- derived_via: MERGE_DERIVED_VIA,
5690
- reinforcement_count: (previous ?? 0) + 1,
5691
- sources
5692
- };
5693
- }
5694
-
5695
5606
  // src/dedup/merge-judge.ts
5696
5607
  import { z } from "zod";
5697
5608
  var MERGE_JUDGE_SYSTEM_PROMPT = `You maintain a long-term memory store. Given a NEW memory and up to N EXISTING
@@ -24324,9 +24235,9 @@ function readStructuralSymbol(entry) {
24324
24235
  const rec = entry;
24325
24236
  const symbol = typeof rec.symbol === "string" ? rec.symbol.trim() : "";
24326
24237
  if (!symbol) return null;
24327
- const path42 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
24238
+ const path41 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
24328
24239
  const kind = typeof rec.kind === "string" && rec.kind.length > 0 ? rec.kind : void 0;
24329
- const item = path42 !== void 0 && kind !== void 0 ? { symbol, path: path42, kind } : path42 !== void 0 ? { symbol, path: path42 } : kind !== void 0 ? { symbol, kind } : { symbol };
24240
+ const item = path41 !== void 0 && kind !== void 0 ? { symbol, path: path41, kind } : path41 !== void 0 ? { symbol, path: path41 } : kind !== void 0 ? { symbol, kind } : { symbol };
24330
24241
  return item;
24331
24242
  }
24332
24243
  function classifySpawnError(err) {
@@ -26302,305 +26213,10 @@ function walkFiles(dir, callback) {
26302
26213
  }
26303
26214
  }
26304
26215
 
26305
- // src/dedup/index.ts
26306
- import crypto5 from "crypto";
26216
+ // src/sync/index.ts
26307
26217
  import fs4 from "fs";
26308
26218
  import path31 from "path";
26309
- var DEFAULT_DEDUP_THRESHOLD = 0.85;
26310
- var DEFAULT_MAX_LOAD = 1e4;
26311
- function normalizeThreshold(value, defaultValue) {
26312
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1 ? value : defaultValue;
26313
- }
26314
- function normalizeMaxLoad(value, defaultValue) {
26315
- return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : defaultValue;
26316
- }
26317
- function findDuplicates(options) {
26318
- const startTime = Date.now();
26319
- const { memoryDir } = options;
26320
- const threshold = normalizeThreshold(options.threshold, DEFAULT_DEDUP_THRESHOLD);
26321
- const maxLoad = normalizeMaxLoad(options.maxLoad, DEFAULT_MAX_LOAD);
26322
- const memories = loadMemories(memoryDir, options.categories, maxLoad);
26323
- const duplicates = [];
26324
- for (let i = 0; i < memories.length; i++) {
26325
- for (let j = i + 1; j < memories.length; j++) {
26326
- const sim = computeSimilarity(memories[i].content, memories[j].content);
26327
- if (sim >= threshold) {
26328
- duplicates.push({
26329
- left: memories[i],
26330
- right: memories[j],
26331
- similarity: sim,
26332
- action: sim >= 0.98 ? "merge" : sim >= 0.9 ? "keep_right" : "keep_left"
26333
- });
26334
- }
26335
- }
26336
- }
26337
- return {
26338
- scanned: memories.length,
26339
- duplicates,
26340
- durationMs: Date.now() - startTime
26341
- };
26342
- }
26343
- function findContradictions(options) {
26344
- const startTime = Date.now();
26345
- const { memoryDir } = options;
26346
- const maxLoad = normalizeMaxLoad(options.maxLoad, DEFAULT_MAX_LOAD);
26347
- const memories = loadMemories(memoryDir, options.categories, maxLoad);
26348
- const contradictions = [];
26349
- for (let i = 0; i < memories.length; i++) {
26350
- for (let j = i + 1; j < memories.length; j++) {
26351
- const contra = detectContradiction(memories[i], memories[j]);
26352
- if (contra) {
26353
- contradictions.push(contra);
26354
- }
26355
- }
26356
- }
26357
- return {
26358
- scanned: memories.length,
26359
- contradictions,
26360
- durationMs: Date.now() - startTime
26361
- };
26362
- }
26363
- function computeSimilarity(a, b) {
26364
- const normA = normalize(a);
26365
- const normB = normalize(b);
26366
- const punctuationBoundariesA = embeddedPunctuationSignature(a);
26367
- const punctuationBoundariesB = embeddedPunctuationSignature(b);
26368
- if (punctuationBoundariesA !== punctuationBoundariesB) return 0;
26369
- if (normA === normB) return 1;
26370
- if (hashContent2(normA) === hashContent2(normB)) return 0.99;
26371
- if (normA.length > 50 && normB.length > 50) {
26372
- if (normA.includes(normB.slice(0, 40)) || normB.includes(normA.slice(0, 40))) {
26373
- return 0.9;
26374
- }
26375
- }
26376
- const wordsA = tokenizeForSimilarity(normA);
26377
- const wordsB = tokenizeForSimilarity(normB);
26378
- const intersection = new Set([...wordsA].filter((w) => wordsB.has(w)));
26379
- const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
26380
- if (union.size === 0) return 0;
26381
- return intersection.size / union.size;
26382
- }
26383
- function embeddedPunctuationSignature(text) {
26384
- const chars = [...text.normalize("NFC")];
26385
- const significant = /[\p{L}\p{M}\p{N}]/u;
26386
- const ignored = /[\s']/u;
26387
- const boundaries = [];
26388
- let position = 0;
26389
- for (let index = 0; index < chars.length; ) {
26390
- const char = chars[index];
26391
- if (char === void 0) break;
26392
- if (significant.test(char)) {
26393
- position++;
26394
- index++;
26395
- continue;
26396
- }
26397
- if (ignored.test(char)) {
26398
- index++;
26399
- continue;
26400
- }
26401
- const start = index;
26402
- while (index < chars.length) {
26403
- const next = chars[index];
26404
- if (next === void 0 || significant.test(next) || ignored.test(next)) break;
26405
- index++;
26406
- }
26407
- const before = chars[start - 1];
26408
- const after = chars[index];
26409
- if (before && after && significant.test(before) && significant.test(after)) {
26410
- boundaries.push(`${position}:${chars.slice(start, index).join("")}`);
26411
- }
26412
- }
26413
- return boundaries.join("\0");
26414
- }
26415
- function normalize(text) {
26416
- return text.normalize("NFC").toLowerCase().replace(/[^\p{ASCII}\p{L}\p{M}\p{N}\s']/gu, " ").replace(/[^\p{L}\p{M}\p{N}\s']/gu, "").replace(/\s+/g, " ").trim();
26417
- }
26418
- function tokenizeForSimilarity(text) {
26419
- if (/\p{Script=Hangul}/u.test(text)) {
26420
- const tokens = normalizeRecallTokenSet(text);
26421
- for (const segment of text.match(/\p{Script=Hangul}+/gu) ?? []) {
26422
- const chars = [...segment];
26423
- for (const size of [1, 2, 3, 4]) {
26424
- for (let index = 0; index <= chars.length - size; index += 1) {
26425
- tokens.add(chars.slice(index, index + size).join(""));
26426
- }
26427
- }
26428
- }
26429
- return tokens;
26430
- }
26431
- if (/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(text)) {
26432
- return normalizeRecallTokenSet(text);
26433
- }
26434
- return text.length === 0 ? /* @__PURE__ */ new Set() : new Set(text.split(/\s+/));
26435
- }
26436
- var NEGATION_WORDS = /* @__PURE__ */ new Set([
26437
- "not",
26438
- "don't",
26439
- "doesn't",
26440
- "isn't",
26441
- "aren't",
26442
- "won't",
26443
- "can't",
26444
- "cannot",
26445
- "never",
26446
- "no",
26447
- "none",
26448
- "neither",
26449
- "nor",
26450
- "nothing",
26451
- "nowhere"
26452
- ]);
26453
- function detectContradiction(a, b) {
26454
- const normA = normalize(a.content);
26455
- const normB = normalize(b.content);
26456
- const aHasNegation = containsNegation(normA);
26457
- const bHasNegation = containsNegation(normB);
26458
- if (aHasNegation === bHasNegation) return null;
26459
- const strippedA = stripNegation(normA);
26460
- const strippedB = stripNegation(normB);
26461
- const sim = computeSimilarity(strippedA, strippedB);
26462
- if (sim < 0.7) return null;
26463
- const oppQuantifiers = [
26464
- ["always", "never"],
26465
- ["all", "none"],
26466
- ["every", "no"],
26467
- ["must", "must not"],
26468
- ["should", "should not"],
26469
- ["can", "cannot"]
26470
- ];
26471
- for (const [pos, neg] of oppQuantifiers) {
26472
- if (a.content.toLowerCase().includes(pos) && b.content.toLowerCase().includes(neg) || a.content.toLowerCase().includes(neg) && b.content.toLowerCase().includes(pos)) {
26473
- return {
26474
- left: a,
26475
- right: b,
26476
- severity: "high",
26477
- reason: `Opposite quantifiers: "${pos}" vs "${neg}"`
26478
- };
26479
- }
26480
- }
26481
- return {
26482
- left: a,
26483
- right: b,
26484
- severity: sim >= 0.85 ? "high" : "medium",
26485
- reason: "Negated version of similar content"
26486
- };
26487
- }
26488
- function containsNegation(text) {
26489
- const words = text.split(/\s+/);
26490
- return words.some((w) => NEGATION_WORDS.has(w));
26491
- }
26492
- function escapeRegExp(value) {
26493
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26494
- }
26495
- var stripNegationPattern = null;
26496
- var stripNegationPatternSource = null;
26497
- function stripNegationRegex() {
26498
- const alternation = [...NEGATION_WORDS].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
26499
- if (stripNegationPattern === null || stripNegationPatternSource !== alternation) {
26500
- stripNegationPattern = new RegExp(`\\b(?:${alternation})\\b`, "gi");
26501
- stripNegationPatternSource = alternation;
26502
- }
26503
- return stripNegationPattern;
26504
- }
26505
- function stripNegation(text) {
26506
- return text.replace(stripNegationRegex(), "").replace(/\s+/g, " ").trim();
26507
- }
26508
- function loadMemories(memoryDir, categories, maxLoad = 1e4) {
26509
- const result = [];
26510
- const allCategories = categories ?? ALL_CATEGORY_DIRS;
26511
- if (!fs4.existsSync(memoryDir)) return result;
26512
- const memoryRootReal = fs4.realpathSync(memoryDir);
26513
- for (const category of allCategories) {
26514
- if (result.length >= maxLoad) break;
26515
- const dir = path31.join(memoryDir, category);
26516
- if (!fs4.existsSync(dir)) continue;
26517
- const categoryStat = fs4.lstatSync(dir);
26518
- if (categoryStat.isSymbolicLink()) {
26519
- throw new Error(`Refusing to scan symlinked memory category directory: ${dir}`);
26520
- }
26521
- if (!categoryStat.isDirectory()) continue;
26522
- const categoryRootReal = fs4.realpathSync(dir);
26523
- assertPathInsideRoot2(memoryRootReal, categoryRootReal, dir);
26524
- walkMdFiles(dir, memoryRootReal, categoryRootReal, (filePath) => {
26525
- if (result.length >= maxLoad) return;
26526
- const content = readFileSafe2(filePath, memoryRootReal, categoryRootReal);
26527
- if (!content) return;
26528
- const fm = parseFrontmatter3(content);
26529
- const body = extractBody3(content);
26530
- if (!fm?.id || !body) return;
26531
- result.push({
26532
- id: fm.id,
26533
- content: body,
26534
- category: fm.category ?? category.slice(0, -1),
26535
- filePath
26536
- });
26537
- });
26538
- }
26539
- return result;
26540
- }
26541
- function hashContent2(content) {
26542
- return crypto5.createHash("sha256").update(content).digest("hex").slice(0, 16);
26543
- }
26544
- function readFileSafe2(filePath, memoryRootReal, categoryRootReal) {
26545
- try {
26546
- const fileStat = fs4.lstatSync(filePath);
26547
- if (fileStat.isSymbolicLink()) {
26548
- throw new Error(`Refusing to read symlinked memory file: ${filePath}`);
26549
- }
26550
- const fileReal = fs4.realpathSync(filePath);
26551
- assertPathInsideRoot2(memoryRootReal, fileReal, filePath);
26552
- assertPathInsideRoot2(categoryRootReal, fileReal, filePath);
26553
- return fs4.readFileSync(filePath, "utf8");
26554
- } catch {
26555
- return null;
26556
- }
26557
- }
26558
- function parseFrontmatter3(content) {
26559
- const match = content.match(/^---\n([\s\S]*?)\n---/);
26560
- if (!match) return null;
26561
- const fm = {};
26562
- for (const line of match[1].split("\n")) {
26563
- const colonIdx = line.indexOf(":");
26564
- if (colonIdx === -1) continue;
26565
- const key = line.slice(0, colonIdx).trim();
26566
- const value = line.slice(colonIdx + 1).trim();
26567
- fm[key] = value;
26568
- }
26569
- return fm;
26570
- }
26571
- function extractBody3(content) {
26572
- const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)/);
26573
- return match ? match[1].trim() : content.trim();
26574
- }
26575
- function assertPathInsideRoot2(rootReal, targetReal, sourcePath) {
26576
- const rel = path31.relative(rootReal, targetReal);
26577
- if (rel === "" || !rel.startsWith("..") && !path31.isAbsolute(rel)) {
26578
- return;
26579
- }
26580
- throw new Error(`Refusing to scan memory path outside root: ${sourcePath}`);
26581
- }
26582
- function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
26583
- for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
26584
- const fullPath = path31.join(dir, entry.name);
26585
- const entryStat = fs4.lstatSync(fullPath);
26586
- if (entryStat.isSymbolicLink()) {
26587
- throw new Error(`Refusing to scan symlinked memory path: ${fullPath}`);
26588
- }
26589
- const entryReal = fs4.realpathSync(fullPath);
26590
- assertPathInsideRoot2(memoryRootReal, entryReal, fullPath);
26591
- assertPathInsideRoot2(categoryRootReal, entryReal, fullPath);
26592
- if (entryStat.isDirectory()) {
26593
- walkMdFiles(fullPath, memoryRootReal, categoryRootReal, callback);
26594
- } else if (entry.name.endsWith(".md")) {
26595
- callback(fullPath);
26596
- }
26597
- }
26598
- }
26599
-
26600
- // src/sync/index.ts
26601
- import fs5 from "fs";
26602
- import path32 from "path";
26603
- import crypto6 from "crypto";
26219
+ import crypto5 from "crypto";
26604
26220
  var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
26605
26221
  var DEFAULT_EXCLUDE2 = /* @__PURE__ */ new Set([
26606
26222
  "node_modules",
@@ -26621,7 +26237,7 @@ function syncChanges(options) {
26621
26237
  } = options;
26622
26238
  const extSet = new Set(extensions);
26623
26239
  const excludeSet = /* @__PURE__ */ new Set([...DEFAULT_EXCLUDE2, ...excludeDirs]);
26624
- const stateFilePath = options.stateFile ?? path32.join(memoryDir, ".sync-state.json");
26240
+ const stateFilePath = options.stateFile ?? path31.join(memoryDir, ".sync-state.json");
26625
26241
  const prevState = loadState(stateFilePath);
26626
26242
  const currentFiles = scanFiles(sourceDir, extSet, excludeSet);
26627
26243
  const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);
@@ -26637,8 +26253,8 @@ function syncChanges(options) {
26637
26253
  for (const [relPath, hash] of Object.entries(currentFiles)) {
26638
26254
  newState.fileHashes[relPath] = hash;
26639
26255
  }
26640
- fs5.mkdirSync(path32.dirname(stateFilePath), { recursive: true });
26641
- fs5.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));
26256
+ fs4.mkdirSync(path31.dirname(stateFilePath), { recursive: true });
26257
+ fs4.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));
26642
26258
  }
26643
26259
  return {
26644
26260
  scanned: Object.keys(currentFiles).length,
@@ -26692,7 +26308,7 @@ function scanFiles(root, extensions, exclude) {
26692
26308
  function walk(dir, isRoot = false) {
26693
26309
  let entries;
26694
26310
  try {
26695
- entries = fs5.readdirSync(dir, { withFileTypes: true });
26311
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
26696
26312
  } catch (err) {
26697
26313
  if (isRoot) {
26698
26314
  throw new Error(
@@ -26703,16 +26319,16 @@ function scanFiles(root, extensions, exclude) {
26703
26319
  }
26704
26320
  for (const entry of entries) {
26705
26321
  if (exclude.has(entry.name)) continue;
26706
- const fullPath = path32.join(dir, entry.name);
26322
+ const fullPath = path31.join(dir, entry.name);
26707
26323
  if (entry.isDirectory()) {
26708
26324
  walk(fullPath);
26709
26325
  } else if (entry.isFile()) {
26710
- const ext = path32.extname(entry.name).toLowerCase();
26326
+ const ext = path31.extname(entry.name).toLowerCase();
26711
26327
  if (!extensions.has(ext)) continue;
26712
- const relPath = path32.relative(root, fullPath);
26328
+ const relPath = path31.relative(root, fullPath);
26713
26329
  try {
26714
- const content = fs5.readFileSync(fullPath, "utf8");
26715
- result[relPath] = hashContent3(content);
26330
+ const content = fs4.readFileSync(fullPath, "utf8");
26331
+ result[relPath] = hashContent2(content);
26716
26332
  } catch {
26717
26333
  }
26718
26334
  }
@@ -26724,11 +26340,11 @@ function scanFiles(root, extensions, exclude) {
26724
26340
  function computeDiff(current, previous, sourceDir) {
26725
26341
  const changes = [];
26726
26342
  for (const [relPath, hash] of Object.entries(current)) {
26727
- const fullPath = path32.join(sourceDir, relPath);
26343
+ const fullPath = path31.join(sourceDir, relPath);
26728
26344
  if (!(relPath in previous)) {
26729
26345
  let size = 0;
26730
26346
  try {
26731
- size = fs5.statSync(fullPath).size;
26347
+ size = fs4.statSync(fullPath).size;
26732
26348
  } catch {
26733
26349
  }
26734
26350
  changes.push({
@@ -26741,7 +26357,7 @@ function computeDiff(current, previous, sourceDir) {
26741
26357
  } else if (previous[relPath] !== hash) {
26742
26358
  let size = 0;
26743
26359
  try {
26744
- size = fs5.statSync(fullPath).size;
26360
+ size = fs4.statSync(fullPath).size;
26745
26361
  } catch {
26746
26362
  }
26747
26363
  changes.push({
@@ -26757,7 +26373,7 @@ function computeDiff(current, previous, sourceDir) {
26757
26373
  for (const relPath of Object.keys(previous)) {
26758
26374
  if (!(relPath in current)) {
26759
26375
  changes.push({
26760
- filePath: path32.join(sourceDir, relPath),
26376
+ filePath: path31.join(sourceDir, relPath),
26761
26377
  relativePath: relPath,
26762
26378
  type: "deleted",
26763
26379
  currentHash: "",
@@ -26769,7 +26385,7 @@ function computeDiff(current, previous, sourceDir) {
26769
26385
  }
26770
26386
  function loadState(stateFilePath) {
26771
26387
  try {
26772
- const raw = fs5.readFileSync(stateFilePath, "utf8");
26388
+ const raw = fs4.readFileSync(stateFilePath, "utf8");
26773
26389
  return JSON.parse(raw);
26774
26390
  } catch {
26775
26391
  return {
@@ -26779,32 +26395,32 @@ function loadState(stateFilePath) {
26779
26395
  };
26780
26396
  }
26781
26397
  }
26782
- function hashContent3(content) {
26783
- return crypto6.createHash("sha256").update(content).digest("hex").slice(0, 16);
26398
+ function hashContent2(content) {
26399
+ return crypto5.createHash("sha256").update(content).digest("hex").slice(0, 16);
26784
26400
  }
26785
26401
 
26786
26402
  // src/spaces/index.ts
26787
26403
  import { spawnSync } from "child_process";
26788
- import crypto7 from "crypto";
26789
- import fs6 from "fs";
26790
- import path33 from "path";
26404
+ import crypto6 from "crypto";
26405
+ import fs5 from "fs";
26406
+ import path32 from "path";
26791
26407
  var historicalIdCache3 = new HistoricalEntityCanonicalIdCache();
26792
26408
  var MANIFEST_VERSION = 1;
26793
26409
  var MANIFEST_LOCK_STALE_MS = 3e4;
26794
26410
  var MANIFEST_LOCK_TIMEOUT_MS = MANIFEST_LOCK_STALE_MS + 1e4;
26795
26411
  var MANIFEST_LOCK_SLEEP_MS = 20;
26796
26412
  function normalizeSpaceMemoryDir(memoryDir) {
26797
- return path33.resolve(memoryDir);
26413
+ return path32.resolve(memoryDir);
26798
26414
  }
26799
26415
  function getSpacesDir(baseDir) {
26800
26416
  const homeDir = baseDir ?? resolveHomeDir();
26801
- return path33.join(homeDir, ".config", "engram", "spaces");
26417
+ return path32.join(homeDir, ".config", "engram", "spaces");
26802
26418
  }
26803
26419
  function getManifestPath(baseDir) {
26804
- return path33.join(getSpacesDir(baseDir), "manifest.json");
26420
+ return path32.join(getSpacesDir(baseDir), "manifest.json");
26805
26421
  }
26806
26422
  function loadManifest(baseDir, memoryDirOverride) {
26807
- if (fs6.existsSync(getManifestPath(baseDir))) {
26423
+ if (fs5.existsSync(getManifestPath(baseDir))) {
26808
26424
  try {
26809
26425
  return readManifestUnlocked(baseDir, memoryDirOverride, { bootstrapIfMissing: false });
26810
26426
  } catch (error) {
@@ -26830,7 +26446,7 @@ function updateManifest(baseDir, updater, memoryDirOverride) {
26830
26446
  }
26831
26447
  function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
26832
26448
  const manifestPath2 = getManifestPath(baseDir);
26833
- if (!fs6.existsSync(manifestPath2)) {
26449
+ if (!fs5.existsSync(manifestPath2)) {
26834
26450
  if (options.bootstrapIfMissing === false) {
26835
26451
  const error = new Error(`Spaces manifest not found: ${manifestPath2}`);
26836
26452
  error.code = "ENOENT";
@@ -26843,21 +26459,21 @@ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
26843
26459
  version: MANIFEST_VERSION
26844
26460
  };
26845
26461
  }
26846
- const raw = JSON.parse(fs6.readFileSync(manifestPath2, "utf8"));
26462
+ const raw = JSON.parse(fs5.readFileSync(manifestPath2, "utf8"));
26847
26463
  return raw;
26848
26464
  }
26849
26465
  function saveManifestUnlocked(manifest, baseDir) {
26850
26466
  const manifestPath2 = getManifestPath(baseDir);
26851
- const manifestDir2 = path33.dirname(manifestPath2);
26852
- fs6.mkdirSync(manifestDir2, { recursive: true });
26853
- const tempPath = path33.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
26467
+ const manifestDir2 = path32.dirname(manifestPath2);
26468
+ fs5.mkdirSync(manifestDir2, { recursive: true });
26469
+ const tempPath = path32.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto6.randomUUID()}.tmp`);
26854
26470
  try {
26855
- fs6.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
26471
+ fs5.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
26856
26472
  `, { flag: "wx" });
26857
- fs6.renameSync(tempPath, manifestPath2);
26473
+ fs5.renameSync(tempPath, manifestPath2);
26858
26474
  } catch (error) {
26859
26475
  try {
26860
- fs6.rmSync(tempPath, { force: true });
26476
+ fs5.rmSync(tempPath, { force: true });
26861
26477
  } catch {
26862
26478
  }
26863
26479
  throw error;
@@ -26865,7 +26481,7 @@ function saveManifestUnlocked(manifest, baseDir) {
26865
26481
  }
26866
26482
  function withManifestLock(baseDir, operation) {
26867
26483
  const lockDir = `${getManifestPath(baseDir)}.lock`;
26868
- fs6.mkdirSync(path33.dirname(lockDir), { recursive: true });
26484
+ fs5.mkdirSync(path32.dirname(lockDir), { recursive: true });
26869
26485
  const lockOwner = acquireManifestLock(lockDir);
26870
26486
  try {
26871
26487
  return operation();
@@ -26878,10 +26494,10 @@ function acquireManifestLock(lockDir) {
26878
26494
  const owner = createManifestLockOwner();
26879
26495
  const reclaimDir = getManifestLockReclaimDir(lockDir);
26880
26496
  while (true) {
26881
- if (fs6.existsSync(reclaimDir)) {
26497
+ if (fs5.existsSync(reclaimDir)) {
26882
26498
  removeStaleManifestReclaimLock(reclaimDir);
26883
26499
  }
26884
- if (fs6.existsSync(reclaimDir)) {
26500
+ if (fs5.existsSync(reclaimDir)) {
26885
26501
  if (Date.now() >= deadline) {
26886
26502
  throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
26887
26503
  }
@@ -26889,15 +26505,15 @@ function acquireManifestLock(lockDir) {
26889
26505
  continue;
26890
26506
  }
26891
26507
  try {
26892
- fs6.mkdirSync(lockDir, { recursive: false });
26508
+ fs5.mkdirSync(lockDir, { recursive: false });
26893
26509
  try {
26894
- fs6.writeFileSync(path33.join(lockDir, "owner"), `${owner}
26510
+ fs5.writeFileSync(path32.join(lockDir, "owner"), `${owner}
26895
26511
  `, { flag: "wx" });
26896
26512
  } catch (error) {
26897
- fs6.rmSync(lockDir, { recursive: true, force: true });
26513
+ fs5.rmSync(lockDir, { recursive: true, force: true });
26898
26514
  throw error;
26899
26515
  }
26900
- if (fs6.existsSync(reclaimDir)) {
26516
+ if (fs5.existsSync(reclaimDir)) {
26901
26517
  releaseManifestLock(lockDir, owner);
26902
26518
  if (Date.now() >= deadline) {
26903
26519
  throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
@@ -26921,9 +26537,9 @@ function acquireManifestLock(lockDir) {
26921
26537
  }
26922
26538
  function releaseManifestLock(lockDir, owner) {
26923
26539
  try {
26924
- const ownerPath = path33.join(lockDir, "owner");
26925
- if (fs6.readFileSync(ownerPath, "utf8").trim() === owner) {
26926
- fs6.rmSync(lockDir, { recursive: true, force: true });
26540
+ const ownerPath = path32.join(lockDir, "owner");
26541
+ if (fs5.readFileSync(ownerPath, "utf8").trim() === owner) {
26542
+ fs5.rmSync(lockDir, { recursive: true, force: true });
26927
26543
  }
26928
26544
  } catch (error) {
26929
26545
  if (error.code !== "ENOENT") {
@@ -26935,12 +26551,12 @@ function removeStaleManifestLock(lockDir) {
26935
26551
  const reclaimDir = getManifestLockReclaimDir(lockDir);
26936
26552
  const reclaimOwner = createManifestLockOwner();
26937
26553
  try {
26938
- fs6.mkdirSync(reclaimDir, { recursive: false });
26554
+ fs5.mkdirSync(reclaimDir, { recursive: false });
26939
26555
  try {
26940
- fs6.writeFileSync(path33.join(reclaimDir, "owner"), `${reclaimOwner}
26556
+ fs5.writeFileSync(path32.join(reclaimDir, "owner"), `${reclaimOwner}
26941
26557
  `, { flag: "wx" });
26942
26558
  } catch (error) {
26943
- fs6.rmSync(reclaimDir, { recursive: true, force: true });
26559
+ fs5.rmSync(reclaimDir, { recursive: true, force: true });
26944
26560
  throw error;
26945
26561
  }
26946
26562
  } catch (error) {
@@ -26958,18 +26574,18 @@ function removeStaleManifestLock(lockDir) {
26958
26574
  if (isManifestLockOwnerActive(snapshot.owner)) {
26959
26575
  return;
26960
26576
  }
26961
- const tombstoneDir = `${lockDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
26577
+ const tombstoneDir = `${lockDir}.stale.${process.pid}.${crypto6.randomUUID()}`;
26962
26578
  try {
26963
- fs6.renameSync(lockDir, tombstoneDir);
26579
+ fs5.renameSync(lockDir, tombstoneDir);
26964
26580
  } catch (error) {
26965
26581
  if (error.code !== "ENOENT") {
26966
26582
  throw error;
26967
26583
  }
26968
26584
  return;
26969
26585
  }
26970
- fs6.rmSync(tombstoneDir, { recursive: true, force: true });
26586
+ fs5.rmSync(tombstoneDir, { recursive: true, force: true });
26971
26587
  } finally {
26972
- fs6.rmSync(reclaimDir, { recursive: true, force: true });
26588
+ fs5.rmSync(reclaimDir, { recursive: true, force: true });
26973
26589
  }
26974
26590
  }
26975
26591
  function getManifestLockReclaimDir(lockDir) {
@@ -26979,7 +26595,7 @@ function createManifestLockOwner() {
26979
26595
  return JSON.stringify({
26980
26596
  pid: process.pid,
26981
26597
  startKey: readProcessStartKey(process.pid),
26982
- token: crypto7.randomUUID()
26598
+ token: crypto6.randomUUID()
26983
26599
  });
26984
26600
  }
26985
26601
  function removeStaleManifestReclaimLock(reclaimDir) {
@@ -26990,21 +26606,21 @@ function removeStaleManifestReclaimLock(reclaimDir) {
26990
26606
  if (isManifestLockOwnerActive(snapshot.owner)) {
26991
26607
  return;
26992
26608
  }
26993
- const tombstoneDir = `${reclaimDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
26609
+ const tombstoneDir = `${reclaimDir}.stale.${process.pid}.${crypto6.randomUUID()}`;
26994
26610
  try {
26995
- fs6.renameSync(reclaimDir, tombstoneDir);
26611
+ fs5.renameSync(reclaimDir, tombstoneDir);
26996
26612
  } catch (error) {
26997
26613
  if (error.code !== "ENOENT") {
26998
26614
  throw error;
26999
26615
  }
27000
26616
  return;
27001
26617
  }
27002
- fs6.rmSync(tombstoneDir, { recursive: true, force: true });
26618
+ fs5.rmSync(tombstoneDir, { recursive: true, force: true });
27003
26619
  }
27004
26620
  function readManifestLockSnapshot(lockDir) {
27005
26621
  let stat5;
27006
26622
  try {
27007
- stat5 = fs6.statSync(lockDir);
26623
+ stat5 = fs5.statSync(lockDir);
27008
26624
  } catch (error) {
27009
26625
  if (error.code === "ENOENT") {
27010
26626
  return void 0;
@@ -27012,7 +26628,7 @@ function readManifestLockSnapshot(lockDir) {
27012
26628
  throw error;
27013
26629
  }
27014
26630
  try {
27015
- const owner = fs6.readFileSync(path33.join(lockDir, "owner"), "utf8").trim();
26631
+ const owner = fs5.readFileSync(path32.join(lockDir, "owner"), "utf8").trim();
27016
26632
  return { mtimeMs: stat5.mtimeMs, owner };
27017
26633
  } catch (error) {
27018
26634
  if (error.code === "ENOENT") {
@@ -27084,9 +26700,9 @@ function sleepSync(ms) {
27084
26700
  }
27085
26701
  function createPersonalSpace(baseDir, memoryDirOverride) {
27086
26702
  const homeDir = baseDir ?? resolveHomeDir();
27087
- const standalonePath = path33.join(homeDir, ".engram", "memory");
27088
- const openclawPath = path33.join(homeDir, ".openclaw", "workspace", "memory", "local");
27089
- const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs6.existsSync(standalonePath) ? standalonePath : fs6.existsSync(openclawPath) ? openclawPath : standalonePath);
26703
+ const standalonePath = path32.join(homeDir, ".engram", "memory");
26704
+ const openclawPath = path32.join(homeDir, ".openclaw", "workspace", "memory", "local");
26705
+ const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs5.existsSync(standalonePath) ? standalonePath : fs5.existsSync(openclawPath) ? openclawPath : standalonePath);
27090
26706
  const normalizedMemoryDir = normalizeSpaceMemoryDir(memoryDir);
27091
26707
  const now = (/* @__PURE__ */ new Date()).toISOString();
27092
26708
  return {
@@ -27114,7 +26730,7 @@ function createSpace(options) {
27114
26730
  const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
27115
26731
  const now = (/* @__PURE__ */ new Date()).toISOString();
27116
26732
  const memoryDir = normalizeSpaceMemoryDir(
27117
- options.memoryDir ?? path33.join(getSpacesDir(options.baseDir), id, "memory")
26733
+ options.memoryDir ?? path32.join(getSpacesDir(options.baseDir), id, "memory")
27118
26734
  );
27119
26735
  const space = updateManifest(options.baseDir, (manifest) => {
27120
26736
  if (manifest.spaces.some((s) => s.id === id)) {
@@ -27134,7 +26750,7 @@ function createSpace(options) {
27134
26750
  owner: readEnvVar("USER"),
27135
26751
  parentSpaceId: options.parentSpaceId
27136
26752
  };
27137
- fs6.mkdirSync(memoryDir, { recursive: true });
26753
+ fs5.mkdirSync(memoryDir, { recursive: true });
27138
26754
  manifest.spaces.push(created);
27139
26755
  manifest.updatedAt = now;
27140
26756
  return created;
@@ -27292,9 +26908,9 @@ async function promoteSpace(sourceSpaceId, targetSpaceId, options) {
27292
26908
  const subjectWarnings = [];
27293
26909
  const warnedMemoryIds = [];
27294
26910
  if (target.kind === "team") {
27295
- const sourceFiles = fs6.existsSync(source.memoryDir) ? walkMd(fs6.realpathSync(source.memoryDir)) : [];
26911
+ const sourceFiles = fs5.existsSync(source.memoryDir) ? walkMd(fs5.realpathSync(source.memoryDir)) : [];
27296
26912
  for (const file of sourceFiles) {
27297
- const fm = parseSimpleFrontmatter(fs6.readFileSync(file, "utf8"));
26913
+ const fm = parseSimpleFrontmatter(fs5.readFileSync(file, "utf8"));
27298
26914
  if (!fm?.id) continue;
27299
26915
  if (options?.memoryIds?.length && !options.memoryIds.includes(fm.id)) continue;
27300
26916
  const decision = evaluateSubjectGuard({
@@ -27360,39 +26976,39 @@ async function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
27360
26976
  };
27361
26977
  }
27362
26978
  function getAuditLog(baseDir) {
27363
- const auditPath = path33.join(getSpacesDir(baseDir), "audit.jsonl");
27364
- if (!fs6.existsSync(auditPath)) return [];
27365
- const lines = fs6.readFileSync(auditPath, "utf8").trim().split("\n");
26979
+ const auditPath = path32.join(getSpacesDir(baseDir), "audit.jsonl");
26980
+ if (!fs5.existsSync(auditPath)) return [];
26981
+ const lines = fs5.readFileSync(auditPath, "utf8").trim().split("\n");
27366
26982
  return lines.filter((l) => l.trim()).map((l) => JSON.parse(l));
27367
26983
  }
27368
26984
  function appendAudit(entry, baseDir) {
27369
- const auditPath = path33.join(getSpacesDir(baseDir), "audit.jsonl");
27370
- fs6.mkdirSync(path33.dirname(auditPath), { recursive: true });
26985
+ const auditPath = path32.join(getSpacesDir(baseDir), "audit.jsonl");
26986
+ fs5.mkdirSync(path32.dirname(auditPath), { recursive: true });
27371
26987
  const full = {
27372
- id: crypto7.randomUUID(),
26988
+ id: crypto6.randomUUID(),
27373
26989
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
27374
26990
  ...entry
27375
26991
  };
27376
- fs6.appendFileSync(auditPath, `${JSON.stringify(full)}
26992
+ fs5.appendFileSync(auditPath, `${JSON.stringify(full)}
27377
26993
  `);
27378
26994
  }
27379
26995
  async function copyMemories(sourceDir, targetDir, options) {
27380
- if (!fs6.existsSync(sourceDir)) {
26996
+ if (!fs5.existsSync(sourceDir)) {
27381
26997
  return { merged: 0, conflicts: [], skipped: 0 };
27382
26998
  }
27383
- const sourceRoot = fs6.realpathSync(sourceDir);
27384
- fs6.mkdirSync(targetDir, { recursive: true });
27385
- const targetRoot = fs6.realpathSync(targetDir);
26999
+ const sourceRoot = fs5.realpathSync(sourceDir);
27000
+ fs5.mkdirSync(targetDir, { recursive: true });
27001
+ const targetRoot = fs5.realpathSync(targetDir);
27386
27002
  const sourceFiles = walkMd(sourceRoot);
27387
27003
  const sourceEntityIds = new Set(
27388
27004
  sourceFiles.filter((p) => {
27389
27005
  const rp = safeRealpath(p);
27390
27006
  if (!rp || !isPathInsideRoot(rp, sourceRoot)) return false;
27391
- const rel = path33.relative(sourceRoot, rp);
27392
- const mapped = path33.resolve(targetRoot, rel);
27007
+ const rel = path32.relative(sourceRoot, rp);
27008
+ const mapped = path32.resolve(targetRoot, rel);
27393
27009
  if (!isPathInsideRoot(mapped, targetRoot)) return false;
27394
27010
  return classifyEntityRefWritePath(targetRoot, mapped) === "entity";
27395
- }).map((p) => path33.basename(p, ".md"))
27011
+ }).map((p) => path32.basename(p, ".md"))
27396
27012
  );
27397
27013
  const firstEntityId = sourceEntityIds.values().next().value;
27398
27014
  if (firstEntityId === void 0) {
@@ -27400,7 +27016,7 @@ async function copyMemories(sourceDir, targetDir, options) {
27400
27016
  }
27401
27017
  return withRawEntityPageMutation(
27402
27018
  targetRoot,
27403
- path33.join(targetRoot, "entities", `${firstEntityId}.md`),
27019
+ path32.join(targetRoot, "entities", `${firstEntityId}.md`),
27404
27020
  async () => copyPreparedMemories(
27405
27021
  sourceFiles,
27406
27022
  sourceRoot,
@@ -27426,19 +27042,19 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27426
27042
  skipped++;
27427
27043
  continue;
27428
27044
  }
27429
- const content = fs6.readFileSync(sourcePath, "utf8");
27430
- const relativePath = path33.relative(sourceRoot, sourceRealPath);
27431
- const targetPath = path33.resolve(targetRoot, relativePath);
27045
+ const content = fs5.readFileSync(sourcePath, "utf8");
27046
+ const relativePath = path32.relative(sourceRoot, sourceRealPath);
27047
+ const targetPath = path32.resolve(targetRoot, relativePath);
27432
27048
  if (!isPathInsideRoot(targetPath, targetRoot)) {
27433
27049
  skipped++;
27434
27050
  continue;
27435
27051
  }
27436
27052
  const targetKind = classifyEntityRefWritePath(targetRoot, targetPath);
27437
- const idsAtCompare = historicalIdCache3.get(path33.join(targetRoot, "state"));
27053
+ const idsAtCompare = historicalIdCache3.get(path32.join(targetRoot, "state"));
27438
27054
  const sourceRef = targetKind === "memory" ? readEntityRefFromFrontmatter(content) : null;
27439
27055
  const refHasSourceClaimant = sourceRef !== null && sourceEntityIds.has(sourceRef);
27440
27056
  const canonicalContent = targetKind === "memory" && !refHasSourceClaimant ? canonicalizeEntityRefFrontmatter(content, idsAtCompare) : content;
27441
- const sourceHash = hashContent4(content);
27057
+ const sourceHash = hashContent3(content);
27442
27058
  if (options?.filterIds?.length) {
27443
27059
  const fm = parseSimpleFrontmatter(content);
27444
27060
  if (!fm?.id || !options.filterIds.includes(fm.id)) {
@@ -27446,7 +27062,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27446
27062
  continue;
27447
27063
  }
27448
27064
  }
27449
- if (fs6.existsSync(targetPath)) {
27065
+ if (fs5.existsSync(targetPath)) {
27450
27066
  const targetStat = safeLstat(targetPath);
27451
27067
  if (!targetStat?.isFile() || targetStat.isSymbolicLink()) {
27452
27068
  skipped++;
@@ -27458,14 +27074,14 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27458
27074
  continue;
27459
27075
  }
27460
27076
  }
27461
- if (fs6.existsSync(targetPath) && !options?.force) {
27462
- const targetContent = fs6.readFileSync(targetPath, "utf8");
27463
- const targetHash = hashContent4(targetContent);
27464
- if (hashContent4(canonicalContent) === targetHash && historicalIdCache3.get(path33.join(targetRoot, "state")) === idsAtCompare) {
27077
+ if (fs5.existsSync(targetPath) && !options?.force) {
27078
+ const targetContent = fs5.readFileSync(targetPath, "utf8");
27079
+ const targetHash = hashContent3(targetContent);
27080
+ if (hashContent3(canonicalContent) === targetHash && historicalIdCache3.get(path32.join(targetRoot, "state")) === idsAtCompare) {
27465
27081
  skipped++;
27466
27082
  continue;
27467
27083
  }
27468
- if (hashContent4(canonicalContent) !== targetHash && sourceHash !== targetHash) {
27084
+ if (hashContent3(canonicalContent) !== targetHash && sourceHash !== targetHash) {
27469
27085
  conflicts.push({
27470
27086
  memoryId: parseSimpleFrontmatter(content)?.id ?? relativePath,
27471
27087
  sourcePath,
@@ -27477,23 +27093,23 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27477
27093
  continue;
27478
27094
  }
27479
27095
  }
27480
- fs6.mkdirSync(path33.dirname(targetPath), { recursive: true });
27481
- const targetParentRealPath = safeRealpath(path33.dirname(targetPath));
27096
+ fs5.mkdirSync(path32.dirname(targetPath), { recursive: true });
27097
+ const targetParentRealPath = safeRealpath(path32.dirname(targetPath));
27482
27098
  if (!targetParentRealPath || !isPathInsideRoot(targetParentRealPath, targetRoot)) {
27483
27099
  skipped++;
27484
27100
  continue;
27485
27101
  }
27486
- const stateDir = path33.join(targetRoot, "state");
27102
+ const stateDir = path32.join(targetRoot, "state");
27487
27103
  if (targetKind === "memory" && !refHasSourceClaimant) {
27488
27104
  const idsAtWrite = historicalIdCache3.get(stateDir);
27489
27105
  const written = canonicalizeEntityRefFrontmatter(content, idsAtWrite);
27490
27106
  let priorTarget = null;
27491
27107
  try {
27492
- priorTarget = fs6.readFileSync(targetPath, "utf8");
27108
+ priorTarget = fs5.readFileSync(targetPath, "utf8");
27493
27109
  } catch {
27494
27110
  priorTarget = null;
27495
27111
  }
27496
- fs6.writeFileSync(targetPath, written);
27112
+ fs5.writeFileSync(targetPath, written);
27497
27113
  try {
27498
27114
  repairContentAfterJournalMoveSync({
27499
27115
  stateDir,
@@ -27501,24 +27117,24 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27501
27117
  idsAtWrite,
27502
27118
  rawContent: content,
27503
27119
  lastWritten: written,
27504
- rewrite: (next) => fs6.writeFileSync(targetPath, next)
27120
+ rewrite: (next) => fs5.writeFileSync(targetPath, next)
27505
27121
  });
27506
27122
  } catch (err) {
27507
27123
  try {
27508
27124
  if (priorTarget === null) {
27509
- fs6.unlinkSync(targetPath);
27125
+ fs5.unlinkSync(targetPath);
27510
27126
  } else {
27511
- fs6.writeFileSync(targetPath, priorTarget);
27127
+ fs5.writeFileSync(targetPath, priorTarget);
27512
27128
  }
27513
27129
  } catch {
27514
27130
  }
27515
27131
  throw err;
27516
27132
  }
27517
27133
  } else if (targetKind === "memory") {
27518
- fs6.writeFileSync(targetPath, content);
27134
+ fs5.writeFileSync(targetPath, content);
27519
27135
  reconcileNeeded = true;
27520
27136
  } else {
27521
- fs6.writeFileSync(targetPath, content);
27137
+ fs5.writeFileSync(targetPath, content);
27522
27138
  if (targetKind === "entity") {
27523
27139
  reconcileNeeded = true;
27524
27140
  }
@@ -27527,18 +27143,18 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27527
27143
  bumpMemoryCorpusVersionForDir(targetRoot);
27528
27144
  }
27529
27145
  if (reconcileNeeded) {
27530
- requestEntityCanonicalIdReconcileSync(path33.join(targetRoot, "state"));
27146
+ requestEntityCanonicalIdReconcileSync(path32.join(targetRoot, "state"));
27531
27147
  }
27532
27148
  return { merged, conflicts, skipped };
27533
27149
  }
27534
- function hashContent4(content) {
27535
- return crypto7.createHash("sha256").update(content).digest("hex").slice(0, 16);
27150
+ function hashContent3(content) {
27151
+ return crypto6.createHash("sha256").update(content).digest("hex").slice(0, 16);
27536
27152
  }
27537
27153
  function walkMd(dir) {
27538
27154
  const results = [];
27539
27155
  function walk(d) {
27540
- for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
27541
- const fullPath = path33.join(d, entry.name);
27156
+ for (const entry of fs5.readdirSync(d, { withFileTypes: true })) {
27157
+ const fullPath = path32.join(d, entry.name);
27542
27158
  if (entry.isSymbolicLink()) {
27543
27159
  continue;
27544
27160
  }
@@ -27554,21 +27170,21 @@ function walkMd(dir) {
27554
27170
  }
27555
27171
  function safeLstat(filePath) {
27556
27172
  try {
27557
- return fs6.lstatSync(filePath);
27173
+ return fs5.lstatSync(filePath);
27558
27174
  } catch {
27559
27175
  return null;
27560
27176
  }
27561
27177
  }
27562
27178
  function safeRealpath(filePath) {
27563
27179
  try {
27564
- return fs6.realpathSync(filePath);
27180
+ return fs5.realpathSync(filePath);
27565
27181
  } catch {
27566
27182
  return null;
27567
27183
  }
27568
27184
  }
27569
27185
  function isPathInsideRoot(candidatePath, rootPath) {
27570
- const relative = path33.relative(rootPath, candidatePath);
27571
- return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
27186
+ const relative = path32.relative(rootPath, candidatePath);
27187
+ return relative === "" || !relative.startsWith("..") && !path32.isAbsolute(relative);
27572
27188
  }
27573
27189
  function parseSimpleFrontmatter(content) {
27574
27190
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -27687,9 +27303,9 @@ var REMNIC_RECALL_DECISION_RULES = `## When to Use Recall vs Direct Read
27687
27303
  `;
27688
27304
 
27689
27305
  // src/memory-extension/codex-publisher.ts
27690
- import fs7 from "fs";
27306
+ import fs6 from "fs";
27691
27307
  import os from "os";
27692
- import path34 from "path";
27308
+ import path33 from "path";
27693
27309
  var REMNIC_EXTENSION_DIR_NAME = "remnic";
27694
27310
  function resolveEnvHome(env) {
27695
27311
  if (env === void 0) return resolveHomeDir();
@@ -27698,12 +27314,12 @@ function resolveEnvHome(env) {
27698
27314
  function expandTildeWithHome(input, homeDir) {
27699
27315
  if (input === "~") return homeDir;
27700
27316
  if (input.startsWith("~/") || input.startsWith("~\\")) {
27701
- return path34.join(homeDir, input.slice(2));
27317
+ return path33.join(homeDir, input.slice(2));
27702
27318
  }
27703
27319
  return input;
27704
27320
  }
27705
27321
  function normalizeHostRoot(input, homeDir) {
27706
- return path34.resolve(expandTildeWithHome(input.trim(), homeDir));
27322
+ return path33.resolve(expandTildeWithHome(input.trim(), homeDir));
27707
27323
  }
27708
27324
  var CodexMemoryExtensionPublisher = class {
27709
27325
  hostId = "codex";
@@ -27717,13 +27333,13 @@ var CodexMemoryExtensionPublisher = class {
27717
27333
  async resolveExtensionRoot(env) {
27718
27334
  const homeDir = resolveEnvHome(env);
27719
27335
  const codexHomeInput = env === void 0 ? readEnvVar("CODEX_HOME")?.trim() : env.CODEX_HOME?.trim();
27720
- const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path34.resolve(homeDir, ".codex");
27721
- return path34.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
27336
+ const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path33.resolve(homeDir, ".codex");
27337
+ return path33.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
27722
27338
  }
27723
27339
  async isHostAvailable() {
27724
27340
  try {
27725
- const home = readEnvVar("CODEX_HOME")?.trim() || path34.join(resolveHomeDir(), ".codex");
27726
- return fs7.existsSync(home);
27341
+ const home = readEnvVar("CODEX_HOME")?.trim() || path33.join(resolveHomeDir(), ".codex");
27342
+ return fs6.existsSync(home);
27727
27343
  } catch {
27728
27344
  return false;
27729
27345
  }
@@ -27773,22 +27389,22 @@ When running inside the Codex phase-2 consolidation sandbox:
27773
27389
  }
27774
27390
  async publish(ctx) {
27775
27391
  const extensionRoot = await this.resolveExtensionRoot();
27776
- const instructionsPath = path34.join(extensionRoot, "instructions.md");
27392
+ const instructionsPath = path33.join(extensionRoot, "instructions.md");
27777
27393
  const filesWritten = [];
27778
27394
  const skipped = [];
27779
27395
  ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);
27780
- fs7.mkdirSync(extensionRoot, { recursive: true });
27396
+ fs6.mkdirSync(extensionRoot, { recursive: true });
27781
27397
  const content = await this.renderInstructions(ctx);
27782
27398
  const tmpPath = `${instructionsPath}.tmp-${process.pid}-${Date.now()}`;
27783
27399
  try {
27784
- fs7.writeFileSync(tmpPath, content, "utf-8");
27785
- fs7.renameSync(tmpPath, instructionsPath);
27400
+ fs6.writeFileSync(tmpPath, content, "utf-8");
27401
+ fs6.renameSync(tmpPath, instructionsPath);
27786
27402
  filesWritten.push(instructionsPath);
27787
27403
  ctx.log.info(`Wrote ${instructionsPath}`);
27788
27404
  } catch (err) {
27789
27405
  try {
27790
- if (fs7.existsSync(tmpPath)) {
27791
- fs7.unlinkSync(tmpPath);
27406
+ if (fs6.existsSync(tmpPath)) {
27407
+ fs6.unlinkSync(tmpPath);
27792
27408
  }
27793
27409
  } catch {
27794
27410
  }
@@ -27803,8 +27419,8 @@ When running inside the Codex phase-2 consolidation sandbox:
27803
27419
  }
27804
27420
  async unpublish() {
27805
27421
  const extensionRoot = await this.resolveExtensionRoot();
27806
- if (fs7.existsSync(extensionRoot)) {
27807
- fs7.rmSync(extensionRoot, { recursive: true, force: true });
27422
+ if (fs6.existsSync(extensionRoot)) {
27423
+ fs6.rmSync(extensionRoot, { recursive: true, force: true });
27808
27424
  }
27809
27425
  }
27810
27426
  };
@@ -28405,9 +28021,9 @@ function buildWeeklyActivitySummary(cards, options) {
28405
28021
  // src/activity/timeline/weekly-persist.ts
28406
28022
  import { randomBytes as randomBytes3 } from "crypto";
28407
28023
  import { mkdirSync, readFileSync as readFileSync2, renameSync, unlinkSync, writeFileSync } from "fs";
28408
- import path35 from "path";
28024
+ import path34 from "path";
28409
28025
  var WEEKLY_SNAPSHOT_FORMAT_VERSION = 1;
28410
- var WEEKLY_SNAPSHOT_DIR = path35.join("activity", "weekly");
28026
+ var WEEKLY_SNAPSHOT_DIR = path34.join("activity", "weekly");
28411
28027
  function requireToken(value, name) {
28412
28028
  if (typeof value !== "string" || value.trim().length === 0) {
28413
28029
  throw new TypeError(`${name} must be a non-empty string`);
@@ -28430,8 +28046,8 @@ function weeklySnapshotPath(input) {
28430
28046
  const identity = hashActivityBody(
28431
28047
  `${input.summary.weekStartUtc}\0${input.summary.weekEndUtc}\0${sourceRevision}\0${configHash}`
28432
28048
  );
28433
- return path35.join(
28434
- path35.resolve(memoryDir),
28049
+ return path34.join(
28050
+ path34.resolve(memoryDir),
28435
28051
  WEEKLY_SNAPSHOT_DIR,
28436
28052
  namespaceIdentityToken(namespace),
28437
28053
  `${weekDate}--${identity}.json`
@@ -28461,7 +28077,7 @@ function persistWeeklySnapshot(input) {
28461
28077
  throw error;
28462
28078
  }
28463
28079
  }
28464
- mkdirSync(path35.dirname(filePath), { recursive: true, mode: 448 });
28080
+ mkdirSync(path34.dirname(filePath), { recursive: true, mode: 448 });
28465
28081
  const tmpPath = `${filePath}.${process.pid}.${randomBytes3(8).toString("hex")}.tmp`;
28466
28082
  writeFileSync(tmpPath, text, { encoding: "utf8", mode: 384 });
28467
28083
  try {
@@ -28734,6 +28350,153 @@ function findRecurringPatterns(input) {
28734
28350
  return patterns;
28735
28351
  }
28736
28352
 
28353
+ // src/activity/timeline/week-dashboard.ts
28354
+ var WEEKLY_DASHBOARD_FORMAT_VERSION = 1;
28355
+ var DEFAULT_TOP_SOURCE_LIMIT = 10;
28356
+ var DAY_MS2 = 864e5;
28357
+ var WEEK_DAYS_MS = 7 * DAY_MS2;
28358
+ function clippedInWeek(cards, weekStartMs, weekEndMs) {
28359
+ const clipped = [];
28360
+ for (const card of cards) {
28361
+ const startMs = Date.parse(card.startUtc);
28362
+ const endMs = Date.parse(card.endUtc);
28363
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) continue;
28364
+ const clipStartMs = Math.max(startMs, weekStartMs);
28365
+ const clipEndMs = Math.min(endMs, weekEndMs);
28366
+ if (clipEndMs > clipStartMs) {
28367
+ clipped.push({ card, startMs, endMs, clipStartMs, clipEndMs });
28368
+ }
28369
+ }
28370
+ return clipped;
28371
+ }
28372
+ function normalizedAttribution(cardId, attributions) {
28373
+ const attribution = attributions?.get(cardId);
28374
+ if (attribution === void 0) return null;
28375
+ if (typeof attribution.app !== "string" || attribution.app.trim() === "") {
28376
+ throw new RangeError(`attribution for card ${cardId} must have a non-blank app`);
28377
+ }
28378
+ const app = attribution.app.trim();
28379
+ if (attribution.domain === void 0) return { app };
28380
+ if (typeof attribution.domain !== "string" || attribution.domain.trim() === "") {
28381
+ throw new RangeError(`attribution for card ${cardId} must have a non-blank domain when present`);
28382
+ }
28383
+ return { app, domain: attribution.domain.trim().toLowerCase() };
28384
+ }
28385
+ function rankTotals(totals, limit) {
28386
+ return [...totals.entries()].map(([key, durationMs]) => ({ key, durationMs })).sort(
28387
+ (a, b) => b.durationMs - a.durationMs || (a.key < b.key ? -1 : a.key > b.key ? 1 : 0)
28388
+ ).slice(0, limit);
28389
+ }
28390
+ function buildWeeklyDashboard(cards, options) {
28391
+ const summary = buildWeeklyActivitySummary(cards, options);
28392
+ const topSourceLimit = options.topSourceLimit ?? DEFAULT_TOP_SOURCE_LIMIT;
28393
+ if (!Number.isInteger(topSourceLimit) || topSourceLimit < 1) {
28394
+ throw new RangeError(`topSourceLimit must be an integer >= 1; got ${topSourceLimit}`);
28395
+ }
28396
+ const weekStartMs = Date.parse(summary.weekStartUtc);
28397
+ const weekEndMs = Date.parse(summary.weekEndUtc);
28398
+ const inWeek = clippedInWeek(cards, weekStartMs, weekEndMs);
28399
+ const applicationTotals = /* @__PURE__ */ new Map();
28400
+ const domainTotals = /* @__PURE__ */ new Map();
28401
+ const applicationOccurrences = [];
28402
+ const categoryOccurrences = [];
28403
+ const evidence = {
28404
+ activityCardCount: 0,
28405
+ idleCardCount: 0,
28406
+ pauseCardCount: 0,
28407
+ evidenceCount: 0,
28408
+ distinctEvidenceCount: 0
28409
+ };
28410
+ const uncertainty = {
28411
+ clippedCardCount: 0,
28412
+ manualEditCount: 0,
28413
+ unclassifiedCardCount: 0
28414
+ };
28415
+ const distinctEvidence = /* @__PURE__ */ new Set();
28416
+ const dayWindows = summary.days.map((day) => ({
28417
+ date: day.date,
28418
+ startMs: Date.parse(day.startUtc),
28419
+ endMs: Date.parse(day.endUtc)
28420
+ }));
28421
+ for (const day of summary.days) {
28422
+ for (const total of day.categories) {
28423
+ if (total.durationMs > 0) {
28424
+ categoryOccurrences.push({ date: day.date, key: total.categoryId, durationMs: total.durationMs });
28425
+ }
28426
+ }
28427
+ }
28428
+ for (const { card, startMs, endMs, clipStartMs, clipEndMs } of inWeek) {
28429
+ if (startMs < weekStartMs || endMs > weekEndMs) {
28430
+ uncertainty.clippedCardCount++;
28431
+ }
28432
+ if (card.kind === "idle") {
28433
+ evidence.idleCardCount++;
28434
+ continue;
28435
+ }
28436
+ if (card.kind === "pause") {
28437
+ evidence.pauseCardCount++;
28438
+ continue;
28439
+ }
28440
+ evidence.activityCardCount++;
28441
+ for (const id of card.evidenceIds) {
28442
+ evidence.evidenceCount++;
28443
+ distinctEvidence.add(id);
28444
+ }
28445
+ if (card.manualEdit !== void 0) uncertainty.manualEditCount++;
28446
+ if (card.confidence === 0) uncertainty.unclassifiedCardCount++;
28447
+ const attribution = normalizedAttribution(card.id, options.attributions);
28448
+ if (attribution === null) continue;
28449
+ const durationMs = clipEndMs - clipStartMs;
28450
+ applicationTotals.set(attribution.app, (applicationTotals.get(attribution.app) ?? 0) + durationMs);
28451
+ if (attribution.domain !== void 0) {
28452
+ domainTotals.set(attribution.domain, (domainTotals.get(attribution.domain) ?? 0) + durationMs);
28453
+ }
28454
+ for (const day of dayWindows) {
28455
+ const dayStartMs = Math.max(clipStartMs, day.startMs);
28456
+ const dayEndMs = Math.min(clipEndMs, day.endMs);
28457
+ if (dayEndMs > dayStartMs) {
28458
+ applicationOccurrences.push({ date: day.date, key: attribution.app, durationMs: dayEndMs - dayStartMs });
28459
+ }
28460
+ }
28461
+ }
28462
+ evidence.distinctEvidenceCount = distinctEvidence.size;
28463
+ let previousPeriod = { available: false };
28464
+ if (options.previousWeekCards !== void 0) {
28465
+ const previousSummary = buildWeeklyActivitySummary(options.previousWeekCards, {
28466
+ ...options,
28467
+ weekStartUtc: new Date(weekStartMs - WEEK_DAYS_MS).toISOString(),
28468
+ weekEndUtc: summary.weekStartUtc
28469
+ });
28470
+ previousPeriod = compareWeeklyPreviousPeriod({
28471
+ previous: previousSummary,
28472
+ current: summary,
28473
+ previousStartUtc: previousSummary.weekStartUtc,
28474
+ previousEndUtc: previousSummary.weekEndUtc
28475
+ });
28476
+ }
28477
+ return {
28478
+ ...summary,
28479
+ previousPeriod,
28480
+ dashboardFormatVersion: WEEKLY_DASHBOARD_FORMAT_VERSION,
28481
+ sources: {
28482
+ applications: rankTotals(applicationTotals, topSourceLimit),
28483
+ domains: rankTotals(domainTotals, topSourceLimit)
28484
+ },
28485
+ recurring: {
28486
+ categories: findRecurringPatterns({
28487
+ occurrences: categoryOccurrences,
28488
+ minDays: options.recurrenceMinDays
28489
+ }),
28490
+ applications: findRecurringPatterns({
28491
+ occurrences: applicationOccurrences,
28492
+ minDays: options.recurrenceMinDays
28493
+ })
28494
+ },
28495
+ evidence,
28496
+ uncertainty
28497
+ };
28498
+ }
28499
+
28737
28500
  // src/activity/timeline/week-config-hash.ts
28738
28501
  var WEEK_START_DAYS = Object.freeze([
28739
28502
  "monday",
@@ -28966,7 +28729,7 @@ function selectRecapForDay(input) {
28966
28729
 
28967
28730
  // src/activity/journal-read.ts
28968
28731
  import { closeSync, constants as fsConstants, fstatSync, lstatSync, openSync, readFileSync as readFileSync3, realpathSync, statSync as statSync2 } from "fs";
28969
- import path36 from "path";
28732
+ import path35 from "path";
28970
28733
 
28971
28734
  // src/activity/journal-section.ts
28972
28735
  function parseAtxHeading(line) {
@@ -29157,8 +28920,8 @@ function readJournalForDate(input) {
29157
28920
  const relative = resolveVaultNotePath(input.vault.dailyNotePath, input.date, {
29158
28921
  timezone: input.timezone
29159
28922
  });
29160
- const root = path36.resolve(expandTildePath(input.vault.vaultPath));
29161
- const filePath = path36.join(root, relative);
28923
+ const root = path35.resolve(expandTildePath(input.vault.vaultPath));
28924
+ const filePath = path35.join(root, relative);
29162
28925
  try {
29163
28926
  const rootStat = lstatSync(root);
29164
28927
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
@@ -29167,8 +28930,8 @@ function readJournalForDate(input) {
29167
28930
  } catch {
29168
28931
  return { ok: true, exists: false, reason: "missing_file", filePath };
29169
28932
  }
29170
- const lexical = path36.relative(root, filePath);
29171
- if (lexical.length === 0 || lexical === ".." || lexical.startsWith(`..${path36.sep}`) || path36.isAbsolute(lexical)) {
28933
+ const lexical = path35.relative(root, filePath);
28934
+ if (lexical.length === 0 || lexical === ".." || lexical.startsWith(`..${path35.sep}`) || path35.isAbsolute(lexical)) {
29172
28935
  return { ok: false, reason: "path_escape", filePath };
29173
28936
  }
29174
28937
  if (!containedBySymlinks(root, filePath)) {
@@ -29201,11 +28964,11 @@ function containedBySymlinks(root, dest) {
29201
28964
  try {
29202
28965
  if (statSync2(probe).isFile() || statSync2(probe).isDirectory()) {
29203
28966
  const real = realpathSync(probe);
29204
- return real === realRoot || real.startsWith(`${realRoot}${path36.sep}`);
28967
+ return real === realRoot || real.startsWith(`${realRoot}${path35.sep}`);
29205
28968
  }
29206
28969
  } catch {
29207
28970
  }
29208
- const parent = path36.dirname(probe);
28971
+ const parent = path35.dirname(probe);
29209
28972
  if (parent === probe) return false;
29210
28973
  probe = parent;
29211
28974
  }
@@ -29216,15 +28979,15 @@ function symlinkRejected() {
29216
28979
  return err;
29217
28980
  }
29218
28981
  function readVerifiedDailyNote(root, filePath) {
29219
- const relative = path36.relative(root, filePath);
29220
- const parts = relative.split(path36.sep).filter((part) => part.length > 0 && part !== ".");
28982
+ const relative = path35.relative(root, filePath);
28983
+ const parts = relative.split(path35.sep).filter((part) => part.length > 0 && part !== ".");
29221
28984
  let cursor = root;
29222
28985
  for (const part of parts) {
29223
- cursor = path36.join(cursor, part);
28986
+ cursor = path35.join(cursor, part);
29224
28987
  const st = lstatSync(cursor);
29225
28988
  if (st.isSymbolicLink()) throw symlinkRejected();
29226
28989
  }
29227
- const parent = path36.dirname(filePath);
28990
+ const parent = path35.dirname(filePath);
29228
28991
  const parentLstat = lstatSync(parent);
29229
28992
  if (parentLstat.isSymbolicLink() || !parentLstat.isDirectory()) throw symlinkRejected();
29230
28993
  const nofollow = fsConstants.O_NOFOLLOW ?? 0;
@@ -29248,7 +29011,7 @@ function readVerifiedDailyNote(root, filePath) {
29248
29011
  try {
29249
29012
  const fdPath = realpathSync(`/proc/self/fd/${fd}`);
29250
29013
  const realRoot = realpathSync(root);
29251
- if (fdPath !== realRoot && !fdPath.startsWith(`${realRoot}${path36.sep}`)) {
29014
+ if (fdPath !== realRoot && !fdPath.startsWith(`${realRoot}${path35.sep}`)) {
29252
29015
  throw symlinkRejected();
29253
29016
  }
29254
29017
  } catch (err) {
@@ -29263,11 +29026,11 @@ function readVerifiedDailyNote(root, filePath) {
29263
29026
  // src/activity/journal-state.ts
29264
29027
  import { createHash as createHash10, randomBytes as randomBytes4 } from "crypto";
29265
29028
  import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "fs";
29266
- import path37 from "path";
29029
+ import path36 from "path";
29267
29030
  var STATE_DIR_NAME = "state";
29268
29031
  var STATE_FILE_NAME = "timeline.json";
29269
29032
  function timelineStatePath(memoryDir) {
29270
- return path37.join(memoryDir, STATE_DIR_NAME, STATE_FILE_NAME);
29033
+ return path36.join(memoryDir, STATE_DIR_NAME, STATE_FILE_NAME);
29271
29034
  }
29272
29035
  function hashJournalText(text) {
29273
29036
  return createHash10("sha256").update(text, "utf8").digest("hex");
@@ -29293,14 +29056,14 @@ function readTimelineState(memoryDir) {
29293
29056
  }
29294
29057
  function writeTimelineState(memoryDir, state) {
29295
29058
  const filePath = timelineStatePath(memoryDir);
29296
- mkdirSync2(path37.dirname(filePath), { recursive: true });
29059
+ mkdirSync2(path36.dirname(filePath), { recursive: true });
29297
29060
  const tempPath = `${filePath}.${process.pid}.${randomBytes4(8).toString("hex")}.tmp`;
29298
29061
  writeFileSync2(tempPath, `${JSON.stringify(state, null, 2)}
29299
29062
  `);
29300
29063
  renameSync2(tempPath, filePath);
29301
29064
  }
29302
29065
  function journalDateLockPath(memoryDir, date) {
29303
- return path37.join(memoryDir, STATE_DIR_NAME, `journal-extract-${date}.lock`);
29066
+ return path36.join(memoryDir, STATE_DIR_NAME, `journal-extract-${date}.lock`);
29304
29067
  }
29305
29068
  function timelineStateLockPath(memoryDir) {
29306
29069
  return `${timelineStatePath(memoryDir)}.lock`;
@@ -29991,8 +29754,24 @@ function filterPrivacyWindow(items, options) {
29991
29754
  });
29992
29755
  }
29993
29756
 
29757
+ // src/activity/privacy-redact.ts
29758
+ function redactActivityFields(item, options) {
29759
+ const result = { ...item };
29760
+ for (const key of options.dropKeys) {
29761
+ delete result[key];
29762
+ }
29763
+ return result;
29764
+ }
29765
+
29766
+ // src/activity/privacy-keys.ts
29767
+ function normalizeDropKeys(keys) {
29768
+ const unique = [...new Set(keys.map((key) => key.trim()).filter((key) => key.length > 0))];
29769
+ unique.sort((a, b) => a.localeCompare(b));
29770
+ return unique;
29771
+ }
29772
+
29994
29773
  // src/activity/vault-wikilink.ts
29995
- import path38 from "path";
29774
+ import path37 from "path";
29996
29775
  function formatVaultWikilink(notePath) {
29997
29776
  if (typeof notePath !== "string" || notePath.trim().length === 0) {
29998
29777
  throw new RangeError("Vault wikilink path must be a non-empty relative path.");
@@ -30000,7 +29779,7 @@ function formatVaultWikilink(notePath) {
30000
29779
  if (notePath.includes("\n") || notePath.includes("\r")) {
30001
29780
  throw new RangeError("Vault wikilink path must not contain newlines.");
30002
29781
  }
30003
- if (path38.posix.isAbsolute(notePath) || path38.win32.isAbsolute(notePath)) {
29782
+ if (path37.posix.isAbsolute(notePath) || path37.win32.isAbsolute(notePath)) {
30004
29783
  throw new RangeError("Vault wikilink path must be relative; absolute paths are rejected.");
30005
29784
  }
30006
29785
  if (notePath.split(/[\\/]/).some((segment) => segment === "..")) {
@@ -30022,7 +29801,7 @@ function stripVaultMdSuffix(notePath) {
30022
29801
  }
30023
29802
 
30024
29803
  // src/activity/vault-join.ts
30025
- import path39 from "path";
29804
+ import path38 from "path";
30026
29805
  function joinVaultSegments(parts) {
30027
29806
  if (parts.length === 0) {
30028
29807
  throw new RangeError("Vault path parts must be a non-empty list.");
@@ -30037,7 +29816,7 @@ function joinVaultSegments(parts) {
30037
29816
  if (part.includes("\n") || part.includes("\r")) {
30038
29817
  throw new RangeError("Vault path part must not contain a newline.");
30039
29818
  }
30040
- if (path39.posix.isAbsolute(part) || path39.win32.isAbsolute(part)) {
29819
+ if (path38.posix.isAbsolute(part) || path38.win32.isAbsolute(part)) {
30041
29820
  throw new RangeError("Vault path part must be relative.");
30042
29821
  }
30043
29822
  if (part.split("/").includes("..")) {
@@ -30092,7 +29871,7 @@ function planVaultDryRun(inputs) {
30092
29871
  // src/session-summaries/index.ts
30093
29872
  import { createHash as createHash11 } from "crypto";
30094
29873
  import { lstat as lstat3, mkdir as mkdir7, readFile as readFile9, readdir as readdir4, rename, rm, stat as stat4, writeFile as writeFile7 } from "fs/promises";
30095
- import path40 from "path";
29874
+ import path39 from "path";
30096
29875
 
30097
29876
  // src/session-summaries/adapters.ts
30098
29877
  var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "other"]);
@@ -30597,7 +30376,7 @@ function shortHash(value, length = 16) {
30597
30376
  async function listTranscriptFiles(root, maxFiles) {
30598
30377
  const out = [];
30599
30378
  let truncated = false;
30600
- const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path40.sep}` : entryName;
30379
+ const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path39.sep}` : entryName;
30601
30380
  async function visit(dir) {
30602
30381
  if (truncated) return;
30603
30382
  const entries = (await readdir4(dir, { withFileTypes: true })).sort(
@@ -30607,13 +30386,13 @@ async function listTranscriptFiles(root, maxFiles) {
30607
30386
  if (truncated) return;
30608
30387
  if (entry.name.startsWith(".")) continue;
30609
30388
  if (entry.isSymbolicLink()) continue;
30610
- const fullPath = path40.join(dir, entry.name);
30389
+ const fullPath = path39.join(dir, entry.name);
30611
30390
  if (entry.isDirectory()) {
30612
30391
  await visit(fullPath);
30613
30392
  continue;
30614
30393
  }
30615
30394
  if (!entry.isFile()) continue;
30616
- const ext = path40.extname(entry.name).toLowerCase();
30395
+ const ext = path39.extname(entry.name).toLowerCase();
30617
30396
  if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
30618
30397
  out.push(fullPath);
30619
30398
  if (out.length > maxFiles) {
@@ -30640,7 +30419,7 @@ function normalizeInputDir(inputDir) {
30640
30419
  if (typeof inputDir !== "string" || inputDir.trim().length === 0) {
30641
30420
  throw new Error("inputDir must be a non-empty string");
30642
30421
  }
30643
- return path40.resolve(expandTildePath(inputDir.trim()));
30422
+ return path39.resolve(expandTildePath(inputDir.trim()));
30644
30423
  }
30645
30424
  function initRoleCounts() {
30646
30425
  return {
@@ -30797,11 +30576,11 @@ async function collectLocalSessionSummaries(options) {
30797
30576
  continue;
30798
30577
  }
30799
30578
  seenFileHashes.add(fileHash);
30800
- const fileExtension = path40.extname(filePath).toLowerCase();
30579
+ const fileExtension = path39.extname(filePath).toLowerCase();
30801
30580
  const parsed = await adapter.parseFile(
30802
30581
  {
30803
30582
  content,
30804
- fileName: path40.basename(filePath),
30583
+ fileName: path39.basename(filePath),
30805
30584
  fileExtension,
30806
30585
  fileRef: fileHash
30807
30586
  },
@@ -30857,7 +30636,7 @@ async function collectLocalSessionSummaries(options) {
30857
30636
  }
30858
30637
  async function readRedactionConfig(pathLike) {
30859
30638
  if (!pathLike) return void 0;
30860
- const raw = await readFile9(path40.resolve(expandTildePath(pathLike)), "utf-8");
30639
+ const raw = await readFile9(path39.resolve(expandTildePath(pathLike)), "utf-8");
30861
30640
  const parsed = JSON.parse(raw);
30862
30641
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
30863
30642
  throw new Error("redaction config must be a JSON object");
@@ -30870,15 +30649,15 @@ function toJsonl(drafts) {
30870
30649
  }
30871
30650
  function defaultDraftOutputPath(memoryDir, generatedAt) {
30872
30651
  const stamp = generatedAt.replace(/[:.]/g, "-");
30873
- return path40.join(
30874
- path40.resolve(expandTildePath(memoryDir)),
30652
+ return path39.join(
30653
+ path39.resolve(expandTildePath(memoryDir)),
30875
30654
  "state",
30876
30655
  "session-summary-drafts",
30877
30656
  `session-summaries-${stamp}.jsonl`
30878
30657
  );
30879
30658
  }
30880
30659
  async function writeDrafts(filePath, drafts) {
30881
- await mkdir7(path40.dirname(filePath), { recursive: true });
30660
+ await mkdir7(path39.dirname(filePath), { recursive: true });
30882
30661
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
30883
30662
  try {
30884
30663
  await writeFile7(tempPath, toJsonl(drafts), "utf-8");
@@ -30896,7 +30675,7 @@ async function runLocalSessionSummaryCliCommand(options) {
30896
30675
  });
30897
30676
  const wroteFiles = [];
30898
30677
  if (options.output) {
30899
- const outputPath = path40.resolve(expandTildePath(options.output));
30678
+ const outputPath = path39.resolve(expandTildePath(options.output));
30900
30679
  await writeDrafts(outputPath, report.drafts);
30901
30680
  wroteFiles.push(outputPath);
30902
30681
  }
@@ -30931,19 +30710,19 @@ async function runLocalSessionSummaryCliCommand(options) {
30931
30710
 
30932
30711
  // src/transfer/capsule-fork.ts
30933
30712
  import { lstat as lstat4, mkdir as mkdir8, readFile as readFile10, realpath as realpath3, writeFile as writeFile8 } from "fs/promises";
30934
- import path41 from "path";
30713
+ import path40 from "path";
30935
30714
  async function forkCapsule(opts) {
30936
30715
  validateForkId(opts.forkId);
30937
- const rootAbs = path41.resolve(opts.targetRoot);
30716
+ const rootAbs = path40.resolve(opts.targetRoot);
30938
30717
  await assertIsDirectoryNotSymlink(rootAbs, "forkCapsule", "targetRoot");
30939
- const forkDirAbs = path41.join(rootAbs, "forks", opts.forkId);
30718
+ const forkDirAbs = path40.join(rootAbs, "forks", opts.forkId);
30940
30719
  const forkEntryExists = await pathEntryExists(forkDirAbs);
30941
30720
  if (forkEntryExists) {
30942
30721
  throw new Error(
30943
30722
  `forkCapsule: fork path already exists \u2014 forkId "${opts.forkId}" is already in use at: ${forkDirAbs}`
30944
30723
  );
30945
30724
  }
30946
- const archiveAbs = path41.resolve(opts.sourceArchive);
30725
+ const archiveAbs = path40.resolve(opts.sourceArchive);
30947
30726
  const importResult = await importCapsule({
30948
30727
  archivePath: archiveAbs,
30949
30728
  root: rootAbs,
@@ -30965,10 +30744,10 @@ async function forkCapsule(opts) {
30965
30744
  importedRecords: importResult.imported.length,
30966
30745
  skippedRecords: importResult.skipped.length
30967
30746
  };
30968
- const lineagePath = path41.join(forkDirAbs, "lineage.json");
30747
+ const lineagePath = path40.join(forkDirAbs, "lineage.json");
30969
30748
  const rootReal = await realpath3(rootAbs);
30970
30749
  await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, "forkCapsule");
30971
- await mkdir8(path41.dirname(lineagePath), { recursive: true });
30750
+ await mkdir8(path40.dirname(lineagePath), { recursive: true });
30972
30751
  await writeFile8(lineagePath, JSON.stringify(lineage, null, 2) + "\n", "utf-8");
30973
30752
  return {
30974
30753
  archivePath: archiveAbs,
@@ -30982,11 +30761,11 @@ async function readForkLineage(targetRoot, forkId) {
30982
30761
  if (typeof forkId !== "string" || forkId.length === 0 || forkId.length > 64 || !CAPSULE_ID_PATTERN.test(forkId)) {
30983
30762
  return null;
30984
30763
  }
30985
- const rootAbs = path41.resolve(targetRoot);
30764
+ const rootAbs = path40.resolve(targetRoot);
30986
30765
  const rootReal = await realpath3(rootAbs).catch(() => rootAbs);
30987
- const lineagePath = path41.join(rootReal, "forks", forkId, "lineage.json");
30988
- const rel = path41.relative(rootReal, lineagePath);
30989
- if (rel.startsWith("..") || path41.isAbsolute(rel)) {
30766
+ const lineagePath = path40.join(rootReal, "forks", forkId, "lineage.json");
30767
+ const rel = path40.relative(rootReal, lineagePath);
30768
+ if (rel.startsWith("..") || path40.isAbsolute(rel)) {
30990
30769
  return null;
30991
30770
  }
30992
30771
  if (!await isLineagePathContained(rootReal, lineagePath)) {
@@ -31027,19 +30806,19 @@ async function pathEntryExists(absPath) {
31027
30806
  async function isLineagePathContained(rootReal, lineagePath) {
31028
30807
  let existing = lineagePath;
31029
30808
  const suffix = [];
31030
- while (existing !== path41.dirname(existing)) {
30809
+ while (existing !== path40.dirname(existing)) {
31031
30810
  const st = await lstat4(existing).catch(() => null);
31032
30811
  if (st !== null) break;
31033
- suffix.unshift(path41.basename(existing));
31034
- existing = path41.dirname(existing);
30812
+ suffix.unshift(path40.basename(existing));
30813
+ existing = path40.dirname(existing);
31035
30814
  }
31036
30815
  const existingReal = await realpath3(existing).catch(() => existing);
31037
- const targetReal = suffix.length > 0 ? path41.join(existingReal, ...suffix) : existingReal;
31038
- const rel = path41.relative(rootReal, targetReal);
30816
+ const targetReal = suffix.length > 0 ? path40.join(existingReal, ...suffix) : existingReal;
30817
+ const rel = path40.relative(rootReal, targetReal);
31039
30818
  if (rel === "") return true;
31040
30819
  if (rel === "..") return false;
31041
- if (rel.startsWith(`..${path41.sep}`)) return false;
31042
- if (path41.isAbsolute(rel)) return false;
30820
+ if (rel.startsWith(`..${path40.sep}`)) return false;
30821
+ if (path40.isAbsolute(rel)) return false;
31043
30822
  return true;
31044
30823
  }
31045
30824
 
@@ -31082,8 +30861,6 @@ export {
31082
30861
  generateContextTree,
31083
30862
  onboard,
31084
30863
  curate,
31085
- findDuplicates,
31086
- findContradictions,
31087
30864
  decideSemanticDedup,
31088
30865
  syncChanges,
31089
30866
  watchForChanges,
@@ -31149,6 +30926,9 @@ export {
31149
30926
  DEFAULT_RECURRENCE_MIN_DAYS,
31150
30927
  MIN_RECURRENCE_MIN_DAYS,
31151
30928
  findRecurringPatterns,
30929
+ WEEKLY_DASHBOARD_FORMAT_VERSION,
30930
+ DEFAULT_TOP_SOURCE_LIMIT,
30931
+ buildWeeklyDashboard,
31152
30932
  WEEK_START_DAYS,
31153
30933
  computeWeeklyConfigHash,
31154
30934
  batchObservations,
@@ -31190,6 +30970,8 @@ export {
31190
30970
  ACTIVITY_FEATURE_GATES,
31191
30971
  resolveActivityGates,
31192
30972
  filterPrivacyWindow,
30973
+ redactActivityFields,
30974
+ normalizeDropKeys,
31193
30975
  formatVaultWikilink,
31194
30976
  stripVaultMdSuffix,
31195
30977
  joinVaultSegments,
@@ -31228,4 +31010,4 @@ export {
31228
31010
  qmdStartupCollectionChecksWithTimeout,
31229
31011
  Orchestrator
31230
31012
  };
31231
- //# sourceMappingURL=chunk-K7FXR75S.js.map
31013
+ //# sourceMappingURL=chunk-OKOG5MXF.js.map