@remnic/core 9.69.52 → 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.
Files changed (146) hide show
  1. package/dist/access-admin-ops-surface.d.ts +1 -1
  2. package/dist/access-admin-ops-surface.js +4 -4
  3. package/dist/access-authorization-probe.d.ts +1 -1
  4. package/dist/access-authorization-probe.js +5 -5
  5. package/dist/access-boundary.d.ts +1 -1
  6. package/dist/access-boundary.js +4 -4
  7. package/dist/access-cli.js +23 -23
  8. package/dist/access-extraction-force-flush.d.ts +1 -1
  9. package/dist/access-extraction-force-flush.js +4 -4
  10. package/dist/access-http-briefing.d.ts +1 -1
  11. package/dist/access-http-briefing.js +5 -5
  12. package/dist/access-http-lcm-compaction.d.ts +1 -1
  13. package/dist/access-http-lifecycle-flush.d.ts +1 -1
  14. package/dist/access-http-offline-stream.d.ts +1 -1
  15. package/dist/access-http-query.js +4 -4
  16. package/dist/access-http.d.ts +2 -2
  17. package/dist/access-http.js +16 -16
  18. package/dist/access-identity-continuity-surface.js +4 -4
  19. package/dist/access-lcm-surface.d.ts +1 -1
  20. package/dist/access-lcm-surface.js +4 -4
  21. package/dist/access-mcp-shared-context-tools.d.ts +1 -1
  22. package/dist/access-mcp.d.ts +1 -1
  23. package/dist/access-mcp.js +11 -11
  24. package/dist/access-namespace-preflight.js +4 -4
  25. package/dist/access-observe-write-surface.d.ts +1 -1
  26. package/dist/access-observe-write-surface.js +4 -4
  27. package/dist/access-operations-batch.js +5 -5
  28. package/dist/access-operations.d.ts +1 -1
  29. package/dist/access-operations.js +10 -10
  30. package/dist/access-recall-concurrency.d.ts +1 -1
  31. package/dist/access-recall-concurrency.js +4 -4
  32. package/dist/access-recall-response.d.ts +1 -1
  33. package/dist/access-recall-response.js +4 -4
  34. package/dist/access-recall-surface.d.ts +1 -1
  35. package/dist/access-recall-surface.js +4 -4
  36. package/dist/access-schema.d.ts +113 -113
  37. package/dist/{access-service-CUNM5RfA.d.ts → access-service-H-qEdnSs.d.ts} +64 -64
  38. package/dist/access-service-helpers.d.ts +1 -1
  39. package/dist/access-service.d.ts +1 -1
  40. package/dist/access-service.js +4 -4
  41. package/dist/access-surface-catalog.d.ts +1 -1
  42. package/dist/access-surface-catalog.js +6 -6
  43. package/dist/active-recall.js +1 -1
  44. package/dist/{chunk-TEQQQTQS.js → chunk-2HJKAVAS.js} +2 -2
  45. package/dist/{chunk-SEQ6DWXD.js → chunk-5IKW2VP6.js} +2 -2
  46. package/dist/{chunk-HWT6KRGS.js → chunk-6UEM5EOB.js} +2 -2
  47. package/dist/{chunk-UCY4EHAG.js → chunk-A4TQFNRX.js} +3 -3
  48. package/dist/{chunk-72N2P44O.js → chunk-ARRKSBQU.js} +2 -2
  49. package/dist/{chunk-WBW34YCJ.js → chunk-BCXZ4BDS.js} +2 -2
  50. package/dist/{chunk-V5BU5PVE.js → chunk-CO7KBRPF.js} +2 -2
  51. package/dist/{chunk-HG42J2EX.js → chunk-EQNZC3BH.js} +2 -2
  52. package/dist/{chunk-35EH6KW4.js → chunk-EYK3UHAP.js} +2 -2
  53. package/dist/{chunk-DCRK7I5E.js → chunk-HUQMVI3A.js} +93 -4
  54. package/dist/chunk-HUQMVI3A.js.map +1 -0
  55. package/dist/{chunk-UVZBDIJX.js → chunk-KGY6F4VJ.js} +488 -48
  56. package/dist/chunk-KGY6F4VJ.js.map +1 -0
  57. package/dist/{chunk-U6CMLAP4.js → chunk-M7HFDWOS.js} +2 -2
  58. package/dist/{chunk-2UDAABV6.js → chunk-MTJUIIM4.js} +2 -2
  59. package/dist/{chunk-GRX6YH4E.js → chunk-MXTCV3B5.js} +2 -2
  60. package/dist/{chunk-MSTKJI5T.js → chunk-OKOG5MXF.js} +590 -684
  61. package/dist/chunk-OKOG5MXF.js.map +1 -0
  62. package/dist/{chunk-6KCQNFDQ.js → chunk-SOOWBRMH.js} +8 -8
  63. package/dist/{chunk-72AV2H3R.js → chunk-U3NBO4IX.js} +39 -3
  64. package/dist/chunk-U3NBO4IX.js.map +1 -0
  65. package/dist/{chunk-EW3Y73MN.js → chunk-UJBIWY5M.js} +2 -2
  66. package/dist/{chunk-SVUUOLKM.js → chunk-VMKAA5SU.js} +2 -2
  67. package/dist/{chunk-JE4H2QBO.js → chunk-WJ5KWJLK.js} +2 -2
  68. package/dist/{chunk-UQTXER7M.js → chunk-ZADYEZY4.js} +2 -2
  69. package/dist/{chunk-2INZSV27.js → chunk-ZANEMAEV.js} +2 -2
  70. package/dist/{chunk-SOFATSHJ.js → chunk-ZHV6FGVF.js} +8 -8
  71. package/dist/{cli-CCc34xV7.d.ts → cli-VGDSn2zY.d.ts} +1 -1
  72. package/dist/cli.d.ts +2 -2
  73. package/dist/cli.js +20 -20
  74. package/dist/config.js +1 -1
  75. package/dist/deep-recall-http-glue.d.ts +1 -1
  76. package/dist/deep-recall-mcp-tools.d.ts +1 -1
  77. package/dist/directory-sidecars.d.ts +30 -7
  78. package/dist/directory-sidecars.js +5 -1
  79. package/dist/external-wiki-access.d.ts +1 -1
  80. package/dist/external-wiki-access.js +5 -5
  81. package/dist/external-wiki-mcp-tools.d.ts +1 -1
  82. package/dist/index.d.ts +819 -721
  83. package/dist/index.js +35 -25
  84. package/dist/mcp-memory-inspector-app.d.ts +1 -1
  85. package/dist/memory-browse-http-glue.d.ts +1 -1
  86. package/dist/memory-browse-mcp-tools.d.ts +1 -1
  87. package/dist/memory-browse-operations.js +5 -5
  88. package/dist/memory-browse.js +3 -3
  89. package/dist/memory-subject-cli.d.ts +1 -1
  90. package/dist/memory-subject.d.ts +1 -1
  91. package/dist/operator-toolkit.js +2 -2
  92. package/dist/orchestrator.js +23 -23
  93. package/dist/{public-http-UWVcqMcb.d.ts → public-http-P0cnHzYB.d.ts} +1 -1
  94. package/dist/recall-navigation-http-glue.d.ts +1 -1
  95. package/dist/recall-navigation-mcp-tools.d.ts +1 -1
  96. package/dist/recall-navigation-operations.js +5 -5
  97. package/dist/recall-navigation.js +4 -4
  98. package/dist/recall-state-view-admission.js +3 -3
  99. package/dist/resume-bundles.js +2 -2
  100. package/dist/schemas.d.ts +160 -160
  101. package/dist/shared-context/manager.d.ts +8 -8
  102. package/dist/support-passport/index.d.ts +4 -4
  103. package/dist/support-passport/index.js +7 -7
  104. package/dist/transfer/types.d.ts +78 -78
  105. package/dist/who-knows-cli.d.ts +1 -1
  106. package/dist/who-knows.d.ts +1 -1
  107. package/package.json +2 -2
  108. package/src/activity/index.ts +2 -0
  109. package/src/activity/timeline/index.ts +1 -0
  110. package/src/activity/timeline/week-dashboard.test.ts +362 -0
  111. package/src/activity/timeline/week-dashboard.ts +273 -0
  112. package/src/activity/timeline/weekly.ts +2 -1
  113. package/src/directory-sidecars.test.ts +113 -0
  114. package/src/directory-sidecars.ts +70 -8
  115. package/src/experience/session-experience-mining.test.ts +186 -0
  116. package/src/experience/session-experience-mining.ts +90 -0
  117. package/src/lifecycle/seed-graduation.test.ts +209 -0
  118. package/src/lifecycle/seed-graduation.ts +94 -11
  119. package/src/orchestration/persistence-index.ts +50 -0
  120. package/src/orchestration/recall-recognition-index.test.ts +249 -0
  121. package/src/orchestration/recall-recognition-index.ts +169 -0
  122. package/src/procedural/procedure-miner.ts +8 -6
  123. package/src/testing/subjects/recall-recognition-index.test.ts +97 -0
  124. package/dist/chunk-72AV2H3R.js.map +0 -1
  125. package/dist/chunk-DCRK7I5E.js.map +0 -1
  126. package/dist/chunk-MSTKJI5T.js.map +0 -1
  127. package/dist/chunk-UVZBDIJX.js.map +0 -1
  128. /package/dist/{chunk-TEQQQTQS.js.map → chunk-2HJKAVAS.js.map} +0 -0
  129. /package/dist/{chunk-SEQ6DWXD.js.map → chunk-5IKW2VP6.js.map} +0 -0
  130. /package/dist/{chunk-HWT6KRGS.js.map → chunk-6UEM5EOB.js.map} +0 -0
  131. /package/dist/{chunk-UCY4EHAG.js.map → chunk-A4TQFNRX.js.map} +0 -0
  132. /package/dist/{chunk-72N2P44O.js.map → chunk-ARRKSBQU.js.map} +0 -0
  133. /package/dist/{chunk-WBW34YCJ.js.map → chunk-BCXZ4BDS.js.map} +0 -0
  134. /package/dist/{chunk-V5BU5PVE.js.map → chunk-CO7KBRPF.js.map} +0 -0
  135. /package/dist/{chunk-HG42J2EX.js.map → chunk-EQNZC3BH.js.map} +0 -0
  136. /package/dist/{chunk-35EH6KW4.js.map → chunk-EYK3UHAP.js.map} +0 -0
  137. /package/dist/{chunk-U6CMLAP4.js.map → chunk-M7HFDWOS.js.map} +0 -0
  138. /package/dist/{chunk-2UDAABV6.js.map → chunk-MTJUIIM4.js.map} +0 -0
  139. /package/dist/{chunk-GRX6YH4E.js.map → chunk-MXTCV3B5.js.map} +0 -0
  140. /package/dist/{chunk-6KCQNFDQ.js.map → chunk-SOOWBRMH.js.map} +0 -0
  141. /package/dist/{chunk-EW3Y73MN.js.map → chunk-UJBIWY5M.js.map} +0 -0
  142. /package/dist/{chunk-SVUUOLKM.js.map → chunk-VMKAA5SU.js.map} +0 -0
  143. /package/dist/{chunk-JE4H2QBO.js.map → chunk-WJ5KWJLK.js.map} +0 -0
  144. /package/dist/{chunk-UQTXER7M.js.map → chunk-ZADYEZY4.js.map} +0 -0
  145. /package/dist/{chunk-2INZSV27.js.map → chunk-ZANEMAEV.js.map} +0 -0
  146. /package/dist/{chunk-SOFATSHJ.js.map → chunk-ZHV6FGVF.js.map} +0 -0
@@ -111,7 +111,7 @@ import {
111
111
  import {
112
112
  indexStateViewAnnotatedResults,
113
113
  resolveRecallStateViewActive
114
- } from "./chunk-72N2P44O.js";
114
+ } from "./chunk-ARRKSBQU.js";
115
115
  import {
116
116
  applyRecallStateViews
117
117
  } from "./chunk-DBB5JPSL.js";
@@ -231,7 +231,7 @@ import {
231
231
  runTimelinePublishCli,
232
232
  screenCandidateFact,
233
233
  summarizeVaultPublish
234
- } from "./chunk-SOFATSHJ.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,
@@ -344,9 +345,12 @@ import {
344
345
  resolveVaultNotePath
345
346
  } from "./chunk-3KJTSCSX.js";
346
347
  import {
348
+ buildRecognitionIndex,
347
349
  decideRecognitionTier,
348
350
  loadRecognitionIndex,
349
- runRecognitionTier
351
+ recognitionIndexPath,
352
+ runRecognitionTier,
353
+ saveRecognitionIndex
350
354
  } from "./chunk-4KQIWY4A.js";
351
355
  import {
352
356
  AMBIENT_CAPTURE_PROMPT_RULE,
@@ -354,7 +358,7 @@ import {
354
358
  } from "./chunk-QW27DOHG.js";
355
359
  import {
356
360
  MeetingsInputError
357
- } from "./chunk-6KCQNFDQ.js";
361
+ } from "./chunk-SOOWBRMH.js";
358
362
  import {
359
363
  WearablesInputError,
360
364
  describeErrorForOperator
@@ -378,8 +382,10 @@ import {
378
382
  SessionContextCoordinator,
379
383
  capturePassiveCorrections,
380
384
  clusterByKey,
381
- detectPassiveCorrections
382
- } from "./chunk-DCRK7I5E.js";
385
+ detectPassiveCorrections,
386
+ renderSessionExperiencePreview,
387
+ scoreSessionExperienceForPrompt
388
+ } from "./chunk-HUQMVI3A.js";
383
389
  import {
384
390
  importCapsule
385
391
  } from "./chunk-VVX5PZFS.js";
@@ -419,7 +425,7 @@ import {
419
425
  isTopLevelArchivePath,
420
426
  journalPath,
421
427
  writeJournalFile
422
- } from "./chunk-TEQQQTQS.js";
428
+ } from "./chunk-2HJKAVAS.js";
423
429
  import {
424
430
  searchHarmonicRetrieval
425
431
  } from "./chunk-TQT4AOPO.js";
