@graphorin/memory 0.6.0 → 0.7.0
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 +83 -0
- package/README.md +24 -7
- package/dist/conflict/index.js +0 -1
- package/dist/conflict/pipeline.js +0 -1
- package/dist/conflict/pipeline.js.map +1 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js +0 -1
- package/dist/conflict/stages/stage3-heuristic-regex.js.map +1 -1
- package/dist/conflict/types.d.ts +1 -1
- package/dist/conflict/types.js.map +1 -1
- package/dist/consolidator/index.js +2 -0
- package/dist/consolidator/index.js.map +1 -1
- package/dist/consolidator/lock.js +7 -1
- package/dist/consolidator/lock.js.map +1 -1
- package/dist/consolidator/phases/deep.js +19 -12
- package/dist/consolidator/phases/deep.js.map +1 -1
- package/dist/consolidator/phases/induce.d.ts.map +1 -1
- package/dist/consolidator/phases/induce.js +2 -3
- package/dist/consolidator/phases/induce.js.map +1 -1
- package/dist/consolidator/phases/reflect.js +35 -8
- package/dist/consolidator/phases/reflect.js.map +1 -1
- package/dist/consolidator/phases/standard.js +199 -28
- package/dist/consolidator/phases/standard.js.map +1 -1
- package/dist/consolidator/reconcile.js +16 -15
- package/dist/consolidator/reconcile.js.map +1 -1
- package/dist/consolidator/runtime.d.ts.map +1 -1
- package/dist/consolidator/runtime.js +129 -47
- package/dist/consolidator/runtime.js.map +1 -1
- package/dist/consolidator/scheduler.d.ts +17 -3
- package/dist/consolidator/scheduler.d.ts.map +1 -1
- package/dist/consolidator/scheduler.js.map +1 -1
- package/dist/consolidator/types.d.ts +30 -1
- package/dist/consolidator/types.d.ts.map +1 -1
- package/dist/consolidator/types.js +10 -0
- package/dist/consolidator/types.js.map +1 -1
- package/dist/context-engine/compaction/clear-tool-results.d.ts +1 -1
- package/dist/context-engine/compaction/clear-tool-results.js +1 -1
- package/dist/context-engine/compaction/clear-tool-results.js.map +1 -1
- package/dist/context-engine/compaction/compactor.d.ts +1 -0
- package/dist/context-engine/compaction/compactor.d.ts.map +1 -1
- package/dist/context-engine/compaction/compactor.js +32 -8
- package/dist/context-engine/compaction/compactor.js.map +1 -1
- package/dist/context-engine/compaction/hooks/types.d.ts +1 -1
- package/dist/context-engine/compaction/index.d.ts +1 -0
- package/dist/context-engine/compaction/templates/marker.d.ts +33 -0
- package/dist/context-engine/compaction/templates/marker.d.ts.map +1 -0
- package/dist/context-engine/compaction/templates/marker.js +34 -0
- package/dist/context-engine/compaction/templates/marker.js.map +1 -0
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts +1 -1
- package/dist/context-engine/compaction/templates/summary-9-section.d.ts.map +1 -1
- package/dist/context-engine/compaction/templates/summary-9-section.js +4 -3
- package/dist/context-engine/compaction/templates/summary-9-section.js.map +1 -1
- package/dist/context-engine/engine.d.ts +7 -176
- package/dist/context-engine/engine.d.ts.map +1 -1
- package/dist/context-engine/engine.js +0 -2
- package/dist/context-engine/engine.js.map +1 -1
- package/dist/context-engine/index.d.ts +8 -49
- package/dist/context-engine/io-types.d.ts +144 -0
- package/dist/context-engine/io-types.d.ts.map +1 -0
- package/dist/context-engine/locale-packs/types.d.ts +1 -1
- package/dist/context-engine/locale-packs/types.js.map +1 -1
- package/dist/context-engine/privacy-filter.d.ts +1 -2
- package/dist/context-engine/privacy-filter.d.ts.map +1 -1
- package/dist/context-engine/privacy-filter.js +1 -2
- package/dist/context-engine/privacy-filter.js.map +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +2 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/facade.d.ts +43 -52
- package/dist/facade.d.ts.map +1 -1
- package/dist/facade.js +5 -3
- package/dist/facade.js.map +1 -1
- package/dist/graph/entity-resolver.js +1 -1
- package/dist/graph/entity-resolver.js.map +1 -1
- package/dist/index.d.ts +17 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/internal/envelope.js +57 -0
- package/dist/internal/envelope.js.map +1 -0
- package/dist/internal/injection-heuristics.js +0 -0
- package/dist/internal/injection-heuristics.js.map +1 -1
- package/dist/internal/llm-json.js +40 -0
- package/dist/internal/llm-json.js.map +1 -0
- package/dist/internal/storage-adapter.d.ts +23 -10
- package/dist/internal/storage-adapter.d.ts.map +1 -1
- package/dist/memory-interface.d.ts +131 -0
- package/dist/memory-interface.d.ts.map +1 -0
- package/dist/package.js +6 -0
- package/dist/package.js.map +1 -0
- package/dist/search/iterative.d.ts.map +1 -1
- package/dist/search/iterative.js +2 -3
- package/dist/search/iterative.js.map +1 -1
- package/dist/search/query-transform.d.ts.map +1 -1
- package/dist/search/query-transform.js +2 -3
- package/dist/search/query-transform.js.map +1 -1
- package/dist/tiers/episodic-memory.js +3 -3
- package/dist/tiers/episodic-memory.js.map +1 -1
- package/dist/tiers/index.d.ts +2 -2
- package/dist/tiers/insight-memory.js +1 -1
- package/dist/tiers/insight-memory.js.map +1 -1
- package/dist/tiers/procedural-memory.d.ts +1 -1
- package/dist/tiers/procedural-memory.js +2 -2
- package/dist/tiers/procedural-memory.js.map +1 -1
- package/dist/tiers/semantic-memory.d.ts +58 -4
- package/dist/tiers/semantic-memory.d.ts.map +1 -1
- package/dist/tiers/semantic-memory.js +103 -20
- package/dist/tiers/semantic-memory.js.map +1 -1
- package/dist/tools/block-tools.d.ts +19 -57
- package/dist/tools/block-tools.d.ts.map +1 -1
- package/dist/tools/block-tools.js.map +1 -1
- package/dist/tools/fact-tools.d.ts +53 -214
- package/dist/tools/fact-tools.d.ts.map +1 -1
- package/dist/tools/fact-tools.js +3 -2
- package/dist/tools/fact-tools.js.map +1 -1
- package/dist/tools/recall-tools.d.ts +34 -175
- package/dist/tools/recall-tools.d.ts.map +1 -1
- package/dist/tools/recall-tools.js +5 -4
- package/dist/tools/recall-tools.js.map +1 -1
- package/dist/tools/runbook-tools.d.ts +15 -58
- package/dist/tools/runbook-tools.d.ts.map +1 -1
- package/dist/tools/runbook-tools.js.map +1 -1
- package/dist/tools/types.d.ts +1 -1
- package/package.json +18 -17
- package/src/conflict/index.ts +29 -0
- package/src/conflict/locale-packs/en.ts +142 -0
- package/src/conflict/locale-packs/index.ts +17 -0
- package/src/conflict/locale-packs/types.ts +138 -0
- package/src/conflict/pipeline.ts +371 -0
- package/src/conflict/stages/helpers.ts +65 -0
- package/src/conflict/stages/stage1-exact-dedup.ts +40 -0
- package/src/conflict/stages/stage2-embedding-three-zone.ts +54 -0
- package/src/conflict/stages/stage3-heuristic-regex.ts +51 -0
- package/src/conflict/stages/stage4-subject-predicate.ts +46 -0
- package/src/conflict/stages/stage5-defer-to-deep.ts +34 -0
- package/src/conflict/types.ts +263 -0
- package/src/consolidator/budget.ts +312 -0
- package/src/consolidator/decay.ts +246 -0
- package/src/consolidator/dlq.ts +61 -0
- package/src/consolidator/errors.ts +87 -0
- package/src/consolidator/idempotency.ts +27 -0
- package/src/consolidator/index.ts +269 -0
- package/src/consolidator/lock.ts +141 -0
- package/src/consolidator/noise-filter.ts +235 -0
- package/src/consolidator/phases/deep.ts +308 -0
- package/src/consolidator/phases/induce.ts +423 -0
- package/src/consolidator/phases/learned-context.ts +233 -0
- package/src/consolidator/phases/light.ts +166 -0
- package/src/consolidator/phases/reflect.ts +504 -0
- package/src/consolidator/phases/standard.ts +1002 -0
- package/src/consolidator/reconcile.ts +273 -0
- package/src/consolidator/runtime.ts +1049 -0
- package/src/consolidator/scheduler.ts +182 -0
- package/src/consolidator/triggers.ts +134 -0
- package/src/consolidator/types.ts +622 -0
- package/src/context-engine/annotations.ts +161 -0
- package/src/context-engine/auto-recall.ts +92 -0
- package/src/context-engine/compaction/clear-tool-results.ts +232 -0
- package/src/context-engine/compaction/compactor.ts +403 -0
- package/src/context-engine/compaction/hooks/reanchor-persona-block.ts +55 -0
- package/src/context-engine/compaction/hooks/reanchor-pinned-facts.ts +66 -0
- package/src/context-engine/compaction/hooks/reanchor-project-rules.ts +57 -0
- package/src/context-engine/compaction/hooks/reanchor-recent-results.ts +116 -0
- package/src/context-engine/compaction/hooks/types.ts +68 -0
- package/src/context-engine/compaction/index.ts +60 -0
- package/src/context-engine/compaction/templates/marker.ts +33 -0
- package/src/context-engine/compaction/templates/summary-9-section.ts +223 -0
- package/src/context-engine/compaction/thresholds.ts +107 -0
- package/src/context-engine/compaction/types.ts +255 -0
- package/src/context-engine/engine.ts +970 -0
- package/src/context-engine/index.ts +118 -0
- package/src/context-engine/io-types.ts +162 -0
- package/src/context-engine/locale-packs/en.ts +107 -0
- package/src/context-engine/locale-packs/index.ts +19 -0
- package/src/context-engine/locale-packs/resolver.ts +197 -0
- package/src/context-engine/locale-packs/types.ts +177 -0
- package/src/context-engine/metadata.ts +114 -0
- package/src/context-engine/preambles/inbound-en.ts +19 -0
- package/src/context-engine/privacy-filter.ts +190 -0
- package/src/context-engine/templates/base-en.ts +19 -0
- package/src/context-engine/templates/composer.ts +91 -0
- package/src/context-engine/token-budget.ts +319 -0
- package/src/context-engine/token-counter.ts +157 -0
- package/src/errors/index.ts +273 -0
- package/src/facade.ts +750 -0
- package/src/graph/entity-resolver.ts +364 -0
- package/src/graph/index.ts +24 -0
- package/src/index.ts +259 -0
- package/src/internal/contextualize.ts +173 -0
- package/src/internal/embedder-binding.ts +53 -0
- package/src/internal/envelope.ts +70 -0
- package/src/internal/id.ts +23 -0
- package/src/internal/injection-heuristics.ts +0 -0
- package/src/internal/llm-json.ts +38 -0
- package/src/internal/spans.ts +35 -0
- package/src/internal/storage-adapter.ts +963 -0
- package/src/memory-interface.ts +158 -0
- package/src/migration/embedder-migration.ts +237 -0
- package/src/migration/index.ts +13 -0
- package/src/search/explain.ts +123 -0
- package/src/search/fit-weights.ts +109 -0
- package/src/search/graph-ppr.ts +38 -0
- package/src/search/index.ts +55 -0
- package/src/search/iterative.ts +658 -0
- package/src/search/query-transform.ts +285 -0
- package/src/search/rrf.ts +225 -0
- package/src/search/trust.ts +55 -0
- package/src/search/types.ts +50 -0
- package/src/tiers/episodic-memory.ts +463 -0
- package/src/tiers/index.ts +50 -0
- package/src/tiers/insight-memory.ts +208 -0
- package/src/tiers/procedural-memory.ts +482 -0
- package/src/tiers/semantic-memory.ts +1729 -0
- package/src/tiers/session-memory.ts +271 -0
- package/src/tiers/shared-memory.ts +62 -0
- package/src/tiers/working-memory.ts +368 -0
- package/src/tools/block-tools.ts +172 -0
- package/src/tools/fact-tools.ts +473 -0
- package/src/tools/index.ts +109 -0
- package/src/tools/recall-tools.ts +303 -0
- package/src/tools/runbook-tools.ts +107 -0
- package/src/tools/taint.ts +39 -0
- package/src/tools/types.ts +37 -0
- package/dist/conflict/locale-packs/index.js +0 -4
- package/dist/context-engine/compaction/index.js +0 -10
- package/dist/context-engine/index.d.ts.map +0 -1
- package/dist/context-engine/index.js +0 -25
- package/dist/context-engine/locale-packs/index.js +0 -4
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the per-locale assets the
|
|
3
|
+
* {@link ContextEngine} consumes when assembling the layered system
|
|
4
|
+
* prompt. The framework ships an English default ({@link enLocalePack});
|
|
5
|
+
* additional locales are supplied by application code via
|
|
6
|
+
* {@link defineContextLocalePack}.
|
|
7
|
+
*
|
|
8
|
+
* The framework is locale-agnostic - no language is privileged in
|
|
9
|
+
* core. Every consumer-facing string is reachable through this
|
|
10
|
+
* surface so consumers can swap them without forking the framework.
|
|
11
|
+
*
|
|
12
|
+
* @packageDocumentation
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Trigger mode used by the auto-recall heuristic. The framework ships
|
|
17
|
+
* regex patterns for the bundled locale; consumers may extend or
|
|
18
|
+
* replace them via {@link defineContextLocalePack}.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
export interface AutoRecallTriggers {
|
|
23
|
+
/**
|
|
24
|
+
* Case-insensitive regex set evaluated against the last user
|
|
25
|
+
* message. A match indicates the model would benefit from the
|
|
26
|
+
* top-K facts being injected into Layer 6.
|
|
27
|
+
*/
|
|
28
|
+
readonly factTriggers: ReadonlyArray<RegExp>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Layer 1 template fragments. The `composeTemplate` function
|
|
33
|
+
* picks the right fragment based on `memoryBaseMode`.
|
|
34
|
+
*
|
|
35
|
+
* @stable
|
|
36
|
+
*/
|
|
37
|
+
export interface BaseTemplateFragments {
|
|
38
|
+
/** Full educational template (~250-350 tokens). */
|
|
39
|
+
readonly full: string;
|
|
40
|
+
/** Minimal template for high-end models (~80-120 tokens). */
|
|
41
|
+
readonly minimal: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* D4 inbound-sanitization preamble fragment. Injected after the
|
|
46
|
+
* cache breakpoint (Layer 5/6 territory) on steps containing
|
|
47
|
+
* untrusted tool output.
|
|
48
|
+
*
|
|
49
|
+
* @stable
|
|
50
|
+
*/
|
|
51
|
+
export interface InboundSanitizationPreamble {
|
|
52
|
+
/** Verbatim text appended to the system message when fired. */
|
|
53
|
+
readonly text: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Structured section-template fragments used by the auto-compaction
|
|
58
|
+
* summarizer (RB-46). Section order is fixed; only the per-section
|
|
59
|
+
* header / preamble text is locale-extensible.
|
|
60
|
+
*
|
|
61
|
+
* Each entry is the human-readable header for the corresponding
|
|
62
|
+
* section. Section indices are 1-based to match the documented
|
|
63
|
+
* layout in the architecture doc.
|
|
64
|
+
*
|
|
65
|
+
* @stable
|
|
66
|
+
*/
|
|
67
|
+
export interface CompactionSummaryTemplate {
|
|
68
|
+
/** Preamble injected at the top of the summarizer prompt. */
|
|
69
|
+
readonly preamble: string;
|
|
70
|
+
/** 12 section headers. The last two are filled by the harness. */
|
|
71
|
+
readonly sections: readonly [
|
|
72
|
+
string, // 1: Session goal and current task
|
|
73
|
+
string, // 2: Decisions made and rationale
|
|
74
|
+
string, // 3: Constraints and non-negotiables (C4)
|
|
75
|
+
string, // 4: Key facts established
|
|
76
|
+
string, // 5: Open questions and ambiguities
|
|
77
|
+
string, // 6: Tools used and their outcomes
|
|
78
|
+
string, // 7: Files / artifacts referenced
|
|
79
|
+
string, // 8: Persona / preferences / project rules surfaced
|
|
80
|
+
string, // 9: Errors encountered and resolutions (SOTA-6)
|
|
81
|
+
string, // 10: Next steps (SOTA-6)
|
|
82
|
+
string, // 11: Recent turns preserved verbatim (harness-filled)
|
|
83
|
+
string, // 12: Compaction metadata (harness-filled)
|
|
84
|
+
];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* A `LocalePack` collects every consumer-facing string the
|
|
89
|
+
* ContextEngine renders. Consumers register additional locales via
|
|
90
|
+
* {@link defineContextLocalePack}. The framework is locale-agnostic
|
|
91
|
+
* - no language is privileged in core.
|
|
92
|
+
*
|
|
93
|
+
* @stable
|
|
94
|
+
*/
|
|
95
|
+
export interface ContextLocalePack {
|
|
96
|
+
/** Stable lowercase identifier (`'en'`, `'de'`, `'fr'`, …). */
|
|
97
|
+
readonly id: string;
|
|
98
|
+
/** Layer 1 base template fragments. */
|
|
99
|
+
readonly baseTemplate: BaseTemplateFragments;
|
|
100
|
+
/** Auto-recall trigger regex set. */
|
|
101
|
+
readonly autoRecallTriggers: AutoRecallTriggers;
|
|
102
|
+
/** Inbound-sanitization preamble (D4 - RB-43). */
|
|
103
|
+
readonly inboundSanitizationPreamble: InboundSanitizationPreamble;
|
|
104
|
+
/** Structured compaction summary template (RB-46). */
|
|
105
|
+
readonly compactionSummaryTemplate: CompactionSummaryTemplate;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Builder used by application code that wants to ship a custom
|
|
110
|
+
* locale pack. The builder freezes every input so the pack can be
|
|
111
|
+
* safely reused across multiple `Memory` instances without
|
|
112
|
+
* accidental mutation.
|
|
113
|
+
*
|
|
114
|
+
* Partial packs are accepted: any field omitted falls back to the
|
|
115
|
+
* English default at compose time, with a one-time WARN per locale
|
|
116
|
+
* per missing surface (the safety guarantee is preserved even when
|
|
117
|
+
* an operator installs a partial locale pack).
|
|
118
|
+
*
|
|
119
|
+
* @stable
|
|
120
|
+
*/
|
|
121
|
+
export interface PartialContextLocalePack {
|
|
122
|
+
readonly id: string;
|
|
123
|
+
readonly baseTemplate?: Partial<BaseTemplateFragments>;
|
|
124
|
+
readonly autoRecallTriggers?: Partial<AutoRecallTriggers>;
|
|
125
|
+
readonly inboundSanitizationPreamble?: Partial<InboundSanitizationPreamble>;
|
|
126
|
+
readonly compactionSummaryTemplate?: Partial<CompactionSummaryTemplate>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Build a {@link ContextLocalePack} from a partial input. Missing
|
|
131
|
+
* fields fall back to the English default at compose time.
|
|
132
|
+
*
|
|
133
|
+
* @stable
|
|
134
|
+
*/
|
|
135
|
+
export function defineContextLocalePack(input: PartialContextLocalePack): PartialContextLocalePack {
|
|
136
|
+
if (typeof input.id !== 'string' || input.id.length === 0) {
|
|
137
|
+
throw new TypeError(
|
|
138
|
+
'[graphorin/memory] defineContextLocalePack: `id` must be a non-empty lowercase string.',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return Object.freeze({
|
|
142
|
+
id: input.id,
|
|
143
|
+
...(input.baseTemplate !== undefined
|
|
144
|
+
? { baseTemplate: Object.freeze({ ...input.baseTemplate }) }
|
|
145
|
+
: {}),
|
|
146
|
+
...(input.autoRecallTriggers !== undefined
|
|
147
|
+
? {
|
|
148
|
+
autoRecallTriggers: Object.freeze({
|
|
149
|
+
...(input.autoRecallTriggers.factTriggers !== undefined
|
|
150
|
+
? { factTriggers: Object.freeze([...input.autoRecallTriggers.factTriggers]) }
|
|
151
|
+
: {}),
|
|
152
|
+
}) as Partial<AutoRecallTriggers>,
|
|
153
|
+
}
|
|
154
|
+
: {}),
|
|
155
|
+
...(input.inboundSanitizationPreamble !== undefined
|
|
156
|
+
? {
|
|
157
|
+
inboundSanitizationPreamble: Object.freeze({ ...input.inboundSanitizationPreamble }),
|
|
158
|
+
}
|
|
159
|
+
: {}),
|
|
160
|
+
...(input.compactionSummaryTemplate !== undefined
|
|
161
|
+
? {
|
|
162
|
+
compactionSummaryTemplate: Object.freeze({
|
|
163
|
+
...(input.compactionSummaryTemplate.preamble !== undefined
|
|
164
|
+
? { preamble: input.compactionSummaryTemplate.preamble }
|
|
165
|
+
: {}),
|
|
166
|
+
...(input.compactionSummaryTemplate.sections !== undefined
|
|
167
|
+
? {
|
|
168
|
+
sections: Object.freeze([
|
|
169
|
+
...input.compactionSummaryTemplate.sections,
|
|
170
|
+
]) as CompactionSummaryTemplate['sections'],
|
|
171
|
+
}
|
|
172
|
+
: {}),
|
|
173
|
+
}) as Partial<CompactionSummaryTemplate>,
|
|
174
|
+
}
|
|
175
|
+
: {}),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gatherMemoryMetadata(...)` - implements the
|
|
3
|
+
* {@link Memory.metadata} surface for Phase 10d. Aggregates
|
|
4
|
+
* per-tier counts + last-consolidation timestamps + active
|
|
5
|
+
* embedder id + active locale.
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { MemoryMetadata, SessionScope } from '@graphorin/core';
|
|
11
|
+
import type { Consolidator } from '../consolidator/index.js';
|
|
12
|
+
import type { MemoryStoreAdapter } from '../internal/storage-adapter.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Per-call dependency surface. Mirrors the `Memory` facade fields
|
|
16
|
+
* the metadata gatherer needs without taking a hard dependency on
|
|
17
|
+
* the facade type itself (the facade and the gatherer are
|
|
18
|
+
* mutually-referenced through the engine).
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
export interface MemoryMetadataDeps {
|
|
23
|
+
readonly store: MemoryStoreAdapter;
|
|
24
|
+
readonly consolidator: Consolidator;
|
|
25
|
+
readonly embedderId: () => string | null;
|
|
26
|
+
readonly localeId: string;
|
|
27
|
+
/** Optional free-form metadata tags surfaced through `MemoryMetadata.tags`. */
|
|
28
|
+
readonly tags?: ReadonlyArray<string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build the {@link MemoryMetadata} block. Pure async - no side
|
|
33
|
+
* effects beyond the storage reads.
|
|
34
|
+
*
|
|
35
|
+
* @stable
|
|
36
|
+
*/
|
|
37
|
+
export async function gatherMemoryMetadata(
|
|
38
|
+
scope: SessionScope,
|
|
39
|
+
deps: MemoryMetadataDeps,
|
|
40
|
+
): Promise<MemoryMetadata> {
|
|
41
|
+
// CE-5 / MST-6: real `COUNT(*)` surfaces per tier - the old
|
|
42
|
+
// `search({ query: '*', topK: 1 })` probe was capped at 1 and matched zero
|
|
43
|
+
// rows on real SQLite (`escapeFtsQuery('*')` tokenises to nothing), so a
|
|
44
|
+
// production deploy told the model "Facts: 0" regardless of content. Counts
|
|
45
|
+
// never materialise rows; `messageCount` no longer lists up to 1000 messages.
|
|
46
|
+
const [blocks, rules, episodeCount, messageCount, factCount, status] = await Promise.all([
|
|
47
|
+
deps.store.working.list(scope),
|
|
48
|
+
deps.store.procedural.list(scope),
|
|
49
|
+
countOrZero(deps.store.episodic, scope),
|
|
50
|
+
countOrZero(deps.store.session, scope),
|
|
51
|
+
countOrZero(deps.store.semantic, scope),
|
|
52
|
+
deps.consolidator.status(),
|
|
53
|
+
]);
|
|
54
|
+
// The metadata block surfaces the embedder id + active locale so
|
|
55
|
+
// the model can reason about the available embeddings; both are
|
|
56
|
+
// informational tags exposed through the optional `tags` field.
|
|
57
|
+
const embedderId = deps.embedderId();
|
|
58
|
+
const baseTags = deps.tags !== undefined ? [...deps.tags] : [];
|
|
59
|
+
const tags: string[] = [...baseTags, `locale:${deps.localeId}`];
|
|
60
|
+
if (embedderId !== null) tags.push(`embedder:${embedderId}`);
|
|
61
|
+
|
|
62
|
+
const meta: MemoryMetadata = {
|
|
63
|
+
factCount,
|
|
64
|
+
episodeCount,
|
|
65
|
+
messageCount,
|
|
66
|
+
// MST-6: "Active rules" excludes still-quarantined (e.g. induced) rules.
|
|
67
|
+
activeRuleCount: rules.filter((rule) => rule.status !== 'quarantined').length,
|
|
68
|
+
workingBlockCount: blocks.length,
|
|
69
|
+
...(status.lastRunAt !== undefined ? { lastConsolidatedAt: status.lastRunAt } : {}),
|
|
70
|
+
tags: Object.freeze(tags),
|
|
71
|
+
};
|
|
72
|
+
return Object.freeze(meta);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Read a tier's `count(scope)` when the adapter exposes it (CE-5), degrading to
|
|
77
|
+
* `0` for adapters that do not - honest, and never the old 0/1 probe.
|
|
78
|
+
*/
|
|
79
|
+
async function countOrZero(
|
|
80
|
+
store: { count?(scope: SessionScope): Promise<number> },
|
|
81
|
+
scope: SessionScope,
|
|
82
|
+
): Promise<number> {
|
|
83
|
+
if (typeof store.count !== 'function') return 0;
|
|
84
|
+
try {
|
|
85
|
+
return await store.count(scope);
|
|
86
|
+
} catch {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Render the {@link MemoryMetadata} block as the `<memory_metadata>`
|
|
93
|
+
* XML fragment used inside Layer 5 of the layered system prompt.
|
|
94
|
+
*
|
|
95
|
+
* @stable
|
|
96
|
+
*/
|
|
97
|
+
export function renderMetadataBlock(meta: MemoryMetadata): string {
|
|
98
|
+
const lines = [
|
|
99
|
+
'<memory_metadata>',
|
|
100
|
+
` Working blocks: ${meta.workingBlockCount}`,
|
|
101
|
+
` Active rules: ${meta.activeRuleCount}`,
|
|
102
|
+
` Indexed messages: ${meta.messageCount}`,
|
|
103
|
+
` Episodes: ${meta.episodeCount}`,
|
|
104
|
+
` Facts: ${meta.factCount}`,
|
|
105
|
+
];
|
|
106
|
+
if (meta.lastConsolidatedAt !== undefined) {
|
|
107
|
+
lines.push(` Last consolidation: ${meta.lastConsolidatedAt}`);
|
|
108
|
+
}
|
|
109
|
+
if (meta.tags !== undefined && meta.tags.length > 0) {
|
|
110
|
+
lines.push(` Tags: ${meta.tags.join(', ')}`);
|
|
111
|
+
}
|
|
112
|
+
lines.push('</memory_metadata>');
|
|
113
|
+
return lines.join('\n');
|
|
114
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenience re-export of the English D4 inbound-sanitization
|
|
3
|
+
* preamble. Lives at the documented path
|
|
4
|
+
* `@graphorin/memory/context-engine/preambles/inbound-en.ts` so
|
|
5
|
+
* Phase 12 (agent runtime) can import it directly when wiring
|
|
6
|
+
* the per-step preamble injection.
|
|
7
|
+
*
|
|
8
|
+
* The framework is locale-agnostic - additional preambles plug in
|
|
9
|
+
* through `defineContextLocalePack({ inboundSanitizationPreamble:
|
|
10
|
+
* { text } })`. A partial locale pack falls back to this English
|
|
11
|
+
* default with a one-time WARN per locale.
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { enLocalePack } from '../locale-packs/en.js';
|
|
17
|
+
|
|
18
|
+
/** English D4 preamble text (~80-120 tokens, cache-friendly). */
|
|
19
|
+
export const INBOUND_SANITIZATION_PREAMBLE_EN = enLocalePack.inboundSanitizationPreamble.text;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D2 sensitivity-tier filter. Tags every memory record with a
|
|
3
|
+
* decision (`'pass'` / `'drop'`) before the assembled prompt is
|
|
4
|
+
* compiled. The filter is *tier-based* - it trusts the
|
|
5
|
+
* `record.sensitivity` tag the consolidator / writer set; the
|
|
6
|
+
* outbound prompt-redaction middleware (D3, ADR-045) is the
|
|
7
|
+
* universal backstop for content the filter cannot see (raw user
|
|
8
|
+
* input, tool results, MCP responses, skill bodies, agent
|
|
9
|
+
* instructions, tool-call args).
|
|
10
|
+
*
|
|
11
|
+
* @packageDocumentation
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { LocalProviderTrust, Sensitivity } from '@graphorin/core';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Filter input: the record-level sensitivity tag + the per-tier
|
|
18
|
+
* trust class of the active provider.
|
|
19
|
+
*
|
|
20
|
+
* @stable
|
|
21
|
+
*/
|
|
22
|
+
export interface PrivacyFilterContext {
|
|
23
|
+
/**
|
|
24
|
+
* Sensitivity tiers the active provider is allowed to receive.
|
|
25
|
+
* When omitted, the per-tier defaults below apply.
|
|
26
|
+
*/
|
|
27
|
+
readonly providerAcceptsSensitivity?: ReadonlyArray<Sensitivity>;
|
|
28
|
+
/**
|
|
29
|
+
* Trust class of the active provider as classified by
|
|
30
|
+
* `@graphorin/provider/trust/classify-local-provider.ts`. Defaults
|
|
31
|
+
* to `'public-tls'` (the conservative cloud default).
|
|
32
|
+
*/
|
|
33
|
+
readonly providerTrust?: LocalProviderTrust;
|
|
34
|
+
/**
|
|
35
|
+
* Per-user opt-in for cloud upload of `'internal'`-tier content.
|
|
36
|
+
* Default `false` - `'internal'` content does not leave the
|
|
37
|
+
* loopback boundary unless the user explicitly opts in.
|
|
38
|
+
*/
|
|
39
|
+
readonly cloudUploadConsent?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Override the default sensitivity applied to records that are
|
|
42
|
+
* missing a tag. Default `'internal'` per DEC-126.
|
|
43
|
+
*/
|
|
44
|
+
readonly defaultSensitivity?: Sensitivity;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Output of {@link decide} for a single record.
|
|
49
|
+
*
|
|
50
|
+
* @stable
|
|
51
|
+
*/
|
|
52
|
+
export type PrivacyDecision = 'pass' | 'drop';
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Reason the decision was made. Surfaced to the metadata block so
|
|
56
|
+
* operators can audit per-tier drops.
|
|
57
|
+
*
|
|
58
|
+
* @stable
|
|
59
|
+
*/
|
|
60
|
+
export type PrivacyDecisionReason =
|
|
61
|
+
| 'provider-rejects-secret'
|
|
62
|
+
| 'provider-rejects-internal'
|
|
63
|
+
| 'no-cloud-upload-consent'
|
|
64
|
+
| 'allowed';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the effective `acceptsSensitivity` set for a provider.
|
|
68
|
+
* The function is pure: callers can supply a custom matrix to
|
|
69
|
+
* override the defaults (used by tests + custom adapters).
|
|
70
|
+
*
|
|
71
|
+
* Defaults (DEC-149, ADR-013 ext):
|
|
72
|
+
*
|
|
73
|
+
* - `'loopback'` → `['public', 'internal', 'secret']`.
|
|
74
|
+
* - `'private'` → `['public', 'internal']`.
|
|
75
|
+
* - `'public-tls'` / `'public-cleartext'` → `['public']`.
|
|
76
|
+
*
|
|
77
|
+
* Per-provider overrides always win over the defaults - pass
|
|
78
|
+
* `providerAcceptsSensitivity` explicitly to override.
|
|
79
|
+
*
|
|
80
|
+
* @stable
|
|
81
|
+
*/
|
|
82
|
+
export function effectiveAcceptsSensitivity(
|
|
83
|
+
context: PrivacyFilterContext,
|
|
84
|
+
): ReadonlyArray<Sensitivity> {
|
|
85
|
+
if (context.providerAcceptsSensitivity !== undefined) {
|
|
86
|
+
return context.providerAcceptsSensitivity;
|
|
87
|
+
}
|
|
88
|
+
switch (context.providerTrust ?? 'public-tls') {
|
|
89
|
+
case 'loopback':
|
|
90
|
+
return ['public', 'internal', 'secret'];
|
|
91
|
+
case 'private':
|
|
92
|
+
return ['public', 'internal'];
|
|
93
|
+
case 'public-tls':
|
|
94
|
+
case 'public-cleartext':
|
|
95
|
+
return ['public'];
|
|
96
|
+
default:
|
|
97
|
+
return ['public'];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Decide whether a single record is safe to send to the active
|
|
103
|
+
* provider. The decision logic mirrors the DoD spec:
|
|
104
|
+
*
|
|
105
|
+
* - `'public'` always passes.
|
|
106
|
+
* - `'secret'` passes only when the provider explicitly accepts
|
|
107
|
+
* `'secret'` AND the trust class is `'loopback'`.
|
|
108
|
+
* - `'internal'` passes when the provider accepts `'internal'`
|
|
109
|
+
* AND `cloudUploadConsent === true` (or the trust class is
|
|
110
|
+
* `'loopback'` / `'private'`).
|
|
111
|
+
*
|
|
112
|
+
* @stable
|
|
113
|
+
*/
|
|
114
|
+
export function decide(
|
|
115
|
+
recordSensitivity: Sensitivity | undefined,
|
|
116
|
+
context: PrivacyFilterContext,
|
|
117
|
+
): { readonly decision: PrivacyDecision; readonly reason: PrivacyDecisionReason } {
|
|
118
|
+
const sensitivity = recordSensitivity ?? context.defaultSensitivity ?? 'internal';
|
|
119
|
+
const accepts = effectiveAcceptsSensitivity(context);
|
|
120
|
+
const trust = context.providerTrust ?? 'public-tls';
|
|
121
|
+
if (sensitivity === 'public') {
|
|
122
|
+
return { decision: 'pass', reason: 'allowed' };
|
|
123
|
+
}
|
|
124
|
+
if (sensitivity === 'secret') {
|
|
125
|
+
if (accepts.includes('secret') && trust === 'loopback') {
|
|
126
|
+
return { decision: 'pass', reason: 'allowed' };
|
|
127
|
+
}
|
|
128
|
+
return { decision: 'drop', reason: 'provider-rejects-secret' };
|
|
129
|
+
}
|
|
130
|
+
// 'internal'
|
|
131
|
+
if (!accepts.includes('internal')) {
|
|
132
|
+
return { decision: 'drop', reason: 'provider-rejects-internal' };
|
|
133
|
+
}
|
|
134
|
+
if (trust === 'loopback' || trust === 'private') {
|
|
135
|
+
return { decision: 'pass', reason: 'allowed' };
|
|
136
|
+
}
|
|
137
|
+
if (context.cloudUploadConsent === true) {
|
|
138
|
+
return { decision: 'pass', reason: 'allowed' };
|
|
139
|
+
}
|
|
140
|
+
return { decision: 'drop', reason: 'no-cloud-upload-consent' };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Bookkeeping returned by {@link partition}. Carries both the
|
|
145
|
+
* surviving + dropped records and a per-reason counter for the
|
|
146
|
+
* audit trail.
|
|
147
|
+
*
|
|
148
|
+
* @stable
|
|
149
|
+
*/
|
|
150
|
+
export interface PartitionResult<TRecord> {
|
|
151
|
+
readonly kept: ReadonlyArray<TRecord>;
|
|
152
|
+
readonly dropped: ReadonlyArray<{
|
|
153
|
+
readonly record: TRecord;
|
|
154
|
+
readonly reason: PrivacyDecisionReason;
|
|
155
|
+
}>;
|
|
156
|
+
readonly counters: Readonly<Record<PrivacyDecisionReason, number>>;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Partition a record list against the supplied filter context.
|
|
161
|
+
*
|
|
162
|
+
* @stable
|
|
163
|
+
*/
|
|
164
|
+
export function partition<TRecord extends { readonly sensitivity?: Sensitivity }>(
|
|
165
|
+
records: ReadonlyArray<TRecord>,
|
|
166
|
+
context: PrivacyFilterContext,
|
|
167
|
+
): PartitionResult<TRecord> {
|
|
168
|
+
const kept: TRecord[] = [];
|
|
169
|
+
const dropped: Array<{ readonly record: TRecord; readonly reason: PrivacyDecisionReason }> = [];
|
|
170
|
+
const counters: Record<PrivacyDecisionReason, number> = {
|
|
171
|
+
allowed: 0,
|
|
172
|
+
'no-cloud-upload-consent': 0,
|
|
173
|
+
'provider-rejects-internal': 0,
|
|
174
|
+
'provider-rejects-secret': 0,
|
|
175
|
+
};
|
|
176
|
+
for (const record of records) {
|
|
177
|
+
const outcome = decide(record.sensitivity, context);
|
|
178
|
+
counters[outcome.reason] += 1;
|
|
179
|
+
if (outcome.decision === 'pass') {
|
|
180
|
+
kept.push(record);
|
|
181
|
+
} else {
|
|
182
|
+
dropped.push({ record, reason: outcome.reason });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return Object.freeze({
|
|
186
|
+
kept: Object.freeze(kept),
|
|
187
|
+
dropped: Object.freeze(dropped),
|
|
188
|
+
counters: Object.freeze(counters),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convenience re-export of the English Layer 1 base-template
|
|
3
|
+
* fragments so call sites can import the bundled defaults without
|
|
4
|
+
* dragging the full locale pack.
|
|
5
|
+
*
|
|
6
|
+
* The framework is locale-agnostic - no language is privileged in
|
|
7
|
+
* core. Application code can register additional locales via
|
|
8
|
+
* `defineContextLocalePack`.
|
|
9
|
+
*
|
|
10
|
+
* @packageDocumentation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { enLocalePack } from '../locale-packs/en.js';
|
|
14
|
+
|
|
15
|
+
/** English Layer 1 in `'full'` mode (~250-350 tokens). */
|
|
16
|
+
export const BASE_TEMPLATE_EN_FULL = enLocalePack.baseTemplate.full;
|
|
17
|
+
|
|
18
|
+
/** English Layer 1 in `'minimal'` mode (~80-120 tokens). */
|
|
19
|
+
export const BASE_TEMPLATE_EN_MINIMAL = enLocalePack.baseTemplate.minimal;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 1 base-template composer. Resolves the locale pack, picks
|
|
3
|
+
* the right base fragment based on `memoryBaseMode`, and exposes
|
|
4
|
+
* the per-step inbound preamble as an additive Layer 5/6 fragment
|
|
5
|
+
* (so the Layer 1-4 cache prefix stays bytes-equal across the run).
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ContextLocalePack } from '../locale-packs/index.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Layer 1 mode discriminator. `'full'` ships the full educational
|
|
14
|
+
* template (~250-350 tok); `'minimal'` ships the shortened version
|
|
15
|
+
* (~80-120 tok) for top-tier models.
|
|
16
|
+
*
|
|
17
|
+
* @stable
|
|
18
|
+
*/
|
|
19
|
+
export type MemoryBaseMode = 'full' | 'minimal';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Compose Layer 1 from the resolved locale pack.
|
|
23
|
+
*
|
|
24
|
+
* @stable
|
|
25
|
+
*/
|
|
26
|
+
export function composeLayer1(pack: ContextLocalePack, mode: MemoryBaseMode): string {
|
|
27
|
+
return mode === 'minimal' ? pack.baseTemplate.minimal : pack.baseTemplate.full;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Render the optional Layer 2 (`agent_instructions`) wrapper.
|
|
32
|
+
* Returns the empty string when `instructions` is empty so the
|
|
33
|
+
* layer is dropped from the assembled prompt.
|
|
34
|
+
*
|
|
35
|
+
* @stable
|
|
36
|
+
*/
|
|
37
|
+
export function composeLayer2(instructions: string | undefined): string {
|
|
38
|
+
if (instructions === undefined || instructions.trim().length === 0) return '';
|
|
39
|
+
return `<agent_instructions>\n${instructions}\n</agent_instructions>`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Render Layer 4 skills metadata cards. Each entry is a
|
|
44
|
+
* `<skill ... />` self-closing tag so the model sees only the
|
|
45
|
+
* progressive-disclosure metadata - the full SKILL.md body is
|
|
46
|
+
* loaded on-demand via `activate_skill(name)` per RB-04.
|
|
47
|
+
*
|
|
48
|
+
* @stable
|
|
49
|
+
*/
|
|
50
|
+
export interface SkillMetadataCard {
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly description: string;
|
|
53
|
+
readonly location?: string;
|
|
54
|
+
/** When `true`, the card is excluded from the assembled prompt. */
|
|
55
|
+
readonly disableModelInvocation?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function composeLayer4Skills(cards: ReadonlyArray<SkillMetadataCard>): string {
|
|
59
|
+
const visible = cards.filter((c) => c.disableModelInvocation !== true);
|
|
60
|
+
if (visible.length === 0) return '';
|
|
61
|
+
const lines = ['<skills_available>'];
|
|
62
|
+
for (const card of visible) {
|
|
63
|
+
const description = ` description="${escapeXmlAttr(card.description)}"`;
|
|
64
|
+
const location =
|
|
65
|
+
card.location !== undefined ? ` location="${escapeXmlAttr(card.location)}"` : '';
|
|
66
|
+
lines.push(` <skill name="${escapeXmlAttr(card.name)}"${description}${location} />`);
|
|
67
|
+
}
|
|
68
|
+
lines.push('</skills_available>');
|
|
69
|
+
return lines.join('\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Render the inbound-sanitization preamble fragment. Emitted
|
|
74
|
+
* AFTER the cache breakpoint so the Layer 1-4 cache prefix is
|
|
75
|
+
* unaffected. Caller threads the fragment into the system content
|
|
76
|
+
* post-Layer 5; the fragment is returned as a string so the
|
|
77
|
+
* caller does not need to know the cache-breakpoint policy.
|
|
78
|
+
*
|
|
79
|
+
* @stable
|
|
80
|
+
*/
|
|
81
|
+
export function composeInboundPreamble(pack: ContextLocalePack): string {
|
|
82
|
+
return pack.inboundSanitizationPreamble.text;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function escapeXmlAttr(value: string): string {
|
|
86
|
+
return value
|
|
87
|
+
.replace(/&/g, '&')
|
|
88
|
+
.replace(/</g, '<')
|
|
89
|
+
.replace(/>/g, '>')
|
|
90
|
+
.replace(/"/g, '"');
|
|
91
|
+
}
|