@remnic/core 9.3.664 → 9.3.666

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 (150) hide show
  1. package/dist/access-audit.js +2 -2
  2. package/dist/access-cli.js +41 -40
  3. package/dist/access-cli.js.map +1 -1
  4. package/dist/access-http.d.ts +3 -2
  5. package/dist/access-http.js +25 -25
  6. package/dist/access-mcp.d.ts +3 -2
  7. package/dist/access-mcp.js +22 -22
  8. package/dist/access-schema.js +3 -3
  9. package/dist/{access-service-D0SLB4MH.d.ts → access-service-DsS-TatL.d.ts} +1 -1
  10. package/dist/access-service.d.ts +3 -2
  11. package/dist/access-service.js +21 -21
  12. package/dist/adapters/index.js +4 -4
  13. package/dist/adapters/registry.js +2 -2
  14. package/dist/bootstrap.d.ts +2 -1
  15. package/dist/briefing.js +4 -3
  16. package/dist/capabilities.d.ts +73 -0
  17. package/dist/capabilities.js +8 -0
  18. package/dist/capabilities.js.map +1 -0
  19. package/dist/causal-behavior.js +2 -2
  20. package/dist/causal-chain.js +2 -2
  21. package/dist/causal-consolidation.js +7 -6
  22. package/dist/causal-consolidation.js.map +1 -1
  23. package/dist/causal-retrieval.js +2 -2
  24. package/dist/causal-trajectory.js +1 -1
  25. package/dist/{chunk-ROHLEUTH.js → chunk-23EBQ27U.js} +5 -5
  26. package/dist/{chunk-YW52BQSU.js → chunk-2TCHDANJ.js} +2 -2
  27. package/dist/{chunk-IROWLAWG.js → chunk-46WUVFOD.js} +4 -4
  28. package/dist/{chunk-XB5P5P2L.js → chunk-4T7P2HLJ.js} +3 -3
  29. package/dist/{chunk-7XH7VJN4.js → chunk-6T4LTI2F.js} +4 -4
  30. package/dist/{chunk-TVVEYCNW.js → chunk-7K5Q6COX.js} +4 -4
  31. package/dist/{chunk-BZG2CWOQ.js → chunk-A5TEHAR4.js} +3 -3
  32. package/dist/{chunk-C7AF236A.js → chunk-AARDBQTA.js} +2 -2
  33. package/dist/{chunk-IHG6CC7T.js → chunk-BQJUPECT.js} +2 -2
  34. package/dist/{chunk-7OGJQP7T.js → chunk-CRO4LCQ6.js} +5 -5
  35. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  36. package/dist/{chunk-LIERUFPO.js → chunk-GZ6QAYSH.js} +94 -74
  37. package/dist/chunk-GZ6QAYSH.js.map +1 -0
  38. package/dist/{chunk-UXA5L2DZ.js → chunk-HQCGRSRU.js} +2 -2
  39. package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
  40. package/dist/{chunk-XW3W4PV4.js → chunk-JTPXSXHC.js} +2 -2
  41. package/dist/{chunk-OHJFJ4HI.js → chunk-KOXGLQS7.js} +2 -2
  42. package/dist/{chunk-NLF54XMD.js → chunk-MPXYHC35.js} +26 -26
  43. package/dist/{chunk-6JBKHTQD.js → chunk-MR4PJ277.js} +2 -2
  44. package/dist/{chunk-EXXBA5OM.js → chunk-OI4BXFSB.js} +4 -4
  45. package/dist/{chunk-SQZ42MKH.js → chunk-OQH5XUH3.js} +6 -3
  46. package/dist/chunk-OQH5XUH3.js.map +1 -0
  47. package/dist/{chunk-2HEZXPYU.js → chunk-Q2LQZYQ7.js} +3 -3
  48. package/dist/{chunk-YKX63GBK.js → chunk-QHWJG5C5.js} +8 -8
  49. package/dist/{chunk-T2AN3BSP.js → chunk-QZ7ODIVL.js} +2 -2
  50. package/dist/chunk-RI5XBIZ6.js +23 -0
  51. package/dist/chunk-RI5XBIZ6.js.map +1 -0
  52. package/dist/{chunk-7ILWCUWH.js → chunk-TJ7HH5LB.js} +28 -3
  53. package/dist/chunk-TJ7HH5LB.js.map +1 -0
  54. package/dist/{chunk-V25ZAOSB.js → chunk-UOBLE67F.js} +4 -4
  55. package/dist/{chunk-JIX3ZL2J.js → chunk-UVUTV7CM.js} +15 -15
  56. package/dist/{chunk-VH6EIKVS.js → chunk-WKMCC4NQ.js} +35 -16
  57. package/dist/chunk-WKMCC4NQ.js.map +1 -0
  58. package/dist/{chunk-SSOMTUCA.js → chunk-WXGTC424.js} +1 -1
  59. package/dist/{chunk-KHGE6PMF.js → chunk-WXXLSZHA.js} +2 -2
  60. package/dist/{chunk-DSLUOQDY.js → chunk-XMWF6AU3.js} +2 -2
  61. package/dist/{chunk-DQY7NJ5L.js → chunk-XS2CWEHZ.js} +2 -2
  62. package/dist/{cli-BQRqR9N-.d.ts → cli-BypxcNqq.d.ts} +2 -2
  63. package/dist/cli.d.ts +4 -3
  64. package/dist/cli.js +42 -42
  65. package/dist/compounding/engine.js +4 -3
  66. package/dist/connectors/codex-materialize-runner.js +4 -3
  67. package/dist/connectors/index.js +4 -3
  68. package/dist/consolidation-provenance-check.js +2 -2
  69. package/dist/conversation-index/backend.js +2 -2
  70. package/dist/dashboard-runtime.js +2 -2
  71. package/dist/direct-answer-wiring.d.ts +13 -3
  72. package/dist/direct-answer-wiring.js +1 -1
  73. package/dist/entity-retrieval.js +4 -3
  74. package/dist/explicit-capture.d.ts +2 -1
  75. package/dist/index.d.ts +5 -4
  76. package/dist/index.js +66 -65
  77. package/dist/index.js.map +1 -1
  78. package/dist/lcm/engine.js +2 -2
  79. package/dist/lcm/index.js +4 -4
  80. package/dist/maintenance/memory-governance.js +4 -4
  81. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +4 -3
  82. package/dist/maintenance/rebuild-memory-projection.js +5 -5
  83. package/dist/mcp-memory-inspector-app.d.ts +3 -2
  84. package/dist/namespaces/migrate.js +11 -11
  85. package/dist/namespaces/search.js +7 -7
  86. package/dist/namespaces/storage.d.ts +13 -0
  87. package/dist/namespaces/storage.js +4 -3
  88. package/dist/operator-toolkit.js +15 -15
  89. package/dist/{orchestrator-Cg1UkvmO.d.ts → orchestrator-DZqPVoMI.d.ts} +8 -0
  90. package/dist/orchestrator.d.ts +2 -1
  91. package/dist/orchestrator.js +32 -31
  92. package/dist/recall-planner-llm.d.ts +2 -1
  93. package/dist/recall-planner-llm.js +3 -2
  94. package/dist/recall-planner-llm.js.map +1 -1
  95. package/dist/search/factory.js +6 -6
  96. package/dist/search/index.js +10 -10
  97. package/dist/search/lancedb-backend.js +1 -1
  98. package/dist/search/meilisearch-backend.js +1 -1
  99. package/dist/search/orama-backend.js +1 -1
  100. package/dist/semantic-consolidation.js +5 -4
  101. package/dist/semantic-rule-promotion.js +4 -3
  102. package/dist/semantic-rule-verifier.js +4 -3
  103. package/dist/storage.js +3 -2
  104. package/dist/transfer/backup.js +2 -2
  105. package/dist/transfer/capsule-export.js +2 -2
  106. package/dist/transfer/capsule-import.js +1 -1
  107. package/dist/verified-recall.js +4 -3
  108. package/package.json +1 -1
  109. package/src/capabilities.test.ts +97 -0
  110. package/src/capabilities.ts +86 -0
  111. package/src/direct-answer-wiring.test.ts +53 -2
  112. package/src/direct-answer-wiring.ts +18 -5
  113. package/src/namespaces/catalog.test.ts +12 -12
  114. package/src/namespaces/storage.ts +28 -1
  115. package/src/orchestrator.ts +69 -19
  116. package/src/recall-planner-llm.test.ts +12 -11
  117. package/src/recall-planner-llm.ts +7 -1
  118. package/src/storage-fallback-category-dirs.test.ts +150 -1
  119. package/src/storage.ts +51 -14
  120. package/dist/chunk-7ILWCUWH.js.map +0 -1
  121. package/dist/chunk-LIERUFPO.js.map +0 -1
  122. package/dist/chunk-SQZ42MKH.js.map +0 -1
  123. package/dist/chunk-VH6EIKVS.js.map +0 -1
  124. /package/dist/{chunk-ROHLEUTH.js.map → chunk-23EBQ27U.js.map} +0 -0
  125. /package/dist/{chunk-YW52BQSU.js.map → chunk-2TCHDANJ.js.map} +0 -0
  126. /package/dist/{chunk-IROWLAWG.js.map → chunk-46WUVFOD.js.map} +0 -0
  127. /package/dist/{chunk-XB5P5P2L.js.map → chunk-4T7P2HLJ.js.map} +0 -0
  128. /package/dist/{chunk-7XH7VJN4.js.map → chunk-6T4LTI2F.js.map} +0 -0
  129. /package/dist/{chunk-TVVEYCNW.js.map → chunk-7K5Q6COX.js.map} +0 -0
  130. /package/dist/{chunk-BZG2CWOQ.js.map → chunk-A5TEHAR4.js.map} +0 -0
  131. /package/dist/{chunk-C7AF236A.js.map → chunk-AARDBQTA.js.map} +0 -0
  132. /package/dist/{chunk-IHG6CC7T.js.map → chunk-BQJUPECT.js.map} +0 -0
  133. /package/dist/{chunk-7OGJQP7T.js.map → chunk-CRO4LCQ6.js.map} +0 -0
  134. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  135. /package/dist/{chunk-UXA5L2DZ.js.map → chunk-HQCGRSRU.js.map} +0 -0
  136. /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
  137. /package/dist/{chunk-XW3W4PV4.js.map → chunk-JTPXSXHC.js.map} +0 -0
  138. /package/dist/{chunk-OHJFJ4HI.js.map → chunk-KOXGLQS7.js.map} +0 -0
  139. /package/dist/{chunk-NLF54XMD.js.map → chunk-MPXYHC35.js.map} +0 -0
  140. /package/dist/{chunk-6JBKHTQD.js.map → chunk-MR4PJ277.js.map} +0 -0
  141. /package/dist/{chunk-EXXBA5OM.js.map → chunk-OI4BXFSB.js.map} +0 -0
  142. /package/dist/{chunk-2HEZXPYU.js.map → chunk-Q2LQZYQ7.js.map} +0 -0
  143. /package/dist/{chunk-YKX63GBK.js.map → chunk-QHWJG5C5.js.map} +0 -0
  144. /package/dist/{chunk-T2AN3BSP.js.map → chunk-QZ7ODIVL.js.map} +0 -0
  145. /package/dist/{chunk-V25ZAOSB.js.map → chunk-UOBLE67F.js.map} +0 -0
  146. /package/dist/{chunk-JIX3ZL2J.js.map → chunk-UVUTV7CM.js.map} +0 -0
  147. /package/dist/{chunk-SSOMTUCA.js.map → chunk-WXGTC424.js.map} +0 -0
  148. /package/dist/{chunk-KHGE6PMF.js.map → chunk-WXXLSZHA.js.map} +0 -0
  149. /package/dist/{chunk-DSLUOQDY.js.map → chunk-XMWF6AU3.js.map} +0 -0
  150. /package/dist/{chunk-DQY7NJ5L.js.map → chunk-XS2CWEHZ.js.map} +0 -0
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-HQ6NIBL6.js";
4
4
  import {
5
5
  resolveCausalTrajectoryStoreDir
6
- } from "./chunk-SSOMTUCA.js";
6
+ } from "./chunk-WXGTC424.js";
7
7
  import {
8
8
  normalizeRecallTokens
9
9
  } from "./chunk-ZBJMUXZH.js";