@@ -661,9 +667,6 @@ import {
661
667
  import {
662
668
  fallbackLlmRuntimeContextFromConfig
663
669
  } from "./chunk-EP6BA7VV.js";
664
- import {
665
- normalizeRecallTokenSet
666
- } from "./chunk-YCVB7X56.js";
667
670
  import {
668
671
  graphemeUnits,
669
672
  truncateGraphemeSafe
@@ -850,7 +853,7 @@ import {
850
853
  } from "./chunk-3WCASNQZ.js";
851
854
 
852
855
  // src/orchestrator.ts
853
- import path22 from "path";
856
+ import path23 from "path";
854
857
  import { createHash as createHash9 } from "crypto";
855
858
 
856
859
  // src/orchestration/tier-migration-coordinator.ts
@@ -5600,93 +5603,6 @@ async function persistRepairedContentHash(storage, targetId, committedContent, c
5600
5603
  }
5601
5604
  }
5602
5605
 
5603
- // src/dedup/merge-provenance.ts
5604
- var MERGE_DERIVED_VIA = "merge";
5605
- var ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
5606
- function assertIsoInstant(value, field) {
5607
- if (typeof value !== "string" || !ISO_TIMESTAMP.test(value)) {
5608
- throw new RangeError(`${field} must be a full ISO 8601 instant, got ${JSON.stringify(value)}`);
5609
- }
5610
- const ms = Date.parse(value);
5611
- if (Number.isNaN(ms)) {
5612
- throw new RangeError(`${field} must be a full ISO 8601 instant, got ${JSON.stringify(value)}`);
5613
- }
5614
- if (!new Date(ms).toISOString().startsWith(value.slice(0, 10))) {
5615
- throw new RangeError(`${field} is not a real calendar date: ${JSON.stringify(value)}`);
5616
- }
5617
- return value;
5618
- }
5619
- function assertOffset(value, field) {
5620
- if (value === void 0) return;
5621
- if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
5622
- throw new RangeError(`${field} must be a non-negative integer when present, got ${String(value)}`);
5623
- }
5624
- }
5625
- function normalizeSource(source, index) {
5626
- const label = `sources[${index}]`;
5627
- for (const field of ["sessionKey", "quote"]) {
5628
- const value = source[field];
5629
- if (typeof value !== "string" || value.trim() === "") {
5630
- throw new RangeError(`${label}.${field} must be a non-blank string`);
5631
- }
5632
- }
5633
- assertIsoInstant(source.observedAt, `${label}.observedAt`);
5634
- if (source.turnId !== void 0 && (typeof source.turnId !== "string" || source.turnId.trim() === "")) {
5635
- throw new RangeError(`${label}.turnId must be a non-blank string when present`);
5636
- }
5637
- assertOffset(source.charStart, `${label}.charStart`);
5638
- assertOffset(source.charEnd, `${label}.charEnd`);
5639
- if (source.charStart !== void 0 && source.charEnd !== void 0 && source.charEnd < source.charStart) {
5640
- throw new RangeError(`${label}.charEnd must be >= charStart`);
5641
- }
5642
- const copy = {
5643
- sessionKey: source.sessionKey,
5644
- observedAt: source.observedAt,
5645
- quote: source.quote
5646
- };
5647
- if (source.turnId !== void 0) copy.turnId = source.turnId;
5648
- if (source.charStart !== void 0) copy.charStart = source.charStart;
5649
- if (source.charEnd !== void 0) copy.charEnd = source.charEnd;
5650
- return copy;
5651
- }
5652
- function sourceKey(source) {
5653
- return JSON.stringify([
5654
- source.sessionKey,
5655
- source.turnId ?? null,
5656
- source.observedAt,
5657
- source.quote,
5658
- source.charStart ?? null,
5659
- source.charEnd ?? null
5660
- ]);
5661
- }
5662
- function buildMergeFrontmatterUpdate(input) {
5663
- const updated = assertIsoInstant(input.nowIso, "nowIso");
5664
- const previous = input.targetReinforcementCount;
5665
- if (previous !== void 0 && (typeof previous !== "number" || !Number.isInteger(previous) || previous < 0)) {
5666
- throw new RangeError(
5667
- `reinforcement_count must be a non-negative integer when supplied, got ${String(previous)}`
5668
- );
5669
- }
5670
- const seen = /* @__PURE__ */ new Set();
5671
- const sources = [];
5672
- const append = (source, index) => {
5673
- const copy = normalizeSource(source, index);
5674
- const key = sourceKey(copy);
5675
- if (seen.has(key)) return;
5676
- seen.add(key);
5677
- sources.push(copy);
5678
- };
5679
- const target = input.targetSources ?? [];
5680
- target.forEach(append);
5681
- input.incomingSources.forEach((source, i) => append(source, target.length + i));
5682
- return {
5683
- updated,
5684
- derived_via: MERGE_DERIVED_VIA,
5685
- reinforcement_count: (previous ?? 0) + 1,
5686
- sources
5687
- };
5688
- }
5689
-
5690
5606
  // src/dedup/merge-judge.ts
5691
5607
  import { z } from "zod";
5692
5608
  var MERGE_JUDGE_SYSTEM_PROMPT = `You maintain a long-term memory store. Given a NEW memory and up to N EXISTING
@@ -9897,44 +9813,8 @@ var GraphRecallCoordinator = class {
9897
9813
  }
9898
9814
  };
9899
9815
 
9900
- // src/experience/session-experience-recall.ts
9901
- function tokenOverlapScore(prompt, memoryText) {
9902
- const norm = (s) => s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2);
9903
- const promptTokens = new Set(norm(prompt));
9904
- const memTokens = new Set(norm(memoryText));
9905
- if (promptTokens.size === 0 || memTokens.size === 0) return 0;
9906
- let inter = 0;
9907
- for (const t of promptTokens) {
9908
- if (memTokens.has(t)) inter++;
9909
- }
9910
- const union = /* @__PURE__ */ new Set([...promptTokens, ...memTokens]);
9911
- return inter / Math.max(1, union.size);
9912
- }
9913
- function isSessionExperienceMemory(memory) {
9914
- const hash = memory.frontmatter.structuredAttributes?.experience_session_hash;
9915
- return typeof hash === "string" && hash.length > 0;
9916
- }
9917
- function scoreSessionExperienceForPrompt(memory, prompt) {
9918
- if (!isSessionExperienceMemory(memory)) return null;
9919
- const situation = memory.frontmatter.structuredAttributes?.experience_situation ?? "";
9920
- const situationScore = tokenOverlapScore(prompt, situation);
9921
- const bodyScore = tokenOverlapScore(prompt, memory.content);
9922
- return Math.min(1, situationScore * 0.75 + bodyScore * 0.25);
9923
- }
9924
- function renderSessionExperiencePreview(memory) {
9925
- if (!isSessionExperienceMemory(memory)) return null;
9926
- const attrs = memory.frontmatter.structuredAttributes ?? {};
9927
- const situation = (attrs.experience_situation ?? "").trim();
9928
- const approach = (attrs.experience_approach ?? "").trim();
9929
- const reflection = (attrs.experience_reflection ?? "").trim();
9930
- if (situation.length === 0 && approach.length === 0 && reflection.length === 0) {
9931
- return `Experience. ${memory.content.replace(/\s+/g, " ").trim()}`;
9932
- }
9933
- return `Experience. Situation: ${situation} Approach: ${approach} Reflection: ${reflection}`.replace(/\s+/g, " ").trim();
9934
- }
9935
-
9936
9816
  // src/procedural/procedure-recall.ts
9937
- function tokenOverlapScore2(prompt, memoryText) {
9817
+ function tokenOverlapScore(prompt, memoryText) {
9938
9818
  const norm = (s) => s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2);
9939
9819
  const promptTokens = new Set(norm(prompt));
9940
9820
  const memTokens = new Set(norm(memoryText));
@@ -9949,7 +9829,7 @@ function tokenOverlapScore2(prompt, memoryText) {
9949
9829
  function scoreProcedureForPrompt(m, prompt, queryIntent, experienceEnabled) {
9950
9830
  const memText = `${m.content}
9951
9831
  ${(m.frontmatter.tags ?? []).join(" ")}`;
9952
- const jaccard = tokenOverlapScore2(prompt, memText);
9832
+ const jaccard = tokenOverlapScore(prompt, memText);
9953
9833
  const memIntent = inferIntentFromText(m.content.slice(0, 2e3));
9954
9834
  const intentScore = intentCompatibilityScore(queryIntent, memIntent);
9955
9835
  const base = jaccard * 0.55 + intentScore * 0.45;
@@ -17655,7 +17535,121 @@ var OrchestratorInitCoordinator = class {
17655
17535
  };
17656
17536
 
17657
17537
  // src/orchestration/persistence-index.ts
17538
+ import path19 from "path";
17539
+
17540
+ // src/orchestration/recall-recognition-index.ts
17541
+ import { promises as fsp } from "fs";
17658
17542
  import path18 from "path";
17543
+ import { setTimeout as delay } from "timers/promises";
17544
+ var writeChains = /* @__PURE__ */ new Map();
17545
+ var LOCK_POLL_MS = 10;
17546
+ var LOCK_ATTEMPTS = 50;
17547
+ var LOCK_STALE_MS = 6e4;
17548
+ function recognitionDescriptionFromContent(content) {
17549
+ const trimmed = content.trim();
17550
+ if (trimmed.length === 0) return "";
17551
+ const newline = trimmed.indexOf("\n");
17552
+ return newline === -1 ? trimmed : trimmed.slice(0, newline).trimEnd();
17553
+ }
17554
+ function withIndexChain(indexPath, op) {
17555
+ const previous = writeChains.get(indexPath) ?? Promise.resolve();
17556
+ const run = previous.then(op, op);
17557
+ const tail = run.then(() => void 0, () => void 0);
17558
+ writeChains.set(indexPath, tail);
17559
+ void tail.then(() => {
17560
+ if (writeChains.get(indexPath) === tail) writeChains.delete(indexPath);
17561
+ });
17562
+ return run;
17563
+ }
17564
+ async function removeAbandonedRecognitionLock(lockDir) {
17565
+ try {
17566
+ const info = await fsp.lstat(lockDir);
17567
+ if (info.isSymbolicLink()) return "blocked";
17568
+ if (!info.isDirectory()) {
17569
+ await fsp.rm(lockDir, { force: true });
17570
+ return "removed";
17571
+ }
17572
+ if (Date.now() - info.mtimeMs < LOCK_STALE_MS) return "wait";
17573
+ await fsp.rm(lockDir, { recursive: true, force: true });
17574
+ return "removed";
17575
+ } catch (error) {
17576
+ if (error?.code === "ENOENT") return "removed";
17577
+ return "blocked";
17578
+ }
17579
+ }
17580
+ async function withRecognitionIndexLock(memoryDir, op) {
17581
+ const lockDir = `${recognitionIndexPath(memoryDir)}.lock.d`;
17582
+ for (let attempt = 0; attempt < LOCK_ATTEMPTS; attempt++) {
17583
+ try {
17584
+ await fsp.mkdir(lockDir);
17585
+ try {
17586
+ return await op();
17587
+ } finally {
17588
+ await fsp.rm(lockDir, { recursive: true, force: true }).catch(() => void 0);
17589
+ }
17590
+ } catch (error) {
17591
+ const code = error?.code;
17592
+ if (code === "ENOENT") {
17593
+ await fsp.mkdir(path18.dirname(lockDir), { recursive: true }).catch(() => void 0);
17594
+ continue;
17595
+ }
17596
+ if (code !== "EEXIST") throw error;
17597
+ const cleanup = await removeAbandonedRecognitionLock(lockDir);
17598
+ if (cleanup === "blocked") {
17599
+ throw new Error(`recognition index lock blocked at ${lockDir}`);
17600
+ }
17601
+ if (cleanup === "removed") continue;
17602
+ await delay(LOCK_POLL_MS);
17603
+ }
17604
+ }
17605
+ throw new Error(`recognition index lock timed out at ${lockDir}`);
17606
+ }
17607
+ function applyChanges(entries, changes) {
17608
+ const next = entries.map((entry) => ({ id: entry.id, description: entry.description }));
17609
+ const byId = new Map(next.map((entry, index) => [entry.id, index]));
17610
+ for (const change of changes) {
17611
+ const id = change.id.trim();
17612
+ if (id.length === 0) continue;
17613
+ if (change.action === "remove") {
17614
+ const index = byId.get(id);
17615
+ if (index === void 0) continue;
17616
+ next.splice(index, 1);
17617
+ byId.delete(id);
17618
+ for (const [key, value] of byId) {
17619
+ if (value > index) byId.set(key, value - 1);
17620
+ }
17621
+ continue;
17622
+ }
17623
+ const description = recognitionDescriptionFromContent(change.content);
17624
+ const existing = byId.get(id);
17625
+ if (existing !== void 0) {
17626
+ next[existing] = { id, description };
17627
+ } else {
17628
+ byId.set(id, next.length);
17629
+ next.push({ id, description });
17630
+ }
17631
+ }
17632
+ return next;
17633
+ }
17634
+ function meaningfulChanges(changes) {
17635
+ return changes.filter((change) => change.id.trim().length > 0);
17636
+ }
17637
+ async function maintainRecognitionIndexAfterWrite(args) {
17638
+ if (args.enabled !== true) return;
17639
+ const changes = meaningfulChanges(args.changes);
17640
+ if (changes.length === 0) return;
17641
+ const indexPath = recognitionIndexPath(args.memoryDir);
17642
+ await withIndexChain(
17643
+ indexPath,
17644
+ () => withRecognitionIndexLock(args.memoryDir, async () => {
17645
+ const loaded = await loadRecognitionIndex(args.memoryDir);
17646
+ const entries = applyChanges(loaded?.entries ?? [], changes);
17647
+ await saveRecognitionIndex(args.memoryDir, buildRecognitionIndex(entries));
17648
+ })
17649
+ );
17650
+ }
17651
+
17652
+ // src/orchestration/persistence-index.ts
17659
17653
  var PersistenceIndexCoordinator = class {
17660
17654
  constructor(deps) {
17661
17655
  this.deps = deps;
@@ -17847,7 +17841,7 @@ var PersistenceIndexCoordinator = class {
17847
17841
  const allMems = allMemsForGraph ?? [];
17848
17842
  for (const m of allMems) {
17849
17843
  if (m.frontmatter.entityRef === entityRef) {
17850
- const rel = path18.relative(storage.dir, m.path);
17844
+ const rel = path19.relative(storage.dir, m.path);
17851
17845
  if (rel !== memoryRelPath) entitySiblings.push(rel);
17852
17846
  }
17853
17847
  }
@@ -17898,6 +17892,7 @@ var PersistenceIndexCoordinator = class {
17898
17892
  * Fail-open: any error is logged but does not abort extraction.
17899
17893
  */
17900
17894
  async updateTemporalTagIndexes(storage, persistedIds) {
17895
+ await this.maintainRecognitionIndex(storage, persistedIds);
17901
17896
  const caps = resolveCapabilities(this.deps.config);
17902
17897
  if (!resolveIndexingCapabilities(this.deps.config).queryAwareIndexing && !caps.parallelRetrieval && !resolveRecallEnhancementCapabilities(this.deps.config).eventOrderRecall)
17903
17898
  return;
@@ -17998,11 +17993,51 @@ var PersistenceIndexCoordinator = class {
17998
17993
  );
17999
17994
  return enriched;
18000
17995
  }
17996
+ /**
17997
+ * Upsert persisted memories into the namespace recognition index.
17998
+ * `recallRecognitionTier !== true` returns before any index I/O.
17999
+ * A missing index is bootstrapped from the active corpus so the first
18000
+ * post-enable write cannot publish a partial index that recall would
18001
+ * treat as the full working set.
18002
+ */
18003
+ async maintainRecognitionIndex(storage, persistedIds) {
18004
+ try {
18005
+ if (this.deps.config.recallRecognitionTier !== true) return;
18006
+ const indexMissing = await loadRecognitionIndex(storage.dir) === null;
18007
+ if (!indexMissing && persistedIds.length === 0) return;
18008
+ const changes = [];
18009
+ const pushUpsert = (memory) => {
18010
+ if (!isActiveMemoryStatus(memory.frontmatter.status)) return;
18011
+ const memoryId = memory.frontmatter.id;
18012
+ if (typeof memoryId !== "string" || memoryId.trim().length === 0) return;
18013
+ changes.push({ action: "upsert", id: memoryId, content: memory.content ?? "" });
18014
+ };
18015
+ if (indexMissing) {
18016
+ const corpus = await storage.readAllMemories().catch(() => []);
18017
+ for (const memory of corpus) pushUpsert(memory);
18018
+ }
18019
+ for (const id of persistedIds) {
18020
+ let memory = await storage.getMemoryById(id);
18021
+ if (!memory) {
18022
+ const cold = await storage.getMemoryByIdIncludingArchived(id).catch(() => null);
18023
+ if (cold && isActiveMemoryStatus(cold.frontmatter.status)) memory = cold;
18024
+ }
18025
+ if (memory) pushUpsert(memory);
18026
+ }
18027
+ await maintainRecognitionIndexAfterWrite({
18028
+ memoryDir: storage.dir,
18029
+ enabled: true,
18030
+ changes
18031
+ });
18032
+ } catch (err) {
18033
+ log.debug(`recognition-index update error (non-fatal): ${err}`);
18034
+ }
18035
+ }
18001
18036
  };
18002
18037
 
18003
18038
  // src/orchestration/workspace-ops.ts
18004
18039
  import { lstat as lstat2, mkdir as mkdir6, readFile as readFile8, readdir as readdir3, realpath as realpath2, writeFile as writeFile6 } from "fs/promises";
18005
- import path19 from "path";
18040
+ import path20 from "path";
18006
18041
 
18007
18042
  // src/maintenance/pattern-reinforcement.ts
18008
18043
  function patternReinforcementKey(content) {
@@ -19906,8 +19941,8 @@ var MeetingsService = class {
19906
19941
  // src/orchestration/workspace-ops.ts
19907
19942
  var MEETINGS_BUILD_DEBOUNCE_MS = 5e3;
19908
19943
  function matchesMemoryPath(candidatePath, requestedPath, memoryDir) {
19909
- const normalizedCandidate = path19.resolve(candidatePath);
19910
- return normalizedCandidate === path19.resolve(requestedPath) || normalizedCandidate === path19.resolve(memoryDir, requestedPath);
19944
+ const normalizedCandidate = path20.resolve(candidatePath);
19945
+ return normalizedCandidate === path20.resolve(requestedPath) || normalizedCandidate === path20.resolve(memoryDir, requestedPath);
19911
19946
  }
19912
19947
  function accessTrackingPathCandidates(storageDir, memoryPath) {
19913
19948
  const candidates = qmdResultPathCandidates(storageDir, memoryPath);
@@ -19915,12 +19950,12 @@ function accessTrackingPathCandidates(storageDir, memoryPath) {
19915
19950
  if (!parts) return candidates;
19916
19951
  candidates.push(
19917
19952
  ...qmdResultPathCandidates(storageDir, parts.relativePath),
19918
- ...qmdResultPathCandidates(path19.join(storageDir, "cold"), parts.relativePath)
19953
+ ...qmdResultPathCandidates(path20.join(storageDir, "cold"), parts.relativePath)
19919
19954
  );
19920
19955
  return [...new Set(candidates)];
19921
19956
  }
19922
19957
  function canonicalMemoryPath(memoryPath, memoryDir) {
19923
- return path19.resolve(path19.isAbsolute(memoryPath) ? memoryPath : path19.resolve(memoryDir, memoryPath));
19958
+ return path20.resolve(path20.isAbsolute(memoryPath) ? memoryPath : path20.resolve(memoryDir, memoryPath));
19924
19959
  }
19925
19960
  var WorkspaceOpsCoordinator = class {
19926
19961
  constructor(deps) {
@@ -20002,15 +20037,15 @@ var WorkspaceOpsCoordinator = class {
20002
20037
  this.deps.lastFileHygieneRunAtMs = now;
20003
20038
  if (hygiene.rotateEnabled) {
20004
20039
  for (const rel of hygiene.rotatePaths) {
20005
- const abs = path19.isAbsolute(rel) ? rel : path19.join(this.deps.config.workspaceDir, rel);
20040
+ const abs = path20.isAbsolute(rel) ? rel : path20.join(this.deps.config.workspaceDir, rel);
20006
20041
  try {
20007
20042
  const raw = await readFile8(abs, "utf-8");
20008
20043
  if (raw.length > hygiene.rotateMaxBytes) {
20009
- const archiveDir = path19.join(
20044
+ const archiveDir = path20.join(
20010
20045
  this.deps.config.workspaceDir,
20011
20046
  hygiene.archiveDir
20012
20047
  );
20013
- const base = path19.basename(abs);
20048
+ const base = path20.basename(abs);
20014
20049
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
20015
20050
  const { newContent } = await rotateMarkdownFileToArchive({
20016
20051
  filePath: abs,
@@ -20035,8 +20070,8 @@ var WorkspaceOpsCoordinator = class {
20035
20070
  log.warn(w.message);
20036
20071
  }
20037
20072
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
20038
- const fp = path19.join(this.deps.config.memoryDir, hygiene.warningsLogPath);
20039
- await mkdir6(path19.dirname(fp), { recursive: true });
20073
+ const fp = path20.join(this.deps.config.memoryDir, hygiene.warningsLogPath);
20074
+ await mkdir6(path20.dirname(fp), { recursive: true });
20040
20075
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
20041
20076
  const block = `
