@graphorin/memory 0.5.0 → 0.6.1

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 (236) hide show
  1. package/CHANGELOG.md +102 -1
  2. package/README.md +54 -37
  3. package/dist/conflict/index.d.ts +3 -1
  4. package/dist/conflict/index.js +1 -1
  5. package/dist/conflict/locale-packs/en.d.ts +13 -0
  6. package/dist/conflict/locale-packs/en.d.ts.map +1 -0
  7. package/dist/conflict/locale-packs/en.js +1 -1
  8. package/dist/conflict/locale-packs/en.js.map +1 -1
  9. package/dist/conflict/pipeline.d.ts +2 -2
  10. package/dist/conflict/pipeline.js +3 -3
  11. package/dist/conflict/pipeline.js.map +1 -1
  12. package/dist/conflict/stages/helpers.js +1 -1
  13. package/dist/conflict/stages/helpers.js.map +1 -1
  14. package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -1
  15. package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -1
  16. package/dist/conflict/stages/stage3-heuristic-regex.js +1 -1
  17. package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
  18. package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -1
  19. package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -1
  20. package/dist/conflict/types.d.ts +7 -7
  21. package/dist/conflict/types.js +1 -1
  22. package/dist/conflict/types.js.map +1 -1
  23. package/dist/consolidator/budget.js +16 -3
  24. package/dist/consolidator/budget.js.map +1 -1
  25. package/dist/consolidator/decay.d.ts +13 -2
  26. package/dist/consolidator/decay.js +17 -5
  27. package/dist/consolidator/decay.js.map +1 -1
  28. package/dist/consolidator/dlq.js +1 -1
  29. package/dist/consolidator/dlq.js.map +1 -1
  30. package/dist/consolidator/errors.js +2 -2
  31. package/dist/consolidator/errors.js.map +1 -1
  32. package/dist/consolidator/index.js +8 -6
  33. package/dist/consolidator/index.js.map +1 -1
  34. package/dist/consolidator/lock.js +1 -1
  35. package/dist/consolidator/lock.js.map +1 -1
  36. package/dist/consolidator/noise-filter.js.map +1 -1
  37. package/dist/consolidator/phases/deep.js +6 -2
  38. package/dist/consolidator/phases/deep.js.map +1 -1
  39. package/dist/consolidator/phases/induce.d.ts +10 -10
  40. package/dist/consolidator/phases/induce.js +6 -6
  41. package/dist/consolidator/phases/induce.js.map +1 -1
  42. package/dist/consolidator/phases/learned-context.js +129 -0
  43. package/dist/consolidator/phases/learned-context.js.map +1 -0
  44. package/dist/consolidator/phases/light.js +2 -1
  45. package/dist/consolidator/phases/light.js.map +1 -1
  46. package/dist/consolidator/phases/reflect.js +5 -4
  47. package/dist/consolidator/phases/reflect.js.map +1 -1
  48. package/dist/consolidator/phases/standard.js +37 -8
  49. package/dist/consolidator/phases/standard.js.map +1 -1
  50. package/dist/consolidator/reconcile.js +1 -1
  51. package/dist/consolidator/reconcile.js.map +1 -1
  52. package/dist/consolidator/runtime.d.ts +4 -4
  53. package/dist/consolidator/runtime.d.ts.map +1 -1
  54. package/dist/consolidator/runtime.js +73 -16
  55. package/dist/consolidator/runtime.js.map +1 -1
  56. package/dist/consolidator/scheduler.d.ts +5 -5
  57. package/dist/consolidator/scheduler.js +1 -1
  58. package/dist/consolidator/scheduler.js.map +1 -1
  59. package/dist/consolidator/triggers.js +7 -7
  60. package/dist/consolidator/triggers.js.map +1 -1
  61. package/dist/consolidator/types.d.ts +68 -20
  62. package/dist/consolidator/types.d.ts.map +1 -1
  63. package/dist/consolidator/types.js +16 -6
  64. package/dist/consolidator/types.js.map +1 -1
  65. package/dist/context-engine/annotations.d.ts +9 -9
  66. package/dist/context-engine/annotations.js +3 -3
  67. package/dist/context-engine/annotations.js.map +1 -1
  68. package/dist/context-engine/auto-recall.js.map +1 -1
  69. package/dist/context-engine/compaction/clear-tool-results.d.ts +18 -3
  70. package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -1
  71. package/dist/context-engine/compaction/clear-tool-results.js +26 -5
  72. package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
  73. package/dist/context-engine/compaction/compactor.js +19 -6
  74. package/dist/context-engine/compaction/compactor.js.map +1 -1
  75. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +4 -0
  76. package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -1
  77. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +1 -0
  78. package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -1
  79. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +2 -1
  80. package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -1
  81. package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts +28 -0
  82. package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts.map +1 -0
  83. package/dist/context-engine/compaction/hooks/reanchor-recent-results.js +71 -0
  84. package/dist/context-engine/compaction/hooks/reanchor-recent-results.js.map +1 -0
  85. package/dist/context-engine/compaction/hooks/types.d.ts +15 -3
  86. package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -1
  87. package/dist/context-engine/compaction/index.d.ts +1 -0
  88. package/dist/context-engine/compaction/index.js +2 -1
  89. package/dist/context-engine/compaction/templates/summary-9-section.d.ts +10 -3
  90. package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
  91. package/dist/context-engine/compaction/templates/summary-9-section.js +42 -5
  92. package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
  93. package/dist/context-engine/compaction/thresholds.js.map +1 -1
  94. package/dist/context-engine/compaction/types.d.ts +47 -7
  95. package/dist/context-engine/compaction/types.d.ts.map +1 -1
  96. package/dist/context-engine/engine.d.ts +23 -4
  97. package/dist/context-engine/engine.d.ts.map +1 -1
  98. package/dist/context-engine/engine.js +69 -15
  99. package/dist/context-engine/engine.js.map +1 -1
  100. package/dist/context-engine/index.d.ts +1 -0
  101. package/dist/context-engine/index.d.ts.map +1 -1
  102. package/dist/context-engine/index.js +2 -1
  103. package/dist/context-engine/locale-packs/en.js +14 -13
  104. package/dist/context-engine/locale-packs/en.js.map +1 -1
  105. package/dist/context-engine/locale-packs/types.d.ts +10 -10
  106. package/dist/context-engine/locale-packs/types.d.ts.map +1 -1
  107. package/dist/context-engine/locale-packs/types.js.map +1 -1
  108. package/dist/context-engine/metadata.d.ts +1 -1
  109. package/dist/context-engine/metadata.js +2 -2
  110. package/dist/context-engine/metadata.js.map +1 -1
  111. package/dist/context-engine/preambles/inbound-en.d.ts +1 -1
  112. package/dist/context-engine/preambles/inbound-en.js +1 -1
  113. package/dist/context-engine/preambles/inbound-en.js.map +1 -1
  114. package/dist/context-engine/privacy-filter.d.ts +2 -2
  115. package/dist/context-engine/privacy-filter.js +1 -1
  116. package/dist/context-engine/privacy-filter.js.map +1 -1
  117. package/dist/context-engine/templates/base-en.d.ts +1 -1
  118. package/dist/context-engine/templates/base-en.js +1 -1
  119. package/dist/context-engine/templates/base-en.js.map +1 -1
  120. package/dist/context-engine/templates/composer.d.ts +1 -1
  121. package/dist/context-engine/templates/composer.js.map +1 -1
  122. package/dist/context-engine/token-budget.d.ts +1 -1
  123. package/dist/context-engine/token-budget.js +1 -1
  124. package/dist/context-engine/token-budget.js.map +1 -1
  125. package/dist/context-engine/token-counter.d.ts +16 -7
  126. package/dist/context-engine/token-counter.d.ts.map +1 -1
  127. package/dist/context-engine/token-counter.js +31 -9
  128. package/dist/context-engine/token-counter.js.map +1 -1
  129. package/dist/errors/index.d.ts +2 -2
  130. package/dist/errors/index.js +4 -4
  131. package/dist/errors/index.js.map +1 -1
  132. package/dist/facade.d.ts +44 -15
  133. package/dist/facade.d.ts.map +1 -1
  134. package/dist/facade.js +16 -8
  135. package/dist/facade.js.map +1 -1
  136. package/dist/graph/entity-resolver.d.ts +2 -2
  137. package/dist/graph/entity-resolver.js +2 -2
  138. package/dist/graph/entity-resolver.js.map +1 -1
  139. package/dist/index.d.ts +9 -6
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +11 -5
  142. package/dist/index.js.map +1 -1
  143. package/dist/internal/contextualize.js +2 -2
  144. package/dist/internal/contextualize.js.map +1 -1
  145. package/dist/internal/embedder-binding.js +1 -1
  146. package/dist/internal/embedder-binding.js.map +1 -1
  147. package/dist/internal/id.js +1 -1
  148. package/dist/internal/id.js.map +1 -1
  149. package/dist/internal/injection-heuristics.js +25 -2
  150. package/dist/internal/injection-heuristics.js.map +1 -1
  151. package/dist/internal/spans.js +1 -1
  152. package/dist/internal/spans.js.map +1 -1
  153. package/dist/internal/storage-adapter.d.ts +71 -22
  154. package/dist/internal/storage-adapter.d.ts.map +1 -1
  155. package/dist/migration/embedder-migration.d.ts +7 -7
  156. package/dist/migration/embedder-migration.js.map +1 -1
  157. package/dist/package.js +6 -0
  158. package/dist/package.js.map +1 -0
  159. package/dist/search/explain.d.ts +7 -4
  160. package/dist/search/explain.d.ts.map +1 -1
  161. package/dist/search/explain.js +1 -1
  162. package/dist/search/explain.js.map +1 -1
  163. package/dist/search/fit-weights.d.ts +48 -0
  164. package/dist/search/fit-weights.d.ts.map +1 -0
  165. package/dist/search/fit-weights.js +70 -0
  166. package/dist/search/fit-weights.js.map +1 -0
  167. package/dist/search/graph-ppr.d.ts +36 -0
  168. package/dist/search/graph-ppr.d.ts.map +1 -0
  169. package/dist/search/graph-ppr.js +38 -0
  170. package/dist/search/graph-ppr.js.map +1 -0
  171. package/dist/search/index.d.ts +4 -1
  172. package/dist/search/index.js +4 -1
  173. package/dist/search/iterative.d.ts +15 -8
  174. package/dist/search/iterative.d.ts.map +1 -1
  175. package/dist/search/iterative.js +10 -9
  176. package/dist/search/iterative.js.map +1 -1
  177. package/dist/search/query-transform.d.ts +6 -6
  178. package/dist/search/query-transform.js +6 -6
  179. package/dist/search/query-transform.js.map +1 -1
  180. package/dist/search/rrf.d.ts +4 -4
  181. package/dist/search/rrf.js +4 -4
  182. package/dist/search/rrf.js.map +1 -1
  183. package/dist/search/trust.d.ts +30 -0
  184. package/dist/search/trust.d.ts.map +1 -0
  185. package/dist/search/trust.js +50 -0
  186. package/dist/search/trust.js.map +1 -0
  187. package/dist/search/types.d.ts +1 -1
  188. package/dist/tiers/episodic-memory.d.ts +6 -4
  189. package/dist/tiers/episodic-memory.d.ts.map +1 -1
  190. package/dist/tiers/episodic-memory.js +3 -2
  191. package/dist/tiers/episodic-memory.js.map +1 -1
  192. package/dist/tiers/index.d.ts +1 -1
  193. package/dist/tiers/index.js +1 -1
  194. package/dist/tiers/insight-memory.d.ts +4 -4
  195. package/dist/tiers/insight-memory.js +3 -3
  196. package/dist/tiers/insight-memory.js.map +1 -1
  197. package/dist/tiers/procedural-memory.d.ts +29 -7
  198. package/dist/tiers/procedural-memory.d.ts.map +1 -1
  199. package/dist/tiers/procedural-memory.js +66 -6
  200. package/dist/tiers/procedural-memory.js.map +1 -1
  201. package/dist/tiers/semantic-memory.d.ts +111 -30
  202. package/dist/tiers/semantic-memory.d.ts.map +1 -1
  203. package/dist/tiers/semantic-memory.js +120 -27
  204. package/dist/tiers/semantic-memory.js.map +1 -1
  205. package/dist/tiers/session-memory.d.ts +7 -7
  206. package/dist/tiers/session-memory.js +7 -7
  207. package/dist/tiers/session-memory.js.map +1 -1
  208. package/dist/tiers/shared-memory.d.ts +1 -1
  209. package/dist/tiers/shared-memory.js +1 -1
  210. package/dist/tiers/shared-memory.js.map +1 -1
  211. package/dist/tiers/working-memory.d.ts +3 -3
  212. package/dist/tiers/working-memory.js +2 -2
  213. package/dist/tiers/working-memory.js.map +1 -1
  214. package/dist/tools/block-tools.d.ts +3 -3
  215. package/dist/tools/block-tools.js +3 -3
  216. package/dist/tools/block-tools.js.map +1 -1
  217. package/dist/tools/fact-tools.d.ts +48 -31
  218. package/dist/tools/fact-tools.d.ts.map +1 -1
  219. package/dist/tools/fact-tools.js +30 -18
  220. package/dist/tools/fact-tools.js.map +1 -1
  221. package/dist/tools/index.d.ts +9 -2
  222. package/dist/tools/index.d.ts.map +1 -1
  223. package/dist/tools/index.js +4 -2
  224. package/dist/tools/index.js.map +1 -1
  225. package/dist/tools/recall-tools.d.ts +40 -20
  226. package/dist/tools/recall-tools.d.ts.map +1 -1
  227. package/dist/tools/recall-tools.js +29 -10
  228. package/dist/tools/recall-tools.js.map +1 -1
  229. package/dist/tools/runbook-tools.d.ts +76 -0
  230. package/dist/tools/runbook-tools.d.ts.map +1 -0
  231. package/dist/tools/runbook-tools.js +53 -0
  232. package/dist/tools/runbook-tools.js.map +1 -0
  233. package/dist/tools/taint.js +33 -0
  234. package/dist/tools/taint.js.map +1 -0
  235. package/dist/tools/types.d.ts +1 -1
  236. package/package.json +8 -8
