@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.
- package/CHANGELOG.md +102 -1
- package/README.md +54 -37
- package/dist/conflict/index.d.ts +3 -1
- package/dist/conflict/index.js +1 -1
- package/dist/conflict/locale-packs/en.d.ts +13 -0
- package/dist/conflict/locale-packs/en.d.ts.map +1 -0
- package/dist/conflict/locale-packs/en.js +1 -1
- package/dist/conflict/locale-packs/en.js.map +1 -1
- package/dist/conflict/pipeline.d.ts +2 -2
- package/dist/conflict/pipeline.js +3 -3
- package/dist/conflict/pipeline.js.map +1 -1
- package/dist/conflict/stages/helpers.js +1 -1
- package/dist/conflict/stages/helpers.js.map +1 -1
- package/dist/conflict/stages/stage1-exact-dedup.js.map +1 -1
- package/dist/conflict/stages/stage2-embedding-three-zone.js.map +1 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js +1 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
- package/dist/conflict/stages/stage4-subject-predicate.js.map +1 -1
- package/dist/conflict/stages/stage5-defer-to-deep.js.map +1 -1
- package/dist/conflict/types.d.ts +7 -7
- package/dist/conflict/types.js +1 -1
- package/dist/conflict/types.js.map +1 -1
- package/dist/consolidator/budget.js +16 -3
- package/dist/consolidator/budget.js.map +1 -1
- package/dist/consolidator/decay.d.ts +13 -2
- package/dist/consolidator/decay.js +17 -5
- package/dist/consolidator/decay.js.map +1 -1
- package/dist/consolidator/dlq.js +1 -1
- package/dist/consolidator/dlq.js.map +1 -1
- package/dist/consolidator/errors.js +2 -2
- package/dist/consolidator/errors.js.map +1 -1
- package/dist/consolidator/index.js +8 -6
- package/dist/consolidator/index.js.map +1 -1
- package/dist/consolidator/lock.js +1 -1
- package/dist/consolidator/lock.js.map +1 -1
- package/dist/consolidator/noise-filter.js.map +1 -1
- package/dist/consolidator/phases/deep.js +6 -2
- package/dist/consolidator/phases/deep.js.map +1 -1
- package/dist/consolidator/phases/induce.d.ts +10 -10
- package/dist/consolidator/phases/induce.js +6 -6
- package/dist/consolidator/phases/induce.js.map +1 -1
- package/dist/consolidator/phases/learned-context.js +129 -0
- package/dist/consolidator/phases/learned-context.js.map +1 -0
- package/dist/consolidator/phases/light.js +2 -1
- package/dist/consolidator/phases/light.js.map +1 -1
- package/dist/consolidator/phases/reflect.js +5 -4
- package/dist/consolidator/phases/reflect.js.map +1 -1
- package/dist/consolidator/phases/standard.js +37 -8
- package/dist/consolidator/phases/standard.js.map +1 -1
- package/dist/consolidator/reconcile.js +1 -1
- package/dist/consolidator/reconcile.js.map +1 -1
- package/dist/consolidator/runtime.d.ts +4 -4
- package/dist/consolidator/runtime.d.ts.map +1 -1
- package/dist/consolidator/runtime.js +73 -16
- package/dist/consolidator/runtime.js.map +1 -1
- package/dist/consolidator/scheduler.d.ts +5 -5
- package/dist/consolidator/scheduler.js +1 -1
- package/dist/consolidator/scheduler.js.map +1 -1
- package/dist/consolidator/triggers.js +7 -7
- package/dist/consolidator/triggers.js.map +1 -1
- package/dist/consolidator/types.d.ts +68 -20
- package/dist/consolidator/types.d.ts.map +1 -1
- package/dist/consolidator/types.js +16 -6
- package/dist/consolidator/types.js.map +1 -1
- package/dist/context-engine/annotations.d.ts +9 -9
- package/dist/context-engine/annotations.js +3 -3
- package/dist/context-engine/annotations.js.map +1 -1
- package/dist/context-engine/auto-recall.js.map +1 -1
- package/dist/context-engine/compaction/clear-tool-results.d.ts +18 -3
- package/dist/context-engine/compaction/clear-tool-results.d.ts.map +1 -1
- package/dist/context-engine/compaction/clear-tool-results.js +26 -5
- package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
- package/dist/context-engine/compaction/compactor.js +19 -6
- package/dist/context-engine/compaction/compactor.js.map +1 -1
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js +4 -0
- package/dist/context-engine/compaction/hooks/reanchor-persona-block.js.map +1 -1
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-pinned-facts.js.map +1 -1
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js +2 -1
- package/dist/context-engine/compaction/hooks/reanchor-project-rules.js.map +1 -1
- package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts +28 -0
- package/dist/context-engine/compaction/hooks/reanchor-recent-results.d.ts.map +1 -0
- package/dist/context-engine/compaction/hooks/reanchor-recent-results.js +71 -0
- package/dist/context-engine/compaction/hooks/reanchor-recent-results.js.map +1 -0
- package/dist/context-engine/compaction/hooks/types.d.ts +15 -3
- package/dist/context-engine/compaction/hooks/types.d.ts.map +1 -1
- package/dist/context-engine/compaction/index.d.ts +1 -0
- package/dist/context-engine/compaction/index.js +2 -1
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts +10 -3
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
- package/dist/context-engine/compaction/templates/summary-9-section.js +42 -5
- package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
- package/dist/context-engine/compaction/thresholds.js.map +1 -1
- package/dist/context-engine/compaction/types.d.ts +47 -7
- package/dist/context-engine/compaction/types.d.ts.map +1 -1
- package/dist/context-engine/engine.d.ts +23 -4
- package/dist/context-engine/engine.d.ts.map +1 -1
- package/dist/context-engine/engine.js +69 -15
- package/dist/context-engine/engine.js.map +1 -1
- package/dist/context-engine/index.d.ts +1 -0
- package/dist/context-engine/index.d.ts.map +1 -1
- package/dist/context-engine/index.js +2 -1
- package/dist/context-engine/locale-packs/en.js +14 -13
- package/dist/context-engine/locale-packs/en.js.map +1 -1
- package/dist/context-engine/locale-packs/types.d.ts +10 -10
- package/dist/context-engine/locale-packs/types.d.ts.map +1 -1
- package/dist/context-engine/locale-packs/types.js.map +1 -1
- package/dist/context-engine/metadata.d.ts +1 -1
- package/dist/context-engine/metadata.js +2 -2
- package/dist/context-engine/metadata.js.map +1 -1
- package/dist/context-engine/preambles/inbound-en.d.ts +1 -1
- package/dist/context-engine/preambles/inbound-en.js +1 -1
- package/dist/context-engine/preambles/inbound-en.js.map +1 -1
- package/dist/context-engine/privacy-filter.d.ts +2 -2
- package/dist/context-engine/privacy-filter.js +1 -1
- package/dist/context-engine/privacy-filter.js.map +1 -1
- package/dist/context-engine/templates/base-en.d.ts +1 -1
- package/dist/context-engine/templates/base-en.js +1 -1
- package/dist/context-engine/templates/base-en.js.map +1 -1
- package/dist/context-engine/templates/composer.d.ts +1 -1
- package/dist/context-engine/templates/composer.js.map +1 -1
- package/dist/context-engine/token-budget.d.ts +1 -1
- package/dist/context-engine/token-budget.js +1 -1
- package/dist/context-engine/token-budget.js.map +1 -1
- package/dist/context-engine/token-counter.d.ts +16 -7
- package/dist/context-engine/token-counter.d.ts.map +1 -1
- package/dist/context-engine/token-counter.js +31 -9
- package/dist/context-engine/token-counter.js.map +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +4 -4
- package/dist/errors/index.js.map +1 -1
- package/dist/facade.d.ts +44 -15
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +16 -8
- package/dist/facade.js.map +1 -1
- package/dist/graph/entity-resolver.d.ts +2 -2
- package/dist/graph/entity-resolver.js +2 -2
- package/dist/graph/entity-resolver.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -5
- package/dist/index.js.map +1 -1
- package/dist/internal/contextualize.js +2 -2
- package/dist/internal/contextualize.js.map +1 -1
- package/dist/internal/embedder-binding.js +1 -1
- package/dist/internal/embedder-binding.js.map +1 -1
- package/dist/internal/id.js +1 -1
- package/dist/internal/id.js.map +1 -1
- package/dist/internal/injection-heuristics.js +25 -2
- package/dist/internal/injection-heuristics.js.map +1 -1
- package/dist/internal/spans.js +1 -1
- package/dist/internal/spans.js.map +1 -1
- package/dist/internal/storage-adapter.d.ts +71 -22
- package/dist/internal/storage-adapter.d.ts.map +1 -1
- package/dist/migration/embedder-migration.d.ts +7 -7
- package/dist/migration/embedder-migration.js.map +1 -1
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/search/explain.d.ts +7 -4
- package/dist/search/explain.d.ts.map +1 -1
- package/dist/search/explain.js +1 -1
- package/dist/search/explain.js.map +1 -1
- package/dist/search/fit-weights.d.ts +48 -0
- package/dist/search/fit-weights.d.ts.map +1 -0
- package/dist/search/fit-weights.js +70 -0
- package/dist/search/fit-weights.js.map +1 -0
- package/dist/search/graph-ppr.d.ts +36 -0
- package/dist/search/graph-ppr.d.ts.map +1 -0
- package/dist/search/graph-ppr.js +38 -0
- package/dist/search/graph-ppr.js.map +1 -0
- package/dist/search/index.d.ts +4 -1
- package/dist/search/index.js +4 -1
- package/dist/search/iterative.d.ts +15 -8
- package/dist/search/iterative.d.ts.map +1 -1
- package/dist/search/iterative.js +10 -9
- package/dist/search/iterative.js.map +1 -1
- package/dist/search/query-transform.d.ts +6 -6
- package/dist/search/query-transform.js +6 -6
- package/dist/search/query-transform.js.map +1 -1
- package/dist/search/rrf.d.ts +4 -4
- package/dist/search/rrf.js +4 -4
- package/dist/search/rrf.js.map +1 -1
- package/dist/search/trust.d.ts +30 -0
- package/dist/search/trust.d.ts.map +1 -0
- package/dist/search/trust.js +50 -0
- package/dist/search/trust.js.map +1 -0
- package/dist/search/types.d.ts +1 -1
- package/dist/tiers/episodic-memory.d.ts +6 -4
- package/dist/tiers/episodic-memory.d.ts.map +1 -1
- package/dist/tiers/episodic-memory.js +3 -2
- package/dist/tiers/episodic-memory.js.map +1 -1
- package/dist/tiers/index.d.ts +1 -1
- package/dist/tiers/index.js +1 -1
- package/dist/tiers/insight-memory.d.ts +4 -4
- package/dist/tiers/insight-memory.js +3 -3
- package/dist/tiers/insight-memory.js.map +1 -1
- package/dist/tiers/procedural-memory.d.ts +29 -7
- package/dist/tiers/procedural-memory.d.ts.map +1 -1
- package/dist/tiers/procedural-memory.js +66 -6
- package/dist/tiers/procedural-memory.js.map +1 -1
- package/dist/tiers/semantic-memory.d.ts +111 -30
- package/dist/tiers/semantic-memory.d.ts.map +1 -1
- package/dist/tiers/semantic-memory.js +120 -27
- package/dist/tiers/semantic-memory.js.map +1 -1
- package/dist/tiers/session-memory.d.ts +7 -7
- package/dist/tiers/session-memory.js +7 -7
- package/dist/tiers/session-memory.js.map +1 -1
- package/dist/tiers/shared-memory.d.ts +1 -1
- package/dist/tiers/shared-memory.js +1 -1
- package/dist/tiers/shared-memory.js.map +1 -1
- package/dist/tiers/working-memory.d.ts +3 -3
- package/dist/tiers/working-memory.js +2 -2
- package/dist/tiers/working-memory.js.map +1 -1
- package/dist/tools/block-tools.d.ts +3 -3
- package/dist/tools/block-tools.js +3 -3
- package/dist/tools/block-tools.js.map +1 -1
- package/dist/tools/fact-tools.d.ts +48 -31
- package/dist/tools/fact-tools.d.ts.map +1 -1
- package/dist/tools/fact-tools.js +30 -18
- package/dist/tools/fact-tools.js.map +1 -1
- package/dist/tools/index.d.ts +9 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/recall-tools.d.ts +40 -20
- package/dist/tools/recall-tools.d.ts.map +1 -1
- package/dist/tools/recall-tools.js +29 -10
- package/dist/tools/recall-tools.js.map +1 -1
- package/dist/tools/runbook-tools.d.ts +76 -0
- package/dist/tools/runbook-tools.d.ts.map +1 -0
- package/dist/tools/runbook-tools.js +53 -0
- package/dist/tools/runbook-tools.js.map +1 -0
- package/dist/tools/taint.js +33 -0
- package/dist/tools/taint.js.map +1 -0
- package/dist/tools/types.d.ts +1 -1
- 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
|
|
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`
|
|
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}
|
|
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
|
|
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
|
|
215
|
-
* feeds the system prompt
|
|
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
|
|
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 1
|
|
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
|
|
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`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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'`
|
|
272
|
-
* (a memory-poisoning candidate). `'synthesized'`
|
|
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`
|
|
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
|
|
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
|
|
367
|
-
* (`expandHops: 1`) on each reformulation pass
|
|
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
|
|
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(...)`
|
|
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(...)`
|
|
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}
|
|
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(...)`
|
|
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
|
|
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)
|
|
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":"
|
|
1
|
+
{"version":3,"file":"semantic-memory.d.ts","names":[],"sources":["../../src/tiers/semantic-memory.ts"],"sourcesContent":[],"mappings":"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"}
|