20042
20077
 
@@ -20076,7 +20111,7 @@ var WorkspaceOpsCoordinator = class {
20076
20111
  for (const categoryDir of RECALL_FALLBACK_DIRS) {
20077
20112
  if (memoryRootReal === null) break;
20078
20113
  for (const date of datesToScan) {
20079
- const dateDir = path19.join(storage.dir, categoryDir, date);
20114
+ const dateDir = path20.join(storage.dir, categoryDir, date);
20080
20115
  try {
20081
20116
  const dirStat = await lstat2(dateDir);
20082
20117
  if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) continue;
@@ -20085,7 +20120,7 @@ var WorkspaceOpsCoordinator = class {
20085
20120
  for (const entry of entries) {
20086
20121
  if (entry.isSymbolicLink()) continue;
20087
20122
  if (!entry.name.endsWith(".md")) continue;
20088
- const fullPath = path19.join(dateDir, entry.name);
20123
+ const fullPath = path20.join(dateDir, entry.name);
20089
20124
  try {
20090
20125
  assertPathInsideRoot(memoryRootReal, await realpath2(fullPath), fullPath);
20091
20126
  const memory = await storage.readMemoryByPath(fullPath);
@@ -20107,13 +20142,13 @@ var WorkspaceOpsCoordinator = class {
20107
20142
  return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
20108
20143
  });
20109
20144
  const hourlySummaries = [];
20110
- const hourlyBaseDir = path19.join(storage.dir, "summaries", "hourly");
20145
+ const hourlyBaseDir = path20.join(storage.dir, "summaries", "hourly");
20111
20146
  try {
20112
20147
  const sessionKeys = await readdir3(hourlyBaseDir, { withFileTypes: true });
20113
20148
  for (const sk of sessionKeys) {
20114
20149
  if (!sk.isDirectory()) continue;
20115
20150
  for (const date of datesToScan) {
20116
- const summaryFile = path19.join(hourlyBaseDir, sk.name, `${date}.md`);
20151
+ const summaryFile = path20.join(hourlyBaseDir, sk.name, `${date}.md`);
20117
20152
  try {
20118
20153
  const raw = await readFile8(summaryFile, "utf-8");
20119
20154
  const filtered = filterHourlySummaryMarkdownForLocalDay(
@@ -20590,18 +20625,18 @@ ${reflectionsContent.trim()}
20590
20625
  // src/orchestration/namespace-read-fanout.ts
20591
20626
  import { existsSync, readFileSync } from "fs";
20592
20627
  import { stat as stat3 } from "fs/promises";
20593
- import path21 from "path";
20628
+ import path22 from "path";
20594
20629
 
20595
20630
  // src/orchestration/orchestrator-namespace-scope.ts
20596
- import path20 from "path";
20631
+ import path21 from "path";
20597
20632
  function computeSemanticDedupScope(targetStorage, config) {
20598
20633
  if (!resolveNamespaceCapabilities(config).namespaces) return {};
20599
- const memoryDir = path20.resolve(config.memoryDir);
20600
- const storageDir = path20.resolve(targetStorage.dir);
20634
+ const memoryDir = path21.resolve(config.memoryDir);
20635
+ const storageDir = path21.resolve(targetStorage.dir);
20601
20636
  if (storageDir === memoryDir) {
20602
20637
  return { pathExcludePrefixes: ["namespaces/"] };
20603
20638
  }
20604
- let rel = path20.relative(memoryDir, storageDir);
20639
+ let rel = path21.relative(memoryDir, storageDir);
20605
20640
  if (!rel || rel.startsWith("..")) {
20606
20641
  log.debug(
20607
20642
  `semantic dedup: target storage dir ${storageDir} is outside memoryDir ${memoryDir}; scoping lookup to absolute path prefix`
@@ -20654,7 +20689,7 @@ var NamespaceReadFanoutCoordinator = class {
20654
20689
  loadNamespaceStorageDirHintsFromCatalog() {
20655
20690
  if (this.deps.namespaceStorageDirHintsLoaded || !this.deps.namespaceCatalog.enabled) return;
20656
20691
  this.deps.namespaceStorageDirHintsLoaded = true;
20657
- const catalogPath = path21.join(this.deps.config.memoryDir, "state", "namespaces.jsonl");
20692
+ const catalogPath = path22.join(this.deps.config.memoryDir, "state", "namespaces.jsonl");
20658
20693
  if (!existsSync(catalogPath)) return;
20659
20694
  let body;
20660
20695
  try {
@@ -20691,7 +20726,7 @@ var NamespaceReadFanoutCoordinator = class {
20691
20726
  if (!this.deps.storageDirMatchesNamespaceHint(record.namespace, record.storageDir)) {
20692
20727
  continue;
20693
20728
  }
20694
- const resolvedStorageDir = path21.resolve(record.storageDir);
20729
+ const resolvedStorageDir = path22.resolve(record.storageDir);
20695
20730
  const current = preferredByStorageDir.get(resolvedStorageDir);
20696
20731
  preferredByStorageDir.set(
20697
20732
  resolvedStorageDir,
@@ -20876,12 +20911,12 @@ var NamespaceReadFanoutCoordinator = class {
20876
20911
  }
20877
20912
  async resolveStateDirForNamespace(namespace) {
20878
20913
  if (!resolveNamespaceCapabilities(this.deps.config).namespaces) {
20879
- return path21.join(this.deps.config.memoryDir, "state");
20914
+ return path22.join(this.deps.config.memoryDir, "state");
20880
20915
  }
20881
20916
  if (namespace !== this.deps.config.defaultNamespace) {
20882
- return path21.join(this.deps.config.memoryDir, "namespaces", namespace, "state");
20917
+ return path22.join(this.deps.config.memoryDir, "namespaces", namespace, "state");
20883
20918
  }
20884
- const candidate = path21.join(
20919
+ const candidate = path22.join(
20885
20920
  this.deps.config.memoryDir,
20886
20921
  "namespaces",
20887
20922
  this.deps.config.defaultNamespace
@@ -20889,11 +20924,11 @@ var NamespaceReadFanoutCoordinator = class {
20889
20924
  try {
20890
20925
  const candidateStat = await stat3(candidate);
20891
20926
  if (candidateStat.isDirectory()) {
20892
- return path21.join(candidate, "state");
20927
+ return path22.join(candidate, "state");
20893
20928
  }
20894
20929
  } catch {
20895
20930
  }
20896
- return path21.join(this.deps.config.memoryDir, "state");
20931
+ return path22.join(this.deps.config.memoryDir, "state");
20897
20932
  }
20898
20933
  namespaceFromPath(p) {
20899
20934
  if (!resolveNamespaceCapabilities(this.deps.config).namespaces) return this.deps.config.defaultNamespace;
@@ -21348,7 +21383,7 @@ var Orchestrator = class {
21348
21383
  if (this.destroyed) throw new Error("orchestrator has been destroyed");
21349
21384
  if (!this._dependencyPropagationDelivery) {
21350
21385
  this._dependencyPropagationDelivery = new DependencyPropagationDelivery({
21351
- queueRoot: path22.join(this.config.memoryDir, "state", "dependency-propagation"),
21386
+ queueRoot: path23.join(this.config.memoryDir, "state", "dependency-propagation"),
21352
21387
  config: this.config,
21353
21388
  extraction: this.extraction,
21354
21389
  getStorage: (namespace) => this.getStorage(namespace),
@@ -21703,7 +21738,7 @@ var Orchestrator = class {
21703
21738
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
21704
21739
  if (ns !== defaultNs && !isSafeRouteNamespace(ns)) return;
21705
21740
  if (!this.storageDirMatchesNamespaceHint(ns, storageDir)) return;
21706
- const resolvedStorageDir = path22.resolve(storageDir);
21741
+ const resolvedStorageDir = path23.resolve(storageDir);
21707
21742
  let hints = this.namespaceStorageDirHints.get(resolvedStorageDir);
21708
21743
  if (!hints) {
21709
21744
  hints = /* @__PURE__ */ new Set();
@@ -21714,21 +21749,21 @@ var Orchestrator = class {
21714
21749
  storageDirMatchesNamespaceHint(namespace, storageDir) {
21715
21750
  const ns = normalizeNamespaceIdentity(namespace);
21716
21751
  if (!ns) return false;
21717
- const resolvedStorageDir = path22.resolve(storageDir);
21718
- const resolvedMemoryDir = path22.resolve(this.config.memoryDir);
21752
+ const resolvedStorageDir = path23.resolve(storageDir);
21753
+ const resolvedMemoryDir = path23.resolve(this.config.memoryDir);
21719
21754
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
21720
21755
  if (resolvedStorageDir === resolvedMemoryDir) return ns === defaultNs;
21721
- const resolvedNamespacesDir = path22.join(resolvedMemoryDir, "namespaces");
21756
+ const resolvedNamespacesDir = path23.join(resolvedMemoryDir, "namespaces");
21722
21757
  if (!isPathInsideStorageRoot(resolvedNamespacesDir, resolvedStorageDir)) return false;
21723
- const rawRoot = path22.resolve(resolvedNamespacesDir, ns);
21724
- const tokenRoot = path22.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
21758
+ const rawRoot = path23.resolve(resolvedNamespacesDir, ns);
21759
+ const tokenRoot = path23.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
21725
21760
  return resolvedStorageDir === rawRoot || resolvedStorageDir === tokenRoot;
21726
21761
  }
21727
21762
  namespaceStorageDirHintOwnershipRank(record, resolvedStorageDir, configured) {
21728
- if (resolvedStorageDir === path22.resolve(this.config.memoryDir)) {
21763
+ if (resolvedStorageDir === path23.resolve(this.config.memoryDir)) {
21729
21764
  return record.namespace === normalizeNamespaceIdentity(this.config.defaultNamespace) ? 0 : 3;
21730
21765
  }
21731
- const leaf = path22.basename(resolvedStorageDir);
21766
+ const leaf = path23.basename(resolvedStorageDir);
21732
21767
  const tokenOwnsRoot = namespaceIdentityToken(record.namespace) === leaf;
21733
21768
  if (tokenOwnsRoot && configured.has(record.namespace)) return 0;
21734
21769
  if (record.namespace === leaf) return 1;
@@ -21869,7 +21904,7 @@ var Orchestrator = class {
21869
21904
  StorageManager.setOkfConformanceDefault(config.memoryDir, config.okf?.conformanceEnabled);
21870
21905
  this.profiler = new ProfilingCollector({
21871
21906
  enabled: resolvePipelineProcessingCapabilities(this.config).profiling,
21872
- storageDir: config.profilingStorageDir || path22.join(config.memoryDir, "profiling"),
21907
+ storageDir: config.profilingStorageDir || path23.join(config.memoryDir, "profiling"),
21873
21908
  maxTraces: config.profilingMaxTraces
21874
21909
  });
21875
21910
  this.namespaceCatalog = new NamespaceCatalog(config);
@@ -21956,7 +21991,7 @@ var Orchestrator = class {
21956
21991
  this.compounding = resolveConsolidationCapabilities(config).compounding ? new CompoundingEngine(config, this.storage) : void 0;
21957
21992
  this.buffer = new SmartBuffer(config, this.storage);
21958
21993
  this.transcript = new TranscriptManager(config);
21959
- this.conversationIndexDir = path22.join(
21994
+ this.conversationIndexDir = path23.join(
21960
21995
  config.memoryDir,
21961
21996
  "conversation-index",
21962
21997
  "chunks"
@@ -22107,7 +22142,7 @@ var Orchestrator = class {
22107
22142
  getHandleHistory: () => this.handleHistory
22108
22143
  });
22109
22144
  this.threading = new ThreadingManager(
22110
- path22.join(config.memoryDir, "threads"),
22145
+ path23.join(config.memoryDir, "threads"),
22111
22146
  config.threadingGapMinutes
22112
22147
  );
22113
22148
  const lifecycleCaps = resolveMemoryLifecycleCapabilities(config);
@@ -24275,8 +24310,8 @@ var DEFAULT_MAX_BINARY_SIZE_BYTES = 50 * 1024 * 1024;
24275
24310
  var DEFAULT_GRACE_PERIOD_DAYS = 7;
24276
24311
 
24277
24312
  // src/binary-lifecycle/backend.ts
24278
- import fsp from "fs/promises";
24279
- import path23 from "path";
24313
+ import fsp2 from "fs/promises";
24314
+ import path24 from "path";
24280
24315
  var FilesystemBackend = class {
24281
24316
  type = "filesystem";
24282
24317
  basePath;
@@ -24284,30 +24319,30 @@ var FilesystemBackend = class {
24284
24319
  if (!basePath || basePath.trim().length === 0) {
24285
24320
  throw new Error("FilesystemBackend requires a non-empty basePath");
24286
24321
  }
24287
- this.basePath = path23.resolve(basePath);
24322
+ this.basePath = path24.resolve(basePath);
24288
24323
  }
24289
24324
  resolveRemotePath(remotePath) {
24290
- const resolved = path23.isAbsolute(remotePath) ? path23.resolve(remotePath) : path23.resolve(this.basePath, remotePath);
24291
- const relative = path23.relative(this.basePath, resolved);
24292
- if (relative === ".." || relative.startsWith(`..${path23.sep}`) || path23.isAbsolute(relative)) {
24325
+ const resolved = path24.isAbsolute(remotePath) ? path24.resolve(remotePath) : path24.resolve(this.basePath, remotePath);
24326
+ const relative = path24.relative(this.basePath, resolved);
24327
+ if (relative === ".." || relative.startsWith(`..${path24.sep}`) || path24.isAbsolute(relative)) {
24293
24328
  throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
24294
24329
  }
24295
24330
  return resolved;
24296
24331
  }
24297
24332
  isInsideBase(candidate, realBase) {
24298
- const relative = path23.relative(realBase, candidate);
24299
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path23.sep}`) && !path23.isAbsolute(relative);
24333
+ const relative = path24.relative(realBase, candidate);
24334
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path24.sep}`) && !path24.isAbsolute(relative);
24300
24335
  }
24301
24336
  async realBasePathIfExists() {
24302
24337
  try {
24303
- const stat5 = await fsp.lstat(this.basePath);
24338
+ const stat5 = await fsp2.lstat(this.basePath);
24304
24339
  if (stat5.isSymbolicLink()) {
24305
24340
  throw new Error(`FilesystemBackend basePath must not be a symlink: ${this.basePath}`);
24306
24341
  }
24307
24342
  if (!stat5.isDirectory()) {
24308
24343
  throw new Error(`FilesystemBackend basePath must be a directory: ${this.basePath}`);
24309
24344
  }
24310
- return await fsp.realpath(this.basePath);
24345
+ return await fsp2.realpath(this.basePath);
24311
24346
  } catch (err) {
24312
24347
  if (err.code === "ENOENT") {
24313
24348
  return null;
@@ -24316,7 +24351,7 @@ var FilesystemBackend = class {
24316
24351
  }
24317
24352
  }
24318
24353
  async ensureBaseDirectory() {
24319
- await fsp.mkdir(this.basePath, { recursive: true });
24354
+ await fsp2.mkdir(this.basePath, { recursive: true });
24320
24355
  const realBase = await this.realBasePathIfExists();
24321
24356
  if (realBase === null) {
24322
24357
  throw new Error(`FilesystemBackend failed to create basePath: ${this.basePath}`);
@@ -24325,15 +24360,15 @@ var FilesystemBackend = class {
24325
24360
  }
24326
24361
  async ensureSafeParentDirectory(dest) {
24327
24362
  const realBase = await this.ensureBaseDirectory();
24328
- const destDir = path23.dirname(dest);
24329
- const relativeDir = path23.relative(this.basePath, destDir);
24330
- const segments = relativeDir === "" ? [] : relativeDir.split(path23.sep);
24363
+ const destDir = path24.dirname(dest);
24364
+ const relativeDir = path24.relative(this.basePath, destDir);
24365
+ const segments = relativeDir === "" ? [] : relativeDir.split(path24.sep);
24331
24366
  let current = this.basePath;
24332
24367
  for (const segment of segments) {
24333
24368
  if (segment === "." || segment === "") continue;
24334
- current = path23.join(current, segment);
24369
+ current = path24.join(current, segment);
24335
24370
  try {
24336
- const stat5 = await fsp.lstat(current);
24371
+ const stat5 = await fsp2.lstat(current);
24337
24372
  if (stat5.isSymbolicLink()) {
24338
24373
  throw new Error(`FilesystemBackend remotePath traverses symlink: ${current}`);
24339
24374
  }
@@ -24344,10 +24379,10 @@ var FilesystemBackend = class {
24344
24379
  if (err.code !== "ENOENT") {
24345
24380
  throw err;
24346
24381
  }
24347
- await fsp.mkdir(current);
24382
+ await fsp2.mkdir(current);
24348
24383
  }
24349
24384
  }
24350
- const realParent = await fsp.realpath(destDir);
24385
+ const realParent = await fsp2.realpath(destDir);
24351
24386
  if (!this.isInsideBase(realParent, realBase)) {
24352
24387
  throw new Error(`FilesystemBackend remotePath parent escapes basePath: ${dest}`);
24353
24388
  }
@@ -24359,16 +24394,16 @@ var FilesystemBackend = class {
24359
24394
  if (realBase === null) {
24360
24395
  return null;
24361
24396
  }
24362
- const destDir = path23.dirname(dest);
24363
- const relativeDir = path23.relative(this.basePath, destDir);
24364
- const segments = relativeDir === "" ? [] : relativeDir.split(path23.sep);
24397
+ const destDir = path24.dirname(dest);
24398
+ const relativeDir = path24.relative(this.basePath, destDir);
24399
+ const segments = relativeDir === "" ? [] : relativeDir.split(path24.sep);
24365
24400
  let current = this.basePath;
24366
24401
  for (const segment of segments) {
24367
24402
  if (segment === "." || segment === "") continue;
24368
- current = path23.join(current, segment);
24403
+ current = path24.join(current, segment);
24369
24404
  let stat5;
24370
24405
  try {
24371
- stat5 = await fsp.lstat(current);
24406
+ stat5 = await fsp2.lstat(current);
24372
24407
  } catch (err) {
24373
24408
  if (err.code === "ENOENT") {
24374
24409
  return null;
@@ -24382,7 +24417,7 @@ var FilesystemBackend = class {
24382
24417
  return null;
24383
24418
  }
24384
24419
  }
24385
- const realParent = await fsp.realpath(destDir).catch((err) => {
24420
+ const realParent = await fsp2.realpath(destDir).catch((err) => {
24386
24421
  if (isErrnoCode(err, "ENOENT")) return null;
24387
24422
  throw err;
24388
24423
  });
@@ -24393,7 +24428,7 @@ var FilesystemBackend = class {
24393
24428
  throw new Error(`FilesystemBackend remotePath parent escapes basePath: ${JSON.stringify(remotePath)}`);
24394
24429
  }
24395
24430
  try {
24396
- const stat5 = await fsp.lstat(dest);
24431
+ const stat5 = await fsp2.lstat(dest);
24397
24432
  if (stat5.isSymbolicLink()) {
24398
24433
  throw new Error(`FilesystemBackend remotePath points to symlink: ${dest}`);
24399
24434
  }
@@ -24406,20 +24441,20 @@ var FilesystemBackend = class {
24406
24441
  }
24407
24442
  throw err;
24408
24443
  }
24409
- const realDest = await fsp.realpath(dest);
24444
+ const realDest = await fsp2.realpath(dest);
24410
24445
  if (!this.isInsideBase(realDest, realBase)) {
24411
24446
  throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
24412
24447
  }
24413
24448
  return dest;
24414
24449
  }
24415
24450
  async upload(localPath, remotePath) {
24416
- if (path23.isAbsolute(remotePath)) {
24451
+ if (path24.isAbsolute(remotePath)) {
24417
24452
  throw new Error(`FilesystemBackend upload remotePath must be relative: ${JSON.stringify(remotePath)}`);
24418
24453
  }
24419
24454
  const dest = this.resolveRemotePath(remotePath);
24420
24455
  const realBase = await this.ensureSafeParentDirectory(dest);
24421
24456
  try {
24422
- const stat5 = await fsp.lstat(dest);
24457
+ const stat5 = await fsp2.lstat(dest);
24423
24458
  if (stat5.isSymbolicLink()) {
24424
24459
  throw new Error(`FilesystemBackend remotePath points to symlink: ${dest}`);
24425
24460
  }
@@ -24428,8 +24463,8 @@ var FilesystemBackend = class {
24428
24463
  throw err;
24429
24464
  }
24430
24465
  }
24431
- await fsp.copyFile(localPath, dest);
24432
- const realDest = await fsp.realpath(dest);
24466
+ await fsp2.copyFile(localPath, dest);
24467
+ const realDest = await fsp2.realpath(dest);
24433
24468
  if (!this.isInsideBase(realDest, realBase)) {
24434
24469
  throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
24435
24470
  }
@@ -24445,7 +24480,7 @@ var FilesystemBackend = class {
24445
24480
  return;
24446
24481
  }
24447
24482
  try {
24448
- await fsp.unlink(dest);
24483
+ await fsp2.unlink(dest);
24449
24484
  } catch (err) {
24450
24485
  if (err.code !== "ENOENT") throw err;
24451
24486
  }
@@ -24485,8 +24520,8 @@ function createBackend(cfg) {
24485
24520
  }
24486
24521
 
24487
24522
  // src/binary-lifecycle/scanner.ts
24488
- import fsp2 from "fs/promises";
24489
- import path24 from "path";
24523
+ import fsp3 from "fs/promises";
24524
+ import path25 from "path";
24490
24525
  import crypto from "crypto";
24491
24526
  function matchesPatterns(filename, patterns) {
24492
24527
  const lower = filename.toLowerCase();
@@ -24506,13 +24541,13 @@ async function scanForBinaries(memoryDir, config, manifest) {
24506
24541
  async function walk(dir) {
24507
24542
  let entries;
24508
24543
  try {
24509
- entries = await fsp2.readdir(dir, { withFileTypes: true });
24544
+ entries = await fsp3.readdir(dir, { withFileTypes: true });
24510
24545
  } catch {
24511
24546
  return;
24512
24547
  }
24513
24548
  for (const entry of entries) {
24514
- const fullPath = path24.join(dir, entry.name);
24515
- const relativePath = path24.relative(memoryDir, fullPath).split(path24.sep).join("/");
24549
+ const fullPath = path25.join(dir, entry.name);
24550
+ const relativePath = path25.relative(memoryDir, fullPath).split(path25.sep).join("/");
24516
24551
  if (entry.isDirectory()) {
24517
24552
  if (entry.name === ".binary-lifecycle") continue;
24518
24553
  await walk(fullPath);
@@ -24521,7 +24556,7 @@ async function scanForBinaries(memoryDir, config, manifest) {
24521
24556
  if (!entry.isFile()) continue;
24522
24557
  if (!matchesPatterns(entry.name, config.scanPatterns)) continue;
24523
24558
  try {
24524
- const stat5 = await fsp2.stat(fullPath);
24559
+ const stat5 = await fsp3.stat(fullPath);
24525
24560
  if (stat5.size > config.maxBinarySizeBytes) continue;
24526
24561
  if (stat5.size === 0) continue;
24527
24562
  const existing = tracked.get(relativePath);
@@ -24546,27 +24581,27 @@ async function scanForBinaries(memoryDir, config, manifest) {
24546
24581
  return results;
24547
24582
  }
24548
24583
  async function hashFile(filePath) {
24549
- const content = await fsp2.readFile(filePath);
24584
+ const content = await fsp3.readFile(filePath);
24550
24585
  return crypto.createHash("sha256").update(content).digest("hex");
24551
24586
  }
24552
24587
 
24553
24588
  // src/binary-lifecycle/manifest.ts
24554
- import fsp3 from "fs/promises";
24555
- import path25 from "path";
24589
+ import fsp4 from "fs/promises";
24590
+ import path26 from "path";
24556
24591
  import crypto2 from "crypto";
24557
24592
  var MANIFEST_DIR = ".binary-lifecycle";
24558
24593
  var MANIFEST_FILE = "manifest.json";
24559
24594
  function manifestDir(memoryDir) {
24560
- return path25.join(memoryDir, MANIFEST_DIR);
24595
+ return path26.join(memoryDir, MANIFEST_DIR);
24561
24596
  }
24562
24597
  function manifestPath(memoryDir) {
24563
- return path25.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
24598
+ return path26.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
24564
24599
  }
24565
24600
  async function readManifest(memoryDir) {
24566
24601
  const filePath = manifestPath(memoryDir);
24567
24602
  let raw;
24568
24603
  try {
24569
- raw = await fsp3.readFile(filePath, "utf-8");
24604
+ raw = await fsp4.readFile(filePath, "utf-8");
24570
24605
  } catch (err) {
24571
24606
  if (err.code === "ENOENT") {
24572
24607
  return emptyManifest();
@@ -24590,17 +24625,17 @@ async function readManifest(memoryDir) {
24590
24625
  }
24591
24626
  async function writeManifest(memoryDir, manifest) {
24592
24627
  const dir = manifestDir(memoryDir);
24593
- await fsp3.mkdir(dir, { recursive: true });
24628
+ await fsp4.mkdir(dir, { recursive: true });
24594
24629
  const dest = manifestPath(memoryDir);
24595
24630
  const tmpSuffix = crypto2.randomBytes(8).toString("hex");
24596
24631
  const tmpPath = `${dest}.${tmpSuffix}.tmp`;
24597
24632
  const content = JSON.stringify(manifest, null, 2) + "\n";
24598
- await fsp3.writeFile(tmpPath, content, "utf-8");
24633
+ await fsp4.writeFile(tmpPath, content, "utf-8");
24599
24634
  try {
24600
- await fsp3.rename(tmpPath, dest);
24635
+ await fsp4.rename(tmpPath, dest);
24601
24636
  } catch (renameErr) {
24602
24637
  try {
24603
- await fsp3.unlink(tmpPath);
24638
+ await fsp4.unlink(tmpPath);
24604
24639
  } catch {
24605
24640
  }
24606
24641
  throw renameErr;
@@ -24611,18 +24646,18 @@ function emptyManifest() {
24611
24646
  }
24612
24647
 
24613
24648
  // src/binary-lifecycle/pipeline.ts
24614
- import fsp4 from "fs/promises";
24615
- import path26 from "path";
24649
+ import fsp5 from "fs/promises";
24650
+ import path27 from "path";
24616
24651
  import crypto3 from "crypto";
24617
24652
  var historicalIdCache = new HistoricalEntityCanonicalIdCache();
24618
24653
  function isRecallCorpusMarkdown(mdPath, memoryDir) {
24619
- const rel = path26.relative(memoryDir, mdPath);
24620
- if (rel.startsWith("..") || path26.isAbsolute(rel)) return false;
24621
- const top = rel.split(path26.sep)[0];
24654
+ const rel = path27.relative(memoryDir, mdPath);
24655
+ if (rel.startsWith("..") || path27.isAbsolute(rel)) return false;
24656
+ const top = rel.split(path27.sep)[0];
24622
24657
  return RECALL_FALLBACK_DIRS.includes(top);
24623
24658
  }
24624
24659
  async function hashFile2(filePath) {
24625
- const content = await fsp4.readFile(filePath);
24660
+ const content = await fsp5.readFile(filePath);
24626
24661
  return crypto3.createHash("sha256").update(content).digest("hex");
24627
24662
  }
24628
24663
  function guessMimeType(ext) {
@@ -24642,13 +24677,13 @@ function escapeRegex(s) {
24642
24677
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24643
24678
  }
24644
24679
  function resolveManifestAssetPath(memoryDir, originalPath) {
24645
- if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path26.isAbsolute(originalPath) || path26.win32.isAbsolute(originalPath)) {
24680
+ if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path27.isAbsolute(originalPath) || path27.win32.isAbsolute(originalPath)) {
24646
24681
  return null;
24647
24682
  }
24648
- const memoryRoot = path26.resolve(memoryDir);
24649
- const fullPath = path26.resolve(memoryRoot, originalPath);
24650
- const relative = path26.relative(memoryRoot, fullPath);
24651
- if (relative === "" || relative === ".." || relative.startsWith(`..${path26.sep}`) || path26.isAbsolute(relative)) {
24683
+ const memoryRoot = path27.resolve(memoryDir);
24684
+ const fullPath = path27.resolve(memoryRoot, originalPath);
24685
+ const relative = path27.relative(memoryRoot, fullPath);
24686
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path27.sep}`) || path27.isAbsolute(relative)) {
24652
24687
  return null;
24653
24688
  }
24654
24689
  return fullPath;
@@ -24659,7 +24694,7 @@ function validateBinaryLifecycleConfig(config) {
24659
24694
  }
24660
24695
  }
24661
24696
  function remotePathForAsset(backend, relPath) {
24662
- const normalized = relPath.split(path26.sep).join("/");
24697
+ const normalized = relPath.split(path27.sep).join("/");
24663
24698
  if (backend.type === "filesystem") {
24664
24699
  return `.binary-lifecycle/mirrors/${normalized}`;
24665
24700
  }
@@ -24672,11 +24707,11 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
24672
24707
  let mirrored = 0;
24673
24708
  const errors = [];
24674
24709
  for (const relPath of newPaths) {
24675
- const fullPath = path26.join(memoryDir, relPath);
24710
+ const fullPath = path27.join(memoryDir, relPath);
24676
24711
  try {
24677
- const stat5 = await fsp4.stat(fullPath);
24712
+ const stat5 = await fsp5.stat(fullPath);
24678
24713
  const contentHash = await hashFile2(fullPath);
24679
- const ext = path26.extname(relPath);
24714
+ const ext = path27.extname(relPath);
24680
24715
  const mimeType = guessMimeType(ext);
24681
24716
  const remotePath = remotePathForAsset(backend, relPath);
24682
24717
  let backendLocation = remotePath;
@@ -24827,7 +24862,7 @@ async function stageRedirect(memoryDir, assets, log2, dryRun, readMarkdownFile,
24827
24862
  for (const update of updates) {
24828
24863
  try {
24829
24864
  await withRawEntityPageMutation(memoryDir, update.mdPath, async () => {
24830
- const stateDir = path26.join(memoryDir, "state");
24865
+ const stateDir = path27.join(memoryDir, "state");
24831
24866
  const kind = classifyEntityRefWritePath(memoryDir, update.mdPath);
24832
24867
  if (kind === "memory") {
24833
24868
  const idsAtWrite = historicalIdCache.get(stateDir);
@@ -24920,10 +24955,10 @@ async function countRemainingLocalReferences(memoryDir, asset, assetAbsolute, md
24920
24955
  return { remaining, errors };
24921
24956
  }
24922
24957
  function markdownReferencePattern(asset, assetAbsolute, mdPath) {
24923
- const mdDir = path26.dirname(mdPath);
24958
+ const mdDir = path27.dirname(mdPath);
24924
24959
  const candidates = /* @__PURE__ */ new Set();
24925
24960
  const addCandidate = (candidate) => {
24926
- const normalized = candidate.split(path26.sep).join("/");
24961
+ const normalized = candidate.split(path27.sep).join("/");
24927
24962
  if (normalized.length === 0) return;
24928
24963
  candidates.add(normalized);
24929
24964
  const isParentTraversal = normalized === ".." || normalized.startsWith("../");
@@ -24931,10 +24966,10 @@ function markdownReferencePattern(asset, assetAbsolute, mdPath) {
24931
24966
  candidates.add(`./${normalized}`);
24932
24967
  }
24933
24968
  };
24934
- addCandidate(path26.relative(mdDir, assetAbsolute));
24935
- const originalPath = asset.originalPath.split(path26.sep).join("/");
24936
- const originalAsFileRelative = path26.resolve(mdDir, ...originalPath.split("/"));
24937
- if (path26.resolve(originalAsFileRelative) === path26.resolve(assetAbsolute)) {
24969
+ addCandidate(path27.relative(mdDir, assetAbsolute));
24970
+ const originalPath = asset.originalPath.split(path27.sep).join("/");
24971
+ const originalAsFileRelative = path27.resolve(mdDir, ...originalPath.split("/"));
24972
+ if (path27.resolve(originalAsFileRelative) === path27.resolve(assetAbsolute)) {
24938
24973
  addCandidate(originalPath);
24939
24974
  }
24940
24975
  addCandidate(`/${originalPath}`);
@@ -24998,7 +25033,7 @@ async function stageClean(memoryDir, assets, backend, gracePeriodDays, log2, dry
24998
25033
  continue;
24999
25034
  }
25000
25035
  if (!dryRun) {
25001
- await fsp4.unlink(fullPath);
25036
+ await fsp5.unlink(fullPath);
25002
25037
  asset.status = "cleaned";
25003
25038
  asset.cleanedAt = (/* @__PURE__ */ new Date()).toISOString();
25004
25039
  }
@@ -25025,12 +25060,12 @@ async function findMarkdownFiles(dir) {
25025
25060
  async function walk(current) {
25026
25061
  let entries;
25027
25062
  try {
25028
- entries = await fsp4.readdir(current, { withFileTypes: true });
25063
+ entries = await fsp5.readdir(current, { withFileTypes: true });
25029
25064
  } catch {
25030
25065
  return;
25031
25066
  }
25032
25067
  for (const entry of entries) {
25033
- const full = path26.join(current, entry.name);
25068
+ const full = path27.join(current, entry.name);
25034
25069
  if (entry.isDirectory()) {
25035
25070
  if (entry.name === ".binary-lifecycle") continue;
25036
25071
  await walk(full);
@@ -25072,8 +25107,8 @@ async function runBinaryLifecyclePipeline(memoryDir, config, backend, log2, opts
25072
25107
  manifest.assets,
25073
25108
  log2,
25074
25109
  dryRun,
25075
- opts?.readMarkdownFile ?? ((filePath) => fsp4.readFile(filePath, "utf-8")),
25076
- opts?.writeMarkdownFile ?? ((filePath, content) => fsp4.writeFile(filePath, content, "utf-8"))
25110
+ opts?.readMarkdownFile ?? ((filePath) => fsp5.readFile(filePath, "utf-8")),
25111
+ opts?.writeMarkdownFile ?? ((filePath, content) => fsp5.writeFile(filePath, content, "utf-8"))
25077
25112
  );
25078
25113
  const cleanResult = await stageClean(
25079
25114
  memoryDir,
@@ -25105,7 +25140,7 @@ async function runBinaryLifecyclePipeline(memoryDir, config, backend, log2, opts
25105
25140
 
25106
25141
  // src/projection/index.ts
25107
25142
  import fs from "fs";
25108
- import path27 from "path";
25143
+ import path28 from "path";
25109
25144
  var VALID_PROJECTION_CATEGORIES = new Set(ALL_CATEGORY_KEYS);
25110
25145
  async function generateContextTree(options) {
25111
25146
  const startTime = Date.now();
@@ -25120,8 +25155,8 @@ async function generateContextTree(options) {
25120
25155
  let nodesGenerated = 0;
25121
25156
  let nodesSkipped = 0;
25122
25157
  const categoryCounts = {};
25123
- const resolvedMemoryDir = path27.resolve(memoryDir);
25124
- const resolvedOutputDir = path27.resolve(outputDir);
25158
+ const resolvedMemoryDir = path28.resolve(memoryDir);
25159
+ const resolvedOutputDir = path28.resolve(outputDir);
25125
25160
  const requestedCategories = validateProjectionCategories(filterCategories);
25126
25161
  const realMemoryDir = assertSafeMemoryRoot(resolvedMemoryDir);
25127
25162
  assertNotSymlink(resolvedOutputDir, "context tree outputDir");
@@ -25159,7 +25194,7 @@ async function generateContextTree(options) {
25159
25194
  }
25160
25195
  }
25161
25196
  if (includeEntities) {
25162
- const entitiesDir = path27.join(memoryDir, "entities");
25197
+ const entitiesDir = path28.join(memoryDir, "entities");
25163
25198
  if (fs.existsSync(entitiesDir)) {
25164
25199
  assertSafeInputRoot(realMemoryDir, entitiesDir, "entities root");
25165
25200
  categoryCounts["entity"] = 0;
@@ -25171,7 +25206,7 @@ async function generateContextTree(options) {
25171
25206
  continue;
25172
25207
  }
25173
25208
  const content = fs.readFileSync(filePath, "utf8");
25174
- const fileName = path27.basename(filePath, ".md");
25209
+ const fileName = path28.basename(filePath, ".md");
25175
25210
  const node = projectEntityNode(fileName, content);
25176
25211
  const outputPath = resolveContainedOutputPath(realOutputDir, "entities", `${fileName}.md`);
25177
25212
  writeProjectedContent(realOutputDir, outputPath, node.content);
@@ -25183,7 +25218,7 @@ async function generateContextTree(options) {
25183
25218
  }
25184
25219
  const shouldIncludeQuestions = includeQuestions && (requestedCategories === void 0 || requestedCategories.includes("question"));
25185
25220
  if (shouldIncludeQuestions) {
25186
- const questionsDir = path27.join(memoryDir, "questions");
25221
+ const questionsDir = path28.join(memoryDir, "questions");
25187
25222
  if (fs.existsSync(questionsDir)) {
25188
25223
  assertSafeInputRoot(realMemoryDir, questionsDir, "questions root");
25189
25224
  categoryCounts["question"] = 0;
@@ -25224,8 +25259,8 @@ async function generateContextTree(options) {
25224
25259
  };
25225
25260
  }
25226
25261
  function isPathInside(root, candidate) {
25227
- const relative = path27.relative(root, candidate);
25228
- return relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative);
25262
+ const relative = path28.relative(root, candidate);
25263
+ return relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative);
25229
25264
  }
25230
25265
  function assertNotSymlink(targetPath, label) {
25231
25266
  try {
@@ -25262,12 +25297,12 @@ function assertSafeInputRoot(realMemoryDir, targetPath, label) {
25262
25297
  }
25263
25298
  function assertSafeOutputTarget(realOutputDir, outputPath) {
25264
25299
  let current = realOutputDir;
25265
- const relative = path27.relative(realOutputDir, outputPath);
25266
- if (relative.startsWith("..") || path27.isAbsolute(relative)) {
25300
+ const relative = path28.relative(realOutputDir, outputPath);
25301
+ if (relative.startsWith("..") || path28.isAbsolute(relative)) {
25267
25302
  throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
25268
25303
  }
25269
- for (const segment of relative.split(path27.sep).filter(Boolean)) {
25270
- current = path27.join(current, segment);
25304
+ for (const segment of relative.split(path28.sep).filter(Boolean)) {
25305
+ current = path28.join(current, segment);
25271
25306
  try {
25272
25307
  const stat5 = fs.lstatSync(current);
25273
25308
  if (stat5.isSymbolicLink()) {
@@ -25297,17 +25332,17 @@ function validateProjectionCategories(categories) {
25297
25332
  return validated;
25298
25333
  }
25299
25334
  function resolveContainedOutputPath(outputRoot, ...segments) {
25300
- const resolved = path27.resolve(outputRoot, ...segments);
25301
- const relative = path27.relative(outputRoot, resolved);
25302
- if (relative === "" || !relative.startsWith("..") && !path27.isAbsolute(relative)) {
25335
+ const resolved = path28.resolve(outputRoot, ...segments);
25336
+ const relative = path28.relative(outputRoot, resolved);
25337
+ if (relative === "" || !relative.startsWith("..") && !path28.isAbsolute(relative)) {
25303
25338
  return resolved;
25304
25339
  }
25305
25340
  throw new Error(`context tree output path escapes outputDir: ${segments.join("/")}`);
25306
25341
  }
25307
25342
  function writeProjectedContent(realOutputDir, outputPath, generatedContent) {
25308
25343
  assertSafeOutputTarget(realOutputDir, outputPath);
25309
- fs.mkdirSync(path27.dirname(outputPath), { recursive: true });
25310
- const realParent = fs.realpathSync(path27.dirname(outputPath));
25344
+ fs.mkdirSync(path28.dirname(outputPath), { recursive: true });
25345
+ const realParent = fs.realpathSync(path28.dirname(outputPath));
25311
25346
  if (!isPathInside(realOutputDir, realParent)) {
25312
25347
  throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
25313
25348
  }
@@ -25346,7 +25381,7 @@ function walkR(dir, realMemoryDir) {
25346
25381
  const results = [];
25347
25382
  function walk(directory) {
25348
25383
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
25349
- const fullPath = path27.join(directory, entry.name);
25384
+ const fullPath = path28.join(directory, entry.name);
25350
25385
  if (entry.isSymbolicLink()) {
25351
25386
  throw new Error(`context tree input path contains symlink: ${fullPath}`);
25352
25387
  }
@@ -25399,13 +25434,13 @@ function extractBody(content) {
25399
25434
  }
25400
25435
  function projectNode(filePath, category, fm, rawContent) {
25401
25436
  const body = extractBody(rawContent);
25402
- const fileName = path27.basename(filePath, ".md");
25403
- const dateDir = path27.basename(path27.dirname(filePath));
25437
+ const fileName = path28.basename(filePath, ".md");
25438
+ const dateDir = path28.basename(path28.dirname(filePath));
25404
25439
  let relPath;
25405
25440
  if (/^\d{4}-\d{2}-\d{2}$/.test(dateDir)) {
25406
- relPath = path27.join(category, dateDir, `${fileName}.md`);
25441
+ relPath = path28.join(category, dateDir, `${fileName}.md`);
25407
25442
  } else {
25408
- relPath = path27.join(category, `${fileName}.md`);
25443
+ relPath = path28.join(category, `${fileName}.md`);
25409
25444
  }
25410
25445
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
25411
25446
  const md = `# ${fm.id}
@@ -25451,7 +25486,7 @@ function projectEntityNode(fileName, content) {
25451
25486
  ${content}
25452
25487
  `;
25453
25488
  return {
25454
- path: path27.join("entities", `${fileName}.md`),
25489
+ path: path28.join("entities", `${fileName}.md`),
25455
25490
  category: "entity",
25456
25491
  title: fileName,
25457
25492
  content: md,
@@ -25504,7 +25539,7 @@ function generateIndex(categoryCounts, outputDir) {
25504
25539
 
25505
25540
  // src/onboarding/index.ts
25506
25541
  import fs2 from "fs";
25507
- import path28 from "path";
25542
+ import path29 from "path";
25508
25543
  var LANGUAGE_RULES = [
25509
25544
  {
25510
25545
  language: "TypeScript",
@@ -25606,7 +25641,7 @@ function onboard(options) {
25606
25641
  maxDepth = 6,
25607
25642
  excludeDirs = []
25608
25643
  } = options;
25609
- const directory = path28.resolve(options.directory ?? process.cwd());
25644
+ const directory = path29.resolve(options.directory ?? process.cwd());
25610
25645
  let rootStat;
25611
25646
  try {
25612
25647
  rootStat = fs2.statSync(directory);
@@ -25648,7 +25683,7 @@ function walkDir(root, exclude, maxDepth) {
25648
25683
  }
25649
25684
  for (const entry of entries) {
25650
25685
  if (exclude.has(entry.name)) continue;
25651
- const fullPath = path28.join(dir, entry.name);
25686
+ const fullPath = path29.join(dir, entry.name);
25652
25687
  if (entry.isDirectory()) {
25653
25688
  walk(fullPath, depth + 1);
25654
25689
  } else if (entry.isFile()) {
@@ -25663,11 +25698,11 @@ function detectLanguages(files, root) {
25663
25698
  const results = [];
25664
25699
  const extCounts = /* @__PURE__ */ new Map();
25665
25700
  for (const f of files) {
25666
- const ext = path28.extname(f).toLowerCase();
25701
+ const ext = path29.extname(f).toLowerCase();
25667
25702
  if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
25668
25703
  }
25669
25704
  const rootFiles = new Set(
25670
- files.filter((f) => path28.dirname(f) === root).map((f) => path28.basename(f))
25705
+ files.filter((f) => path29.dirname(f) === root).map((f) => path29.basename(f))
25671
25706
  );
25672
25707
  for (const rule of LANGUAGE_RULES) {
25673
25708
  const evidence = [];
@@ -25711,7 +25746,7 @@ function detectLanguages(files, root) {
25711
25746
  }
25712
25747
  function detectShape(files, root) {
25713
25748
  const rootFiles = new Set(
25714
- files.filter((f) => path28.dirname(f) === root).map((f) => path28.basename(f))
25749
+ files.filter((f) => path29.dirname(f) === root).map((f) => path29.basename(f))
25715
25750
  );
25716
25751
  const rootDirs = /* @__PURE__ */ new Set();
25717
25752
  try {
@@ -25722,7 +25757,7 @@ function detectShape(files, root) {
25722
25757
  }
25723
25758
  const evidence = [];
25724
25759
  if (rootFiles.has("package.json")) {
25725
- const pkg = readJsonSafe(path28.join(root, "package.json"));
25760
+ const pkg = readJsonSafe(path29.join(root, "package.json"));
25726
25761
  if (pkg?.workspaces) {
25727
25762
  evidence.push("package.json has workspaces");
25728
25763
  return { shape: "monorepo", evidence };
@@ -25736,13 +25771,13 @@ function detectShape(files, root) {
25736
25771
  evidence.push("workspace manifest found");
25737
25772
  return { shape: "workspace", evidence };
25738
25773
  }
25739
- const cargoToml = readTomlWorkspace(path28.join(root, "Cargo.toml"));
25774
+ const cargoToml = readTomlWorkspace(path29.join(root, "Cargo.toml"));
25740
25775
  if (cargoToml) {
25741
25776
  evidence.push("Cargo.toml has workspace");
25742
25777
  return { shape: "workspace", evidence };
25743
25778
  }
25744
25779
  if (rootFiles.has("package.json")) {
25745
- const pkg = readJsonSafe(path28.join(root, "package.json"));
25780
+ const pkg = readJsonSafe(path29.join(root, "package.json"));
25746
25781
  if (pkg?.exports || pkg?.main) {
25747
25782
  if (pkg?.bin) {
25748
25783
  evidence.push("package.json has bin");
@@ -25776,8 +25811,8 @@ function discoverDocs(files, root) {
25776
25811
  { pattern: /^\.editorconfig$/i, kind: "config" }
25777
25812
  ];
25778
25813
  for (const filePath of files) {
25779
- const basename = path28.basename(filePath).toLowerCase();
25780
- const relPath = path28.relative(root, filePath);
25814
+ const basename = path29.basename(filePath).toLowerCase();
25815
+ const relPath = path29.relative(root, filePath);
25781
25816
  let kind;
25782
25817
  for (const { pattern, kind: k } of docPatterns) {
25783
25818
  if (pattern.test(basename)) {
@@ -25789,7 +25824,7 @@ function discoverDocs(files, root) {
25789
25824
  kind = "docs";
25790
25825
  }
25791
25826
  if (!kind && (basename.endsWith(".md") || basename.endsWith(".mdx"))) {
25792
- if (path28.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
25827
+ if (path29.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
25793
25828
  kind = "docs";
25794
25829
  }
25795
25830
  }
@@ -25810,7 +25845,7 @@ function discoverDocs(files, root) {
25810
25845
  return docs;
25811
25846
  }
25812
25847
  function isUnderDocsDir(relPath) {
25813
- const parts = relPath.split(path28.sep);
25848
+ const parts = relPath.split(path29.sep);
25814
25849
  return parts[0] === "docs" || parts[0] === "doc" || parts[0] === "documentation";
25815
25850
  }
25816
25851
  function buildPlan(languages, shape, docs, _root) {
@@ -25854,7 +25889,7 @@ function readTomlWorkspace(filePath) {
25854
25889
 
25855
25890
  // src/curation/index.ts
25856
25891
  import fs3 from "fs";
25857
- import path29 from "path";
25892
+ import path30 from "path";
25858
25893
  import crypto4 from "crypto";
25859
25894
  async function curate(options) {
25860
25895
  const startTime = Date.now();
@@ -25945,12 +25980,12 @@ function resolveTargets(targetPath) {
25945
25980
  const extensions = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
25946
25981
  function walk(dir) {
25947
25982
  for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
25948
- const fullPath = path29.join(dir, entry.name);
25983
+ const fullPath = path30.join(dir, entry.name);
25949
25984
  if (entry.isDirectory()) {
25950
25985
  if (entry.name !== "node_modules" && entry.name !== ".git") {
25951
25986
  walk(fullPath);
25952
25987
  }
25953
- } else if (extensions.has(path29.extname(entry.name).toLowerCase())) {
25988
+ } else if (extensions.has(path30.extname(entry.name).toLowerCase())) {
25954
25989
  results.push(fullPath);
25955
25990
  }
25956
25991
  }
@@ -25959,12 +25994,12 @@ function resolveTargets(targetPath) {
25959
25994
  return results;
25960
25995
  }
25961
25996
  function resolveProvenanceRoot(targetPath) {
25962
- const resolvedTarget = path29.resolve(targetPath);
25997
+ const resolvedTarget = path30.resolve(targetPath);
25963
25998
  const stat5 = fs3.statSync(resolvedTarget);
25964
- return stat5.isFile() ? path29.dirname(resolvedTarget) : resolvedTarget;
25999
+ return stat5.isFile() ? path30.dirname(resolvedTarget) : resolvedTarget;
25965
26000
  }
25966
26001
  function extractStatements(content, filePath, projectRoot, source, sourceFileHash, categoryOverride, confidence, entityRef, tags) {
25967
- const relativePath = path29.relative(projectRoot, path29.resolve(filePath)) || path29.basename(filePath);
26002
+ const relativePath = path30.relative(projectRoot, path30.resolve(filePath)) || path30.basename(filePath);
25968
26003
  const statements = [];
25969
26004
  const now = (/* @__PURE__ */ new Date()).toISOString();
25970
26005
  const paragraphs = content.split(/\n{2,}/).map((p) => p.trim()).filter((p) => p.length > 20 && p.length < 2e3);
@@ -26058,7 +26093,7 @@ function loadExistingMemories(memoryDir) {
26058
26093
  if (!fs3.existsSync(memoryDir)) return result;
26059
26094
  const dirs = ALL_CATEGORY_DIRS;
26060
26095
  for (const dir of dirs) {
26061
- const fullDir = path29.join(memoryDir, dir);
26096
+ const fullDir = path30.join(memoryDir, dir);
26062
26097
  if (!fs3.existsSync(fullDir)) continue;
26063
26098
  walkFiles(fullDir, (filePath) => {
26064
26099
  const content = readFileSafe(filePath);
@@ -26081,11 +26116,11 @@ function writeStatement(stmt, memoryDir) {
26081
26116
  const now = /* @__PURE__ */ new Date();
26082
26117
  const dateDir = now.toISOString().split("T")[0];
26083
26118
  const categoryDir = getCategoryDir(memoryDir, stmt.category);
26084
- const dir = path29.join(categoryDir, dateDir);
26119
+ const dir = path30.join(categoryDir, dateDir);
26085
26120
  fs3.mkdirSync(dir, { recursive: true });
26086
26121
  const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;
26087
- const filePath = path29.join(dir, fileName);
26088
- const historicalIds = historicalIdCache2.get(path29.join(memoryDir, "state"));
26122
+ const filePath = path30.join(dir, fileName);
26123
+ const historicalIds = historicalIdCache2.get(path30.join(memoryDir, "state"));
26089
26124
  const rawBody = `${[
26090
26125
  "---",
26091
26126
  `id: ${stmt.id}`,
@@ -26108,7 +26143,7 @@ ${stmt.content}
26108
26143
  fs3.writeFileSync(filePath, body);
26109
26144
  try {
26110
26145
  repairContentAfterJournalMoveSync({
26111
- stateDir: path29.join(memoryDir, "state"),
26146
+ stateDir: path30.join(memoryDir, "state"),
26112
26147
  cache: historicalIdCache2,
26113
26148
  idsAtWrite: historicalIds,
26114
26149
  rawContent: rawBody,
@@ -26169,7 +26204,7 @@ function extractBody2(content) {
26169
26204
  }
26170
26205
  function walkFiles(dir, callback) {
26171
26206
  for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
26172
- const fullPath = path29.join(dir, entry.name);
26207
+ const fullPath = path30.join(dir, entry.name);
26173
26208
  if (entry.isDirectory()) {
26174
26209
  walkFiles(fullPath, callback);
26175
26210
  } else if (entry.name.endsWith(".md")) {
@@ -26178,305 +26213,10 @@ function walkFiles(dir, callback) {
26178
26213
  }
26179
26214
  }
26180
26215
 
26181
- // src/dedup/index.ts
26182
- import crypto5 from "crypto";
26183
- import fs4 from "fs";
26184
- import path30 from "path";
26185
- var DEFAULT_DEDUP_THRESHOLD = 0.85;
26186
- var DEFAULT_MAX_LOAD = 1e4;
26187
- function normalizeThreshold(value, defaultValue) {
26188
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1 ? value : defaultValue;
26189
- }
26190
- function normalizeMaxLoad(value, defaultValue) {
26191
- return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : defaultValue;
26192
- }
26193
- function findDuplicates(options) {
26194
- const startTime = Date.now();
26195
- const { memoryDir } = options;
26196
- const threshold = normalizeThreshold(options.threshold, DEFAULT_DEDUP_THRESHOLD);
26197
- const maxLoad = normalizeMaxLoad(options.maxLoad, DEFAULT_MAX_LOAD);
26198
- const memories = loadMemories(memoryDir, options.categories, maxLoad);
26199
- const duplicates = [];
26200
- for (let i = 0; i < memories.length; i++) {
26201
- for (let j = i + 1; j < memories.length; j++) {
26202
- const sim = computeSimilarity(memories[i].content, memories[j].content);
26203
- if (sim >= threshold) {
26204
- duplicates.push({
26205
- left: memories[i],
26206
- right: memories[j],
26207
- similarity: sim,
26208
- action: sim >= 0.98 ? "merge" : sim >= 0.9 ? "keep_right" : "keep_left"
26209
- });
26210
- }
26211
- }
26212
- }
26213
- return {
26214
- scanned: memories.length,
26215
- duplicates,
26216
- durationMs: Date.now() - startTime
26217
- };
26218
- }
26219
- function findContradictions(options) {
26220
- const startTime = Date.now();
26221
- const { memoryDir } = options;
26222
- const maxLoad = normalizeMaxLoad(options.maxLoad, DEFAULT_MAX_LOAD);
26223
- const memories = loadMemories(memoryDir, options.categories, maxLoad);
26224
- const contradictions = [];
26225
- for (let i = 0; i < memories.length; i++) {
26226
- for (let j = i + 1; j < memories.length; j++) {
26227
- const contra = detectContradiction(memories[i], memories[j]);
26228
- if (contra) {
26229
- contradictions.push(contra);
26230
- }
26231
- }
26232
- }
26233
- return {
26234
- scanned: memories.length,
26235
- contradictions,
26236
- durationMs: Date.now() - startTime
26237
- };
26238
- }
26239
- function computeSimilarity(a, b) {
26240
- const normA = normalize(a);
26241
- const normB = normalize(b);
26242
- const punctuationBoundariesA = embeddedPunctuationSignature(a);
26243
- const punctuationBoundariesB = embeddedPunctuationSignature(b);
26244
- if (punctuationBoundariesA !== punctuationBoundariesB) return 0;
26245
- if (normA === normB) return 1;
26246
- if (hashContent2(normA) === hashContent2(normB)) return 0.99;
26247
- if (normA.length > 50 && normB.length > 50) {
26248
- if (normA.includes(normB.slice(0, 40)) || normB.includes(normA.slice(0, 40))) {
26249
- return 0.9;
26250
- }
26251
- }
26252
- const wordsA = tokenizeForSimilarity(normA);
26253
- const wordsB = tokenizeForSimilarity(normB);
26254
- const intersection = new Set([...wordsA].filter((w) => wordsB.has(w)));
26255
- const union = /* @__PURE__ */ new Set([...wordsA, ...wordsB]);
26256
- if (union.size === 0) return 0;
26257
- return intersection.size / union.size;
26258
- }
26259
- function embeddedPunctuationSignature(text) {
26260
- const chars = [...text.normalize("NFC")];
26261
- const significant = /[\p{L}\p{M}\p{N}]/u;
26262
- const ignored = /[\s']/u;
26263
- const boundaries = [];
26264
- let position = 0;
26265
- for (let index = 0; index < chars.length; ) {
26266
- const char = chars[index];
26267
- if (char === void 0) break;
26268
- if (significant.test(char)) {
26269
- position++;
26270
- index++;
26271
- continue;
26272
- }
26273
- if (ignored.test(char)) {
26274
- index++;
26275
- continue;
26276
- }
26277
- const start = index;
26278
- while (index < chars.length) {
26279
- const next = chars[index];
26280
- if (next === void 0 || significant.test(next) || ignored.test(next)) break;
26281
- index++;
26282
- }
26283
- const before = chars[start - 1];
26284
- const after = chars[index];
26285
- if (before && after && significant.test(before) && significant.test(after)) {
26286
- boundaries.push(`${position}:${chars.slice(start, index).join("")}`);
26287
- }
26288
- }
26289
- return boundaries.join("\0");
26290
- }
26291
- function normalize(text) {
26292
- 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();
26293
- }
26294
- function tokenizeForSimilarity(text) {
26295
- if (/\p{Script=Hangul}/u.test(text)) {
26296
- const tokens = normalizeRecallTokenSet(text);
26297
- for (const segment of text.match(/\p{Script=Hangul}+/gu) ?? []) {
26298
- const chars = [...segment];
26299
- for (const size of [1, 2, 3, 4]) {
26300
- for (let index = 0; index <= chars.length - size; index += 1) {
26301
- tokens.add(chars.slice(index, index + size).join(""));
26302
- }
26303
- }
26304
- }
26305
- return tokens;
26306
- }
26307
- if (/[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}]/u.test(text)) {
26308
- return normalizeRecallTokenSet(text);
26309
- }
26310
- return text.length === 0 ? /* @__PURE__ */ new Set() : new Set(text.split(/\s+/));
26311
- }
26312
- var NEGATION_WORDS = /* @__PURE__ */ new Set([
26313
- "not",
26314
- "don't",
26315
- "doesn't",
26316
- "isn't",
26317
- "aren't",
26318
- "won't",
26319
- "can't",
26320
- "cannot",
26321
- "never",
26322
- "no",
26323
- "none",
26324
- "neither",
26325
- "nor",
26326
- "nothing",
26327
- "nowhere"
26328
- ]);
26329
- function detectContradiction(a, b) {
26330
- const normA = normalize(a.content);
26331
- const normB = normalize(b.content);
26332
- const aHasNegation = containsNegation(normA);
26333
- const bHasNegation = containsNegation(normB);
26334
- if (aHasNegation === bHasNegation) return null;
26335
- const strippedA = stripNegation(normA);
26336
- const strippedB = stripNegation(normB);
26337
- const sim = computeSimilarity(strippedA, strippedB);
26338
- if (sim < 0.7) return null;
26339
- const oppQuantifiers = [
26340
- ["always", "never"],
26341
- ["all", "none"],
26342
- ["every", "no"],
26343
- ["must", "must not"],
26344
- ["should", "should not"],
26345
- ["can", "cannot"]
26346
- ];
26347
- for (const [pos, neg] of oppQuantifiers) {
26348
- if (a.content.toLowerCase().includes(pos) && b.content.toLowerCase().includes(neg) || a.content.toLowerCase().includes(neg) && b.content.toLowerCase().includes(pos)) {
26349
- return {
26350
- left: a,
26351
- right: b,
26352
- severity: "high",
26353
- reason: `Opposite quantifiers: "${pos}" vs "${neg}"`
26354
- };
26355
- }
26356
- }
26357
- return {
26358
- left: a,
26359
- right: b,
26360
- severity: sim >= 0.85 ? "high" : "medium",
26361
- reason: "Negated version of similar content"
26362
- };
26363
- }
26364
- function containsNegation(text) {
26365
- const words = text.split(/\s+/);
26366
- return words.some((w) => NEGATION_WORDS.has(w));
26367
- }
26368
- function escapeRegExp(value) {
26369
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
26370
- }
26371
- var stripNegationPattern = null;
26372
- var stripNegationPatternSource = null;
26373
- function stripNegationRegex() {
26374
- const alternation = [...NEGATION_WORDS].sort((a, b) => b.length - a.length).map(escapeRegExp).join("|");
26375
- if (stripNegationPattern === null || stripNegationPatternSource !== alternation) {
26376
- stripNegationPattern = new RegExp(`\\b(?:${alternation})\\b`, "gi");
26377
- stripNegationPatternSource = alternation;
26378
- }
26379
- return stripNegationPattern;
26380
- }
26381
- function stripNegation(text) {
26382
- return text.replace(stripNegationRegex(), "").replace(/\s+/g, " ").trim();
26383
- }
26384
- function loadMemories(memoryDir, categories, maxLoad = 1e4) {
26385
- const result = [];
26386
- const allCategories = categories ?? ALL_CATEGORY_DIRS;
26387
- if (!fs4.existsSync(memoryDir)) return result;
26388
- const memoryRootReal = fs4.realpathSync(memoryDir);
26389
- for (const category of allCategories) {
26390
- if (result.length >= maxLoad) break;
26391
- const dir = path30.join(memoryDir, category);
26392
- if (!fs4.existsSync(dir)) continue;
26393
- const categoryStat = fs4.lstatSync(dir);
26394
- if (categoryStat.isSymbolicLink()) {
26395
- throw new Error(`Refusing to scan symlinked memory category directory: ${dir}`);
26396
- }
26397
- if (!categoryStat.isDirectory()) continue;
26398
- const categoryRootReal = fs4.realpathSync(dir);
26399
- assertPathInsideRoot2(memoryRootReal, categoryRootReal, dir);
26400
- walkMdFiles(dir, memoryRootReal, categoryRootReal, (filePath) => {
26401
- if (result.length >= maxLoad) return;
26402
- const content = readFileSafe2(filePath, memoryRootReal, categoryRootReal);
26403
- if (!content) return;
26404
- const fm = parseFrontmatter3(content);
26405
- const body = extractBody3(content);
26406
- if (!fm?.id || !body) return;
26407
- result.push({
26408
- id: fm.id,
26409
- content: body,
26410
- category: fm.category ?? category.slice(0, -1),
26411
- filePath
26412
- });
26413
- });
26414
- }
26415
- return result;
26416
- }
26417
- function hashContent2(content) {
26418
- return crypto5.createHash("sha256").update(content).digest("hex").slice(0, 16);
26419
- }
26420
- function readFileSafe2(filePath, memoryRootReal, categoryRootReal) {
26421
- try {
26422
- const fileStat = fs4.lstatSync(filePath);
26423
- if (fileStat.isSymbolicLink()) {
26424
- throw new Error(`Refusing to read symlinked memory file: ${filePath}`);
26425
- }
26426
- const fileReal = fs4.realpathSync(filePath);
26427
- assertPathInsideRoot2(memoryRootReal, fileReal, filePath);
26428
- assertPathInsideRoot2(categoryRootReal, fileReal, filePath);
26429
- return fs4.readFileSync(filePath, "utf8");
26430
- } catch {
26431
- return null;
26432
- }
26433
- }
26434
- function parseFrontmatter3(content) {
26435
- const match = content.match(/^---\n([\s\S]*?)\n---/);
26436
- if (!match) return null;
26437
- const fm = {};
26438
- for (const line of match[1].split("\n")) {
26439
- const colonIdx = line.indexOf(":");
26440
- if (colonIdx === -1) continue;
26441
- const key = line.slice(0, colonIdx).trim();
26442
- const value = line.slice(colonIdx + 1).trim();
26443
- fm[key] = value;
26444
- }
26445
- return fm;
26446
- }
26447
- function extractBody3(content) {
26448
- const match = content.match(/^---\n[\s\S]*?\n---\n([\s\S]*)/);
26449
- return match ? match[1].trim() : content.trim();
26450
- }
26451
- function assertPathInsideRoot2(rootReal, targetReal, sourcePath) {
26452
- const rel = path30.relative(rootReal, targetReal);
26453
- if (rel === "" || !rel.startsWith("..") && !path30.isAbsolute(rel)) {
26454
- return;
26455
- }
26456
- throw new Error(`Refusing to scan memory path outside root: ${sourcePath}`);
26457
- }
26458
- function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
26459
- for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
26460
- const fullPath = path30.join(dir, entry.name);
26461
- const entryStat = fs4.lstatSync(fullPath);
26462
- if (entryStat.isSymbolicLink()) {
26463
- throw new Error(`Refusing to scan symlinked memory path: ${fullPath}`);
26464
- }
26465
- const entryReal = fs4.realpathSync(fullPath);
26466
- assertPathInsideRoot2(memoryRootReal, entryReal, fullPath);
26467
- assertPathInsideRoot2(categoryRootReal, entryReal, fullPath);
26468
- if (entryStat.isDirectory()) {
26469
- walkMdFiles(fullPath, memoryRootReal, categoryRootReal, callback);
26470
- } else if (entry.name.endsWith(".md")) {
26471
- callback(fullPath);
26472
- }
26473
- }
26474
- }
26475
-
26476
26216
  // src/sync/index.ts
26477
- import fs5 from "fs";
26217
+ import fs4 from "fs";
26478
26218
  import path31 from "path";
26479
- import crypto6 from "crypto";
26219
+ import crypto5 from "crypto";
26480
26220
  var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
26481
26221
  var DEFAULT_EXCLUDE2 = /* @__PURE__ */ new Set([
26482
26222
  "node_modules",
@@ -26513,8 +26253,8 @@ function syncChanges(options) {
26513
26253
  for (const [relPath, hash] of Object.entries(currentFiles)) {
26514
26254
  newState.fileHashes[relPath] = hash;
26515
26255
  }
26516
- fs5.mkdirSync(path31.dirname(stateFilePath), { recursive: true });
26517
- 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));
26518
26258
  }
26519
26259
  return {
26520
26260
  scanned: Object.keys(currentFiles).length,
@@ -26568,7 +26308,7 @@ function scanFiles(root, extensions, exclude) {
26568
26308
  function walk(dir, isRoot = false) {
26569
26309
  let entries;
26570
26310
  try {
26571
- entries = fs5.readdirSync(dir, { withFileTypes: true });
26311
+ entries = fs4.readdirSync(dir, { withFileTypes: true });
26572
26312
  } catch (err) {
26573
26313
  if (isRoot) {
26574
26314
  throw new Error(
@@ -26587,8 +26327,8 @@ function scanFiles(root, extensions, exclude) {
26587
26327
  if (!extensions.has(ext)) continue;
26588
26328
  const relPath = path31.relative(root, fullPath);
26589
26329
  try {
26590
- const content = fs5.readFileSync(fullPath, "utf8");
26591
- result[relPath] = hashContent3(content);
26330
+ const content = fs4.readFileSync(fullPath, "utf8");
26331
+ result[relPath] = hashContent2(content);
26592
26332
  } catch {
26593
26333
  }
26594
26334
  }
@@ -26604,7 +26344,7 @@ function computeDiff(current, previous, sourceDir) {
26604
26344
  if (!(relPath in previous)) {
26605
26345
  let size = 0;
26606
26346
  try {
26607
- size = fs5.statSync(fullPath).size;
26347
+ size = fs4.statSync(fullPath).size;
26608
26348
  } catch {
26609
26349
  }
26610
26350
  changes.push({
@@ -26617,7 +26357,7 @@ function computeDiff(current, previous, sourceDir) {
26617
26357
  } else if (previous[relPath] !== hash) {
26618
26358
  let size = 0;
26619
26359
  try {
26620
- size = fs5.statSync(fullPath).size;
26360
+ size = fs4.statSync(fullPath).size;
26621
26361
  } catch {
26622
26362
  }
26623
26363
  changes.push({
@@ -26645,7 +26385,7 @@ function computeDiff(current, previous, sourceDir) {
26645
26385
  }
26646
26386
  function loadState(stateFilePath) {
26647
26387
  try {
26648
- const raw = fs5.readFileSync(stateFilePath, "utf8");
26388
+ const raw = fs4.readFileSync(stateFilePath, "utf8");
26649
26389
  return JSON.parse(raw);
26650
26390
  } catch {
26651
26391
  return {
@@ -26655,14 +26395,14 @@ function loadState(stateFilePath) {
26655
26395
  };
26656
26396
  }
26657
26397
  }
26658
- function hashContent3(content) {
26659
- 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);
26660
26400
  }
26661
26401
 
26662
26402
  // src/spaces/index.ts
26663
26403
  import { spawnSync } from "child_process";
26664
- import crypto7 from "crypto";
26665
- import fs6 from "fs";
26404
+ import crypto6 from "crypto";
26405
+ import fs5 from "fs";
26666
26406
  import path32 from "path";
26667
26407
  var historicalIdCache3 = new HistoricalEntityCanonicalIdCache();
26668
26408
  var MANIFEST_VERSION = 1;
@@ -26680,7 +26420,7 @@ function getManifestPath(baseDir) {
26680
26420
  return path32.join(getSpacesDir(baseDir), "manifest.json");
26681
26421
  }
26682
26422
  function loadManifest(baseDir, memoryDirOverride) {
26683
- if (fs6.existsSync(getManifestPath(baseDir))) {
26423
+ if (fs5.existsSync(getManifestPath(baseDir))) {
26684
26424
  try {
26685
26425
  return readManifestUnlocked(baseDir, memoryDirOverride, { bootstrapIfMissing: false });
26686
26426
  } catch (error) {
@@ -26706,7 +26446,7 @@ function updateManifest(baseDir, updater, memoryDirOverride) {
26706
26446
  }
26707
26447
  function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
26708
26448
  const manifestPath2 = getManifestPath(baseDir);
26709
- if (!fs6.existsSync(manifestPath2)) {
26449
+ if (!fs5.existsSync(manifestPath2)) {
26710
26450
  if (options.bootstrapIfMissing === false) {
26711
26451
  const error = new Error(`Spaces manifest not found: ${manifestPath2}`);
26712
26452
  error.code = "ENOENT";
@@ -26719,21 +26459,21 @@ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
26719
26459
  version: MANIFEST_VERSION
26720
26460
  };
26721
26461
  }
26722
- const raw = JSON.parse(fs6.readFileSync(manifestPath2, "utf8"));
26462
+ const raw = JSON.parse(fs5.readFileSync(manifestPath2, "utf8"));
26723
26463
  return raw;
26724
26464
  }
26725
26465
  function saveManifestUnlocked(manifest, baseDir) {
26726
26466
  const manifestPath2 = getManifestPath(baseDir);
26727
26467
  const manifestDir2 = path32.dirname(manifestPath2);
26728
- fs6.mkdirSync(manifestDir2, { recursive: true });
26729
- const tempPath = path32.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
26468
+ fs5.mkdirSync(manifestDir2, { recursive: true });
26469
+ const tempPath = path32.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto6.randomUUID()}.tmp`);
26730
26470
  try {
26731
- fs6.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
26471
+ fs5.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
26732
26472
  `, { flag: "wx" });
26733
- fs6.renameSync(tempPath, manifestPath2);
26473
+ fs5.renameSync(tempPath, manifestPath2);
26734
26474
  } catch (error) {
26735
26475
  try {
26736
- fs6.rmSync(tempPath, { force: true });
26476
+ fs5.rmSync(tempPath, { force: true });
26737
26477
  } catch {
26738
26478
  }
26739
26479
  throw error;
@@ -26741,7 +26481,7 @@ function saveManifestUnlocked(manifest, baseDir) {
26741
26481
  }
26742
26482
  function withManifestLock(baseDir, operation) {
26743
26483
  const lockDir = `${getManifestPath(baseDir)}.lock`;
26744
- fs6.mkdirSync(path32.dirname(lockDir), { recursive: true });
26484
+ fs5.mkdirSync(path32.dirname(lockDir), { recursive: true });
26745
26485
  const lockOwner = acquireManifestLock(lockDir);
26746
26486
  try {
26747
26487
  return operation();
@@ -26754,10 +26494,10 @@ function acquireManifestLock(lockDir) {
26754
26494
  const owner = createManifestLockOwner();
26755
26495
  const reclaimDir = getManifestLockReclaimDir(lockDir);
26756
26496
  while (true) {
26757
- if (fs6.existsSync(reclaimDir)) {
26497
+ if (fs5.existsSync(reclaimDir)) {
26758
26498
  removeStaleManifestReclaimLock(reclaimDir);
26759
26499
  }
26760
- if (fs6.existsSync(reclaimDir)) {
26500
+ if (fs5.existsSync(reclaimDir)) {
26761
26501
  if (Date.now() >= deadline) {
26762
26502
  throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
26763
26503
  }
@@ -26765,15 +26505,15 @@ function acquireManifestLock(lockDir) {
26765
26505
  continue;
26766
26506
  }
26767
26507
  try {
26768
- fs6.mkdirSync(lockDir, { recursive: false });
26508
+ fs5.mkdirSync(lockDir, { recursive: false });
26769
26509
  try {
26770
- fs6.writeFileSync(path32.join(lockDir, "owner"), `${owner}
26510
+ fs5.writeFileSync(path32.join(lockDir, "owner"), `${owner}
26771
26511
  `, { flag: "wx" });
26772
26512
  } catch (error) {
26773
- fs6.rmSync(lockDir, { recursive: true, force: true });
26513
+ fs5.rmSync(lockDir, { recursive: true, force: true });
26774
26514
  throw error;
26775
26515
  }
26776
- if (fs6.existsSync(reclaimDir)) {
26516
+ if (fs5.existsSync(reclaimDir)) {
26777
26517
  releaseManifestLock(lockDir, owner);
26778
26518
  if (Date.now() >= deadline) {
26779
26519
  throw new Error(`Timed out waiting for spaces manifest reclaim lock: ${reclaimDir}`);
@@ -26798,8 +26538,8 @@ function acquireManifestLock(lockDir) {
26798
26538
  function releaseManifestLock(lockDir, owner) {
26799
26539
  try {
26800
26540
  const ownerPath = path32.join(lockDir, "owner");
26801
- if (fs6.readFileSync(ownerPath, "utf8").trim() === owner) {
26802
- fs6.rmSync(lockDir, { recursive: true, force: true });
26541
+ if (fs5.readFileSync(ownerPath, "utf8").trim() === owner) {
26542
+ fs5.rmSync(lockDir, { recursive: true, force: true });
26803
26543
  }
26804
26544
  } catch (error) {
26805
26545
  if (error.code !== "ENOENT") {
@@ -26811,12 +26551,12 @@ function removeStaleManifestLock(lockDir) {
26811
26551
  const reclaimDir = getManifestLockReclaimDir(lockDir);
26812
26552
  const reclaimOwner = createManifestLockOwner();
26813
26553
  try {
26814
- fs6.mkdirSync(reclaimDir, { recursive: false });
26554
+ fs5.mkdirSync(reclaimDir, { recursive: false });
26815
26555
  try {
26816
- fs6.writeFileSync(path32.join(reclaimDir, "owner"), `${reclaimOwner}
26556
+ fs5.writeFileSync(path32.join(reclaimDir, "owner"), `${reclaimOwner}
26817
26557
  `, { flag: "wx" });
26818
26558
  } catch (error) {
26819
- fs6.rmSync(reclaimDir, { recursive: true, force: true });
26559
+ fs5.rmSync(reclaimDir, { recursive: true, force: true });
26820
26560
  throw error;
26821
26561
  }
26822
26562
  } catch (error) {
@@ -26834,18 +26574,18 @@ function removeStaleManifestLock(lockDir) {
26834
26574
  if (isManifestLockOwnerActive(snapshot.owner)) {
26835
26575
  return;
26836
26576
  }
26837
- const tombstoneDir = `${lockDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
26577
+ const tombstoneDir = `${lockDir}.stale.${process.pid}.${crypto6.randomUUID()}`;
26838
26578
  try {
26839
- fs6.renameSync(lockDir, tombstoneDir);
26579
+ fs5.renameSync(lockDir, tombstoneDir);
26840
26580
  } catch (error) {
26841
26581
  if (error.code !== "ENOENT") {
26842
26582
  throw error;
26843
26583
  }
26844
26584
  return;
26845
26585
  }
26846
- fs6.rmSync(tombstoneDir, { recursive: true, force: true });
26586
+ fs5.rmSync(tombstoneDir, { recursive: true, force: true });
26847
26587
  } finally {
26848
- fs6.rmSync(reclaimDir, { recursive: true, force: true });
26588
+ fs5.rmSync(reclaimDir, { recursive: true, force: true });
26849
26589
  }
26850
26590
  }
26851
26591
  function getManifestLockReclaimDir(lockDir) {
@@ -26855,7 +26595,7 @@ function createManifestLockOwner() {
26855
26595
  return JSON.stringify({
26856
26596
  pid: process.pid,
26857
26597
  startKey: readProcessStartKey(process.pid),
26858
- token: crypto7.randomUUID()
26598
+ token: crypto6.randomUUID()
26859
26599
  });
26860
26600
  }
26861
26601
  function removeStaleManifestReclaimLock(reclaimDir) {
@@ -26866,21 +26606,21 @@ function removeStaleManifestReclaimLock(reclaimDir) {
26866
26606
  if (isManifestLockOwnerActive(snapshot.owner)) {
26867
26607
  return;
26868
26608
  }
26869
- const tombstoneDir = `${reclaimDir}.stale.${process.pid}.${crypto7.randomUUID()}`;
26609
+ const tombstoneDir = `${reclaimDir}.stale.${process.pid}.${crypto6.randomUUID()}`;
26870
26610
  try {
26871
- fs6.renameSync(reclaimDir, tombstoneDir);
26611
+ fs5.renameSync(reclaimDir, tombstoneDir);
26872
26612
  } catch (error) {
26873
26613
  if (error.code !== "ENOENT") {
26874
26614
  throw error;
26875
26615
  }
26876
26616
  return;
26877
26617
  }
26878
- fs6.rmSync(tombstoneDir, { recursive: true, force: true });
26618
+ fs5.rmSync(tombstoneDir, { recursive: true, force: true });
26879
26619
  }
26880
26620
  function readManifestLockSnapshot(lockDir) {
26881
26621
  let stat5;
26882
26622
  try {
26883
- stat5 = fs6.statSync(lockDir);
26623
+ stat5 = fs5.statSync(lockDir);
26884
26624
  } catch (error) {
26885
26625
  if (error.code === "ENOENT") {
26886
26626
  return void 0;
@@ -26888,7 +26628,7 @@ function readManifestLockSnapshot(lockDir) {
26888
26628
  throw error;
26889
26629
  }
26890
26630
  try {
26891
- const owner = fs6.readFileSync(path32.join(lockDir, "owner"), "utf8").trim();
26631
+ const owner = fs5.readFileSync(path32.join(lockDir, "owner"), "utf8").trim();
26892
26632
  return { mtimeMs: stat5.mtimeMs, owner };
26893
26633
  } catch (error) {
26894
26634
  if (error.code === "ENOENT") {
@@ -26962,7 +26702,7 @@ function createPersonalSpace(baseDir, memoryDirOverride) {
26962
26702
  const homeDir = baseDir ?? resolveHomeDir();
26963
26703
  const standalonePath = path32.join(homeDir, ".engram", "memory");
26964
26704
  const openclawPath = path32.join(homeDir, ".openclaw", "workspace", "memory", "local");
26965
- const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs6.existsSync(standalonePath) ? standalonePath : fs6.existsSync(openclawPath) ? openclawPath : standalonePath);
26705
+ const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs5.existsSync(standalonePath) ? standalonePath : fs5.existsSync(openclawPath) ? openclawPath : standalonePath);
26966
26706
  const normalizedMemoryDir = normalizeSpaceMemoryDir(memoryDir);
26967
26707
  const now = (/* @__PURE__ */ new Date()).toISOString();
26968
26708
  return {
@@ -27010,7 +26750,7 @@ function createSpace(options) {
27010
26750
  owner: readEnvVar("USER"),
27011
26751
  parentSpaceId: options.parentSpaceId
27012
26752
  };
27013
- fs6.mkdirSync(memoryDir, { recursive: true });
26753
+ fs5.mkdirSync(memoryDir, { recursive: true });
27014
26754
  manifest.spaces.push(created);
27015
26755
  manifest.updatedAt = now;
27016
26756
  return created;
@@ -27168,9 +26908,9 @@ async function promoteSpace(sourceSpaceId, targetSpaceId, options) {
27168
26908
  const subjectWarnings = [];
27169
26909
  const warnedMemoryIds = [];
27170
26910
  if (target.kind === "team") {
27171
- const sourceFiles = fs6.existsSync(source.memoryDir) ? walkMd(fs6.realpathSync(source.memoryDir)) : [];
26911
+ const sourceFiles = fs5.existsSync(source.memoryDir) ? walkMd(fs5.realpathSync(source.memoryDir)) : [];
27172
26912
  for (const file of sourceFiles) {
27173
- const fm = parseSimpleFrontmatter(fs6.readFileSync(file, "utf8"));
26913
+ const fm = parseSimpleFrontmatter(fs5.readFileSync(file, "utf8"));
27174
26914
  if (!fm?.id) continue;
27175
26915
  if (options?.memoryIds?.length && !options.memoryIds.includes(fm.id)) continue;
27176
26916
  const decision = evaluateSubjectGuard({
@@ -27237,28 +26977,28 @@ async function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
27237
26977
  }
27238
26978
  function getAuditLog(baseDir) {
27239
26979
  const auditPath = path32.join(getSpacesDir(baseDir), "audit.jsonl");
27240
- if (!fs6.existsSync(auditPath)) return [];
27241
- const lines = fs6.readFileSync(auditPath, "utf8").trim().split("\n");
26980
+ if (!fs5.existsSync(auditPath)) return [];
26981
+ const lines = fs5.readFileSync(auditPath, "utf8").trim().split("\n");
27242
26982
  return lines.filter((l) => l.trim()).map((l) => JSON.parse(l));
27243
26983
  }
27244
26984
  function appendAudit(entry, baseDir) {
27245
26985
  const auditPath = path32.join(getSpacesDir(baseDir), "audit.jsonl");
27246
- fs6.mkdirSync(path32.dirname(auditPath), { recursive: true });
26986
+ fs5.mkdirSync(path32.dirname(auditPath), { recursive: true });
27247
26987
  const full = {
27248
- id: crypto7.randomUUID(),
26988
+ id: crypto6.randomUUID(),
27249
26989
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
27250
26990
  ...entry
27251
26991
  };
27252
- fs6.appendFileSync(auditPath, `${JSON.stringify(full)}
26992
+ fs5.appendFileSync(auditPath, `${JSON.stringify(full)}
27253
26993
  `);
27254
26994
  }
27255
26995
  async function copyMemories(sourceDir, targetDir, options) {
27256
- if (!fs6.existsSync(sourceDir)) {
26996
+ if (!fs5.existsSync(sourceDir)) {
27257
26997
  return { merged: 0, conflicts: [], skipped: 0 };
27258
26998
  }
27259
- const sourceRoot = fs6.realpathSync(sourceDir);
27260
- fs6.mkdirSync(targetDir, { recursive: true });
27261
- const targetRoot = fs6.realpathSync(targetDir);
26999
+ const sourceRoot = fs5.realpathSync(sourceDir);
27000
+ fs5.mkdirSync(targetDir, { recursive: true });
27001
+ const targetRoot = fs5.realpathSync(targetDir);
27262
27002
  const sourceFiles = walkMd(sourceRoot);
27263
27003
  const sourceEntityIds = new Set(
27264
27004
  sourceFiles.filter((p) => {
@@ -27302,7 +27042,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27302
27042
  skipped++;
27303
27043
  continue;
27304
27044
  }
27305
- const content = fs6.readFileSync(sourcePath, "utf8");
27045
+ const content = fs5.readFileSync(sourcePath, "utf8");
27306
27046
  const relativePath = path32.relative(sourceRoot, sourceRealPath);
27307
27047
  const targetPath = path32.resolve(targetRoot, relativePath);
27308
27048
  if (!isPathInsideRoot(targetPath, targetRoot)) {
@@ -27314,7 +27054,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27314
27054
  const sourceRef = targetKind === "memory" ? readEntityRefFromFrontmatter(content) : null;
27315
27055
  const refHasSourceClaimant = sourceRef !== null && sourceEntityIds.has(sourceRef);
27316
27056
  const canonicalContent = targetKind === "memory" && !refHasSourceClaimant ? canonicalizeEntityRefFrontmatter(content, idsAtCompare) : content;
27317
- const sourceHash = hashContent4(content);
27057
+ const sourceHash = hashContent3(content);
27318
27058
  if (options?.filterIds?.length) {
27319
27059
  const fm = parseSimpleFrontmatter(content);
27320
27060
  if (!fm?.id || !options.filterIds.includes(fm.id)) {
@@ -27322,7 +27062,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27322
27062
  continue;
27323
27063
  }
27324
27064
  }
27325
- if (fs6.existsSync(targetPath)) {
27065
+ if (fs5.existsSync(targetPath)) {
27326
27066
  const targetStat = safeLstat(targetPath);
27327
27067
  if (!targetStat?.isFile() || targetStat.isSymbolicLink()) {
27328
27068
  skipped++;
@@ -27334,14 +27074,14 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27334
27074
  continue;
27335
27075
  }
27336
27076
  }
27337
- if (fs6.existsSync(targetPath) && !options?.force) {
27338
- const targetContent = fs6.readFileSync(targetPath, "utf8");
27339
- const targetHash = hashContent4(targetContent);
27340
- if (hashContent4(canonicalContent) === targetHash && historicalIdCache3.get(path32.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) {
27341
27081
  skipped++;
27342
27082
  continue;
27343
27083
  }
27344
- if (hashContent4(canonicalContent) !== targetHash && sourceHash !== targetHash) {
27084
+ if (hashContent3(canonicalContent) !== targetHash && sourceHash !== targetHash) {
27345
27085
  conflicts.push({
27346
27086
  memoryId: parseSimpleFrontmatter(content)?.id ?? relativePath,
27347
27087
  sourcePath,
@@ -27353,7 +27093,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27353
27093
  continue;
27354
27094
  }
27355
27095
  }
27356
- fs6.mkdirSync(path32.dirname(targetPath), { recursive: true });
27096
+ fs5.mkdirSync(path32.dirname(targetPath), { recursive: true });
27357
27097
  const targetParentRealPath = safeRealpath(path32.dirname(targetPath));
27358
27098
  if (!targetParentRealPath || !isPathInsideRoot(targetParentRealPath, targetRoot)) {
27359
27099
  skipped++;
@@ -27365,11 +27105,11 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27365
27105
  const written = canonicalizeEntityRefFrontmatter(content, idsAtWrite);
27366
27106
  let priorTarget = null;
27367
27107
  try {
27368
- priorTarget = fs6.readFileSync(targetPath, "utf8");
27108
+ priorTarget = fs5.readFileSync(targetPath, "utf8");
27369
27109
  } catch {
27370
27110
  priorTarget = null;
27371
27111
  }
27372
- fs6.writeFileSync(targetPath, written);
27112
+ fs5.writeFileSync(targetPath, written);
27373
27113
  try {
27374
27114
  repairContentAfterJournalMoveSync({
27375
27115
  stateDir,
@@ -27377,24 +27117,24 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27377
27117
  idsAtWrite,
27378
27118
  rawContent: content,
27379
27119
  lastWritten: written,
27380
- rewrite: (next) => fs6.writeFileSync(targetPath, next)
27120
+ rewrite: (next) => fs5.writeFileSync(targetPath, next)
27381
27121
  });
27382
27122
  } catch (err) {
27383
27123
  try {
27384
27124
  if (priorTarget === null) {
27385
- fs6.unlinkSync(targetPath);
27125
+ fs5.unlinkSync(targetPath);
27386
27126
  } else {
27387
- fs6.writeFileSync(targetPath, priorTarget);
27127
+ fs5.writeFileSync(targetPath, priorTarget);
27388
27128
  }
27389
27129
  } catch {
27390
27130
  }
27391
27131
  throw err;
27392
27132
  }
27393
27133
  } else if (targetKind === "memory") {
27394
- fs6.writeFileSync(targetPath, content);
27134
+ fs5.writeFileSync(targetPath, content);
27395
27135
  reconcileNeeded = true;
27396
27136
  } else {
27397
- fs6.writeFileSync(targetPath, content);
27137
+ fs5.writeFileSync(targetPath, content);
27398
27138
  if (targetKind === "entity") {
27399
27139
  reconcileNeeded = true;
27400
27140
  }
@@ -27407,13 +27147,13 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
27407
27147
  }
27408
27148
  return { merged, conflicts, skipped };
27409
27149
  }
27410
- function hashContent4(content) {
27411
- 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);
27412
27152
  }
27413
27153
  function walkMd(dir) {
27414
27154
  const results = [];
27415
27155
  function walk(d) {
27416
- for (const entry of fs6.readdirSync(d, { withFileTypes: true })) {
27156
+ for (const entry of fs5.readdirSync(d, { withFileTypes: true })) {
27417
27157
  const fullPath = path32.join(d, entry.name);
27418
27158
  if (entry.isSymbolicLink()) {
27419
27159
  continue;
@@ -27430,14 +27170,14 @@ function walkMd(dir) {
27430
27170
  }
27431
27171
  function safeLstat(filePath) {
27432
27172
  try {
27433
- return fs6.lstatSync(filePath);
27173
+ return fs5.lstatSync(filePath);
27434
27174
  } catch {
27435
27175
  return null;
27436
27176
  }
27437
27177
  }
27438
27178
  function safeRealpath(filePath) {
27439
27179
  try {
27440
- return fs6.realpathSync(filePath);
27180
+ return fs5.realpathSync(filePath);
27441
27181
  } catch {
27442
27182
  return null;
27443
27183
  }
@@ -27563,7 +27303,7 @@ var REMNIC_RECALL_DECISION_RULES = `## When to Use Recall vs Direct Read
27563
27303
  `;
27564
27304
 
27565
27305
  // src/memory-extension/codex-publisher.ts
27566
- import fs7 from "fs";
27306
+ import fs6 from "fs";
27567
27307
  import os from "os";
27568
27308
  import path33 from "path";
27569
27309
  var REMNIC_EXTENSION_DIR_NAME = "remnic";
@@ -27599,7 +27339,7 @@ var CodexMemoryExtensionPublisher = class {
27599
27339
  async isHostAvailable() {
27600
27340
  try {
27601
27341
  const home = readEnvVar("CODEX_HOME")?.trim() || path33.join(resolveHomeDir(), ".codex");
27602
- return fs7.existsSync(home);
27342
+ return fs6.existsSync(home);
27603
27343
  } catch {
27604
27344
  return false;
27605
27345
  }
@@ -27653,18 +27393,18 @@ When running inside the Codex phase-2 consolidation sandbox:
27653
27393
  const filesWritten = [];
27654
27394
  const skipped = [];
27655
27395
  ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);
27656
- fs7.mkdirSync(extensionRoot, { recursive: true });
27396
+ fs6.mkdirSync(extensionRoot, { recursive: true });
27657
27397
  const content = await this.renderInstructions(ctx);
27658
27398
  const tmpPath = `${instructionsPath}.tmp-${process.pid}-${Date.now()}`;
27659
27399
  try {
27660
- fs7.writeFileSync(tmpPath, content, "utf-8");
27661
- fs7.renameSync(tmpPath, instructionsPath);
27400
+ fs6.writeFileSync(tmpPath, content, "utf-8");
27401
+ fs6.renameSync(tmpPath, instructionsPath);
27662
27402
  filesWritten.push(instructionsPath);
27663
27403
  ctx.log.info(`Wrote ${instructionsPath}`);
27664
27404
  } catch (err) {
27665
27405
  try {
27666
- if (fs7.existsSync(tmpPath)) {
27667
- fs7.unlinkSync(tmpPath);
27406
+ if (fs6.existsSync(tmpPath)) {
27407
+ fs6.unlinkSync(tmpPath);
27668
27408
  }
27669
27409
  } catch {
27670
27410
  }
@@ -27679,8 +27419,8 @@ When running inside the Codex phase-2 consolidation sandbox:
27679
27419
  }
27680
27420
  async unpublish() {
27681
27421
  const extensionRoot = await this.resolveExtensionRoot();
27682
- if (fs7.existsSync(extensionRoot)) {
27683
- fs7.rmSync(extensionRoot, { recursive: true, force: true });
27422
+ if (fs6.existsSync(extensionRoot)) {
27423
+ fs6.rmSync(extensionRoot, { recursive: true, force: true });
27684
27424
  }
27685
27425
  }
27686
27426
  };
@@ -28610,6 +28350,153 @@ function findRecurringPatterns(input) {
28610
28350
  return patterns;
28611
28351
  }
28612
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
+
28613
28500
  // src/activity/timeline/week-config-hash.ts
28614
28501
  var WEEK_START_DAYS = Object.freeze([
28615
28502
  "monday",
@@ -29867,6 +29754,22 @@ function filterPrivacyWindow(items, options) {
29867
29754
  });
29868
29755
  }
29869
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
+
29870
29773
  // src/activity/vault-wikilink.ts
29871
29774
  import path37 from "path";
29872
29775
  function formatVaultWikilink(notePath) {
@@ -30958,8 +30861,6 @@ export {
30958
30861
  generateContextTree,
30959
30862
  onboard,
30960
30863
  curate,
30961
- findDuplicates,
30962
- findContradictions,
30963
30864
  decideSemanticDedup,
30964
30865
  syncChanges,
30965
30866
  watchForChanges,
@@ -31025,6 +30926,9 @@ export {
31025
30926
  DEFAULT_RECURRENCE_MIN_DAYS,
31026
30927
  MIN_RECURRENCE_MIN_DAYS,
31027
30928
  findRecurringPatterns,
30929
+ WEEKLY_DASHBOARD_FORMAT_VERSION,
30930
+ DEFAULT_TOP_SOURCE_LIMIT,
30931
+ buildWeeklyDashboard,
31028
30932
  WEEK_START_DAYS,
31029
30933
  computeWeeklyConfigHash,
31030
30934
  batchObservations,
@@ -31066,6 +30970,8 @@ export {
31066
30970
  ACTIVITY_FEATURE_GATES,
31067
30971
  resolveActivityGates,
31068
30972
  filterPrivacyWindow,
30973
+ redactActivityFields,
30974
+ normalizeDropKeys,
31069
30975
  formatVaultWikilink,
31070
30976
  stripVaultMdSuffix,
31071
30977
  joinVaultSegments,
@@ -31104,4 +31010,4 @@ export {
31104
31010
  qmdStartupCollectionChecksWithTimeout,
31105
31011
  Orchestrator
31106
31012
  };
31107
- //# sourceMappingURL=chunk-MSTKJI5T.js.map
31013
+ //# sourceMappingURL=chunk-OKOG5MXF.js.map