@@ -5,11 +5,11 @@ import { newMemoryId } from "../internal/id.js";
5
5
  import { detectMemoryInjection } from "../internal/injection-heuristics.js";
6
6
 
7
7
  //#region src/tiers/procedural-memory.ts
8
- /** Default priority for an *induced* procedure below the author default
8
+ /** Default priority for an *induced* procedure - below the author default
9
9
  * (50) because an induced, still-quarantined procedure is provisional. */
10
10
  const INDUCED_PRIORITY = 40;
11
11
  /**
12
- * `ProceduralMemory` standing orders activated when the agent's
12
+ * `ProceduralMemory` - standing orders activated when the agent's
13
13
  * current context matches the rule's predicate. The activation rules
14
14
  * are deterministic so the agent runtime + ContextEngine can render
15
15
  * the active set into the system prompt every step.
@@ -44,12 +44,12 @@ var ProceduralMemory = class {
44
44
  * (MCON-2 part 4). A success increments the rule's persistent
45
45
  * `successCount`; when `procedurePromotion.afterSuccesses` is
46
46
  * configured and a QUARANTINED procedure reaches the threshold it is
47
- * promoted through {@link validate} the injection gate still
47
+ * promoted through {@link validate} - the injection gate still
48
48
  * applies, so a flagged text refuses promotion (surfaced as
49
49
  * `refused: true`) no matter how many successes accumulate.
50
50
  * Failures are observed but not persisted (no negative counter yet).
51
51
  *
52
- * Callers decide what "success" means typically
52
+ * Callers decide what "success" means - typically
53
53
  * `checkSuccessCriteria(...)` over the procedure's stored
54
54
  * `successCriteria`, or an operator's judgement.
55
55
  *
@@ -122,6 +122,7 @@ var ProceduralMemory = class {
122
122
  ...input.steps !== void 0 ? { steps: Object.freeze([...input.steps]) } : {},
123
123
  ...input.variables !== void 0 ? { variables: Object.freeze([...input.variables]) } : {},
124
124
  ...input.successCriteria !== void 0 ? { successCriteria: Object.freeze([...input.successCriteria]) } : {},
125
+ ...input.owner !== void 0 ? { owner: input.owner } : {},
125
126
  createdAt: now,
126
127
  updatedAt: now
127
128
  };
@@ -166,6 +167,7 @@ var ProceduralMemory = class {
166
167
  successCriteria: Object.freeze([...induced.successCriteria]),
167
168
  provenance: "induction",
168
169
  status: "quarantined",
170
+ owner: "agent",
169
171
  createdAt: now,
170
172
  updatedAt: now
171
173
  };
@@ -211,13 +213,67 @@ var ProceduralMemory = class {
211
213
  * always-active so callers do not silently lose rules.
212
214
  *
213
215
  * **Quarantined procedures are excluded** (P1-4 / P2-2): an induced
214
- * procedure must not drive actions until validated, so activation which
215
- * feeds the system prompt never surfaces it.
216
+ * procedure must not drive actions until validated, so activation - which
217
+ * feeds the system prompt - never surfaces it.
216
218
  */
217
219
  async activate(scope, context = {}) {
218
220
  return (await this.list(scope)).filter((rule) => rule.status !== "quarantined" && predicateMatches(rule, context)).sort((a, b) => b.priority - a.priority);
219
221
  }