@@ -339,4 +339,4 @@ export {
339
339
  scoreStitchCandidate,
340
340
  stitchCausalChain
341
341
  };
342
- //# sourceMappingURL=chunk-UXA5L2DZ.js.map
342
+ //# sourceMappingURL=chunk-HQCGRSRU.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ ReplitAdapter
3
+ } from "./chunk-TMSXWOBZ.js";
1
4
  import {
2
5
  ClaudeCodeAdapter
3
6
  } from "./chunk-J64TK33U.js";
@@ -7,9 +10,6 @@ import {
7
10
  import {
8
11
  HermesAdapter
9
12
  } from "./chunk-42NQ7AVG.js";
10
- import {
11
- ReplitAdapter
12
- } from "./chunk-TMSXWOBZ.js";
13
13
 
14
14
  // src/adapters/registry.ts
15
15
  var AdapterRegistry = class {
@@ -53,4 +53,4 @@ var AdapterRegistry = class {
53
53
  export {
54
54
  AdapterRegistry
55
55
  };
56
- //# sourceMappingURL=chunk-RKNJBZ55.js.map
56
+ //# sourceMappingURL=chunk-JBPKEARU.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runPostConsolidationMaterialize
3
- } from "./chunk-C7AF236A.js";
3
+ } from "./chunk-AARDBQTA.js";
4
4
  import {
5
5
  discoverMemoryExtensions,
6
6
  renderExtensionsBlock,
@@ -217,4 +217,4 @@ export {
217
217
  buildExtensionsBlockForConsolidation,
218
218
  materializeAfterSemanticConsolidation
219
219
  };
220
- //# sourceMappingURL=chunk-XW3W4PV4.js.map
220
+ //# sourceMappingURL=chunk-JTPXSXHC.js.map
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-DRD2Q7HQ.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-VH6EIKVS.js";
7
+ } from "./chunk-WKMCC4NQ.js";
8
8
  import {
9
9
  parseContinuityImprovementLoops
10
10
  } from "./chunk-LDXUBPMO.js";
@@ -1261,4 +1261,4 @@ export {
1261
1261
  defaultTierMigrationCycleBudget,
1262
1262
  CompoundingEngine
1263
1263
  };
1264
- //# sourceMappingURL=chunk-OHJFJ4HI.js.map
1264
+ //# sourceMappingURL=chunk-KOXGLQS7.js.map
@@ -25,12 +25,12 @@ import {
25
25
  import {
26
26
  backupMemoryDir
27
27
  } from "./chunk-2NLLXCJG.js";
28
- import {
29
- exportJsonBundle
30
- } from "./chunk-UP6MOYCB.js";
31
28
  import {
32
29
  exportMarkdownBundle
33
30
  } from "./chunk-765K3SAT.js";
31
+ import {
32
+ exportJsonBundle
33
+ } from "./chunk-UP6MOYCB.js";
34
34
  import {
35
35
  TailscaleHelper
36
36
  } from "./chunk-EEC4PCG5.js";
@@ -42,23 +42,23 @@ import {
42
42
  clampBatchSize,
43
43
  runReplay
44
44
  } from "./chunk-HOJZMQ4J.js";
45
+ import {
46
+ migrateObservations
47
+ } from "./chunk-D6RIFG4O.js";
45
48
  import {
46
49
  rebuildMemoryLifecycleLedger
47
- } from "./chunk-6JBKHTQD.js";
50
+ } from "./chunk-MR4PJ277.js";
48
51
  import {
49
52
  rebuildMemoryProjection,
50
53
  repairMemoryProjection,
51
54
  verifyMemoryProjection
52
- } from "./chunk-BZG2CWOQ.js";
55
+ } from "./chunk-A5TEHAR4.js";
53
56
  import {
54
57
  rebuildObservations
55
58
  } from "./chunk-LZZNTPLR.js";
56
59
  import {
57
60
  archiveObservations
58
61
  } from "./chunk-WMWVO45V.js";
59
- import {
60
- migrateObservations
61
- } from "./chunk-D6RIFG4O.js";
62
62
  import {
63
63
  getBulkImportSource,
64
64
  listBulkImportSources,
@@ -74,7 +74,7 @@ import {
74
74
  } from "./chunk-EVWIEEKZ.js";
75
75
  import {
76
76
  promoteSemanticRuleFromMemory
77
- } from "./chunk-IHG6CC7T.js";
77
+ } from "./chunk-BQJUPECT.js";
78
78
  import {
79
79
  resolveAgentAccessAuthToken
80
80
  } from "./chunk-TGQ2NTWH.js";
@@ -93,12 +93,12 @@ import {
93
93
  runOperatorInventory,
94
94
  runOperatorRepair,
95
95
  runOperatorSetup
96
- } from "./chunk-YKX63GBK.js";
96
+ } from "./chunk-QHWJG5C5.js";
97
97
  import {
98
98
  listNamespaces,
99
99
  runNamespaceMigration,
100
100
  verifyNamespaces
101
- } from "./chunk-XB5P5P2L.js";
101
+ } from "./chunk-4T7P2HLJ.js";
102
102
  import {
103
103
  collectPatternMemories,
104
104
  explainPatternMemory,
@@ -110,14 +110,14 @@ import {
110
110
  } from "./chunk-3T74IZB3.js";
111
111
  import {
112
112
  GraphDashboardServer
113
- } from "./chunk-YNDLCWXS.js";
113
+ } from "./chunk-EZ25VE3G.js";
114
+ import {
115
+ RoutingRulesStore
116
+ } from "./chunk-X6IRLNOO.js";
114
117
  import {
115
118
  isReplaySource,
116
119
  normalizeReplaySessionKey
117
120
  } from "./chunk-2PRQG7PV.js";