220
222
  /**
223
+ * Runbook content search (D3): "find the procedure for this task" -
224
+ * lexical recall over rule text, as opposed to predicate
225
+ * {@link activate}. Returns **whole validated procedures** (the full
226
+ * {@link Rule} incl. steps / variables / success criteria) so a match
227
+ * can be followed file-style rather than re-synthesized from
228
+ * fragments. Quarantined (unvalidated induced) procedures are
229
+ * excluded - they must not drive actions - unless the inspector opts
230
+ * in via `includeQuarantined`.
231
+ *
232
+ * Uses the storage adapter's FTS surface when available
233
+ * (`procedural.search`, the default `@graphorin/store-sqlite` adapter
234
+ * implements it via migration 028); otherwise degrades to an offline
235
+ * in-memory token-overlap scan over {@link list}, so custom adapters
236
+ * keep working without the index.
237
+ */
238
+ async search(scope, query, opts = {}) {
239
+ return withMemorySpan(this.#tracer, "memory.read.procedural", scope, { "memory.procedural.action": "search" }, async (span) => {
240
+ const topK = opts.topK ?? 5;
241
+ const store = this.#store.procedural;
242
+ if (typeof store.search === "function") {
243
+ const hits = await store.search(scope, query, {
244
+ topK,
245
+ ...opts.includeQuarantined === true ? { includeQuarantined: true } : {}
246
+ });
247
+ span.setAttributes({
248
+ "memory.read.procedural.count": hits.length,
249
+ "memory.read.procedural.search_backend": "store"
250
+ });
251
+ return hits;
252
+ }
253
+ const tokens = tokenizeForRunbook(query);
254
+ const scored = (await this.list(scope)).filter((rule) => opts.includeQuarantined === true || rule.status !== "quarantined").map((rule) => {
255
+ const haystack = tokenizeForRunbook([
256
+ rule.text,
257
+ ...rule.steps ?? [],
258
+ ...rule.tags ?? []
259
+ ].join(" "));
260
+ return {
261
+ rule,
262
+ overlap: tokens.filter((t) => haystack.includes(t)).length
263
+ };
264
+ }).filter((entry) => entry.overlap > 0).sort((a, b) => b.overlap - a.overlap || (a.rule.id < b.rule.id ? -1 : 1)).slice(0, topK);
265
+ span.setAttributes({
266
+ "memory.read.procedural.count": scored.length,
267
+ "memory.read.procedural.search_backend": "fallback"
268
+ });
269
+ return scored.map((entry) => ({
270
+ record: entry.rule,
271
+ score: tokens.length === 0 ? 0 : entry.overlap / tokens.length,
272
+ signals: { lexical: entry.overlap }
273
+ }));
274
+ });
275
+ }
276
+ /**
221
277
  * Promote a quarantined (induced) procedure into `activate()` (MCON-2).
222
278
  * Mirrors {@link SemanticMemory.validate}: re-derives the injection verdict
223
279
  * from the stored rule text and **refuses** promotion of an injection-flagged
@@ -249,6 +305,10 @@ var ProceduralMemory = class {
249
305
  function renderProcedureText(procedure) {
250
306
  return [procedure.title, ...procedure.steps.map((step, i) => `${i + 1}. ${step}`)].join("\n");
251
307
  }
308
+ /** Lowercased word tokens for the offline runbook-search fallback (D3). */
309
+ function tokenizeForRunbook(text) {
310
+ return text.toLowerCase().split(/[^\p{L}\p{N}]+/u).filter((token) => token.length > 1);
311
+ }
252
312
  function predicateMatches(rule, context) {
253
313
  if (rule.condition === void 0 || rule.condition === "always") return true;
254
314
  if (rule.condition.startsWith("topic=")) {
@@ -1 +1 @@
1
- {"version":3,"file":"procedural-memory.js","names":["#store","#tracer","#inducer","#promoteAfterSuccesses","rule: Rule"],"sources":["../../src/tiers/procedural-memory.ts"],"sourcesContent":["import type {\n MemoryProvenance,\n MemoryStatus,\n Rule,\n RunState,\n Sensitivity,\n SessionScope,\n Tracer,\n} from '@graphorin/core';\nimport {\n type InducedProcedure,\n runWorkflowInduction,\n type Trajectory,\n trajectoryFromRunState,\n type WorkflowInducer,\n} from '../consolidator/phases/induce.js';\nimport {\n ProcedureInductionNotConfiguredError,\n QuarantinePromotionRefusedError,\n} from '../errors/index.js';\nimport { newMemoryId } from '../internal/id.js';\nimport { detectMemoryInjection } from '../internal/injection-heuristics.js';\nimport { withMemorySpan } from '../internal/spans.js';\nimport type { MemoryStoreAdapter } from '../internal/storage-adapter.js';\n\n/** Default priority for an *induced* procedure — below the author default\n * (50) because an induced, still-quarantined procedure is provisional. */\nconst INDUCED_PRIORITY = 40;\n\n/**\n * Author-time rule payload accepted by {@link ProceduralMemory.define}.\n *\n * @stable\n */\nexport interface RuleInput {\n readonly text: string;\n /**\n * Free-form predicate evaluated by {@link ProceduralMemory.activate}.\n * The predicate language is intentionally narrow in v0.1: either\n * the literal string `'always'`, or a `'topic=...'` / `'tag=...'`\n * shorthand. Custom predicates should be expressed as a callable\n * matched in `activate(...)`'s `customMatchers` argument.\n */\n readonly condition?: string;\n /** Default `'public'` per DEC-126 — rules are NOT user data. */\n readonly sensitivity?: Sensitivity;\n readonly priority?: number;\n readonly tags?: ReadonlyArray<string>;\n /**\n * Optional structured workflow payload (P2-2). Usually set by\n * {@link ProceduralMemory.induce}, but accepted here so an author can\n * round-trip a hand-written procedure. See {@link Rule.steps}.\n */\n readonly steps?: ReadonlyArray<string>;\n /** Variable names abstracted into {@link RuleInput.steps} (P2-2). */\n readonly variables?: ReadonlyArray<string>;\n /** Verifiable success criteria stored with the procedure (P2-2). */\n readonly successCriteria?: ReadonlyArray<string>;\n}\n\n/**\n * Options for {@link ProceduralMemory.induce}.\n *\n * @stable\n */\nexport interface InduceOptions {\n /** Sensitivity of the stored procedure. Default `'internal'`. */\n readonly sensitivity?: Sensitivity;\n /** Priority of the stored procedure. Default {@link INDUCED_PRIORITY} (40). */\n readonly priority?: number;\n}\n\n/**\n * Predicate context passed to {@link ProceduralMemory.activate}.\n *\n * @stable\n */\nexport interface RuleActivationContext {\n readonly topic?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * `ProceduralMemory` — standing orders activated when the agent's\n * current context matches the rule's predicate. The activation rules\n * are deterministic so the agent runtime + ContextEngine can render\n * the active set into the system prompt every step.\n *\n * P2-2 adds {@link ProceduralMemory.induce}: distil a reusable workflow\n * from a successful agent trajectory and store it **quarantined** (it must\n * not drive actions until validated). Quarantined procedures are excluded\n * from {@link ProceduralMemory.activate} but remain visible to\n * {@link ProceduralMemory.list}.\n *\n * @stable\n */\nexport class ProceduralMemory {\n readonly #store: MemoryStoreAdapter;\n readonly #tracer: Tracer;\n /** Opt-in workflow inducer (P2-2). `null` ⇒ {@link induce} throws. */\n readonly #inducer: WorkflowInducer | null;\n /**\n * Promotion-by-demonstrated-success threshold (MCON-2 part 4).\n * `null` ⇒ outcomes are counted but never auto-promote.\n */\n readonly #promoteAfterSuccesses: number | null;\n\n constructor(args: {\n store: MemoryStoreAdapter;\n tracer: Tracer;\n inducer?: WorkflowInducer | null;\n promoteAfterSuccesses?: number | null;\n }) {\n this.#store = args.store;\n this.#tracer = args.tracer;\n this.#inducer = args.inducer ?? null;\n const k = args.promoteAfterSuccesses;\n this.#promoteAfterSuccesses =\n typeof k === 'number' && Number.isFinite(k) && k >= 1 ? Math.floor(k) : null;\n }\n\n /**\n * Record the outcome of one demonstrated reuse of a procedure\n * (MCON-2 part 4). A success increments the rule's persistent\n * `successCount`; when `procedurePromotion.afterSuccesses` is\n * configured and a QUARANTINED procedure reaches the threshold it is\n * promoted through {@link validate} — the injection gate still\n * applies, so a flagged text refuses promotion (surfaced as\n * `refused: true`) no matter how many successes accumulate.\n * Failures are observed but not persisted (no negative counter yet).\n *\n * Callers decide what \"success\" means — typically\n * `checkSuccessCriteria(...)` over the procedure's stored\n * `successCriteria`, or an operator's judgement.\n *\n * @stable\n */\n async recordOutcome(\n scope: SessionScope,\n ruleId: string,\n succeeded: boolean,\n ): Promise<{\n readonly successCount: number;\n readonly promoted: boolean;\n readonly refused: boolean;\n }> {\n return withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'record-outcome', 'memory.procedural.rule_id': ruleId },\n async (span) => {\n const store = this.#store.procedural;\n if (!succeeded || typeof store.recordSuccess !== 'function') {\n span.setAttributes({ 'memory.procedural.outcome.counted': false });\n return { successCount: 0, promoted: false, refused: false };\n }\n const successCount = await store.recordSuccess(ruleId);\n span.setAttributes({ 'memory.procedural.outcome.success_count': successCount });\n const threshold = this.#promoteAfterSuccesses;\n if (threshold === null || successCount < threshold) {\n return { successCount, promoted: false, refused: false };\n }\n const existing = (await store.list(scope)).find((rule) => rule.id === ruleId) ?? null;\n if (existing === null || existing.status !== 'quarantined') {\n return { successCount, promoted: false, refused: false };\n }\n try {\n await this.validate(\n scope,\n ruleId,\n `demonstrated-success: ${successCount} >= ${threshold}`,\n );\n span.setAttributes({ 'memory.procedural.outcome.promoted': true });\n return { successCount, promoted: true, refused: false };\n } catch (err) {\n if (err instanceof QuarantinePromotionRefusedError) {\n span.setAttributes({ 'memory.procedural.outcome.refused': true });\n return { successCount, promoted: false, refused: true };\n }\n throw err;\n }\n },\n );\n }\n\n /** Persist a rule. Returns the stored record. */\n async define(scope: SessionScope, input: RuleInput): Promise<Rule> {\n return withMemorySpan(this.#tracer, 'memory.write.procedural', scope, {}, async (span) => {\n const now = new Date().toISOString();\n const rule: Rule = {\n id: newMemoryId('rule'),\n kind: 'procedural',\n userId: scope.userId,\n ...(scope.sessionId !== undefined ? { sessionId: scope.sessionId } : {}),\n ...(scope.agentId !== undefined ? { agentId: scope.agentId } : {}),\n sensitivity: input.sensitivity ?? 'public',\n text: input.text,\n ...(input.condition !== undefined ? { condition: input.condition } : {}),\n priority: input.priority ?? 50,\n ...(input.tags !== undefined ? { tags: Object.freeze([...input.tags]) } : {}),\n ...(input.steps !== undefined ? { steps: Object.freeze([...input.steps]) } : {}),\n ...(input.variables !== undefined\n ? { variables: Object.freeze([...input.variables]) }\n : {}),\n ...(input.successCriteria !== undefined\n ? { successCriteria: Object.freeze([...input.successCriteria]) }\n : {}),\n createdAt: now,\n updatedAt: now,\n };\n await this.#store.procedural.add(rule);\n span.setAttributes({\n 'memory.procedural.priority': rule.priority,\n 'memory.procedural.text_length': input.text.length,\n });\n return rule;\n });\n }\n\n /**\n * Induce a reusable procedure (P2-2) from a successful agent trajectory\n * and store it **quarantined** + `provenance: 'induction'` (P1-4). Returns\n * the stored {@link Rule}, or `null` when the trajectory was unsuccessful /\n * empty or the inducer produced nothing inducible.\n *\n * Throws {@link ProcedureInductionNotConfiguredError} when no inducer was\n * configured (`createMemory({ procedureInduction: { provider } })`).\n */\n async induce(\n scope: SessionScope,\n trajectory: Trajectory,\n opts: InduceOptions = {},\n ): Promise<Rule | null> {\n const inducer = this.#inducer;\n if (inducer === null) throw new ProcedureInductionNotConfiguredError();\n return withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'induce' },\n async (span) => {\n const induced = await runWorkflowInduction(trajectory, inducer, {});\n if (induced === null) {\n span.setAttributes({ 'memory.procedural.induced': 0 });\n return null;\n }\n const now = new Date().toISOString();\n const rule: Rule = {\n id: newMemoryId('rule'),\n kind: 'procedural',\n userId: scope.userId,\n ...(scope.sessionId !== undefined ? { sessionId: scope.sessionId } : {}),\n ...(scope.agentId !== undefined ? { agentId: scope.agentId } : {}),\n // Induced from a run ⇒ user-derived, so 'internal' (not the public\n // default for author-defined rules).\n sensitivity: opts.sensitivity ?? 'internal',\n text: renderProcedureText(induced),\n priority: opts.priority ?? INDUCED_PRIORITY,\n steps: Object.freeze([...induced.steps]),\n variables: Object.freeze([...induced.variables]),\n successCriteria: Object.freeze([...induced.successCriteria]),\n // Highest-poisoning-risk write in the system — procedures drive\n // actions, so it lands quarantined + provenance-tagged (P1-4).\n provenance: 'induction' satisfies MemoryProvenance,\n status: 'quarantined' satisfies MemoryStatus,\n createdAt: now,\n updatedAt: now,\n };\n await this.#store.procedural.add(rule);\n span.setAttributes({\n 'memory.procedural.induced': 1,\n 'memory.procedural.steps': induced.steps.length,\n 'memory.procedural.variables': induced.variables.length,\n });\n return rule;\n },\n );\n }\n\n /**\n * Convenience over {@link induce}: distil the {@link Trajectory} from a\n * completed {@link RunState} (the agent's already-emitted run state) and\n * induce a procedure. The success signal is `status === 'completed'`.\n */\n async induceFromRun(\n scope: SessionScope,\n run: RunState,\n opts: InduceOptions = {},\n ): Promise<Rule | null> {\n return this.induce(scope, trajectoryFromRunState(run), opts);\n }\n\n /** Soft-delete a rule. */\n async remove(scope: SessionScope, ruleId: string, reason?: string): Promise<void> {\n await withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'remove', 'memory.procedural.rule_id': ruleId },\n async () => {\n await this.#store.procedural.remove(ruleId, reason);\n },\n );\n }\n\n /** List every active (non-deleted) rule for the supplied scope. */\n async list(scope: SessionScope): Promise<ReadonlyArray<Rule>> {\n return withMemorySpan(this.#tracer, 'memory.read.procedural', scope, {}, async (span) => {\n const out = await this.#store.procedural.list(scope);\n span.setAttributes({ 'memory.read.procedural.count': out.length });\n return out;\n });\n }\n\n /**\n * Return the rules active under `context`. Rules without a\n * `condition` are always active; the bundled predicate vocabulary\n * supports the literals `'always'`, `'topic=<topic>'`, and\n * `'tag=<tag>'`. Anything outside that grammar is treated as\n * always-active so callers do not silently lose rules.\n *\n * **Quarantined procedures are excluded** (P1-4 / P2-2): an induced\n * procedure must not drive actions until validated, so activation — which\n * feeds the system prompt — never surfaces it.\n */\n async activate(\n scope: SessionScope,\n context: RuleActivationContext = {},\n ): Promise<ReadonlyArray<Rule>> {\n const rules = await this.list(scope);\n return rules\n .filter((rule) => rule.status !== 'quarantined' && predicateMatches(rule, context))\n .sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * Promote a quarantined (induced) procedure into `activate()` (MCON-2).\n * Mirrors {@link SemanticMemory.validate}: re-derives the injection verdict\n * from the stored rule text and **refuses** promotion of an injection-flagged\n * procedure unless an operator passes `{ force: true }`. Induced procedures\n * drive *actions*, so this gate matters most for them.\n */\n async validate(\n scope: SessionScope,\n ruleId: string,\n reason?: string,\n options?: { readonly force?: boolean },\n ): Promise<void> {\n await withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'validate', 'memory.procedural.rule_id': ruleId },\n async (span) => {\n const store = this.#store.procedural;\n if (typeof store.setStatus !== 'function') {\n throw new TypeError(\n '[graphorin/memory] ProceduralMemory.validate(...) requires a storage adapter that implements `procedural.setStatus(id, status)`. ' +\n 'The default `@graphorin/store-sqlite` adapter implements it; custom adapters can opt in via ProceduralMemoryStoreExt.',\n );\n }\n const force = options?.force === true;\n // No store get-by-id for rules — list() already surfaces quarantined.\n const existing = (await store.list(scope)).find((rule) => rule.id === ruleId) ?? null;\n if (existing !== null && !force) {\n const injection = detectMemoryInjection(existing.text);\n if (injection.flagged) {\n span.setAttributes({ 'memory.procedural.validate.refused': true });\n throw new QuarantinePromotionRefusedError(ruleId, injection.markers);\n }\n }\n span.setAttributes({ 'memory.procedural.validate.forced': force });\n await store.setStatus(ruleId, 'active', reason);\n },\n );\n }\n}\n\n/** Render an induced procedure into the human-readable `Rule.text`. */\nfunction renderProcedureText(procedure: InducedProcedure): string {\n const lines = [procedure.title, ...procedure.steps.map((step, i) => `${i + 1}. ${step}`)];\n return lines.join('\\n');\n}\n\nfunction predicateMatches(rule: Rule, context: RuleActivationContext): boolean {\n if (rule.condition === undefined || rule.condition === 'always') return true;\n if (rule.condition.startsWith('topic=')) {\n const wanted = rule.condition.slice('topic='.length).trim();\n return context.topic === wanted;\n }\n if (rule.condition.startsWith('tag=')) {\n const wanted = rule.condition.slice('tag='.length).trim();\n return (context.tags ?? []).includes(wanted);\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"procedural-memory.js","names":["#store","#tracer","#inducer","#promoteAfterSuccesses","rule: Rule"],"sources":["../../src/tiers/procedural-memory.ts"],"sourcesContent":["import type {\n MemoryHit,\n MemoryOwner,\n MemoryProvenance,\n MemoryStatus,\n Rule,\n RunState,\n Sensitivity,\n SessionScope,\n Tracer,\n} from '@graphorin/core';\nimport {\n type InducedProcedure,\n runWorkflowInduction,\n type Trajectory,\n trajectoryFromRunState,\n type WorkflowInducer,\n} from '../consolidator/phases/induce.js';\nimport {\n ProcedureInductionNotConfiguredError,\n QuarantinePromotionRefusedError,\n} from '../errors/index.js';\nimport { newMemoryId } from '../internal/id.js';\nimport { detectMemoryInjection } from '../internal/injection-heuristics.js';\nimport { withMemorySpan } from '../internal/spans.js';\nimport type { MemoryStoreAdapter } from '../internal/storage-adapter.js';\n\n/** Default priority for an *induced* procedure - below the author default\n * (50) because an induced, still-quarantined procedure is provisional. */\nconst INDUCED_PRIORITY = 40;\n\n/**\n * Author-time rule payload accepted by {@link ProceduralMemory.define}.\n *\n * @stable\n */\nexport interface RuleInput {\n readonly text: string;\n /**\n * Free-form predicate evaluated by {@link ProceduralMemory.activate}.\n * The predicate language is intentionally narrow in v0.1: either\n * the literal string `'always'`, or a `'topic=...'` / `'tag=...'`\n * shorthand. Custom predicates should be expressed as a callable\n * matched in `activate(...)`'s `customMatchers` argument.\n */\n readonly condition?: string;\n /** Default `'public'` per DEC-126 - rules are NOT user data. */\n readonly sensitivity?: Sensitivity;\n readonly priority?: number;\n readonly tags?: ReadonlyArray<string>;\n /**\n * Optional structured workflow payload (P2-2). Usually set by\n * {@link ProceduralMemory.induce}, but accepted here so an author can\n * round-trip a hand-written procedure. See {@link Rule.steps}.\n */\n readonly steps?: ReadonlyArray<string>;\n /** Variable names abstracted into {@link RuleInput.steps} (P2-2). */\n readonly variables?: ReadonlyArray<string>;\n /** Verifiable success criteria stored with the procedure (P2-2). */\n readonly successCriteria?: ReadonlyArray<string>;\n /** Principal dimension (D3). Absent ⇒ NULL (treated `'user'`). */\n readonly owner?: MemoryOwner;\n}\n\n/**\n * Options for {@link ProceduralMemory.induce}.\n *\n * @stable\n */\nexport interface InduceOptions {\n /** Sensitivity of the stored procedure. Default `'internal'`. */\n readonly sensitivity?: Sensitivity;\n /** Priority of the stored procedure. Default {@link INDUCED_PRIORITY} (40). */\n readonly priority?: number;\n}\n\n/**\n * Predicate context passed to {@link ProceduralMemory.activate}.\n *\n * @stable\n */\nexport interface RuleActivationContext {\n readonly topic?: string;\n readonly tags?: ReadonlyArray<string>;\n}\n\n/**\n * `ProceduralMemory` - standing orders activated when the agent's\n * current context matches the rule's predicate. The activation rules\n * are deterministic so the agent runtime + ContextEngine can render\n * the active set into the system prompt every step.\n *\n * P2-2 adds {@link ProceduralMemory.induce}: distil a reusable workflow\n * from a successful agent trajectory and store it **quarantined** (it must\n * not drive actions until validated). Quarantined procedures are excluded\n * from {@link ProceduralMemory.activate} but remain visible to\n * {@link ProceduralMemory.list}.\n *\n * @stable\n */\nexport class ProceduralMemory {\n readonly #store: MemoryStoreAdapter;\n readonly #tracer: Tracer;\n /** Opt-in workflow inducer (P2-2). `null` ⇒ {@link induce} throws. */\n readonly #inducer: WorkflowInducer | null;\n /**\n * Promotion-by-demonstrated-success threshold (MCON-2 part 4).\n * `null` ⇒ outcomes are counted but never auto-promote.\n */\n readonly #promoteAfterSuccesses: number | null;\n\n constructor(args: {\n store: MemoryStoreAdapter;\n tracer: Tracer;\n inducer?: WorkflowInducer | null;\n promoteAfterSuccesses?: number | null;\n }) {\n this.#store = args.store;\n this.#tracer = args.tracer;\n this.#inducer = args.inducer ?? null;\n const k = args.promoteAfterSuccesses;\n this.#promoteAfterSuccesses =\n typeof k === 'number' && Number.isFinite(k) && k >= 1 ? Math.floor(k) : null;\n }\n\n /**\n * Record the outcome of one demonstrated reuse of a procedure\n * (MCON-2 part 4). A success increments the rule's persistent\n * `successCount`; when `procedurePromotion.afterSuccesses` is\n * configured and a QUARANTINED procedure reaches the threshold it is\n * promoted through {@link validate} - the injection gate still\n * applies, so a flagged text refuses promotion (surfaced as\n * `refused: true`) no matter how many successes accumulate.\n * Failures are observed but not persisted (no negative counter yet).\n *\n * Callers decide what \"success\" means - typically\n * `checkSuccessCriteria(...)` over the procedure's stored\n * `successCriteria`, or an operator's judgement.\n *\n * @stable\n */\n async recordOutcome(\n scope: SessionScope,\n ruleId: string,\n succeeded: boolean,\n ): Promise<{\n readonly successCount: number;\n readonly promoted: boolean;\n readonly refused: boolean;\n }> {\n return withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'record-outcome', 'memory.procedural.rule_id': ruleId },\n async (span) => {\n const store = this.#store.procedural;\n if (!succeeded || typeof store.recordSuccess !== 'function') {\n span.setAttributes({ 'memory.procedural.outcome.counted': false });\n return { successCount: 0, promoted: false, refused: false };\n }\n const successCount = await store.recordSuccess(ruleId);\n span.setAttributes({ 'memory.procedural.outcome.success_count': successCount });\n const threshold = this.#promoteAfterSuccesses;\n if (threshold === null || successCount < threshold) {\n return { successCount, promoted: false, refused: false };\n }\n const existing = (await store.list(scope)).find((rule) => rule.id === ruleId) ?? null;\n if (existing === null || existing.status !== 'quarantined') {\n return { successCount, promoted: false, refused: false };\n }\n try {\n await this.validate(\n scope,\n ruleId,\n `demonstrated-success: ${successCount} >= ${threshold}`,\n );\n span.setAttributes({ 'memory.procedural.outcome.promoted': true });\n return { successCount, promoted: true, refused: false };\n } catch (err) {\n if (err instanceof QuarantinePromotionRefusedError) {\n span.setAttributes({ 'memory.procedural.outcome.refused': true });\n return { successCount, promoted: false, refused: true };\n }\n throw err;\n }\n },\n );\n }\n\n /** Persist a rule. Returns the stored record. */\n async define(scope: SessionScope, input: RuleInput): Promise<Rule> {\n return withMemorySpan(this.#tracer, 'memory.write.procedural', scope, {}, async (span) => {\n const now = new Date().toISOString();\n const rule: Rule = {\n id: newMemoryId('rule'),\n kind: 'procedural',\n userId: scope.userId,\n ...(scope.sessionId !== undefined ? { sessionId: scope.sessionId } : {}),\n ...(scope.agentId !== undefined ? { agentId: scope.agentId } : {}),\n sensitivity: input.sensitivity ?? 'public',\n text: input.text,\n ...(input.condition !== undefined ? { condition: input.condition } : {}),\n priority: input.priority ?? 50,\n ...(input.tags !== undefined ? { tags: Object.freeze([...input.tags]) } : {}),\n ...(input.steps !== undefined ? { steps: Object.freeze([...input.steps]) } : {}),\n ...(input.variables !== undefined\n ? { variables: Object.freeze([...input.variables]) }\n : {}),\n ...(input.successCriteria !== undefined\n ? { successCriteria: Object.freeze([...input.successCriteria]) }\n : {}),\n ...(input.owner !== undefined ? { owner: input.owner } : {}),\n createdAt: now,\n updatedAt: now,\n };\n await this.#store.procedural.add(rule);\n span.setAttributes({\n 'memory.procedural.priority': rule.priority,\n 'memory.procedural.text_length': input.text.length,\n });\n return rule;\n });\n }\n\n /**\n * Induce a reusable procedure (P2-2) from a successful agent trajectory\n * and store it **quarantined** + `provenance: 'induction'` (P1-4). Returns\n * the stored {@link Rule}, or `null` when the trajectory was unsuccessful /\n * empty or the inducer produced nothing inducible.\n *\n * Throws {@link ProcedureInductionNotConfiguredError} when no inducer was\n * configured (`createMemory({ procedureInduction: { provider } })`).\n */\n async induce(\n scope: SessionScope,\n trajectory: Trajectory,\n opts: InduceOptions = {},\n ): Promise<Rule | null> {\n const inducer = this.#inducer;\n if (inducer === null) throw new ProcedureInductionNotConfiguredError();\n return withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'induce' },\n async (span) => {\n const induced = await runWorkflowInduction(trajectory, inducer, {});\n if (induced === null) {\n span.setAttributes({ 'memory.procedural.induced': 0 });\n return null;\n }\n const now = new Date().toISOString();\n const rule: Rule = {\n id: newMemoryId('rule'),\n kind: 'procedural',\n userId: scope.userId,\n ...(scope.sessionId !== undefined ? { sessionId: scope.sessionId } : {}),\n ...(scope.agentId !== undefined ? { agentId: scope.agentId } : {}),\n // Induced from a run ⇒ user-derived, so 'internal' (not the public\n // default for author-defined rules).\n sensitivity: opts.sensitivity ?? 'internal',\n text: renderProcedureText(induced),\n priority: opts.priority ?? INDUCED_PRIORITY,\n steps: Object.freeze([...induced.steps]),\n variables: Object.freeze([...induced.variables]),\n successCriteria: Object.freeze([...induced.successCriteria]),\n // Highest-poisoning-risk write in the system - procedures drive\n // actions, so it lands quarantined + provenance-tagged (P1-4).\n provenance: 'induction' satisfies MemoryProvenance,\n status: 'quarantined' satisfies MemoryStatus,\n // D3: an induced procedure is the agent's own inference.\n owner: 'agent',\n createdAt: now,\n updatedAt: now,\n };\n await this.#store.procedural.add(rule);\n span.setAttributes({\n 'memory.procedural.induced': 1,\n 'memory.procedural.steps': induced.steps.length,\n 'memory.procedural.variables': induced.variables.length,\n });\n return rule;\n },\n );\n }\n\n /**\n * Convenience over {@link induce}: distil the {@link Trajectory} from a\n * completed {@link RunState} (the agent's already-emitted run state) and\n * induce a procedure. The success signal is `status === 'completed'`.\n */\n async induceFromRun(\n scope: SessionScope,\n run: RunState,\n opts: InduceOptions = {},\n ): Promise<Rule | null> {\n return this.induce(scope, trajectoryFromRunState(run), opts);\n }\n\n /** Soft-delete a rule. */\n async remove(scope: SessionScope, ruleId: string, reason?: string): Promise<void> {\n await withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'remove', 'memory.procedural.rule_id': ruleId },\n async () => {\n await this.#store.procedural.remove(ruleId, reason);\n },\n );\n }\n\n /** List every active (non-deleted) rule for the supplied scope. */\n async list(scope: SessionScope): Promise<ReadonlyArray<Rule>> {\n return withMemorySpan(this.#tracer, 'memory.read.procedural', scope, {}, async (span) => {\n const out = await this.#store.procedural.list(scope);\n span.setAttributes({ 'memory.read.procedural.count': out.length });\n return out;\n });\n }\n\n /**\n * Return the rules active under `context`. Rules without a\n * `condition` are always active; the bundled predicate vocabulary\n * supports the literals `'always'`, `'topic=<topic>'`, and\n * `'tag=<tag>'`. Anything outside that grammar is treated as\n * always-active so callers do not silently lose rules.\n *\n * **Quarantined procedures are excluded** (P1-4 / P2-2): an induced\n * procedure must not drive actions until validated, so activation - which\n * feeds the system prompt - never surfaces it.\n */\n async activate(\n scope: SessionScope,\n context: RuleActivationContext = {},\n ): Promise<ReadonlyArray<Rule>> {\n const rules = await this.list(scope);\n return rules\n .filter((rule) => rule.status !== 'quarantined' && predicateMatches(rule, context))\n .sort((a, b) => b.priority - a.priority);\n }\n\n /**\n * Runbook content search (D3): \"find the procedure for this task\" -\n * lexical recall over rule text, as opposed to predicate\n * {@link activate}. Returns **whole validated procedures** (the full\n * {@link Rule} incl. steps / variables / success criteria) so a match\n * can be followed file-style rather than re-synthesized from\n * fragments. Quarantined (unvalidated induced) procedures are\n * excluded - they must not drive actions - unless the inspector opts\n * in via `includeQuarantined`.\n *\n * Uses the storage adapter's FTS surface when available\n * (`procedural.search`, the default `@graphorin/store-sqlite` adapter\n * implements it via migration 028); otherwise degrades to an offline\n * in-memory token-overlap scan over {@link list}, so custom adapters\n * keep working without the index.\n */\n async search(\n scope: SessionScope,\n query: string,\n opts: { readonly topK?: number; readonly includeQuarantined?: boolean } = {},\n ): Promise<ReadonlyArray<MemoryHit<Rule>>> {\n return withMemorySpan(\n this.#tracer,\n 'memory.read.procedural',\n scope,\n { 'memory.procedural.action': 'search' },\n async (span) => {\n const topK = opts.topK ?? 5;\n const store = this.#store.procedural;\n if (typeof store.search === 'function') {\n const hits = await store.search(scope, query, {\n topK,\n ...(opts.includeQuarantined === true ? { includeQuarantined: true } : {}),\n });\n span.setAttributes({\n 'memory.read.procedural.count': hits.length,\n 'memory.read.procedural.search_backend': 'store',\n });\n return hits;\n }\n // Fallback: offline token-overlap scoring over list(). Coarse by\n // design - it exists so the runbook surface degrades instead of\n // disappearing on adapters without an FTS index.\n const tokens = tokenizeForRunbook(query);\n const rules = await this.list(scope);\n const scored = rules\n .filter((rule) => opts.includeQuarantined === true || rule.status !== 'quarantined')\n .map((rule) => {\n const haystack = tokenizeForRunbook(\n [rule.text, ...(rule.steps ?? []), ...(rule.tags ?? [])].join(' '),\n );\n const overlap = tokens.filter((t) => haystack.includes(t)).length;\n return { rule, overlap };\n })\n .filter((entry) => entry.overlap > 0)\n .sort((a, b) => b.overlap - a.overlap || (a.rule.id < b.rule.id ? -1 : 1))\n .slice(0, topK);\n span.setAttributes({\n 'memory.read.procedural.count': scored.length,\n 'memory.read.procedural.search_backend': 'fallback',\n });\n return scored.map((entry) => ({\n record: entry.rule,\n score: tokens.length === 0 ? 0 : entry.overlap / tokens.length,\n signals: { lexical: entry.overlap },\n }));\n },\n );\n }\n\n /**\n * Promote a quarantined (induced) procedure into `activate()` (MCON-2).\n * Mirrors {@link SemanticMemory.validate}: re-derives the injection verdict\n * from the stored rule text and **refuses** promotion of an injection-flagged\n * procedure unless an operator passes `{ force: true }`. Induced procedures\n * drive *actions*, so this gate matters most for them.\n */\n async validate(\n scope: SessionScope,\n ruleId: string,\n reason?: string,\n options?: { readonly force?: boolean },\n ): Promise<void> {\n await withMemorySpan(\n this.#tracer,\n 'memory.write.procedural',\n scope,\n { 'memory.procedural.action': 'validate', 'memory.procedural.rule_id': ruleId },\n async (span) => {\n const store = this.#store.procedural;\n if (typeof store.setStatus !== 'function') {\n throw new TypeError(\n '[graphorin/memory] ProceduralMemory.validate(...) requires a storage adapter that implements `procedural.setStatus(id, status)`. ' +\n 'The default `@graphorin/store-sqlite` adapter implements it; custom adapters can opt in via ProceduralMemoryStoreExt.',\n );\n }\n const force = options?.force === true;\n // No store get-by-id for rules - list() already surfaces quarantined.\n const existing = (await store.list(scope)).find((rule) => rule.id === ruleId) ?? null;\n if (existing !== null && !force) {\n const injection = detectMemoryInjection(existing.text);\n if (injection.flagged) {\n span.setAttributes({ 'memory.procedural.validate.refused': true });\n throw new QuarantinePromotionRefusedError(ruleId, injection.markers);\n }\n }\n span.setAttributes({ 'memory.procedural.validate.forced': force });\n await store.setStatus(ruleId, 'active', reason);\n },\n );\n }\n}\n\n/** Render an induced procedure into the human-readable `Rule.text`. */\nfunction renderProcedureText(procedure: InducedProcedure): string {\n const lines = [procedure.title, ...procedure.steps.map((step, i) => `${i + 1}. ${step}`)];\n return lines.join('\\n');\n}\n\n/** Lowercased word tokens for the offline runbook-search fallback (D3). */\nfunction tokenizeForRunbook(text: string): ReadonlyArray<string> {\n return text\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((token) => token.length > 1);\n}\n\nfunction predicateMatches(rule: Rule, context: RuleActivationContext): boolean {\n if (rule.condition === undefined || rule.condition === 'always') return true;\n if (rule.condition.startsWith('topic=')) {\n const wanted = rule.condition.slice('topic='.length).trim();\n return context.topic === wanted;\n }\n if (rule.condition.startsWith('tag=')) {\n const wanted = rule.condition.slice('tag='.length).trim();\n return (context.tags ?? []).includes(wanted);\n }\n return true;\n}\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { MemoryStoreAdapter } from "../internal/storage-adapter.js";
2
2
  import { ConflictDecision, ConflictPipeline } from "../conflict/types.js";
3
3
  import "../conflict/index.js";
4
+ import { SalienceWeights } from "../consolidator/decay.js";
4
5
  import { EntityResolver } from "../graph/entity-resolver.js";
5
6
  import { IterativeRetrievalResult, RetrievalGrader } from "../search/iterative.js";
6
7
  import { QueryTransformer } from "../search/query-transform.js";
7
8
  import { ReRanker } from "../search/types.js";
8
- import { EmbedderProvider, Fact, MemoryHit, MemoryProvenance, Sensitivity, SessionScope, Tracer } from "@graphorin/core";
9
+ import { EmbedderProvider, Fact, MemoryHit, MemoryOwner, MemoryProvenance, Sensitivity, SessionScope, Tracer } from "@graphorin/core";
9
10
 
10
11
  //#region src/tiers/semantic-memory.d.ts
11
12
 
@@ -31,21 +32,28 @@ interface FactInput {
31
32
  /**
32
33
  * Trust-provenance tag (P1-4). Writers that synthesize memory pass
33
34
  * `'extraction'` / `'reflection'` so the fact lands quarantined;
34
- * first-party writers pass `'user'` / `'tool'` (or omit it absent ⇒
35
+ * first-party writers pass `'user'` / `'tool'` (or omit it - absent ⇒
35
36
  * treated as first-party `active`). The `status` is *derived* from
36
37
  * this tag plus the injection heuristics; it is never author-set.
37
38
  */
38
39
  readonly provenance?: MemoryProvenance;
39
40
  /**
40
41
  * Importance hint in `[0, 1]` (X-1 / MCON-12). Feeds the multi-signal
41
- * salience score that orders decay archiving and capacity eviction
42
+ * salience score that orders decay archiving and capacity eviction -
42
43
  * higher importance ⇒ evicted later. Values are clamped to `[0, 1]`;
43
44
  * non-finite values are dropped. The consolidator's extraction pass
44
- * fills this from the model's per-fact 110 rating
45
+ * fills this from the model's per-fact 1-10 rating
45
46
  * (`normalizeImportance`); absent ⇒ the neutral midpoint at scoring
46
47
  * time.
47
48
  */
48
49
  readonly importance?: number;
50
+ /**
51
+ * Principal dimension (D3). The consolidator stamps `'agent'` on
52
+ * synthesized writes; user-authored writes may pass `'user'`. Absent
53
+ * (the default) leaves the column NULL - treated as `'user'` at
54
+ * filter time. Never gates default recall.
55
+ */
56
+ readonly owner?: MemoryOwner;
49
57
  }
50
58
  /**
51
59
  * Per-list weights for {@link FusionStrategy} `'weighted'` fusion (X-2),
@@ -61,12 +69,21 @@ interface FusionWeights {
61
69
  readonly fts?: number;
62
70
  /** Weight applied to every vector (incl. HyDE) candidate list. Default `1`. */
63
71
  readonly vector?: number;
72
+ /**
73
+ * Weight applied to the graph-expansion candidate list (D5). Default
74
+ * `1` (the previous neutral behaviour). Tune it once the graph leg's
75
+ * reliability is calibrated against labels - the roadmap's "graph as a
76
+ * first-class tunable fusion weight" (was hardcoded neutral).
77
+ */
78
+ readonly graph?: number;
79
+ /** Weight applied to the exact entity-match candidate list (D5). Default `1`. */
80
+ readonly entity?: number;
64
81
  }
65
82
  /**
66
83
  * Score-fusion strategy for {@link SemanticMemory.search} (X-2).
67
84
  *
68
85
  * - `'rrf'` (the default when `fusion` is omitted) fuses the candidate
69
- * lists through the configured reranker the zero-tuning
86
+ * lists through the configured reranker - the zero-tuning
70
87
  * {@link RRFReranker} unless one was overridden.
71
88
  * - `'weighted'` fuses through {@link WeightedRRFReranker}, scaling each
72
89
  * list's reciprocal-rank contribution by its {@link FusionWeights}, for
@@ -113,7 +130,7 @@ interface FactSearchOptions {
113
130
  readonly asOf?: string;
114
131
  /**
115
132
  * Include quarantined facts in the result (P1-4). Defaults to
116
- * `false` action-driving recall (`fact_search`, auto-recall) never
133
+ * `false` - action-driving recall (`fact_search`, auto-recall) never
117
134
  * returns quarantined rows. Set `true` only for the validation /
118
135
  * inspector path that surfaces quarantined facts to a human for
119
136
  * promotion via {@link SemanticMemory.validate}.
@@ -121,6 +138,29 @@ interface FactSearchOptions {
121
138
  * @stable
122
139
  */
123
140
  readonly includeQuarantined?: boolean;
141
+ /**
142
+ * Rank-time trust discount (C5). `'on'` (default) multiplies each
143
+ * hit's fused score by its trust factor - quarantined-but-included
144
+ * rows by `1 - quarantine` (default 0.3x), foreign-provenance rows by
145
+ * `1 - foreignProvenance` (default 0.8x); first-party active facts are
146
+ * untouched. `'off'` restores pure similarity ranking (inspector /
147
+ * calibration paths). Factors surface as the `trust` signal on hits
148
+ * and in `explainRecall`.
149
+ *
150
+ * @stable
151
+ */
152
+ readonly trustWeighting?: 'on' | 'off';
153
+ /**
154
+ * Include superseded / validity-expired facts (memory-retrieval-01).
155
+ * Defaults to `false`: a default read behaves as `asOf = now`, so a
156
+ * fact whose validity interval was closed (e.g. by `supersede`)
157
+ * never surfaces as current - what the `fact_supersede` tool
158
+ * promises. Set `true` only for inspector / audit paths. Ignored
159
+ * when an explicit {@link asOf} is supplied.
160
+ *
161
+ * @stable
162
+ */
163
+ readonly includeSuperseded?: boolean;
124
164
  /**
125
165
  * Optional decay-aware ranking. When set, the reranker output is
126
166
  * post-multiplied by the per-fact retention curve
@@ -143,7 +183,7 @@ interface FactSearchOptions {
143
183
  * transformer is configured (`createMemory({ queryTransform })`), the
144
184
  * query is fanned into up to `N - 1` reworded variants via one cheap
145
185
  * LLM call; each variant is retrieved (FTS + vector) and **all** lists
146
- * are fused through the existing RRF reranker recovering memories
186
+ * are fused through the existing RRF reranker - recovering memories
147
187
  * whose stored wording differs from the user's phrasing. `N` bounds
148
188
  * the *total* query strings, including the original. Offline (no
149
189
  * transformer, or `N <= 1`) this is a **silent no-op**: search stays
@@ -154,11 +194,11 @@ interface FactSearchOptions {
154
194
  */
155
195
  readonly multiQuery?: number;
156
196
  /**
157
- * HyDE Hypothetical Document Embeddings (arXiv:2212.10496), P2-3.
197
+ * HyDE - Hypothetical Document Embeddings (arXiv:2212.10496), P2-3.
158
198
  * When `true` *and* both a query transformer and an embedder are
159
199
  * configured, generate a short hypothetical answer, embed it, and fuse
160
200
  * its vector neighbours into the result. Helps short / ambiguous
161
- * queries but adds a generate + embed round-trip and can drift hence
201
+ * queries but adds a generate + embed round-trip and can drift - hence
162
202
  * opt-in. With no transformer (or no embedder) this is a silent no-op
163
203
  * and no provider call is made.
164
204
  *
@@ -167,7 +207,7 @@ interface FactSearchOptions {
167
207
  readonly hyde?: boolean;
168
208
  /**
169
209
  * Score-fusion strategy (X-2). Omitted (the default) ⇒ RRF via the
170
- * configured reranker behaviour is unchanged. `{ strategy:
210
+ * configured reranker - behaviour is unchanged. `{ strategy:
171
211
  * 'weighted', weights }` fuses through {@link WeightedRRFReranker},
172
212
  * up-/down-weighting the FTS vs vector candidate lists per
173
213
  * {@link FusionWeights}; reserve it for callers who have calibrated the
@@ -182,17 +222,52 @@ interface FactSearchOptions {
182
222
  * storage adapter (`store.graph`), the facts retrieved by the lexical /
183
223
  * vector candidate pass are treated as seeds: facts sharing a canonical
184
224
  * entity (subject / object) are fetched via a recursive CTE and fused
185
- * in as an extra candidate list before rerank surfacing connected
225
+ * in as an extra candidate list before rerank - surfacing connected
186
226
  * facts the query never matched directly ("what did the person I met in
187
227
  * Tbilisi recommend?"). `0` (the default) or a graph-less adapter ⇒ a
188
- * silent no-op; recall is unchanged. Opt-in + retrieval-heavy.
228
+ * silent no-op; recall is unchanged. Opt-in + retrieval-heavy. `2`
229
+ * (D5) widens to two-hop expansion - pair with `graphScoring: 'ppr'`
230
+ * so distant neighbours decay instead of tying with direct ones.
231
+ *
232
+ * @stable
233
+ */
234
+ readonly expandHops?: 0 | 1 | 2;
235
+ /**
236
+ * Graph-expansion scoring (D5). `'flat'` (the default) scores every
237
+ * graph neighbour `1` (the pre-D5 behaviour). `'ppr'` uses PPR-lite
238
+ * damped spreading activation (`damping^hopDepth`) via the store's
239
+ * `expandActivation`, so a two-hop neighbour ranks below a direct one;
240
+ * a no-op without `expandHops >= 1` or a graph-capable adapter.
241
+ *
242
+ * @stable
243
+ */
244
+ readonly graphScoring?: 'flat' | 'ppr';
245
+ /**
246
+ * Exact entity-match retriever (D5). When set, the query terms are
247
+ * normalized to entity names and facts linked to a matching canonical
248
+ * entity are fused in as a precise candidate leg (`entity` fusion
249
+ * weight), distinct from the fuzzy FTS / vector legs. A no-op without
250
+ * a graph-capable adapter. Opt-in.
189
251
  *
190
252
  * @stable
191
253
  */
192
- readonly expandHops?: 0 | 1;
254
+ readonly entityMatch?: boolean;
255
+ /**
256
+ * Retrieval-time principal filter (D3). When set, only facts whose
257
+ * `owner` is in the requested set are returned - `'user'` for
258
+ * user-stated facts, `'agent'` for the agent's own inferences,
259
+ * `'shared'` for multi-agent shared records. Rows written before the
260
+ * feature (owner NULL) are treated as `'user'`. Applied in-store on
261
+ * the FTS + vector legs and as a record-level filter on the fused
262
+ * result so the HyDE / graph legs obey it too. Absent (the default)
263
+ * ⇒ **no owner filter** - recall is byte-identical.
264
+ *
265
+ * @stable
266
+ */
267
+ readonly owner?: MemoryOwner | ReadonlyArray<MemoryOwner>;
193
268
  }
194
269
  /**
195
- * Per-call options for {@link SemanticMemory.searchIterative} (P2-4)
270
+ * Per-call options for {@link SemanticMemory.searchIterative} (P2-4) -
196
271
  * the gated grade-then-reformulate loop. Extends {@link FactSearchOptions}
197
272
  * (every base option applies to each retrieval pass); `topK` doubles as
198
273
  * the cap on the accumulated result count.
@@ -216,7 +291,7 @@ interface IterativeSearchOptions extends FactSearchOptions {
216
291
  /**
217
292
  * Outcome of {@link SemanticMemory.searchIterative}. Beyond the ranked
218
293
  * `hits`, `sufficient` / `abstained` tell the caller whether the memory
219
- * actually answered the query `abstained: true` means it should say so
294
+ * actually answered the query - `abstained: true` means it should say so
220
295
  * rather than confabulate.
221
296
  *
222
297
  * @stable
@@ -238,7 +313,7 @@ interface FactRememberOptions {
238
313
  /**
239
314
  * Precomputed contextual-retrieval index text (P1-3, advanced). When
240
315
  * supplied it overrides the instance's `'late-chunk'` computation: the
241
- * embedding is computed from and the FTS row indexed against this
316
+ * embedding is computed from - and the FTS row indexed against - this
242
317
  * text, while the canonical `text` is stored unchanged. The background
243
318
  * consolidator passes this in its `'llm'` mode (the one place an LLM is
244
319
  * allowed to write the situating context); first-party callers normally
@@ -249,7 +324,7 @@ interface FactRememberOptions {
249
324
  * Auto-promotion policy (MCON-2). When `true`, a *synthesized* write
250
325
  * (consolidator extraction) that is **clean** by the injection heuristics is
251
326
  * stored `active` instead of quarantined. Injection-flagged writes always
252
- * stay quarantined the security gate is preserved. Off by default; the
327
+ * stay quarantined - the security gate is preserved. Off by default; the
253
328
  * consolidator passes it only when the operator opts in via
254
329
  * `autoPromoteExtraction`. No effect on non-synthesized writes.
255
330
  */
@@ -268,15 +343,15 @@ interface RememberOutcome {
268
343
  readonly decision: ConflictDecision;
269
344
  /**
270
345
  * Why this write landed quarantined, if it did (P1-4 / MRET-3).
271
- * `'injection'` the offline injection heuristics flagged the text
272
- * (a memory-poisoning candidate). `'synthesized'` a consolidator /
346
+ * `'injection'` - the offline injection heuristics flagged the text
347
+ * (a memory-poisoning candidate). `'synthesized'` - a consolidator /
273
348
  * reflection / induction write awaiting validation. Absent when the
274
349
  * fact is `active` or when a dedup returned a pre-existing row.
275
350
  */
276
351
  readonly quarantineReason?: 'injection' | 'synthesized';
277
352
  }
278
353
  /**
279
- * `SemanticMemory` long-term factual store. Hybrid (vector + FTS5)
354
+ * `SemanticMemory` - long-term factual store. Hybrid (vector + FTS5)
280
355
  * search merges the two ranked lists through the configured
281
356
  * {@link ReRanker} (default {@link RRFReranker} with `k = 60`).
282
357
  *
@@ -310,7 +385,7 @@ declare class SemanticMemory {
310
385
  * (default) prepends a deterministic situating context to the text
311
386
  * that is embedded + FTS-indexed, leaving the canonical `text`
312
387
  * untouched; `'off'` indexes the bare text. The hot write path never
313
- * makes an LLM call the `'llm'` enrichment is confined to the
388
+ * makes an LLM call - the `'llm'` enrichment is confined to the
314
389
  * background consolidator, which supplies a precomputed `indexText`.
315
390
  */
316
391
  contextualRetrieval?: 'off' | 'late-chunk';
@@ -332,6 +407,12 @@ declare class SemanticMemory {
332
407
  grader?: RetrievalGrader;
333
408
  /** Default total-pass cap for `searchIterative`. Default 3. */
334
409
  iterativeMaxIterations?: number;
410
+ /**
411
+ * Weights for the rank-time trust discount (C5). Reuses the
412
+ * eviction-path `SalienceWeights` semantics; defaults to
413
+ * `DEFAULT_SALIENCE_WEIGHTS`.
414
+ */
415
+ trustWeights?: SalienceWeights;
335
416
  });
336
417
  /** Replace the active reranker. Returns the previous instance. */
337
418
  setReranker(reranker: ReRanker): ReRanker;
@@ -363,8 +444,8 @@ declare class SemanticMemory {
363
444
  * hard *and* with a grader configured
364
445
  * (`createMemory({ iterativeRetrieval })`), the retrieved set is graded
365
446
  * for sufficiency and, when weak, the query is reformulated and
366
- * retrieved again **widening to one-hop graph expansion**
367
- * (`expandHops: 1`) on each reformulation pass up to `maxIterations`
447
+ * retrieved again - **widening to one-hop graph expansion**
448
+ * (`expandHops: 1`) on each reformulation pass - up to `maxIterations`
368
449
  * (hard-capped at 5). When still insufficient it returns
369
450
  * `abstained: true` so the caller can decline to answer instead of
370
451
  * confabulating.
@@ -382,7 +463,7 @@ declare class SemanticMemory {
382
463
  * zone thresholds are calibrated against them), and it **includes
383
464
  * quarantined facts** so prior synthesized memories are visible to
384
465
  * reconciliation. Returns `[]` when no embedder / vector adapter is
385
- * configured the consolidator then treats every candidate as a
466
+ * configured - the consolidator then treats every candidate as a
386
467
  * fresh `add`, degrading gracefully to the pre-reconcile behaviour.
387
468
  *
388
469
  * @stable
@@ -397,7 +478,7 @@ declare class SemanticMemory {
397
478
  * to, oldest → newest, including superseded / soft-deleted rows so
398
479
  * callers can answer "how did this fact change over time". Requires
399
480
  * a storage adapter that implements
400
- * `SemanticMemoryStoreExt.historyOf(...)` the default
481
+ * `SemanticMemoryStoreExt.historyOf(...)` - the default
401
482
  * `@graphorin/store-sqlite` adapter wires this through. P0-2.
402
483
  *
403
484
  * @stable
@@ -408,12 +489,12 @@ declare class SemanticMemory {
408
489
  * that admits a synthesized memory into action-driving recall once a
409
490
  * human (or trusted non-agent caller) has reviewed it. Writes a
410
491
  * `memory_history` audit row. Requires a storage adapter that
411
- * implements `SemanticMemoryStoreExt.setStatus(...)` the default
492
+ * implements `SemanticMemoryStoreExt.setStatus(...)` - the default
412
493
  * `@graphorin/store-sqlite` adapter wires this through.
413
494
  *
414
495
  * MRET-3 / MST-1: promotion of a fact whose text still trips the
415
496
  * offline injection heuristics is **refused** with
416
- * {@link QuarantinePromotionRefusedError} the model-facing
497
+ * {@link QuarantinePromotionRefusedError} - the model-facing
417
498
  * `fact_validate` tool calls this with no `force`, so a poisoned
418
499
  * memory can never be promoted by the agent itself (the one-turn
419
500
  * `fact_remember(poison)` → `fact_validate(id)` chain is closed). An
@@ -437,7 +518,7 @@ declare class SemanticMemory {
437
518
  * Hard-delete a fact (GDPR path). Distinct from {@link forget}: the
438
519
  * record is removed from storage entirely instead of soft-archived.
439
520
  * Requires a storage adapter that implements
440
- * `SemanticMemoryStoreExt.purge(...)` the default
521
+ * `SemanticMemoryStoreExt.purge(...)` - the default
441
522
  * `@graphorin/store-sqlite` adapter wires this through.
442
523
  */
443
524
  purge(scope: SessionScope, factId: string, reason?: string): Promise<void>;
@@ -446,10 +527,10 @@ declare class SemanticMemory {
446
527
  topK?: number;
447
528
  signal?: AbortSignal;
448
529
  }): Promise<ReadonlyArray<MemoryHit<Fact>>>;
449
- /** Pure-fusion helper exposed for callers that already collected results. */
530
+ /** Pure-fusion helper - exposed for callers that already collected results. */
450
531
  static fuseRrf<TRecord extends Fact>(lists: ReadonlyArray<ReadonlyArray<MemoryHit<TRecord>>>, k?: number): ReadonlyArray<MemoryHit<TRecord>>;
451
532
  /**
452
- * Pure weighted-fusion helper (X-2) like {@link SemanticMemory.fuseRrf}
533
+ * Pure weighted-fusion helper (X-2) - like {@link SemanticMemory.fuseRrf}
453
534
  * but scales each list `i`'s reciprocal-rank contribution by
454
535
  * `weights[i]`. A missing / invalid entry defaults to `1`, so equal or
455
536
  * absent weights reproduce RRF.
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-memory.d.ts","names":[],"sources":["../../src/tiers/semantic-memory.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAsCA;;;;;AAwCiB,UAxCA,SAAA,CAwCa;EAoBlB,SAAA,IAAA,EAAA,MAAc;EAcT,SAAA,OAAA,CAAA,EAAA,MAAiB;EAEd,SAAA,SAAA,CAAA,EAAA,MAAA;EAUF,SAAA,MAAA,CAAA,EAAA,MAAA;EA4EE,SAAA,IAAA,CAAA,EA7JF,aA6JE,CAAA,MAAA,CAAA;EAAc,SAAA,UAAA,CAAA,EAAA,MAAA;EAwBjB,SAAA,WAAA,CAAA,EAnLQ,WAmLe;EAuB5B,SAAA,SAAA,CAAA,EAAA,MAAqB;EAAsC,SAAA,OAAA,CAAA,EAAA,MAAA;EAAV,SAAA,UAAA,CAAA,EAAA,MAAA;EAAzB;;AAWpC;AAiCA;AA0BA;;;EAgBc,SAAA,UAAA,CAAA,EArRU,gBAqRV;EAEA;;;;;;;;;EAwEH,SAAA,UAAA,CAAA,EAAA,MAAA;;;;;;;;;;;AAqO0B,UA9iBpB,aAAA,CA8iBoB;EAAV;EAAd,SAAA,GAAA,CAAA,EAAA,MAAA;EAAR;EAqLM,SAAA,MAAA,CAAA,EAAA,MAAA;;;;;;;;;;;;;;;AA0G2C,KAzzB1C,cAAA,GAyzB0C;EAyC3C,SAAA,QAAA,EAAA,KAAA;CAIN,GAAA;EAoCM,SAAA,QAAA,EAAA,UAAA;EAEG,SAAA,OAAA,EAx4BU,aAw4BV;EAEqC;EAA9C,SAAA,CAAA,CAAA,EAAA,MAAA;CAwBiB;;;;;;AAwCG,UAh8BR,iBAAA,CAg8BQ;EAAd,SAAA,IAAA,CAAA,EAAA,MAAA;EAC4B,SAAA,MAAA,CAAA,EA/7BnB,WA+7BmB;EACF;EAAV,SAAA,aAAA,CAAA,EAAA,MAAA;EAAd;;;;;;;EAQgB,SAAA,IAAA,CAAA,EA97BX,aA87BW,CAAA,MAAA,CAAA;EAAV;;;;;;;;;EAcA,SAAA,IAAA,CAAA,EAAA,MAAA;EAAd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oBAh4Be;;;;;;;;;;;;;;;;;;;;;;;UAwBH,sBAAA,SAA+B;;;;;;;;;;;;;;;;;;;;;;KAuBpC,qBAAA,GAAwB,yBAAyB,UAAU;;;;;;;;;;UAWtD,mBAAA;;;oBAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8BH,eAAA;iBACA;qBACI;;;;;;;;;;;;;;;;;;;;;;;cAwBR,cAAA;;;WAcF;YACC;cACE;;cAEA;uBACS;;;;;;;;uBAQA;;;;;;;;;;;;;;;;;qBAiBF;;;;;;;;aAQR;;;;;wBAkBW,WAAW;;cAOrB;;;;;;;;kBAYH,qBACA,qBACE,sBACR,QAAQ;;;;;;;;8BAaF,qBACA,qBACE,sBACR,QAAQ;;gBAgNF,oCAED,oBACL,QAAQ,cAAc,UAAU;;;;;;;;;;;;;;;;;;;;yBAqL1B,oCAED,yBACL,QAAQ;;;;;;;;;;;;;mBAiEF;;MAGN,QAAQ,cAAc,UAAU;;aAKlB,+BAA+B,QAAQ;;;;;;;;;;;iBA8BnC,+BAA+B,QAAQ,cAAc;;;;;;;;;;;;;;;;;;;;;kBAyCjE;;MAIN;;mBAoCM,uCAEG,6BAET;;kBAA8C;;;gBAwB7B,gDAAgD;;;;;;;;eAmBjD,gDAAgD;;6BAqB1D,cAAc,cAAc,UAAU;;aACV;MAClC,QAAQ,cAAc,UAAU;;iCAKJ,aACtB,cAAc,cAAc,UAAU,yBAE5C,cAAc,UAAU;;;;;;;sCAUS,aAC3B,cAAc,cAAc,UAAU,qBACpC,gDAER,cAAc,UAAU"}
1
+ {"version":3,"file":"semantic-memory.d.ts","names":[],"sources":["../../src/tiers/semantic-memory.ts"],"sourcesContent":[],"mappings":"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"}