118
- import {
119
- RoutingRulesStore
120
- } from "./chunk-X6IRLNOO.js";
121
121
  import {
122
122
  getUtilityLearningStatus,
123
123
  learnUtilityPromotionWeights
@@ -128,13 +128,13 @@ import {
128
128
  } from "./chunk-TERNBNJB.js";
129
129
  import {
130
130
  searchVerifiedEpisodes
131
- } from "./chunk-YW52BQSU.js";
131
+ } from "./chunk-2TCHDANJ.js";
132
132
  import {
133
133
  ThreadingManager
134
134
  } from "./chunk-W4RVMTHR.js";
135
135
  import {
136
136
  searchVerifiedSemanticRules
137
- } from "./chunk-DSLUOQDY.js";
137
+ } from "./chunk-XMWF6AU3.js";
138
138
  import {
139
139
  getWorkProductLedgerStatus,
140
140
  recordWorkProductLedgerEntry,
@@ -216,16 +216,16 @@ import {
216
216
  } from "./chunk-OADWQ5CR.js";
217
217
  import {
218
218
  EngramAccessHttpServer
219
- } from "./chunk-IROWLAWG.js";
219
+ } from "./chunk-46WUVFOD.js";
220
220
  import {
221
221
  WearablesInputError
222
222
  } from "./chunk-7WV3F5DQ.js";
223
223
  import {
224
224
  EngramMcpServer
225
- } from "./chunk-2HEZXPYU.js";
225
+ } from "./chunk-Q2LQZYQ7.js";
226
226
  import {
227
227
  EngramAccessService
228
- } from "./chunk-JIX3ZL2J.js";
228
+ } from "./chunk-UVUTV7CM.js";
229
229
  import {
230
230
  WorkStorage
231
231
  } from "./chunk-GDB4J2H3.js";
@@ -239,28 +239,28 @@ import {
239
239
  readMemoryGovernanceRunArtifact,
240
240
  restoreMemoryGovernanceRun,
241
241
  runMemoryGovernance
242
- } from "./chunk-EXXBA5OM.js";
242
+ } from "./chunk-OI4BXFSB.js";
243
243
  import {
244
244
  getTrustZoneStoreStatus,
245
245
  promoteTrustZoneRecord,
246
246
  seedTrustZoneDemoDataset
247
247
  } from "./chunk-JGSKJHF7.js";
248
- import {
249
- assertPathInsideRoot
250
- } from "./chunk-5GPPACXK.js";
251
248
  import {
252
249
  getObjectiveStateStoreStatus
253
250
  } from "./chunk-6HMYUWXR.js";
254
251
  import {
255
252
  analyzeGraphHealth
256
253
  } from "./chunk-Y56J7CXW.js";
254
+ import {
255
+ assertPathInsideRoot
256
+ } from "./chunk-5GPPACXK.js";
257
257
  import {
258
258
  RECALL_DISCLOSURE_LEVELS,
259
259
  isRecallDisclosure
260
260
  } from "./chunk-AZBV4RRY.js";
261
261
  import {
262
262
  getCausalTrajectoryStoreStatus
263
- } from "./chunk-SSOMTUCA.js";
263
+ } from "./chunk-WXGTC424.js";
264
264
  import {
265
265
  selectRouteRule,
266
266
  validateRouteTarget
@@ -7311,4 +7311,4 @@ export {
7311
7311
  listMemoryMarkdownFilePaths,
7312
7312
  registerCli
7313
7313
  };
7314
- //# sourceMappingURL=chunk-NLF54XMD.js.map
7314
+ //# sourceMappingURL=chunk-MPXYHC35.js.map
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-SFQ6QNL7.js";
7
7
  import {
8
8
  StorageManager
9
- } from "./chunk-VH6EIKVS.js";
9
+ } from "./chunk-WKMCC4NQ.js";
10
10
  import {
11
11
  buildLifecycleEventsForMemory,
12
12
  sortMemoryLifecycleEvents
@@ -74,4 +74,4 @@ export {
74
74
  backupExistingLedger,
75
75
  rebuildMemoryLifecycleLedger
76
76
  };
77
- //# sourceMappingURL=chunk-6JBKHTQD.js.map
77
+ //# sourceMappingURL=chunk-MR4PJ277.js.map
@@ -1,9 +1,9 @@
1
+ import {
2
+ StorageManager
3
+ } from "./chunk-WKMCC4NQ.js";
1
4
  import {
2
5
  assertPathInsideRoot
3
6
  } from "./chunk-5GPPACXK.js";
4
- import {
5
- StorageManager
6
- } from "./chunk-VH6EIKVS.js";
7
7
  import {
8
8
  decideLifecycleTransition
9
9
  } from "./chunk-TBBDFYXW.js";
@@ -751,4 +751,4 @@ export {
751
751
  listMemoryGovernanceRuns,
752
752
  readMemoryGovernanceRunArtifact
753
753
  };
754
- //# sourceMappingURL=chunk-EXXBA5OM.js.map
754
+ //# sourceMappingURL=chunk-OI4BXFSB.js.map
@@ -116,9 +116,12 @@ var TAXONOMY_KEYWORD_STOPWORDS = /* @__PURE__ */ new Set([
116
116
 
117
117
  // src/direct-answer-wiring.ts
118
118
  async function tryDirectAnswer(input) {
119
- const { query, namespace, config, sources, queryEntityRefs, abortSignal } = input;
119
+ const { query, namespace, config, enabled, sources, queryEntityRefs, abortSignal } = input;
120
120
  const eligibilityConfig = {
121
- enabled: config.recallDirectAnswerEnabled,
121
+ // Prefer the resolved capability when supplied; fall back to the config
122
+ // flag so callers on the old input shape (config-only, no `enabled`) get
123
+ // identical gating (issue #1523 backward-compat).
124
+ enabled: enabled ?? config.recallDirectAnswerEnabled,
122
125
  tokenOverlapFloor: config.recallDirectAnswerTokenOverlapFloor,
123
126
  importanceFloor: config.recallDirectAnswerImportanceFloor,
124
127
  ambiguityMargin: config.recallDirectAnswerAmbiguityMargin,
@@ -177,4 +180,4 @@ export {
177
180
  resolveCategory,
178
181
  tryDirectAnswer
179
182
  };
180
- //# sourceMappingURL=chunk-SQZ42MKH.js.map
183
+ //# sourceMappingURL=chunk-OQH5XUH3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/taxonomy/resolver.ts","../src/direct-answer-wiring.ts"],"sourcesContent":["/**\n * Resolver decision tree for the MECE taxonomy.\n *\n * Given extracted content and its MemoryCategory, determines which\n * taxonomy category the knowledge should be filed under.\n */\n\nimport type { MemoryCategory } from \"../types.js\";\nimport type { ResolverDecision, Taxonomy, TaxonomyCategory } from \"./types.js\";\n\nconst DEFAULT_CATEGORY_ID = \"facts\";\n\n/**\n * Resolve a piece of content to a taxonomy category.\n *\n * Algorithm:\n * 1. Find all taxonomy categories whose `memoryCategories` include\n * the given `memoryCategory`.\n * 2. If exactly one match, return it with confidence 1.0.\n * 3. If multiple matches, prefer the strongest exact-token keyword\n * overlap from filing rules. Use priority only as a tie-breaker.\n * If no filing rules match, choose the explicit/default fallback\n * category instead of letting low-priority generic terms win.\n * 4. If no match, fall back to the \"facts\" category (or first\n * category if \"facts\" is absent) with low confidence.\n * 5. Always populate `alternatives` with other plausible categories.\n */\nexport function resolveCategory(\n content: string,\n memoryCategory: MemoryCategory,\n taxonomy: Taxonomy,\n): ResolverDecision {\n const contentTokens = tokenizeKeywordText(content);\n\n // Step 1: find matching categories\n const matches = taxonomy.categories.filter((cat) =>\n cat.memoryCategories.includes(memoryCategory),\n );\n\n if (matches.length === 0) {\n // No taxonomy category accepts this MemoryCategory — fall back\n const fallback =\n taxonomy.categories.find((c) => c.id === DEFAULT_CATEGORY_ID) ??\n taxonomy.categories[0];\n if (!fallback) {\n return {\n categoryId: DEFAULT_CATEGORY_ID,\n confidence: 0,\n reason: \"Taxonomy is empty; using default category\",\n alternatives: [],\n };\n }\n const alternatives = taxonomy.categories\n .filter((c) => c.id !== fallback.id)\n .map((c) => ({\n categoryId: c.id,\n reason: c.description,\n }));\n return {\n categoryId: fallback.id,\n confidence: 0.3,\n reason: `No taxonomy category maps to MemoryCategory \"${memoryCategory}\"; falling back to \"${fallback.name}\"`,\n alternatives,\n };\n }\n\n if (matches.length === 1) {\n const match = matches[0]!;\n const alternatives = taxonomy.categories\n .filter((c) => c.id !== match.id)\n .map((c) => ({\n categoryId: c.id,\n reason: c.description,\n }));\n return {\n categoryId: match.id,\n confidence: 1.0,\n reason: `Unique match: MemoryCategory \"${memoryCategory}\" maps to \"${match.name}\"`,\n alternatives,\n };\n }\n\n // Multiple matches — use filing rule keyword heuristics + priority\n const scored = matches.map((cat) => ({\n cat,\n keywordScore: computeKeywordScoreForTokens(contentTokens, cat),\n }));\n\n // Sort by keyword score descending, then priority ascending (lower wins)\n scored.sort((a, b) => {\n if (b.keywordScore !== a.keywordScore) return b.keywordScore - a.keywordScore;\n return a.cat.priority - b.cat.priority;\n });\n\n const topScored = scored[0]!;\n const best = topScored.keywordScore > 0\n ? topScored\n : {\n cat: selectFallbackCategory(matches) ?? topScored.cat,\n keywordScore: 0,\n };\n const runnerUp = scored[1];\n\n // Confidence is higher when keyword match clearly differentiates\n const confidence =\n best.keywordScore > 0 && (!runnerUp || best.keywordScore > runnerUp.keywordScore)\n ? 0.9\n : 0.7;\n\n const alternatives = taxonomy.categories\n .filter((c) => c.id !== best.cat.id)\n .map((c) => ({\n categoryId: c.id,\n reason: c.description,\n }));\n\n const reason =\n best.keywordScore > 0\n ? `Filing rules for \"${best.cat.name}\" matched content keywords (priority ${best.cat.priority})`\n : `No filing rules matched content keywords; using fallback category \"${best.cat.name}\"`;\n\n return {\n categoryId: best.cat.id,\n confidence,\n reason,\n alternatives,\n };\n}\n\n/**\n * Compute a simple keyword overlap score between content and\n * a category's filing rules + description.\n */\nfunction computeKeywordScoreForTokens(contentTokens: Set<string>, cat: TaxonomyCategory): number {\n let score = 0;\n const ruleText = [...cat.filingRules, cat.description]\n .join(\" \")\n .toLowerCase();\n\n const keywords = tokenizeKeywordText(ruleText);\n\n for (const kw of keywords) {\n if (contentTokens.has(kw)) {\n score += 1;\n }\n }\n return score;\n}\n\nfunction tokenizeKeywordText(value: string): Set<string> {\n return new Set(\n value\n .toLowerCase()\n .split(/[^a-z0-9]+/)\n .map((word) => word.trim())\n .filter((word) => word.length >= 3 && !TAXONOMY_KEYWORD_STOPWORDS.has(word)),\n );\n}\n\nfunction selectFallbackCategory(\n categories: readonly TaxonomyCategory[],\n): TaxonomyCategory | undefined {\n return categories.find((cat) => cat.id === DEFAULT_CATEGORY_ID) ??\n categories.find((cat) => {\n const text = `${cat.id} ${cat.name} ${cat.description} ${cat.filingRules.join(\" \")}`.toLowerCase();\n return /\\b(general|fallback)\\b/.test(text);\n });\n}\n\nconst TAXONOMY_KEYWORD_STOPWORDS = new Set([\n \"about\",\n \"all\",\n \"and\",\n \"are\",\n \"for\",\n \"from\",\n \"has\",\n \"into\",\n \"not\",\n \"the\",\n \"this\",\n \"was\",\n \"with\",\n]);\n","/**\n * Direct-answer wiring (issue #518 slice 3).\n *\n * Binds the pure eligibility decision (`direct-answer.ts`) to the data\n * sources needed to build candidates: storage, trust-zones, taxonomy,\n * and importance scoring. Kept as a separate module so that:\n *\n * - The eligibility layer stays pure and unit-testable without stores.\n * - Each caller injects its own source accessors. The orchestrator\n * binding is a follow-on slice; tests here use mock sources.\n * - The wiring is safe to ship alone — nothing calls `tryDirectAnswer`\n * yet, so enabling this module's presence does not change recall\n * behavior. The next slice adds exactly one call site before QMD.\n *\n * Short-circuit contract:\n *\n * - When the resolved gate (`input.enabled` if supplied, else\n * `config.recallDirectAnswerEnabled`) is `false`, the function returns the\n * eligibility verdict with reason `\"disabled\"` without touching any source\n * accessor. This is the documented default.\n * - When enabled, the wiring cheaply drops non-trusted-zone memories\n * and ineligible taxonomy buckets before computing importance, so\n * the eligibility module sees a pre-filtered candidate set. The\n * eligibility module still performs the same checks itself — this\n * module is purely an I/O and prefiltering layer.\n */\n\nimport type { MemoryFile, PluginConfig } from \"./types.js\";\nimport type { TrustZoneName } from \"./trust-zones.js\";\nimport type { Taxonomy } from \"./taxonomy/types.js\";\nimport { resolveCategory } from \"./taxonomy/resolver.js\";\nimport { normalizeRecallTokens } from \"./recall-tokenization.js\";\nimport { throwIfAborted } from \"./abort-error.js\";\nimport {\n isDirectAnswerEligible,\n type DirectAnswerCandidate,\n type DirectAnswerConfig,\n type DirectAnswerResult,\n} from \"./direct-answer.js\";\n\n/**\n * Caller-provided accessors for candidate sourcing. Decouples the\n * wiring from any specific storage / trust-zone / importance backend.\n */\nexport interface DirectAnswerSources {\n /**\n * List memories eligible to be considered for direct-answer.\n * Callers are expected to return only active, non-superseded memories\n * in the requested namespace; the wiring will cheaply re-filter on\n * trust zone and taxonomy bucket and hand the rest to the eligibility\n * module, which applies the full gate ladder.\n */\n listCandidateMemories(options: {\n namespace: string;\n abortSignal?: AbortSignal;\n }): Promise<MemoryFile[]>;\n /**\n * Resolve the trust-zone record for a memory. Returns `null` when\n * the memory has no trust-zone record (treated as not trusted).\n */\n trustZoneFor(memoryId: string): Promise<TrustZoneName | null>;\n /**\n * Resolve a calibrated importance score in [0, 1] for a memory.\n */\n importanceFor(memory: MemoryFile): number;\n /**\n * Taxonomy used to classify memories into direct-answer buckets.\n */\n taxonomy: Taxonomy;\n}\n\nexport interface DirectAnswerWiringInput {\n query: string;\n namespace: string;\n config: Pick<\n PluginConfig,\n | \"recallDirectAnswerEnabled\"\n | \"recallDirectAnswerTokenOverlapFloor\"\n | \"recallDirectAnswerImportanceFloor\"\n | \"recallDirectAnswerAmbiguityMargin\"\n | \"recallDirectAnswerEligibleTaxonomyBuckets\"\n >;\n /**\n * Direct-answer capability gate, resolved once at the recall-operation entry\n * (issue #1523: `caps.recallDirectAnswer`). OPTIONAL and additive: when the\n * caller supplies it, this module and the orchestrator agree on a single\n * resolved gate value for the whole operation. When omitted, we fall back to\n * `config.recallDirectAnswerEnabled` so existing callers on the old input\n * shape keep identical behavior.\n */\n enabled?: boolean;\n sources: DirectAnswerSources;\n queryEntityRefs?: string[];\n abortSignal?: AbortSignal;\n}\n\n/**\n * Attempt direct-answer resolution. Returns the eligibility verdict\n * produced by `isDirectAnswerEligible` with candidates materialized\n * from the caller-supplied sources.\n */\nexport async function tryDirectAnswer(\n input: DirectAnswerWiringInput,\n): Promise<DirectAnswerResult> {\n const { query, namespace, config, enabled, sources, queryEntityRefs, abortSignal } = input;\n\n const eligibilityConfig: DirectAnswerConfig = {\n // Prefer the resolved capability when supplied; fall back to the config\n // flag so callers on the old input shape (config-only, no `enabled`) get\n // identical gating (issue #1523 backward-compat).\n enabled: enabled ?? config.recallDirectAnswerEnabled,\n tokenOverlapFloor: config.recallDirectAnswerTokenOverlapFloor,\n importanceFloor: config.recallDirectAnswerImportanceFloor,\n ambiguityMargin: config.recallDirectAnswerAmbiguityMargin,\n eligibleTaxonomyBuckets: config.recallDirectAnswerEligibleTaxonomyBuckets,\n };\n\n // Short-circuit disabled case before touching any I/O.\n if (!eligibilityConfig.enabled) {\n return isDirectAnswerEligible({\n query,\n candidates: [],\n config: eligibilityConfig,\n queryEntityRefs,\n });\n }\n\n // Short-circuit empty-query case before any I/O. isDirectAnswerEligible\n // deterministically returns reason \"empty-query\" when the query\n // normalizes to zero searchable tokens; there's no point materializing\n // candidates just to reach the same verdict, and doing so would\n // surface avoidable upstream errors for requests that should exit\n // immediately.\n if (normalizeRecallTokens(query).length === 0) {\n return isDirectAnswerEligible({\n query,\n candidates: [],\n config: eligibilityConfig,\n queryEntityRefs,\n });\n }\n\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n const memories = await sources.listCandidateMemories({ namespace, abortSignal });\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n const candidates: DirectAnswerCandidate[] = [];\n\n for (const memory of memories) {\n // Throw rather than returning a partial verdict — a mid-loop abort\n // that left competing candidates unprocessed could otherwise surface\n // a spurious \"eligible\" result that the ambiguity gate never got a\n // chance to reject. The check repeats after every await so an\n // abort that lands during the in-flight I/O on the final memory\n // (after which no further iteration would exist) still stops us.\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n const trustZone = await sources.trustZoneFor(memory.frontmatter.id);\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n // Cheap pre-filter: non-trusted memories can't qualify, so skip\n // taxonomy and importance resolution for them.\n if (trustZone !== \"trusted\") continue;\n\n const decision = resolveCategory(\n memory.content,\n memory.frontmatter.category,\n sources.taxonomy,\n );\n const taxonomyBucket = decision.categoryId;\n if (!eligibilityConfig.eligibleTaxonomyBuckets.includes(taxonomyBucket)) continue;\n\n const importanceScore = sources.importanceFor(memory);\n\n candidates.push({\n memory,\n trustZone,\n taxonomyBucket,\n importanceScore,\n });\n }\n\n // Final check — if abort landed during the trust-zone await for the\n // last memory, the loop condition no longer fires. Guard before we\n // hand candidates to the eligibility gate.\n throwIfAborted(abortSignal, \"direct-answer wiring aborted\");\n\n return isDirectAnswerEligible({\n query,\n candidates,\n config: eligibilityConfig,\n queryEntityRefs,\n });\n}\n"],"mappings":"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","names":["alternatives"]}
@@ -11,13 +11,13 @@ import {
11
11
  } from "./chunk-D24OXEPB.js";
12
12
  import {
13
13
  EngramAccessInputError
14
- } from "./chunk-JIX3ZL2J.js";
14
+ } from "./chunk-UVUTV7CM.js";
15
15
  import {
16
16
  projectTagProjectId
17
17
  } from "./chunk-GYSYLGNE.js";
18
18
  import {
19
19
  validateBriefingFormat
20
- } from "./chunk-KHGE6PMF.js";
20
+ } from "./chunk-WXXLSZHA.js";
21
21
  import {
22
22
  resolvePrincipal
23
23
  } from "./chunk-UZYLX7M6.js";
@@ -3117,4 +3117,4 @@ ${body}`;
3117
3117
  export {
3118
3118
  EngramMcpServer
3119
3119
  };
3120
- //# sourceMappingURL=chunk-2HEZXPYU.js.map
3120
+ //# sourceMappingURL=chunk-Q2LQZYQ7.js.map
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  listNamespaces
3
- } from "./chunk-XB5P5P2L.js";
3
+ } from "./chunk-4T7P2HLJ.js";
4
4
  import {
5
5
  runConsolidationProvenanceCheck
6
- } from "./chunk-7XH7VJN4.js";
6
+ } from "./chunk-6T4LTI2F.js";
7
7
  import {
8
8
  reportBufferSurpriseDistribution
9
9
  } from "./chunk-YBPYIAA5.js";
@@ -15,6 +15,9 @@ import {
15
15
  import {
16
16
  resolvePluginEntry
17
17
  } from "./chunk-CMTINOFS.js";
18
+ import {
19
+ readJudgeVerdictStats
20
+ } from "./chunk-AJU4PJGY.js";
18
21
  import {
19
22
  resolveCuratedIncludeFilesStatePath,
20
23
  resolveNativeKnowledgeStatePath,
@@ -27,22 +30,19 @@ import {
27
30
  runEvalBenchmarkCiGate,
28
31
  validateEvalBenchmarkPack
29
32
  } from "./chunk-3PY7VHV7.js";
30
- import {
31
- readJudgeVerdictStats
32
- } from "./chunk-AJU4PJGY.js";
33
33
  import {
34
34
  parseConfig
35
35
  } from "./chunk-CTAV55JM.js";
36
36
  import {
37
37
  listMemoryGovernanceRuns,
38
38
  readMemoryGovernanceRunArtifact
39
- } from "./chunk-EXXBA5OM.js";
39
+ } from "./chunk-OI4BXFSB.js";
40
40
  import {
41
41
  analyzeGraphHealth
42
42
  } from "./chunk-Y56J7CXW.js";
43
43
  import {
44
44
  StorageManager
45
- } from "./chunk-VH6EIKVS.js";
45
+ } from "./chunk-WKMCC4NQ.js";
46
46
  import {
47
47
  lintWorkspaceFiles
48
48
  } from "./chunk-DM2T26WE.js";
@@ -1336,4 +1336,4 @@ export {
1336
1336
  runBenchmarkRecall,
1337
1337
  runOperatorRepair
1338
1338
  };
1339
- //# sourceMappingURL=chunk-YKX63GBK.js.map
1339
+ //# sourceMappingURL=chunk-QHWJG5C5.js.map
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  compareEntityTimestamps,
6
6
  normalizeEntityName
7
- } from "./chunk-VH6EIKVS.js";
7
+ } from "./chunk-WKMCC4NQ.js";
8
8
  import {
9
9
  sanitizeMemoryContent
10
10
  } from "./chunk-FVQJYWH7.js";
@@ -679,4 +679,4 @@ export {
679
679
  entityIndexVersion,
680
680
  entityRecentTranscriptLookbackHours
681
681
  };
682
- //# sourceMappingURL=chunk-T2AN3BSP.js.map
682
+ //# sourceMappingURL=chunk-QZ7ODIVL.js.map
@@ -0,0 +1,23 @@
1
+ // src/capabilities.ts
2
+ function resolveCapabilities(config) {
3
+ return Object.freeze({
4
+ rerankCache: config.rerankCacheEnabled,
5
+ recallDirectAnswer: config.recallDirectAnswerEnabled,
6
+ recallMemoryWorthFilter: config.recallMemoryWorthFilterEnabled,
7
+ recallMmr: config.recallMmrEnabled,
8
+ recallReasoningTraceBoost: config.recallReasoningTraceBoostEnabled,
9
+ recallPlannerLlm: config.recallPlannerLlmEnabled,
10
+ recallPlanner: config.recallPlannerEnabled,
11
+ recallConfidenceGate: config.recallConfidenceGateEnabled,
12
+ graphRecall: config.graphRecallEnabled,
13
+ // Optional flags: preserve the exact default-when-undefined semantics the
14
+ // migrated call sites used (`!== false` = default-on, `=== true` = default-off).
15
+ graphAssistInFullMode: config.graphAssistInFullModeEnabled !== false,
16
+ graphExpandedIntent: config.graphExpandedIntentEnabled === true
17
+ });
18
+ }
19
+
20
+ export {
21
+ resolveCapabilities
22
+ };
23
+ //# sourceMappingURL=chunk-RI5XBIZ6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/capabilities.ts"],"sourcesContent":["/**\n * CapabilitySet — recall-operation feature gates resolved once, then threaded.\n *\n * Issue #1523 (Phase 1 of epic #1520). Root cause this addresses: 161+\n * scattered `config.<flag>Enabled` reads mean each gate is re-derived at every\n * call site, and reviews keep finding parallel code paths where one branch\n * checks a gate the other forgot (CLAUDE.md rule 39 — the \"gate divergence\"\n * defect class). The fix is to resolve a frozen capability projection ONCE at\n * the top of the recall operation and pass it down explicitly.\n *\n * Scope of THIS module (first migration PR): only the recall-operation-scoped\n * flags below. Flags that are also read in graph construction, writes, CLI, or\n * the summarizer are deliberately deferred to a follow-up so we never leave a\n * single flag half-migrated (some sites on `caps.`, some on `config.`).\n *\n * Field naming: each field is the config flag name with the trailing `Enabled`\n * removed (`recallMmrEnabled` → `recallMmr`).\n *\n * This is plumbing, not a feature — there is deliberately NO `enabled` gate for\n * the CapabilitySet itself (rule 30 governs behavior changes; resolving and\n * threading a capability projection must stay behavior-preserving).\n */\n\nimport type { PluginConfig } from \"./types.js\";\n\n/**\n * Frozen projection of recall-operation feature gates.\n *\n * Every field is `readonly boolean`. The composition that maps a config flag to\n * a capability (including default-when-undefined semantics for optional flags)\n * lives ONLY in {@link resolveCapabilities} — call sites must read the\n * capability, never re-derive it from raw config.\n */\nexport interface CapabilitySet {\n /** `rerankCacheEnabled` — cache reranker scores across recall passes. */\n readonly rerankCache: boolean;\n /** `recallDirectAnswerEnabled` — observation-mode direct-answer tier. */\n readonly recallDirectAnswer: boolean;\n /** `recallMemoryWorthFilterEnabled` — Memory-Worth score reweighting. */\n readonly recallMemoryWorthFilter: boolean;\n /** `recallMmrEnabled` — maximal-marginal-relevance diversification. */\n readonly recallMmr: boolean;\n /** `recallReasoningTraceBoostEnabled` — boost reasoning-trace memories. */\n readonly recallReasoningTraceBoost: boolean;\n /** `recallPlannerLlmEnabled` — LLM-backed recall-mode planner. */\n readonly recallPlannerLlm: boolean;\n /** `recallPlannerEnabled` — recall-mode planner (heuristic + optional LLM). */\n readonly recallPlanner: boolean;\n /** `recallConfidenceGateEnabled` — Synapse-style confidence gate. */\n readonly recallConfidenceGate: boolean;\n /** `graphRecallEnabled` — graph-mode recall tier (gates planner graph mode). */\n readonly graphRecall: boolean;\n /** `graphAssistInFullModeEnabled` — graph-assist overlay in full mode. */\n readonly graphAssistInFullMode: boolean;\n /** `graphExpandedIntentEnabled` — promote broad-intent asks to graph mode. */\n readonly graphExpandedIntent: boolean;\n}\n\n/**\n * Resolve the recall-operation {@link CapabilitySet} from parsed config.\n *\n * Call this ONCE per recall operation (at the `recall()` / `recallInternal`\n * entry) and thread the result down. Composition lives here and only here.\n *\n * Session toggles are intentionally not a parameter yet: `session-toggles.ts`\n * is agent-scoped (per session/agent enable-disable of the whole plugin), not\n * flag-scoped — none of the flags projected here have a per-session override,\n * so there is nothing for a toggle argument to compose at this layer.\n */\nexport function resolveCapabilities(config: PluginConfig): CapabilitySet {\n return Object.freeze({\n rerankCache: config.rerankCacheEnabled,\n recallDirectAnswer: config.recallDirectAnswerEnabled,\n recallMemoryWorthFilter: config.recallMemoryWorthFilterEnabled,\n recallMmr: config.recallMmrEnabled,\n recallReasoningTraceBoost: config.recallReasoningTraceBoostEnabled,\n recallPlannerLlm: config.recallPlannerLlmEnabled,\n recallPlanner: config.recallPlannerEnabled,\n recallConfidenceGate: config.recallConfidenceGateEnabled,\n graphRecall: config.graphRecallEnabled,\n // Optional flags: preserve the exact default-when-undefined semantics the\n // migrated call sites used (`!== false` = default-on, `=== true` = default-off).\n graphAssistInFullMode: config.graphAssistInFullModeEnabled !== false,\n graphExpandedIntent: config.graphExpandedIntentEnabled === true,\n });\n}\n"],"mappings":";AAqEO,SAAS,oBAAoB,QAAqC;AACvE,SAAO,OAAO,OAAO;AAAA,IACnB,aAAa,OAAO;AAAA,IACpB,oBAAoB,OAAO;AAAA,IAC3B,yBAAyB,OAAO;AAAA,IAChC,WAAW,OAAO;AAAA,IAClB,2BAA2B,OAAO;AAAA,IAClC,kBAAkB,OAAO;AAAA,IACzB,eAAe,OAAO;AAAA,IACtB,sBAAsB,OAAO;AAAA,IAC7B,aAAa,OAAO;AAAA;AAAA;AAAA,IAGpB,uBAAuB,OAAO,iCAAiC;AAAA,IAC/D,qBAAqB,OAAO,+BAA+B;AAAA,EAC7D,CAAC;AACH;","names":[]}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-ZFXCQPNO.js";
5
5
  import {
6
6
  StorageManager
7
- } from "./chunk-VH6EIKVS.js";
7
+ } from "./chunk-WKMCC4NQ.js";
8
8
  import {
9
9
  isSafeRouteNamespace
10
10
  } from "./chunk-U3PN77QT.js";
@@ -126,6 +126,11 @@ var NamespaceStorageRouter = class {
126
126
  // entry is always removed when the promise settles, so the map cannot grow
127
127
  // unbounded (one transient entry per concurrently-resolving namespace).
128
128
  inFlightResolved = /* @__PURE__ */ new Map();
129
+ // Tracks every in-flight resolve-hook promise so callers can deterministically
130
+ // await the fire-and-forget registrations that `storageFor()` kicks off (see
131
+ // `whenResolveHooksSettled`). Entries are removed as each hook settles, so the
132
+ // set holds at most one promise per concurrently-resolving namespace.
133
+ pendingResolveHooks = /* @__PURE__ */ new Set();
129
134
  // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`
130
135
  // normalizes its input, so default-namespace branches must compare against the
131
136
  // normalized config default too — otherwise a whitespace-padded configured
@@ -180,7 +185,9 @@ var NamespaceStorageRouter = class {
180
185
  if (this.inFlightResolved.get(namespace) === storageDir) return;
181
186
  try {
182
187
  this.inFlightResolved.set(namespace, storageDir);
183
- Promise.resolve(hook(namespace, storageDir)).then(
188
+ const hookResult = Promise.resolve(hook(namespace, storageDir));
189
+ this.pendingResolveHooks.add(hookResult);
190
+ hookResult.then(
184
191
  (persisted) => {
185
192
  if (this.inFlightResolved.get(namespace) === storageDir) {
186
193
  this.inFlightResolved.delete(namespace);
@@ -188,6 +195,7 @@ var NamespaceStorageRouter = class {
188
195
  if (persisted !== false) {
189
196
  this.notifiedResolved.set(namespace, storageDir);
190
197
  }
198
+ this.pendingResolveHooks.delete(hookResult);
191
199
  },
192
200
  () => {
193
201
  if (this.inFlightResolved.get(namespace) === storageDir) {
@@ -196,6 +204,7 @@ var NamespaceStorageRouter = class {
196
204
  if (this.notifiedResolved.get(namespace) === storageDir) {
197
205
  this.notifiedResolved.delete(namespace);
198
206
  }
207
+ this.pendingResolveHooks.delete(hookResult);
199
208
  }
200
209
  );
201
210
  } catch {
@@ -204,6 +213,22 @@ var NamespaceStorageRouter = class {
204
213
  }
205
214
  }
206
215
  }
216
+ /**
217
+ * Resolve once every in-flight `onResolve` registration has settled.
218
+ *
219
+ * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side
220
+ * effect (e.g. `registerResolved(...)`) is not observable the moment
221
+ * `storageFor()` returns. Callers that must act on that side effect — notably
222
+ * tests asserting the catalog was updated — should await this instead of
223
+ * racing a timer. Resolves immediately when no hook is registered or nothing
224
+ * is in flight. The loop re-checks because a settling hook could, in
225
+ * principle, trigger a follow-on resolution.
226
+ */
227
+ async whenResolveHooksSettled() {
228
+ while (this.pendingResolveHooks.size > 0) {
229
+ await Promise.allSettled([...this.pendingResolveHooks]);
230
+ }
231
+ }
207
232
  };
208
233
 
209
234
  export {
@@ -211,4 +236,4 @@ export {
211
236
  resolveNamespaceStorageRoot,
212
237
  NamespaceStorageRouter
213
238
  };
214
- //# sourceMappingURL=chunk-7ILWCUWH.js.map
239
+ //# sourceMappingURL=chunk-TJ7HH5LB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/namespaces/storage.ts"],"sourcesContent":["import path from \"node:path\";\nimport { access, lstat, readdir } from \"node:fs/promises\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport { StorageManager } from \"../storage.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { ALL_CATEGORY_DIRS } from \"../utils/category-dir.js\";\nimport { namespaceIdentityToken, normalizeNamespaceIdentity } from \"./identity.js\";\n\nasync function exists(p: string): Promise<boolean> {\n try {\n await access(p);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function hasStoredEntries(p: string): Promise<boolean> {\n try {\n const entry = await lstat(p);\n if (entry.isSymbolicLink()) return true;\n if (!entry.isDirectory()) return true;\n const children = await readdir(p, { withFileTypes: true });\n for (const child of children) {\n const childPath = path.join(p, child.name);\n if (child.isSymbolicLink() || child.isFile()) return true;\n if (child.isDirectory() && (await hasStoredEntries(childPath))) return true;\n }\n return false;\n } catch {\n return false;\n }\n}\n\n// Build a per-namespace directory under `<memoryDir>/namespaces` and assert the\n// resolved path stays inside that base. Namespace identifiers can originate from\n// operator config (config.defaultNamespace) and request-derived routing, so this\n// containment check prevents directory traversal (CodeQL js/path-injection).\n// For safe segments this returns exactly `path.join(base, segment)`, so there is\n// no behavioral change for valid namespaces.\nfunction resolveNamespaceDir(memoryDir: string, segment: string): string {\n // Mirror isSafeRouteNamespace's separator/parent-ref rejection (without its\n // 64-char cap, so identity tokens still pass). Rejecting separators and \"..\"\n // up front keeps the value a single contained child of <memoryDir>/namespaces.\n if (\n segment.length === 0 ||\n segment.includes(\"/\") ||\n segment.includes(\"\\\\\") ||\n segment.includes(\"..\") ||\n path.isAbsolute(segment)\n ) {\n throw new Error(`unsafe namespace path segment: ${segment}`);\n }\n return path.join(memoryDir, \"namespaces\", segment);\n}\n\nconst LEGACY_NAMESPACE_CONTENT_CHILDREN = [\n ...ALL_CATEGORY_DIRS,\n \"entities\",\n \"artifacts\",\n \"identity\",\n \"config\",\n \"summaries\",\n \"profile.md\",\n] as const;\n\nconst LEGACY_NAMESPACE_RUNTIME_CHILDREN = [\"state\"] as const;\n\nasync function hasAnyLegacyData(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await hasStoredEntries(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\nasync function hasAnyNamespaceStorageMarker(\n rootDir: string,\n options: { includeRuntimeState?: boolean } = {},\n): Promise<boolean> {\n const children = options.includeRuntimeState === true\n ? [...LEGACY_NAMESPACE_CONTENT_CHILDREN, ...LEGACY_NAMESPACE_RUNTIME_CHILDREN]\n : LEGACY_NAMESPACE_CONTENT_CHILDREN;\n for (const child of children) {\n if (await exists(path.join(rootDir, child))) return true;\n }\n return false;\n}\n\n/**\n * Storage routing for namespaces.\n *\n * Compatibility note:\n * - When namespaces are enabled, existing raw namespace roots are preserved.\n * New namespace roots use tokenized names under `memoryDir/namespaces/<token>`.\n * - The default namespace continues to use the legacy `memoryDir` root unless the caller\n * has created `memoryDir/namespaces/<defaultNamespace>` (in which case we use that).\n *\n * This avoids surprising \"lost memories\" when an install flips namespaces on without\n * migrating existing data.\n */\n/**\n * Optional hooks for the storage router. `onResolve` fires whenever a namespace's\n * storage is resolved/created, so a downstream consumer (e.g. the namespace\n * catalog, issue #1499) can register the namespace. The hook MUST NOT throw into\n * the router; the router invokes it defensively and a hook failure never affects\n * storage resolution.\n *\n * The hook MAY return (or resolve to) a boolean indicating whether the\n * registration actually PERSISTED (round 6, codex P2 — NEFoX). When it resolves\n * to `false` (a dropped/no-op registration), the router does NOT mark the\n * (namespace, storageDir) pair as notified, so the next resolve RETRIES it\n * instead of suppressing it forever. A `void`/`undefined` result is treated as\n * success (legacy hooks).\n */\nexport interface NamespaceStorageRouterHooks {\n onResolve?: (\n namespace: string,\n storageDir: string,\n ) => void | boolean | Promise<void | boolean>;\n}\n\n/**\n * Resolve the runtime storage root for the configured DEFAULT namespace.\n *\n * Shared between the live router (`NamespaceStorageRouter.defaultNamespaceRoot`)\n * and the rebuildable catalog (`NamespaceCatalog.rebuildFromDisk`) so the two\n * can never diverge (CLAUDE.md rule #22/#42 — read & write paths resolve through\n * the same logic). The contract is: while legacy memory data still lives\n * directly under `memoryDir`, the default root stays `memoryDir`; only once the\n * legacy root is empty and a `namespaces/<default|token>` dir holds data does\n * the default migrate into that tokenized/legacy-named dir.\n */\nexport async function resolveDefaultNamespaceRoot(config: PluginConfig): Promise<string> {\n if (!config.namespacesEnabled) {\n return config.memoryDir;\n }\n\n // Build the legacy default root from the NORMALIZED (trimmed) name so a\n // whitespace-padded `defaultNamespace` still finds the live `namespaces/default`\n // root (NIabe). `storageFor()` classifies the trimmed value as the default, and\n // the on-disk legacy dir is created under the trimmed name; using the raw spaced\n // name here would look for `namespaces/<spaced>` and miss the real root, falling\n // back to memoryDir/tokenized. `namespaceIdentityToken` already normalizes\n // internally, so the tokenized path is unaffected.\n const defaultIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n const legacyNsDir = resolveNamespaceDir(config.memoryDir, defaultIdentity);\n const tokenizedNsDir = resolveNamespaceDir(\n config.memoryDir,\n namespaceIdentityToken(config.defaultNamespace),\n );\n const tokenizedHasData =\n (await exists(tokenizedNsDir)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedNsDir, { includeRuntimeState: true }));\n const nsDir = tokenizedHasData\n ? tokenizedNsDir\n : (await exists(legacyNsDir))\n ? legacyNsDir\n : tokenizedNsDir;\n return (await exists(nsDir)) && !(await hasAnyLegacyData(config.memoryDir))\n ? nsDir\n : config.memoryDir;\n}\n\n/**\n * Resolve the runtime storage root for ANY namespace exactly as the live router\n * would (`NamespaceStorageRouter.namespaceRoot`). Shared so the rebuildable\n * catalog records the SAME on-disk root the router routes to — a recall/read\n * touch must not guess `namespaces/<token>` when the router actually serves a\n * legacy raw-name dir or a migrated default root (CLAUDE.md rule #22/#42; round\n * 4, cursor Medium). The default namespace delegates to `resolveDefaultNamespaceRoot`;\n * every other namespace prefers the tokenized root when it has a storage marker,\n * else a legacy raw-name dir when present, else the tokenized root.\n */\nexport async function resolveNamespaceStorageRoot(\n config: PluginConfig,\n namespace: string,\n): Promise<string> {\n if (!config.namespacesEnabled) return config.memoryDir;\n // Compare on NORMALIZED identity so a whitespace-padded configured default name\n // still routes to the default root rather than a tokenized non-default dir\n // (NH-FH). The catalog keys records by the same normalized identity.\n if (normalizeNamespaceIdentity(namespace) === normalizeNamespaceIdentity(config.defaultNamespace)) {\n return resolveDefaultNamespaceRoot(config);\n }\n const legacyRoot = resolveNamespaceDir(config.memoryDir, namespace);\n const tokenizedRoot = resolveNamespaceDir(config.memoryDir, namespaceIdentityToken(namespace));\n if (\n (await exists(tokenizedRoot)) &&\n (await hasAnyNamespaceStorageMarker(tokenizedRoot, { includeRuntimeState: true }))\n ) {\n return tokenizedRoot;\n }\n return (await exists(legacyRoot)) ? legacyRoot : tokenizedRoot;\n}\n\nexport class NamespaceStorageRouter {\n private readonly cache = new Map<string, StorageManager>();\n private defaultNsRootResolved: string | null = null;\n // Dedup the resolve hook (round 6, cursor Medium — NCNL2). Recall/extraction\n // call `storageFor` repeatedly; firing `onResolve` (→ catalog loadCompacted +\n // append) on every cache hit grows `namespaces.jsonl` without bound between\n // rebuilds. We fire the hook only when the (namespace, storageDir) pair is new\n // or its dir changed, so a steady-state cache hit is a no-op for the catalog.\n private readonly notifiedResolved = new Map<string, string>();\n // In-flight resolve-hook dedup (NFJV-, codex P2). The catalog's `onResolve`\n // hook is ASYNC (it returns `registerResolved(...)`), so `notifiedResolved` is\n // only set after the hook's promise SETTLES. Without tracking the in-flight\n // window, a burst of `storageFor()` cache hits for the SAME namespace before\n // the first registration finishes would each pass the `notifiedResolved` guard\n // and fire their OWN `onResolve` — queueing N duplicate catalog touches + lock\n // acquisitions despite the once-per-namespace intent. We therefore record the\n // (namespace → storageDir) being registered BEFORE awaiting the hook so a\n // concurrent call for the same pair skips firing. On SUCCESS the pair is\n // promoted to `notifiedResolved` (future calls skip permanently); on `false`\n // (dropped touch — e.g. rebuild-lock timeout) OR rejection the in-flight marker\n // is CLEARED so a later `storageFor()` can RETRY the dropped registration. The\n // entry is always removed when the promise settles, so the map cannot grow\n // unbounded (one transient entry per concurrently-resolving namespace).\n private readonly inFlightResolved = new Map<string, string>();\n // Tracks every in-flight resolve-hook promise so callers can deterministically\n // await the fire-and-forget registrations that `storageFor()` kicks off (see\n // `whenResolveHooksSettled`). Entries are removed as each hook settles, so the\n // set holds at most one promise per concurrently-resolving namespace.\n private readonly pendingResolveHooks = new Set<Promise<unknown>>();\n\n // Normalized (trimmed) default namespace identity (NH-FH). `storageFor`\n // normalizes its input, so default-namespace branches must compare against the\n // normalized config default too — otherwise a whitespace-padded configured\n // default name routes the default namespace to a tokenized non-default root.\n private readonly defaultNamespaceIdentity: string;\n\n constructor(\n private readonly config: PluginConfig,\n private readonly hooks: NamespaceStorageRouterHooks = {},\n ) {\n this.defaultNamespaceIdentity = normalizeNamespaceIdentity(config.defaultNamespace);\n }\n\n private async defaultNamespaceRoot(): Promise<string> {\n this.defaultNsRootResolved = await resolveDefaultNamespaceRoot(this.config);\n return this.defaultNsRootResolved;\n }\n\n private async namespaceRoot(namespace: string): Promise<string> {\n // NOTE: only used after defaultNamespaceRoot() resolution.\n if (!this.config.namespacesEnabled) return this.config.memoryDir;\n if (normalizeNamespaceIdentity(namespace) === this.defaultNamespaceIdentity) {\n return this.defaultNsRootResolved ?? this.config.memoryDir;\n }\n return resolveNamespaceStorageRoot(this.config, namespace);\n }\n\n async storageFor(namespace: string): Promise<StorageManager> {\n const ns = normalizeNamespaceIdentity(namespace || this.config.defaultNamespace);\n if (ns !== this.defaultNamespaceIdentity && !isSafeRouteNamespace(ns)) {\n throw new Error(`unsafe namespace: ${ns}`);\n }\n // Even when the default namespace is exempt from the check above, every\n // on-disk path is built through resolveNamespaceDir(), which rejects\n // traversal segments — so an unsafe configured default still cannot escape\n // <memoryDir>/namespaces (CodeQL js/path-injection).\n\n let root: string;\n if (ns === this.defaultNamespaceIdentity) {\n root = await this.defaultNamespaceRoot();\n const cached = this.cache.get(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n } else {\n const cached = this.cache.get(ns);\n root = await this.namespaceRoot(ns);\n if (cached && cached.dir === root) {\n this.notifyResolved(ns, root);\n return cached;\n }\n }\n\n const sm = new StorageManager(root, this.config.entitySchemas);\n // Propagate the inline-attribution template so that router-created storages\n // (used by extraction and shared-promotion paths) strip citations consistently,\n // matching the behaviour of the primary this.storage instance in the orchestrator.\n sm.citationTemplate = this.config.inlineSourceAttributionFormat;\n this.cache.set(ns, sm);\n this.notifyResolved(ns, root);\n return sm;\n }\n\n /**\n * Fire the resolve hook defensively. A hook failure (e.g. a catalog write\n * error) MUST NOT crash storage resolution — see CLAUDE.md gotcha #13.\n */\n private notifyResolved(namespace: string, storageDir: string): void {\n const hook = this.hooks.onResolve;\n if (!hook) return;\n // Skip when we've already SUCCESSFULLY notified this exact (namespace,\n // storageDir) — a steady-state cache hit must not re-append to the catalog\n // log (NCNL2). A changed dir (rare: migration/realignment) still re-fires\n // once. We mark the pair as notified ONLY AFTER the hook succeeds, and CLEAR\n // it on failure, so a dropped registration (e.g. rebuild-lock timeout) is\n // RETRIED on the next cache hit instead of being suppressed forever (round 6,\n // cursor Medium — ND3EJ).\n if (this.notifiedResolved.get(namespace) === storageDir) return;\n // In-flight dedup (NFJV-, codex P2): if a registration for this exact\n // (namespace, storageDir) is already AWAITING its async hook, do not fire a\n // second one. Without this, concurrent cache-hit bursts before the first\n // append settles each pass the `notifiedResolved` guard above and queue\n // duplicate catalog touches/lock acquisitions. A pair with a DIFFERENT\n // in-flight dir (rare mid-migration realignment) still fires once.\n if (this.inFlightResolved.get(namespace) === storageDir) return;\n try {\n // Handle BOTH synchronous throws and asynchronous rejections (round 6,\n // codex P2 — NDo8C). The hook may be `async`; its rejected promise would\n // bypass this try/catch and, where unhandled rejections are fatal, crash\n // storage resolution. Mark the dedup pair as notified ONLY when the hook\n // resolves to a PERSISTED result (round 6, codex P2 — NEFoX): a result of\n // `false` means the registration was dropped/no-op (e.g. rebuild-lock\n // timeout), so we must NOT suppress its retry. `void`/`undefined` is treated\n // as success for legacy hooks. On rejection we leave it un-notified to retry.\n //\n // Record the in-flight marker BEFORE awaiting so concurrent calls for the\n // same pair skip (NFJV-). It is always cleared once the promise settles, so\n // the map holds at most one transient entry per concurrently-resolving\n // namespace and cannot grow unbounded.\n this.inFlightResolved.set(namespace, storageDir);\n const hookResult = Promise.resolve(hook(namespace, storageDir));\n // Track the in-flight promise so `whenResolveHooksSettled()` can await it.\n this.pendingResolveHooks.add(hookResult);\n hookResult.then(\n (persisted) => {\n // Clear the in-flight marker ONLY if it is still ours (a newer resolve\n // for a different dir may have replaced it).\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (persisted !== false) {\n this.notifiedResolved.set(namespace, storageDir);\n }\n // On `false` (dropped touch) we intentionally do NOT mark notified, so\n // a later `storageFor()` retries the registration. Clearing the\n // in-flight marker above is what re-enables that retry.\n this.pendingResolveHooks.delete(hookResult);\n },\n () => {\n // Registration failed — clear in-flight AND do NOT mark as notified, so\n // it is retried on the next cache hit.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n if (this.notifiedResolved.get(namespace) === storageDir) {\n this.notifiedResolved.delete(namespace);\n }\n this.pendingResolveHooks.delete(hookResult);\n },\n );\n } catch {\n // Synchronous throw: clear any in-flight marker we just set and leave the\n // pair un-notified so a later resolve retries.\n if (this.inFlightResolved.get(namespace) === storageDir) {\n this.inFlightResolved.delete(namespace);\n }\n }\n }\n\n /**\n * Resolve once every in-flight `onResolve` registration has settled.\n *\n * `storageFor()` fires the resolve hook fire-and-forget, so its catalog side\n * effect (e.g. `registerResolved(...)`) is not observable the moment\n * `storageFor()` returns. Callers that must act on that side effect — notably\n * tests asserting the catalog was updated — should await this instead of\n * racing a timer. Resolves immediately when no hook is registered or nothing\n * is in flight. The loop re-checks because a settling hook could, in\n * principle, trigger a follow-on resolution.\n */\n async whenResolveHooksSettled(): Promise<void> {\n while (this.pendingResolveHooks.size > 0) {\n await Promise.allSettled([...this.pendingResolveHooks]);\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ assembleCompressedHistory
3
+ } from "./chunk-KCYE2MZM.js";
1
4
  import {
2
5
  ensureLcmStateDir,
3
6
  openLcmDatabase
@@ -14,9 +17,6 @@ import {
14
17
  import {
15
18
  LcmWorkQueue
16
19
  } from "./chunk-TPDBFYEG.js";
17
- import {
18
- assembleCompressedHistory
19
- } from "./chunk-KCYE2MZM.js";
20
20
  import {
21
21
  log
22
22
  } from "./chunk-2ODBA7MQ.js";
@@ -513,4 +513,4 @@ export {
513
513
  extractLcmConfig,
514
514
  LcmEngine
515
515
  };
516
- //# sourceMappingURL=chunk-V25ZAOSB.js.map
516
+ //# sourceMappingURL=chunk-UOBLE67F.js.map