@fgv/ts-agent-memory 5.1.0-36
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/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +9 -0
- package/.rush/temp/f6a88bfdd66517ccb98c2c7ae1be6e6fe9e15d38.tar.log +282 -0
- package/.rush/temp/operation/build/all.log +9 -0
- package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
- package/.rush/temp/operation/build/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +688 -0
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/config/api-extractor.json +343 -0
- package/config/jest.config.json +14 -0
- package/config/rig.json +4 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/packlets/converters/bodyConverterRegistry.js +51 -0
- package/dist/packlets/converters/bodyConverterRegistry.js.map +1 -0
- package/dist/packlets/converters/envelopeConverter.js +159 -0
- package/dist/packlets/converters/envelopeConverter.js.map +1 -0
- package/dist/packlets/converters/index.js +7 -0
- package/dist/packlets/converters/index.js.map +1 -0
- package/dist/packlets/index/index.js +6 -0
- package/dist/packlets/index/index.js.map +1 -0
- package/dist/packlets/index/memoryIndex.js +170 -0
- package/dist/packlets/index/memoryIndex.js.map +1 -0
- package/dist/packlets/observe/index.js +7 -0
- package/dist/packlets/observe/index.js.map +1 -0
- package/dist/packlets/observe/memoryObservationStore.js +119 -0
- package/dist/packlets/observe/memoryObservationStore.js.map +1 -0
- package/dist/packlets/observe/observer.js +6 -0
- package/dist/packlets/observe/observer.js.map +1 -0
- package/dist/packlets/retrieve/hybridRetriever.js +135 -0
- package/dist/packlets/retrieve/hybridRetriever.js.map +1 -0
- package/dist/packlets/retrieve/index.js +12 -0
- package/dist/packlets/retrieve/index.js.map +1 -0
- package/dist/packlets/retrieve/linkTraversalRetriever.js +143 -0
- package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -0
- package/dist/packlets/retrieve/recencyRetriever.js +33 -0
- package/dist/packlets/retrieve/recencyRetriever.js.map +1 -0
- package/dist/packlets/retrieve/retriever.js +110 -0
- package/dist/packlets/retrieve/retriever.js.map +1 -0
- package/dist/packlets/retrieve/semanticRetriever.js +86 -0
- package/dist/packlets/retrieve/semanticRetriever.js.map +1 -0
- package/dist/packlets/retrieve/structuredFilterRetriever.js +37 -0
- package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -0
- package/dist/packlets/retrieve/tagRetriever.js +37 -0
- package/dist/packlets/retrieve/tagRetriever.js.map +1 -0
- package/dist/packlets/store/fileTreeMemoryStore.js +698 -0
- package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -0
- package/dist/packlets/store/index.js +7 -0
- package/dist/packlets/store/index.js.map +1 -0
- package/dist/packlets/store/scopeEncoding.js +31 -0
- package/dist/packlets/store/scopeEncoding.js.map +1 -0
- package/dist/packlets/types/envelope.js +6 -0
- package/dist/packlets/types/envelope.js.map +1 -0
- package/dist/packlets/types/filenameSafety.js +52 -0
- package/dist/packlets/types/filenameSafety.js.map +1 -0
- package/dist/packlets/types/identityCodec.js +184 -0
- package/dist/packlets/types/identityCodec.js.map +1 -0
- package/dist/packlets/types/ids.js +67 -0
- package/dist/packlets/types/ids.js.map +1 -0
- package/dist/packlets/types/index.js +10 -0
- package/dist/packlets/types/index.js.map +1 -0
- package/dist/packlets/types/writePolicy.js +263 -0
- package/dist/packlets/types/writePolicy.js.map +1 -0
- package/dist/packlets/vector/inMemoryCosineIndex.js +150 -0
- package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -0
- package/dist/packlets/vector/index.js +7 -0
- package/dist/packlets/vector/index.js.map +1 -0
- package/dist/packlets/vector/vectorIndex.js +6 -0
- package/dist/packlets/vector/vectorIndex.js.map +1 -0
- package/dist/test/unit/converters/bodyConverterRegistry.test.js +72 -0
- package/dist/test/unit/converters/bodyConverterRegistry.test.js.map +1 -0
- package/dist/test/unit/converters/envelopeConverter.test.js +196 -0
- package/dist/test/unit/converters/envelopeConverter.test.js.map +1 -0
- package/dist/test/unit/index/memoryIndex.test.js +152 -0
- package/dist/test/unit/index/memoryIndex.test.js.map +1 -0
- package/dist/test/unit/observe/memoryObservationStore.test.js +118 -0
- package/dist/test/unit/observe/memoryObservationStore.test.js.map +1 -0
- package/dist/test/unit/retrieve/linkTraversalRetriever.test.js +182 -0
- package/dist/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -0
- package/dist/test/unit/retrieve/retrievers.test.js +506 -0
- package/dist/test/unit/retrieve/retrievers.test.js.map +1 -0
- package/dist/test/unit/store/embedOnWrite.test.js +260 -0
- package/dist/test/unit/store/embedOnWrite.test.js.map +1 -0
- package/dist/test/unit/store/fileTreeMemoryStore.test.js +647 -0
- package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -0
- package/dist/test/unit/store/observations.test.js +239 -0
- package/dist/test/unit/store/observations.test.js.map +1 -0
- package/dist/test/unit/store/scopeEncoding.test.js +24 -0
- package/dist/test/unit/store/scopeEncoding.test.js.map +1 -0
- package/dist/test/unit/types/identityCodec.test.js +187 -0
- package/dist/test/unit/types/identityCodec.test.js.map +1 -0
- package/dist/test/unit/types/ids.test.js +84 -0
- package/dist/test/unit/types/ids.test.js.map +1 -0
- package/dist/test/unit/types/writePolicy.test.js +241 -0
- package/dist/test/unit/types/writePolicy.test.js.map +1 -0
- package/dist/test/unit/vector/inMemoryCosineIndex.test.js +192 -0
- package/dist/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -0
- package/dist/test/unit/vector/vectorIndex.test.js +42 -0
- package/dist/test/unit/vector/vectorIndex.test.js.map +1 -0
- package/dist/ts-agent-memory.d.ts +1901 -0
- package/dist/tsdoc-metadata.json +11 -0
- package/eslint.config.js +15 -0
- package/etc/ts-agent-memory.api.md +525 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +28 -0
- package/lib/index.js.map +1 -0
- package/lib/packlets/converters/bodyConverterRegistry.d.ts +64 -0
- package/lib/packlets/converters/bodyConverterRegistry.d.ts.map +1 -0
- package/lib/packlets/converters/bodyConverterRegistry.js +55 -0
- package/lib/packlets/converters/bodyConverterRegistry.js.map +1 -0
- package/lib/packlets/converters/envelopeConverter.d.ts +70 -0
- package/lib/packlets/converters/envelopeConverter.d.ts.map +1 -0
- package/lib/packlets/converters/envelopeConverter.js +166 -0
- package/lib/packlets/converters/envelopeConverter.js.map +1 -0
- package/lib/packlets/converters/index.d.ts +3 -0
- package/lib/packlets/converters/index.d.ts.map +1 -0
- package/lib/packlets/converters/index.js +23 -0
- package/lib/packlets/converters/index.js.map +1 -0
- package/lib/packlets/index/index.d.ts +2 -0
- package/lib/packlets/index/index.d.ts.map +1 -0
- package/lib/packlets/index/index.js +22 -0
- package/lib/packlets/index/index.js.map +1 -0
- package/lib/packlets/index/memoryIndex.d.ts +127 -0
- package/lib/packlets/index/memoryIndex.d.ts.map +1 -0
- package/lib/packlets/index/memoryIndex.js +174 -0
- package/lib/packlets/index/memoryIndex.js.map +1 -0
- package/lib/packlets/observe/index.d.ts +3 -0
- package/lib/packlets/observe/index.d.ts.map +1 -0
- package/lib/packlets/observe/index.js +23 -0
- package/lib/packlets/observe/index.js.map +1 -0
- package/lib/packlets/observe/memoryObservationStore.d.ts +91 -0
- package/lib/packlets/observe/memoryObservationStore.d.ts.map +1 -0
- package/lib/packlets/observe/memoryObservationStore.js +123 -0
- package/lib/packlets/observe/memoryObservationStore.js.map +1 -0
- package/lib/packlets/observe/observer.d.ts +110 -0
- package/lib/packlets/observe/observer.d.ts.map +1 -0
- package/lib/packlets/observe/observer.js +7 -0
- package/lib/packlets/observe/observer.js.map +1 -0
- package/lib/packlets/retrieve/hybridRetriever.d.ts +79 -0
- package/lib/packlets/retrieve/hybridRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/hybridRetriever.js +140 -0
- package/lib/packlets/retrieve/hybridRetriever.js.map +1 -0
- package/lib/packlets/retrieve/index.d.ts +8 -0
- package/lib/packlets/retrieve/index.d.ts.map +1 -0
- package/lib/packlets/retrieve/index.js +28 -0
- package/lib/packlets/retrieve/index.js.map +1 -0
- package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +61 -0
- package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/linkTraversalRetriever.js +147 -0
- package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -0
- package/lib/packlets/retrieve/recencyRetriever.d.ts +21 -0
- package/lib/packlets/retrieve/recencyRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/recencyRetriever.js +37 -0
- package/lib/packlets/retrieve/recencyRetriever.js.map +1 -0
- package/lib/packlets/retrieve/retriever.d.ts +133 -0
- package/lib/packlets/retrieve/retriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/retriever.js +119 -0
- package/lib/packlets/retrieve/retriever.js.map +1 -0
- package/lib/packlets/retrieve/semanticRetriever.d.ts +69 -0
- package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/semanticRetriever.js +90 -0
- package/lib/packlets/retrieve/semanticRetriever.js.map +1 -0
- package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +22 -0
- package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/structuredFilterRetriever.js +41 -0
- package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -0
- package/lib/packlets/retrieve/tagRetriever.d.ts +22 -0
- package/lib/packlets/retrieve/tagRetriever.d.ts.map +1 -0
- package/lib/packlets/retrieve/tagRetriever.js +41 -0
- package/lib/packlets/retrieve/tagRetriever.js.map +1 -0
- package/lib/packlets/store/fileTreeMemoryStore.d.ts +327 -0
- package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -0
- package/lib/packlets/store/fileTreeMemoryStore.js +702 -0
- package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -0
- package/lib/packlets/store/index.d.ts +3 -0
- package/lib/packlets/store/index.d.ts.map +1 -0
- package/lib/packlets/store/index.js +23 -0
- package/lib/packlets/store/index.js.map +1 -0
- package/lib/packlets/store/scopeEncoding.d.ts +19 -0
- package/lib/packlets/store/scopeEncoding.d.ts.map +1 -0
- package/lib/packlets/store/scopeEncoding.js +34 -0
- package/lib/packlets/store/scopeEncoding.js.map +1 -0
- package/lib/packlets/types/envelope.d.ts +119 -0
- package/lib/packlets/types/envelope.d.ts.map +1 -0
- package/lib/packlets/types/envelope.js +7 -0
- package/lib/packlets/types/envelope.js.map +1 -0
- package/lib/packlets/types/filenameSafety.d.ts +16 -0
- package/lib/packlets/types/filenameSafety.d.ts.map +1 -0
- package/lib/packlets/types/filenameSafety.js +55 -0
- package/lib/packlets/types/filenameSafety.js.map +1 -0
- package/lib/packlets/types/identityCodec.d.ts +136 -0
- package/lib/packlets/types/identityCodec.d.ts.map +1 -0
- package/lib/packlets/types/identityCodec.js +190 -0
- package/lib/packlets/types/identityCodec.js.map +1 -0
- package/lib/packlets/types/ids.d.ts +55 -0
- package/lib/packlets/types/ids.d.ts.map +1 -0
- package/lib/packlets/types/ids.js +70 -0
- package/lib/packlets/types/ids.js.map +1 -0
- package/lib/packlets/types/index.d.ts +6 -0
- package/lib/packlets/types/index.d.ts.map +1 -0
- package/lib/packlets/types/index.js +26 -0
- package/lib/packlets/types/index.js.map +1 -0
- package/lib/packlets/types/writePolicy.d.ts +213 -0
- package/lib/packlets/types/writePolicy.d.ts.map +1 -0
- package/lib/packlets/types/writePolicy.js +268 -0
- package/lib/packlets/types/writePolicy.js.map +1 -0
- package/lib/packlets/vector/inMemoryCosineIndex.d.ts +69 -0
- package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -0
- package/lib/packlets/vector/inMemoryCosineIndex.js +154 -0
- package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -0
- package/lib/packlets/vector/index.d.ts +3 -0
- package/lib/packlets/vector/index.d.ts.map +1 -0
- package/lib/packlets/vector/index.js +23 -0
- package/lib/packlets/vector/index.js.map +1 -0
- package/lib/packlets/vector/vectorIndex.d.ts +68 -0
- package/lib/packlets/vector/vectorIndex.d.ts.map +1 -0
- package/lib/packlets/vector/vectorIndex.js +7 -0
- package/lib/packlets/vector/vectorIndex.js.map +1 -0
- package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts +2 -0
- package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts.map +1 -0
- package/lib/test/unit/converters/bodyConverterRegistry.test.js +74 -0
- package/lib/test/unit/converters/bodyConverterRegistry.test.js.map +1 -0
- package/lib/test/unit/converters/envelopeConverter.test.d.ts +2 -0
- package/lib/test/unit/converters/envelopeConverter.test.d.ts.map +1 -0
- package/lib/test/unit/converters/envelopeConverter.test.js +198 -0
- package/lib/test/unit/converters/envelopeConverter.test.js.map +1 -0
- package/lib/test/unit/index/memoryIndex.test.d.ts +2 -0
- package/lib/test/unit/index/memoryIndex.test.d.ts.map +1 -0
- package/lib/test/unit/index/memoryIndex.test.js +154 -0
- package/lib/test/unit/index/memoryIndex.test.js.map +1 -0
- package/lib/test/unit/observe/memoryObservationStore.test.d.ts +2 -0
- package/lib/test/unit/observe/memoryObservationStore.test.d.ts.map +1 -0
- package/lib/test/unit/observe/memoryObservationStore.test.js +120 -0
- package/lib/test/unit/observe/memoryObservationStore.test.js.map +1 -0
- package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts +2 -0
- package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts.map +1 -0
- package/lib/test/unit/retrieve/linkTraversalRetriever.test.js +184 -0
- package/lib/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -0
- package/lib/test/unit/retrieve/retrievers.test.d.ts +2 -0
- package/lib/test/unit/retrieve/retrievers.test.d.ts.map +1 -0
- package/lib/test/unit/retrieve/retrievers.test.js +508 -0
- package/lib/test/unit/retrieve/retrievers.test.js.map +1 -0
- package/lib/test/unit/store/embedOnWrite.test.d.ts +2 -0
- package/lib/test/unit/store/embedOnWrite.test.d.ts.map +1 -0
- package/lib/test/unit/store/embedOnWrite.test.js +262 -0
- package/lib/test/unit/store/embedOnWrite.test.js.map +1 -0
- package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts +2 -0
- package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts.map +1 -0
- package/lib/test/unit/store/fileTreeMemoryStore.test.js +649 -0
- package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -0
- package/lib/test/unit/store/observations.test.d.ts +2 -0
- package/lib/test/unit/store/observations.test.d.ts.map +1 -0
- package/lib/test/unit/store/observations.test.js +241 -0
- package/lib/test/unit/store/observations.test.js.map +1 -0
- package/lib/test/unit/store/scopeEncoding.test.d.ts +2 -0
- package/lib/test/unit/store/scopeEncoding.test.d.ts.map +1 -0
- package/lib/test/unit/store/scopeEncoding.test.js +26 -0
- package/lib/test/unit/store/scopeEncoding.test.js.map +1 -0
- package/lib/test/unit/types/identityCodec.test.d.ts +2 -0
- package/lib/test/unit/types/identityCodec.test.d.ts.map +1 -0
- package/lib/test/unit/types/identityCodec.test.js +189 -0
- package/lib/test/unit/types/identityCodec.test.js.map +1 -0
- package/lib/test/unit/types/ids.test.d.ts +2 -0
- package/lib/test/unit/types/ids.test.d.ts.map +1 -0
- package/lib/test/unit/types/ids.test.js +86 -0
- package/lib/test/unit/types/ids.test.js.map +1 -0
- package/lib/test/unit/types/writePolicy.test.d.ts +2 -0
- package/lib/test/unit/types/writePolicy.test.d.ts.map +1 -0
- package/lib/test/unit/types/writePolicy.test.js +243 -0
- package/lib/test/unit/types/writePolicy.test.js.map +1 -0
- package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts +2 -0
- package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts.map +1 -0
- package/lib/test/unit/vector/inMemoryCosineIndex.test.js +194 -0
- package/lib/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -0
- package/lib/test/unit/vector/vectorIndex.test.d.ts +2 -0
- package/lib/test/unit/vector/vectorIndex.test.d.ts.map +1 -0
- package/lib/test/unit/vector/vectorIndex.test.js +44 -0
- package/lib/test/unit/vector/vectorIndex.test.js.map +1 -0
- package/package.json +81 -0
- package/rush-logs/ts-agent-memory.build.cache.log +3 -0
- package/rush-logs/ts-agent-memory.build.log +9 -0
- package/src/index.ts +12 -0
- package/src/packlets/converters/bodyConverterRegistry.ts +105 -0
- package/src/packlets/converters/envelopeConverter.ts +210 -0
- package/src/packlets/converters/index.ts +7 -0
- package/src/packlets/index/index.ts +6 -0
- package/src/packlets/index/memoryIndex.ts +268 -0
- package/src/packlets/observe/index.ts +7 -0
- package/src/packlets/observe/memoryObservationStore.ts +153 -0
- package/src/packlets/observe/observer.ts +119 -0
- package/src/packlets/retrieve/hybridRetriever.ts +181 -0
- package/src/packlets/retrieve/index.ts +12 -0
- package/src/packlets/retrieve/linkTraversalRetriever.ts +169 -0
- package/src/packlets/retrieve/recencyRetriever.ts +54 -0
- package/src/packlets/retrieve/retriever.ts +207 -0
- package/src/packlets/retrieve/semanticRetriever.ts +147 -0
- package/src/packlets/retrieve/structuredFilterRetriever.ts +58 -0
- package/src/packlets/retrieve/tagRetriever.ts +58 -0
- package/src/packlets/store/fileTreeMemoryStore.ts +1073 -0
- package/src/packlets/store/index.ts +7 -0
- package/src/packlets/store/scopeEncoding.ts +36 -0
- package/src/packlets/types/envelope.ts +138 -0
- package/src/packlets/types/filenameSafety.ts +57 -0
- package/src/packlets/types/identityCodec.ts +263 -0
- package/src/packlets/types/ids.ts +124 -0
- package/src/packlets/types/index.ts +10 -0
- package/src/packlets/types/writePolicy.ts +447 -0
- package/src/packlets/vector/inMemoryCosineIndex.ts +173 -0
- package/src/packlets/vector/index.ts +7 -0
- package/src/packlets/vector/vectorIndex.ts +78 -0
- package/src/test/unit/converters/bodyConverterRegistry.test.ts +89 -0
- package/src/test/unit/converters/envelopeConverter.test.ts +261 -0
- package/src/test/unit/index/memoryIndex.test.ts +187 -0
- package/src/test/unit/observe/memoryObservationStore.test.ts +158 -0
- package/src/test/unit/retrieve/linkTraversalRetriever.test.ts +230 -0
- package/src/test/unit/retrieve/retrievers.test.ts +662 -0
- package/src/test/unit/store/embedOnWrite.test.ts +346 -0
- package/src/test/unit/store/fileTreeMemoryStore.test.ts +875 -0
- package/src/test/unit/store/observations.test.ts +290 -0
- package/src/test/unit/store/scopeEncoding.test.ts +37 -0
- package/src/test/unit/types/identityCodec.test.ts +266 -0
- package/src/test/unit/types/ids.test.ts +94 -0
- package/src/test/unit/types/writePolicy.test.ts +325 -0
- package/src/test/unit/vector/inMemoryCosineIndex.test.ts +242 -0
- package/src/test/unit/vector/vectorIndex.test.ts +48 -0
- package/temp/build/lint/_eslint-5eVG3S6w.json +182 -0
- package/temp/build/typescript/ts_8nwakTlr.json +1 -0
- package/temp/ts-agent-memory.api.json +12438 -0
- package/temp/ts-agent-memory.api.md +525 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,1901 @@
|
|
|
1
|
+
import { Brand } from '@fgv/ts-utils';
|
|
2
|
+
import { Converter } from '@fgv/ts-utils';
|
|
3
|
+
import { FileTree } from '@fgv/ts-json-base';
|
|
4
|
+
import { JsonSchema } from '@fgv/ts-json-base';
|
|
5
|
+
import { Logging } from '@fgv/ts-utils';
|
|
6
|
+
import { Result } from '@fgv/ts-utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The admission decision returned by {@link IWritePolicy.admit}.
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export declare type AdmissionDecision = {
|
|
13
|
+
readonly decision: 'accept';
|
|
14
|
+
} | {
|
|
15
|
+
readonly decision: 'reject';
|
|
16
|
+
readonly reason: string;
|
|
17
|
+
} | {
|
|
18
|
+
readonly decision: 'cull-oldest';
|
|
19
|
+
/** {@link MemoryId}s to evict before writing the incoming record. */
|
|
20
|
+
readonly evict: ReadonlyArray<MemoryId>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Validates a single filename stem against the POSIX portable filename set
|
|
25
|
+
* (`[A-Za-z0-9._-]`), rejecting a leading or trailing `.` and reserved Windows
|
|
26
|
+
* device names. This is the cross-platform filename-stem contract for the
|
|
27
|
+
* package: {@link MemoryId} values, the concrete {@link IIdentityCodec}
|
|
28
|
+
* implementations, and the store's `verifyFilenameId` all gate on it.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* A trailing `.` is rejected because Windows silently strips trailing dots from
|
|
32
|
+
* filenames, so such a stem would not round-trip verbatim through the file
|
|
33
|
+
* layer even though it is otherwise portable-set-valid.
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertPortableFilenameStem(stem: string): Result<string>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Default in-memory {@link IBodyConverterRegistry}.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* Both `register` and `registerSchema` adapt the supplied converter/validator
|
|
43
|
+
* into a `Converter<unknown>` via `Converters.generic`, so the heterogeneous
|
|
44
|
+
* per-kind types are stored uniformly with no cast and no `any`. A
|
|
45
|
+
* `Converter<T>` is not structurally a `Converter<unknown>` (the `map` callback
|
|
46
|
+
* makes the type invariant), so the `generic` wrapper — whose callback returns
|
|
47
|
+
* `Result<T>` (assignable to `Result<unknown>`) — is the type-safe bridge.
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export declare class BodyConverterRegistry implements IBodyConverterRegistry {
|
|
51
|
+
private readonly _converters;
|
|
52
|
+
private constructor();
|
|
53
|
+
/** Family-convention factory. */
|
|
54
|
+
static create(): Result<BodyConverterRegistry>;
|
|
55
|
+
/** {@inheritDoc IBodyConverterRegistry.register} */
|
|
56
|
+
register<T>(kind: Kind, converter: Converter<T>): void;
|
|
57
|
+
/** {@inheritDoc IBodyConverterRegistry.registerSchema} */
|
|
58
|
+
registerSchema<T>(kind: Kind, schema: JsonSchema.ISchemaValidator<T>): void;
|
|
59
|
+
/** {@inheritDoc IBodyConverterRegistry.has} */
|
|
60
|
+
has(kind: Kind): boolean;
|
|
61
|
+
/** {@inheritDoc IBodyConverterRegistry.getConverter} */
|
|
62
|
+
getConverter(kind: Kind): Result<Converter<unknown>>;
|
|
63
|
+
/** {@inheritDoc IBodyConverterRegistry.convert} */
|
|
64
|
+
convert(kind: Kind, body: unknown): Result<unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Converters for the branded identifier scalars. Each validates an `unknown`
|
|
69
|
+
* value into the corresponding brand, enforcing the shared hygiene
|
|
70
|
+
* (non-empty, length-capped, trimmed).
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
export declare const Convert: {
|
|
74
|
+
readonly memoryId: Converter<MemoryId>;
|
|
75
|
+
readonly entityId: Converter<EntityId>;
|
|
76
|
+
readonly kind: Converter<Kind>;
|
|
77
|
+
readonly tag: Converter<Tag>;
|
|
78
|
+
readonly scopeKey: Converter<MemoryScopeKey>;
|
|
79
|
+
readonly linkType: Converter<LinkType>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The granularity at which the store deduplicates an incoming write against the
|
|
84
|
+
* existing vault, declared per kind via {@link IWritePolicy.dedupScope}.
|
|
85
|
+
*
|
|
86
|
+
* - `'content'`: scope-wide cross-id content dedup. An identical
|
|
87
|
+
* `{ kind, body, links }` triple ANYWHERE in the scope — even under a
|
|
88
|
+
* different id — is a no-op that returns the existing record. The knowledge
|
|
89
|
+
* kind family uses this.
|
|
90
|
+
* - `'entity'`: same-id content dedup only. An identical re-put of the SAME
|
|
91
|
+
* entity is a no-op, but two DISTINCT entities with identical content never
|
|
92
|
+
* collapse. The experience (memory) kind families use this so that, e.g.,
|
|
93
|
+
* two turns whose summaries happen to be byte-identical both persist.
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
96
|
+
export declare type DedupScope = 'content' | 'entity';
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The default {@link DedupScope} applied when a policy does not declare one.
|
|
100
|
+
* Entity-scoped dedup is the safe default — it never silently collapses two
|
|
101
|
+
* distinct entities with coincidentally-identical content.
|
|
102
|
+
* @public
|
|
103
|
+
*/
|
|
104
|
+
export declare const DEFAULT_DEDUP_SCOPE: DedupScope;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Encode a {@link MemoryScopeKey} to its on-disk directory path. The scope may
|
|
108
|
+
* be multi-segment (`/`-separated) — each component is validated independently
|
|
109
|
+
* against the POSIX portable filename set (via
|
|
110
|
+
* {@link assertPortableFilenameStem}), then rejoined with `/`.
|
|
111
|
+
*
|
|
112
|
+
* @remarks
|
|
113
|
+
* This is the day-one multi-segment scope resolver design-lock §9.1 calls for:
|
|
114
|
+
* the knowledge scope is the single segment `knowledge`, while the Phase-C MTM
|
|
115
|
+
* scope `conversations/<conversationId>` is two segments. Validating each
|
|
116
|
+
* segment independently (rather than the whole path as one stem, which the
|
|
117
|
+
* `ts-prompt-assist` default encoding does) keeps the Phase-C codec additive —
|
|
118
|
+
* no scope-encoding change is needed when MTM ships.
|
|
119
|
+
* @public
|
|
120
|
+
*/
|
|
121
|
+
export declare function defaultMemoryScopeEncoding(scope: MemoryScopeKey): Result<string>;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Converter for an attributed {@link IEdge}.
|
|
125
|
+
* @public
|
|
126
|
+
*/
|
|
127
|
+
export declare const edgeConverter: Converter<IEdge>;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Consumer-supplied domain key. The stable entity identity across versions;
|
|
131
|
+
* the package never mints identity. Equals {@link MemoryId} for non-temporal
|
|
132
|
+
* kinds.
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
export declare type EntityId = Brand<string, 'EntityId'>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Converter for the invariant {@link IMemoryEnvelope}. Validates a plain
|
|
139
|
+
* object (e.g. parsed YAML frontmatter) into a typed envelope.
|
|
140
|
+
* @public
|
|
141
|
+
*/
|
|
142
|
+
export declare const envelopeConverter: Converter<IMemoryEnvelope>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Converter that parses a YAML frontmatter string into an
|
|
146
|
+
* {@link IMemoryEnvelope}.
|
|
147
|
+
* @public
|
|
148
|
+
*/
|
|
149
|
+
export declare const envelopeYamlConverter: Converter<IMemoryEnvelope>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Flat-layout, FileTree-backed {@link IMemoryStore}. The FileTree is the source
|
|
153
|
+
* of truth; the {@link IMemoryIndex} is a derived in-memory view patched on
|
|
154
|
+
* every write. Concurrent writes are serialized through a per-instance async
|
|
155
|
+
* write-lock so the index and the on-disk files never interleave.
|
|
156
|
+
*
|
|
157
|
+
* @remarks
|
|
158
|
+
* B1 supports flat (non-versioned) layout only and string (markdown) bodies.
|
|
159
|
+
* A codec reporting `isVersioned: true`, or a non-string body, fails loudly —
|
|
160
|
+
* the versioned/temporal write path is a fast-follow.
|
|
161
|
+
* @public
|
|
162
|
+
*/
|
|
163
|
+
export declare class FileTreeMemoryStore implements IMemoryStore {
|
|
164
|
+
private readonly _root;
|
|
165
|
+
private readonly _registry;
|
|
166
|
+
private readonly _writePolicies;
|
|
167
|
+
private readonly _codecs;
|
|
168
|
+
private readonly _defaultCodec;
|
|
169
|
+
private readonly _defaultPolicy;
|
|
170
|
+
private readonly _scopeEncoding;
|
|
171
|
+
private readonly _clock;
|
|
172
|
+
private readonly _index;
|
|
173
|
+
private readonly _hasher;
|
|
174
|
+
private readonly _observers;
|
|
175
|
+
private readonly _logger;
|
|
176
|
+
private readonly _vectorIndex;
|
|
177
|
+
private readonly _embed;
|
|
178
|
+
/** Monotonic write counter; incremented inside the write-lock on each put. */
|
|
179
|
+
private _seq;
|
|
180
|
+
/**
|
|
181
|
+
* Monotonic observation-sequence counter. A distinct authority from `_seq`
|
|
182
|
+
* (the envelope write counter): it numbers the audit stream so a single
|
|
183
|
+
* {@link MemoryObservationStore} fed by this store sees strictly increasing
|
|
184
|
+
* `seq`, satisfying the ring buffer's cursor contract.
|
|
185
|
+
*/
|
|
186
|
+
private _observationSeq;
|
|
187
|
+
/** Tail of the write-lock promise chain that serializes mutating ops. */
|
|
188
|
+
private _writeTail;
|
|
189
|
+
/**
|
|
190
|
+
* The record-level mutable-field vocabulary: maps a declared mutable field
|
|
191
|
+
* name to its canonical location on a record. Used to project an incoming
|
|
192
|
+
* record into a merge-patch on update. B1 ships the knowledge-LWW surface
|
|
193
|
+
* (body + envelope metadata); body-internal mutable fields (Phase-C cap-cull)
|
|
194
|
+
* extend this map.
|
|
195
|
+
*/
|
|
196
|
+
private static readonly _mutableFieldAccessors;
|
|
197
|
+
private constructor();
|
|
198
|
+
/**
|
|
199
|
+
* Family-convention factory. Builds the derived index and a default LWW
|
|
200
|
+
* policy, then performs an initial FileTree walk so an existing vault is
|
|
201
|
+
* indexed (and the `seq` counter resumes past the highest persisted `seq`).
|
|
202
|
+
*/
|
|
203
|
+
static create(params: IFileTreeMemoryStoreCreateParams): Result<FileTreeMemoryStore>;
|
|
204
|
+
/** {@inheritDoc IMemoryStore.get} */
|
|
205
|
+
get(kind: Kind, entityId: EntityId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
|
|
206
|
+
/** {@inheritDoc IMemoryStore.getById} */
|
|
207
|
+
getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
|
|
208
|
+
/** {@inheritDoc IMemoryStore.list} */
|
|
209
|
+
list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
210
|
+
/** {@inheritDoc IMemoryStore.put} */
|
|
211
|
+
put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
|
|
212
|
+
/** {@inheritDoc IMemoryStore.delete} */
|
|
213
|
+
delete(kind: Kind, entityId: EntityId): Promise<Result<MemoryId>>;
|
|
214
|
+
/**
|
|
215
|
+
* Serialize a mutating task behind the write-lock. Tasks run in submission
|
|
216
|
+
* order; a failed task does not break the chain for subsequent ones.
|
|
217
|
+
*/
|
|
218
|
+
private _enqueue;
|
|
219
|
+
/**
|
|
220
|
+
* Build and fan out one observation record for a completed op. A no-op when no
|
|
221
|
+
* observers are wired (the additive-default path pays nothing). The store is
|
|
222
|
+
* the seq authority — it mints `seq` / `timestamp` so every observer sees the
|
|
223
|
+
* same record. The scope is resolved best-effort via the codec.
|
|
224
|
+
*/
|
|
225
|
+
private _fireObservation;
|
|
226
|
+
/** Invoke one observer, swallowing any failure or throw (logged at `warn`). */
|
|
227
|
+
private _safeObserve;
|
|
228
|
+
/**
|
|
229
|
+
* Log a swallowed-issue warning (observer failure or best-effort vector
|
|
230
|
+
* maintenance), tolerating a logger that itself throws — diagnostic logging
|
|
231
|
+
* must never make a store op reject.
|
|
232
|
+
*/
|
|
233
|
+
private _warnSwallowed;
|
|
234
|
+
/** Resolve a scope for an observation, best-effort (undefined when unresolvable). */
|
|
235
|
+
private _scopeBestEffort;
|
|
236
|
+
private _putLocked;
|
|
237
|
+
/**
|
|
238
|
+
* Run dedup → policy → stamp → embed → write for a resolved address and content
|
|
239
|
+
* hash. Async because the embed-on-write hook (when wired) does a network call
|
|
240
|
+
* or in-process inference; the whole chain runs inside the write-lock so the
|
|
241
|
+
* vector index, the on-disk file, and the derived index never interleave.
|
|
242
|
+
*/
|
|
243
|
+
private _writeResolved;
|
|
244
|
+
/**
|
|
245
|
+
* Build → embed → persist → evict for an admitted write.
|
|
246
|
+
*
|
|
247
|
+
* The durable record store is authoritative; the vector index is a **derived,
|
|
248
|
+
* rebuildable** view, so vector maintenance is **best-effort** — a failed embed
|
|
249
|
+
* or `add` is logged and the durable write still succeeds (the index can be
|
|
250
|
+
* rebuilt via {@link InMemoryCosineIndex.rebuild}). Only genuine record-store
|
|
251
|
+
* failures (body/codec/policy, persist, file eviction) fail the `put`.
|
|
252
|
+
*
|
|
253
|
+
* Ordering: the embed + `add` run immediately before the single `_persist` so
|
|
254
|
+
* the index-returned `embeddingRef` lands in one durable write (a post-persist
|
|
255
|
+
* stamp would need a second write whose failure path is effectively untestable).
|
|
256
|
+
* Build and persist the replacement BEFORE evicting the cull-oldest cohort, so a
|
|
257
|
+
* later eviction failure never loses data with nothing written in its place.
|
|
258
|
+
*/
|
|
259
|
+
private _admitWrite;
|
|
260
|
+
/**
|
|
261
|
+
* Best-effort embed-on-write. When a vector index AND an embedder are wired,
|
|
262
|
+
* embeds the built record, `add`s the vector (replace semantics handle a same-id
|
|
263
|
+
* re-embed — no explicit remove), and stamps the returned `embeddingRef`. A
|
|
264
|
+
* failure (returned `fail` OR a thrown/rejected hook) is logged and the
|
|
265
|
+
* unembedded record is returned unchanged — the put still persists, and the
|
|
266
|
+
* derived index is reconciled by a later `rebuild`. A pass-through no-op when
|
|
267
|
+
* unwired (byte-identical record).
|
|
268
|
+
*
|
|
269
|
+
* Always succeeds (`Result` is the chain's shape, never a vector-induced
|
|
270
|
+
* failure).
|
|
271
|
+
*/
|
|
272
|
+
private _embedOnWrite;
|
|
273
|
+
/**
|
|
274
|
+
* Evict the records named by a `cull-oldest` decision, best-effort. Runs only
|
|
275
|
+
* after the authoritative `_persist`, so a failed eviction is logged (never
|
|
276
|
+
* fatal) and the cap self-corrects on the next admission. Returns the ids that
|
|
277
|
+
* were actually evicted (so observations / vector pruning cover only those).
|
|
278
|
+
* `accept` / `reject` decisions evict nothing.
|
|
279
|
+
*/
|
|
280
|
+
private _applyEvictions;
|
|
281
|
+
/** Best-effort vector removal for each evicted record (never fails the put). */
|
|
282
|
+
private _removeEvictedVectors;
|
|
283
|
+
/**
|
|
284
|
+
* Run a consumer-supplied vector hook, normalizing a thrown/rejected hook into a
|
|
285
|
+
* `Failure` and logging any failure at `warn`. Best-effort: the caller proceeds
|
|
286
|
+
* regardless, since the index is rebuildable.
|
|
287
|
+
*/
|
|
288
|
+
private _tryVectorOp;
|
|
289
|
+
/**
|
|
290
|
+
* Best-effort vector removal. A no-op unless the full vector lifecycle is wired
|
|
291
|
+
* (both an index AND an embedder), so an unwired store does no vector work and
|
|
292
|
+
* behaves byte-identically. Failures are logged, never surfaced — a committed
|
|
293
|
+
* delete/eviction must not fail because a derived index could not be pruned.
|
|
294
|
+
*/
|
|
295
|
+
private _removeVectorBestEffort;
|
|
296
|
+
/**
|
|
297
|
+
* Build the record to persist. On a first write the incoming envelope is the
|
|
298
|
+
* base (final content equals the incoming content, so the dedup `hash` is
|
|
299
|
+
* reused). On an update the incoming record's mutable fields are projected into
|
|
300
|
+
* a merge-patch and the policy's `applyUpdate` merges them over the existing
|
|
301
|
+
* record (preserving `created`); the persisted body and `contentHash` are then
|
|
302
|
+
* taken from the policy's actual output, so a body-transforming policy is never
|
|
303
|
+
* bypassed and the stored hash always matches the stored `{ kind, body, links }`.
|
|
304
|
+
* The store stamps the transaction-time metadata it owns
|
|
305
|
+
* (`created` / `updated` / `seq` / `contentHash`).
|
|
306
|
+
*/
|
|
307
|
+
private _buildRecord;
|
|
308
|
+
/** Serialize and write a fully-stamped record, then patch the index. */
|
|
309
|
+
private _persist;
|
|
310
|
+
private _deleteLocked;
|
|
311
|
+
/** Evict (physically delete) a single record file by id, patching the index. */
|
|
312
|
+
private _evict;
|
|
313
|
+
/** Project the incoming record's mutable fields into a merge-patch. */
|
|
314
|
+
private _projectMutablePatch;
|
|
315
|
+
/**
|
|
316
|
+
* The admission cohort for a write: every indexed record in `scope` of `kind`
|
|
317
|
+
* except the one at `idStem` (the record being written or updated). This is
|
|
318
|
+
* the set a per-kind cap (e.g. {@link MemoryCapCullPolicy}) counts against, so
|
|
319
|
+
* a bounded-ring policy can keep a per-scope/per-kind family within
|
|
320
|
+
* `maxRecords`. Excluding the target id makes the post-write count uniform
|
|
321
|
+
* across first-writes and updates.
|
|
322
|
+
*/
|
|
323
|
+
private _admissionCohort;
|
|
324
|
+
/** Find a record in `scope` whose `contentHash` equals `hash`, if any. */
|
|
325
|
+
private _findByContentHash;
|
|
326
|
+
private _contentHash;
|
|
327
|
+
private _codecFor;
|
|
328
|
+
private _policyFor;
|
|
329
|
+
/**
|
|
330
|
+
* Read and validate the record at `<scope>/<idStem>.md`, returning `undefined`
|
|
331
|
+
* when the scope directory or file is absent. Verifies the on-disk id ↔
|
|
332
|
+
* filename round-trip on every load.
|
|
333
|
+
*/
|
|
334
|
+
private _readRecord;
|
|
335
|
+
/** Enforce `envelope.id === filename stem` and the codec round-trip on load. */
|
|
336
|
+
private _verifyLoaded;
|
|
337
|
+
/**
|
|
338
|
+
* Resolve the directory for a scope, returning `undefined` when it does not
|
|
339
|
+
* exist. Navigation only — does not create. Folds the path segments through
|
|
340
|
+
* `getChildren` so an absent segment short-circuits to `undefined`.
|
|
341
|
+
*/
|
|
342
|
+
private _resolveScopeDir;
|
|
343
|
+
/** Ensure the scope directory exists, creating segments as needed. */
|
|
344
|
+
private _ensureScopeDir;
|
|
345
|
+
/** Write (create or overwrite) `<scope>/<idStem>.md` with `raw`. */
|
|
346
|
+
private _writeFile;
|
|
347
|
+
/**
|
|
348
|
+
* Physically delete `<scope>/<idStem>.md`. The scope-missing and file-missing
|
|
349
|
+
* guards are unreachable through the callers (`delete` / `_evict` both read the
|
|
350
|
+
* record first, so the directory and file exist) but are kept so a future
|
|
351
|
+
* direct caller degrades loudly rather than silently.
|
|
352
|
+
*/
|
|
353
|
+
private _deleteFile;
|
|
354
|
+
/**
|
|
355
|
+
* Walk the FileTree once and rebuild the index. Also resumes the `seq`
|
|
356
|
+
* counter past the highest persisted `seq` so new writes stay monotonic.
|
|
357
|
+
*/
|
|
358
|
+
private _initialIndex;
|
|
359
|
+
/** Recursively collect every `.md` record under `dir` (scope = path segments). */
|
|
360
|
+
private _collectEntries;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Enforce the loud-degradation contract for the `semantic` and `asOf` axes
|
|
365
|
+
* against a retriever's `capabilities`: a requested capability the retriever
|
|
366
|
+
* does not support fails loudly rather than returning a silent empty result.
|
|
367
|
+
* @public
|
|
368
|
+
*/
|
|
369
|
+
export declare function guardRetrieverCapabilities(query: IMemoryQuery, capabilities: IMemoryRetrieverCapabilities): Result<true>;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Composes several retrievers, dispatches a query to each, and merges their
|
|
373
|
+
* results via an injectable {@link IMergeStrategy}. Its capabilities are the
|
|
374
|
+
* union of the composed retrievers' capabilities.
|
|
375
|
+
*
|
|
376
|
+
* @remarks
|
|
377
|
+
* The hybrid enforces the loud-degradation contract against its OWN (union)
|
|
378
|
+
* capabilities, then projects the query for each child: a child that does not
|
|
379
|
+
* support the `semantic` (or `asOf`) axis is handed a query with that axis
|
|
380
|
+
* stripped, so it returns its normal results instead of loud-failing on a field
|
|
381
|
+
* a sibling handles. A child that genuinely fails (e.g. a wired semantic backend
|
|
382
|
+
* erroring) propagates — the hybrid never silently drops a failure.
|
|
383
|
+
* @public
|
|
384
|
+
*/
|
|
385
|
+
export declare class HybridRetriever implements IMemoryRetriever {
|
|
386
|
+
private readonly _retrievers;
|
|
387
|
+
private readonly _mergeStrategy;
|
|
388
|
+
private readonly _capabilities;
|
|
389
|
+
private constructor();
|
|
390
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
391
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
392
|
+
/**
|
|
393
|
+
* Family-convention factory.
|
|
394
|
+
* @param retrievers - The retrievers to compose (at least one).
|
|
395
|
+
* @param mergeStrategy - How to combine their results.
|
|
396
|
+
*/
|
|
397
|
+
static create(retrievers: ReadonlyArray<IMemoryRetriever>, mergeStrategy: IMergeStrategy): Result<HybridRetriever>;
|
|
398
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
399
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
400
|
+
/**
|
|
401
|
+
* Project the query for one child retriever. Two adjustments:
|
|
402
|
+
*
|
|
403
|
+
* - Strip axes the child does not support (`semantic` / `topK` for a
|
|
404
|
+
* non-semantic child, `asOf` for a non-temporal child, the link axes for a
|
|
405
|
+
* non-link child) so it returns its normal results rather than loud-failing
|
|
406
|
+
* on a field a sibling handles.
|
|
407
|
+
* - Strip `limit` unconditionally: limit is a post-merge concern. A child that
|
|
408
|
+
* pre-truncated its result set would starve the merge strategy of candidates
|
|
409
|
+
* it needs to score correctly (a record both children would surface must
|
|
410
|
+
* reach the merge to score 2). The hybrid applies `limit` once, after merge.
|
|
411
|
+
*/
|
|
412
|
+
private _projectQuery;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Registry of per-kind body Converters. Each memory {@link Kind} registers the
|
|
417
|
+
* validated shape of its body; the store dispatches an `unknown` body through
|
|
418
|
+
* the registered Converter on every write. Type-safe — no `any`.
|
|
419
|
+
* @public
|
|
420
|
+
*/
|
|
421
|
+
export declare interface IBodyConverterRegistry {
|
|
422
|
+
/**
|
|
423
|
+
* Register a Converter for a kind. Replaces any prior registration for the
|
|
424
|
+
* same kind.
|
|
425
|
+
*/
|
|
426
|
+
register<T>(kind: Kind, converter: Converter<T>): void;
|
|
427
|
+
/**
|
|
428
|
+
* Register a `JsonSchema` validator (from `@fgv/ts-json-base`) for a kind —
|
|
429
|
+
* the schema IS a Validator. Replaces any prior registration for the same
|
|
430
|
+
* kind.
|
|
431
|
+
*/
|
|
432
|
+
registerSchema<T>(kind: Kind, schema: JsonSchema.ISchemaValidator<T>): void;
|
|
433
|
+
/** Returns `true` if a converter is registered for the kind. */
|
|
434
|
+
has(kind: Kind): boolean;
|
|
435
|
+
/**
|
|
436
|
+
* Get the raw converter registered for a kind. Fails if the kind is
|
|
437
|
+
* unregistered. Exposed for implementors that need to re-validate a patched
|
|
438
|
+
* body (e.g. a temporal-versioned policy's `applyUpdate`).
|
|
439
|
+
*/
|
|
440
|
+
getConverter(kind: Kind): Result<Converter<unknown>>;
|
|
441
|
+
/**
|
|
442
|
+
* Convert an `unknown` body value for the given kind. Fails with
|
|
443
|
+
* `no converter registered for kind '<k>'` when unregistered.
|
|
444
|
+
*/
|
|
445
|
+
convert(kind: Kind, body: unknown): Result<unknown>;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* An attributed link between two records. Carries the relation type, the
|
|
450
|
+
* target id, and optional confidence / provenance / world-truth validity.
|
|
451
|
+
* Replaces bare string references (e.g. PersonAIlity's `IMtmRef` becomes an
|
|
452
|
+
* `IEdge` with `type: LinkType('mtm-ref')`).
|
|
453
|
+
* @public
|
|
454
|
+
*/
|
|
455
|
+
export declare interface IEdge {
|
|
456
|
+
/** Open-vocabulary relation type. */
|
|
457
|
+
readonly type: LinkType;
|
|
458
|
+
/** The linked-to record. */
|
|
459
|
+
readonly target: MemoryId;
|
|
460
|
+
/** Optional confidence in `[0, 1]`. */
|
|
461
|
+
readonly confidence?: number;
|
|
462
|
+
/** Optional structured provenance for the link itself. */
|
|
463
|
+
readonly provenance?: IProvenance;
|
|
464
|
+
/** World-truth validity start (epoch ms). Present only on temporal edges. */
|
|
465
|
+
readonly valid_at?: number;
|
|
466
|
+
/**
|
|
467
|
+
* World-truth validity end (epoch ms). `null` = still valid; absent = no
|
|
468
|
+
* temporal extent.
|
|
469
|
+
*/
|
|
470
|
+
readonly invalid_at?: number | null;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Parameters for {@link FileTreeMemoryStore.create}.
|
|
475
|
+
* @public
|
|
476
|
+
*/
|
|
477
|
+
export declare interface IFileTreeMemoryStoreCreateParams {
|
|
478
|
+
/** Root directory under which scope-encoded sub-trees live. Must be mutable. */
|
|
479
|
+
readonly root: FileTree.IMutableFileTreeDirectoryItem;
|
|
480
|
+
/** Per-kind body converter registry. Gates every body on write and read. */
|
|
481
|
+
readonly registry: IBodyConverterRegistry;
|
|
482
|
+
/** Per-kind write policies. Kinds without an entry use a default LWW policy. */
|
|
483
|
+
readonly writePolicies?: ReadonlyMap<Kind, IWritePolicy>;
|
|
484
|
+
/** Per-kind identity codecs. */
|
|
485
|
+
readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;
|
|
486
|
+
/** Default codec for kinds without an explicit entry. */
|
|
487
|
+
readonly defaultCodec?: IIdentityCodec;
|
|
488
|
+
/** Scope encoding. Defaults to {@link defaultMemoryScopeEncoding}. */
|
|
489
|
+
readonly scopeEncoding?: (scope: MemoryScopeKey) => Result<string>;
|
|
490
|
+
/**
|
|
491
|
+
* Transaction-time clock. Defaults to `Date.now`. Injectable so tests can
|
|
492
|
+
* make `created` / `updated` deterministic. Also stamps observation
|
|
493
|
+
* `timestamp`s.
|
|
494
|
+
*/
|
|
495
|
+
readonly clock?: () => number;
|
|
496
|
+
/**
|
|
497
|
+
* Optional observers fired once per public `get` / `put` / `delete` call.
|
|
498
|
+
* Purely additive — when absent, no observation records are produced and the
|
|
499
|
+
* store behaves exactly as it did without this parameter. Observer errors
|
|
500
|
+
* never affect the store operation (swallowed, logged to {@link
|
|
501
|
+
* IFileTreeMemoryStoreCreateParams.logger | logger} at `warn`).
|
|
502
|
+
*/
|
|
503
|
+
readonly observers?: ReadonlyArray<IMemoryObserver>;
|
|
504
|
+
/**
|
|
505
|
+
* Diagnostic logger for swallowed observer failures. Defaults to a
|
|
506
|
+
* `Logging.NoOpLogger`.
|
|
507
|
+
*/
|
|
508
|
+
readonly logger?: Logging.ILogger;
|
|
509
|
+
/**
|
|
510
|
+
* Optional vector index for semantic recall. Wired together with
|
|
511
|
+
* {@link IFileTreeMemoryStoreCreateParams.embed | embed}: when both are present
|
|
512
|
+
* the store embeds each written record and maintains the index on
|
|
513
|
+
* `put` / `delete` / cap-cull eviction. Absent (or `embed` absent) → no
|
|
514
|
+
* embedding work happens and the store behaves exactly as it does without this
|
|
515
|
+
* parameter (the additive, zero-overhead-when-unwired default — mirrors the
|
|
516
|
+
* observer hook).
|
|
517
|
+
*/
|
|
518
|
+
readonly vectorIndex?: IVectorIndex;
|
|
519
|
+
/**
|
|
520
|
+
* Optional embedder applied to each record on write, wired together with
|
|
521
|
+
* {@link IFileTreeMemoryStoreCreateParams.vectorIndex | vectorIndex}. The
|
|
522
|
+
* consumer supplies it (e.g. `callProviderEmbedding` or in-process
|
|
523
|
+
* transformers); the store never calls an embedding provider directly, so the
|
|
524
|
+
* core stays embedder-agnostic. Embedding/index maintenance is **best-effort**:
|
|
525
|
+
* a failed (or throwing) `embed` / `add` / `remove` is logged at `warn` via
|
|
526
|
+
* {@link IFileTreeMemoryStoreCreateParams.logger | logger} and the record
|
|
527
|
+
* operation still succeeds — the vector index is a derived view that a later
|
|
528
|
+
* `rebuild` reconciles, so a vector failure never fails an authoritative write.
|
|
529
|
+
*/
|
|
530
|
+
readonly embed?: MemoryEmbedder;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Maps a consumer-supplied domain key ⇄ a FileTree storage address. Injected
|
|
535
|
+
* per kind so the store never touches raw domain keys: the codec owns all
|
|
536
|
+
* filename escaping and the flat-vs-versioned layout dispatch.
|
|
537
|
+
* @public
|
|
538
|
+
*/
|
|
539
|
+
export declare interface IIdentityCodec {
|
|
540
|
+
/**
|
|
541
|
+
* Encode a consumer-supplied entity id to a FileTree address. Deterministic
|
|
542
|
+
* and pure — no I/O.
|
|
543
|
+
*/
|
|
544
|
+
encode(entityId: EntityId): Result<IIdentityCodecResult>;
|
|
545
|
+
/**
|
|
546
|
+
* Decode a FileTree address back to the original {@link EntityId}. The exact
|
|
547
|
+
* inverse of {@link IIdentityCodec.encode | encode} for non-versioned kinds.
|
|
548
|
+
*/
|
|
549
|
+
decode(scope: MemoryScopeKey, encodedStem: string): Result<EntityId>;
|
|
550
|
+
/**
|
|
551
|
+
* Assert that `encode(decode(scope, stem)).idStem === stem`. Used by the
|
|
552
|
+
* store's `verifyFilenameId` check on load.
|
|
553
|
+
*/
|
|
554
|
+
verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* The FileTree storage address an {@link IIdentityCodec} maps a domain key to.
|
|
559
|
+
* @public
|
|
560
|
+
*/
|
|
561
|
+
export declare interface IIdentityCodecResult {
|
|
562
|
+
/** Scope path segment (may be multi-level, e.g. `conversations/<id>`). */
|
|
563
|
+
readonly scope: MemoryScopeKey;
|
|
564
|
+
/** Filename stem (the part before `.md`). Filename-safe after encoding. */
|
|
565
|
+
readonly idStem: string;
|
|
566
|
+
/**
|
|
567
|
+
* Whether this kind uses a versioned layout (temporal: multiple files per
|
|
568
|
+
* entity) vs. a flat layout (one file per entity). Non-temporal = always
|
|
569
|
+
* `false`.
|
|
570
|
+
*/
|
|
571
|
+
readonly isVersioned: boolean;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* A memory record paired with the {@link MemoryScopeKey | scope} it lives
|
|
576
|
+
* under. The scope is not carried on the {@link IMemoryEnvelope} (it is derived
|
|
577
|
+
* from the entity id by the codec), so the store threads it alongside the
|
|
578
|
+
* record when patching or rebuilding the index. The `(scope, id)` pair is the
|
|
579
|
+
* index's primary key, keeping records distinct across scopes that reuse a
|
|
580
|
+
* filename stem (e.g. `turn-0` under different conversations in Phase C).
|
|
581
|
+
* @public
|
|
582
|
+
*/
|
|
583
|
+
export declare interface IIndexedMemoryRecord {
|
|
584
|
+
/** The scope the record is stored under. */
|
|
585
|
+
readonly scope: MemoryScopeKey;
|
|
586
|
+
/** The memory record itself. */
|
|
587
|
+
readonly record: IMemoryRecord<unknown>;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Parameters for {@link MemoryCapCullPolicy.create}.
|
|
592
|
+
* @public
|
|
593
|
+
*/
|
|
594
|
+
export declare interface IMemoryCapCullPolicyParams {
|
|
595
|
+
/**
|
|
596
|
+
* Maximum number of records the policy admits before culling the oldest.
|
|
597
|
+
* Counted over the `existing` cohort passed to {@link IWritePolicy.admit}.
|
|
598
|
+
* Absent = no cap (admission always accepts).
|
|
599
|
+
*/
|
|
600
|
+
readonly maxRecords?: number;
|
|
601
|
+
/**
|
|
602
|
+
* The fields a merge-patch update may touch (drawn from the record-level
|
|
603
|
+
* mutable vocabulary: `body` / `tags` / `links` / `provenance` /
|
|
604
|
+
* `embeddingRef`). Fields outside this list are immutable.
|
|
605
|
+
*/
|
|
606
|
+
readonly mutableFields: ReadonlyArray<string>;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* The invariant identity + transaction-time envelope carried by every memory
|
|
611
|
+
* record, independent of the per-kind body.
|
|
612
|
+
* @public
|
|
613
|
+
*/
|
|
614
|
+
export declare interface IMemoryEnvelope {
|
|
615
|
+
/** Stable file-stem identifier. MUST equal the on-disk filename stem. */
|
|
616
|
+
readonly id: MemoryId;
|
|
617
|
+
/** Consumer-supplied domain key. Equals {@link IMemoryEnvelope.id | id} for non-temporal kinds. */
|
|
618
|
+
readonly entityId: EntityId;
|
|
619
|
+
/** Consumer-registered kind; dispatches the body Converter. */
|
|
620
|
+
readonly kind: Kind;
|
|
621
|
+
/** Open-vocabulary tags. */
|
|
622
|
+
readonly tags: ReadonlyArray<Tag>;
|
|
623
|
+
/** Attributed outbound edges. */
|
|
624
|
+
readonly links: ReadonlyArray<IEdge>;
|
|
625
|
+
/** Epoch ms of the first write. Immutable after creation. */
|
|
626
|
+
readonly created: number;
|
|
627
|
+
/** Epoch ms of the most recent write. */
|
|
628
|
+
readonly updated: number;
|
|
629
|
+
/**
|
|
630
|
+
* Monotonic write counter within the store instance, assigned by the store
|
|
631
|
+
* on every successful put. Enables stable cursor paging over observation
|
|
632
|
+
* records without a full walk.
|
|
633
|
+
*/
|
|
634
|
+
readonly seq: number;
|
|
635
|
+
/**
|
|
636
|
+
* Content hash over the canonical `{ kind, body, links }`. The dedup key:
|
|
637
|
+
* an exact match is a no-op upsert that returns the existing record.
|
|
638
|
+
*/
|
|
639
|
+
readonly contentHash: string;
|
|
640
|
+
/** Structured provenance (never a flat enum). */
|
|
641
|
+
readonly provenance: IProvenance;
|
|
642
|
+
/** Bi-temporal validity. Present only on temporal kinds. */
|
|
643
|
+
readonly temporal?: ITemporalBlock;
|
|
644
|
+
/**
|
|
645
|
+
* Vector-index entry reference, set by the vector index on write. `null` =
|
|
646
|
+
* not embedded; absent = same as `null` (backwards-compat seam).
|
|
647
|
+
*/
|
|
648
|
+
readonly embeddingRef?: string | null;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* The two parts of a memory file: the YAML frontmatter (between the `---`
|
|
653
|
+
* delimiters) and the markdown body (everything after the closing delimiter).
|
|
654
|
+
* @public
|
|
655
|
+
*/
|
|
656
|
+
export declare interface IMemoryFileParts {
|
|
657
|
+
/** The raw YAML frontmatter (delimiters stripped). */
|
|
658
|
+
readonly frontmatter: string;
|
|
659
|
+
/** The raw body text following the closing delimiter. */
|
|
660
|
+
readonly body: string;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* The derived, in-memory secondary indexes the store maintains over its
|
|
665
|
+
* records. Never the source of truth — the FileTree is. The index is fully
|
|
666
|
+
* rebuildable from a walk of the store ({@link IMemoryIndex.rebuild}) and is
|
|
667
|
+
* patched incrementally on every write ({@link IMemoryIndex.patch}).
|
|
668
|
+
*
|
|
669
|
+
* @remarks
|
|
670
|
+
* B1 builds the maps; link-traversal BFS over {@link IMemoryIndex.backlinks}
|
|
671
|
+
* is B2. The accessors return records (not bare ids) so the B2 retrievers can
|
|
672
|
+
* consume them directly.
|
|
673
|
+
* @public
|
|
674
|
+
*/
|
|
675
|
+
export declare interface IMemoryIndex {
|
|
676
|
+
/**
|
|
677
|
+
* Replace the entire index from a full set of records (a store walk).
|
|
678
|
+
* @returns The number of records indexed.
|
|
679
|
+
*/
|
|
680
|
+
rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;
|
|
681
|
+
/**
|
|
682
|
+
* Apply a single incremental change. `'put'` inserts or replaces the entry
|
|
683
|
+
* at its `(scope, id)` key (removing any prior associations first); `'delete'`
|
|
684
|
+
* removes it.
|
|
685
|
+
* @returns The entry that was applied.
|
|
686
|
+
*/
|
|
687
|
+
patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;
|
|
688
|
+
/** Every indexed entry (scope + record). Primary read surface for the store. */
|
|
689
|
+
entries(): ReadonlyArray<IIndexedMemoryRecord>;
|
|
690
|
+
/** Records of the given kind, in recency order (most-recently-updated first). */
|
|
691
|
+
byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
692
|
+
/** Records carrying the given tag, in recency order. */
|
|
693
|
+
byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
694
|
+
/** All records in recency order (most-recently-updated first). */
|
|
695
|
+
byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
696
|
+
/**
|
|
697
|
+
* The ids of records whose `links` point AT `target` (inbound edges).
|
|
698
|
+
* The seed map for B2 link-traversal.
|
|
699
|
+
*/
|
|
700
|
+
backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Query criteria for {@link MemoryObservationStore.query}. All supplied criteria
|
|
705
|
+
* are AND-combined.
|
|
706
|
+
* @public
|
|
707
|
+
*/
|
|
708
|
+
export declare interface IMemoryObservationQuery {
|
|
709
|
+
/** Only records with `seq > sinceSeq` (incremental paging cursor). */
|
|
710
|
+
readonly sinceSeq?: number;
|
|
711
|
+
/** Return at most this many records — the most-recent N, still oldest-first. */
|
|
712
|
+
readonly limit?: number;
|
|
713
|
+
/** Only records with `timestamp >= since`. */
|
|
714
|
+
readonly since?: number;
|
|
715
|
+
/** Only records with `timestamp <= until`. */
|
|
716
|
+
readonly until?: number;
|
|
717
|
+
/** Only records that touched this scope. */
|
|
718
|
+
readonly scope?: MemoryScopeKey;
|
|
719
|
+
/** Only records that targeted this kind. */
|
|
720
|
+
readonly kind?: Kind;
|
|
721
|
+
/** Only records of this phase. */
|
|
722
|
+
readonly phase?: MemoryObservationPhase;
|
|
723
|
+
/** Only records with this outcome. */
|
|
724
|
+
readonly outcome?: MemoryObservationOutcome;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* A single audit record produced by an observed store (or retriever) operation.
|
|
729
|
+
*
|
|
730
|
+
* @remarks
|
|
731
|
+
* `seq` and `timestamp` are assigned by the firing authority (the store) before
|
|
732
|
+
* fan-out, so the same record carries the same `seq` across every observer it
|
|
733
|
+
* lands in. `seq` is strictly increasing per authority, satisfying the
|
|
734
|
+
* `RetainingRingBuffer` cursor contract that {@link MemoryObservationStore}
|
|
735
|
+
* relies on.
|
|
736
|
+
* @public
|
|
737
|
+
*/
|
|
738
|
+
export declare interface IMemoryObservationRecord {
|
|
739
|
+
/**
|
|
740
|
+
* Monotonic 1-based sequence number assigned by the firing authority, stable
|
|
741
|
+
* across a store's ring eviction. The ordering / paging key.
|
|
742
|
+
*/
|
|
743
|
+
readonly seq: number;
|
|
744
|
+
/** Milliseconds since epoch when the firing authority produced the record. */
|
|
745
|
+
readonly timestamp: number;
|
|
746
|
+
/** Which operation this record describes. */
|
|
747
|
+
readonly phase: MemoryObservationPhase;
|
|
748
|
+
/** The scope the operation touched, when resolvable. */
|
|
749
|
+
readonly scope?: MemoryScopeKey;
|
|
750
|
+
/** The record id the operation touched, when resolvable. */
|
|
751
|
+
readonly id?: MemoryId;
|
|
752
|
+
/** The kind the operation targeted, when known. */
|
|
753
|
+
readonly kind?: Kind;
|
|
754
|
+
/** Whether the operation succeeded. */
|
|
755
|
+
readonly outcome: MemoryObservationOutcome;
|
|
756
|
+
/** Present on failure: the failure `Result`'s message. */
|
|
757
|
+
readonly error?: string;
|
|
758
|
+
/** Structured provenance of the write, when the operation carried one. */
|
|
759
|
+
readonly provenance?: IProvenance;
|
|
760
|
+
/**
|
|
761
|
+
* For `'retrieve'` observations: an opaque snapshot of the query that drove
|
|
762
|
+
* the retrieval. Carried verbatim; never interpreted by the store.
|
|
763
|
+
*/
|
|
764
|
+
readonly querySnapshot?: Readonly<Record<string, unknown>>;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Construction options for {@link MemoryObservationStore.create}.
|
|
769
|
+
* @public
|
|
770
|
+
*/
|
|
771
|
+
export declare interface IMemoryObservationStoreCreateParams {
|
|
772
|
+
/**
|
|
773
|
+
* Maximum number of observation records retained before the oldest is
|
|
774
|
+
* overwritten. Defaults to `1000`. Must be a positive integer if supplied.
|
|
775
|
+
*/
|
|
776
|
+
readonly maxRecords?: number;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Single-method async observer hook. The store fires `observe` once per public
|
|
781
|
+
* `get` / `put` / `delete` call when observers are wired.
|
|
782
|
+
*
|
|
783
|
+
* @remarks
|
|
784
|
+
* Observer errors never affect the store operation — the store swallows a
|
|
785
|
+
* failed `Result` or a thrown / rejected `observe`, logging it to the injected
|
|
786
|
+
* diagnostic logger at `warn`.
|
|
787
|
+
* @public
|
|
788
|
+
*/
|
|
789
|
+
export declare interface IMemoryObserver {
|
|
790
|
+
/**
|
|
791
|
+
* When `true`, the store dispatches `observe` without awaiting it, so a slow
|
|
792
|
+
* remote observer (SIEM, network sink) does not extend the store operation's
|
|
793
|
+
* latency. Defaults to `false` (awaited), which is correct for the cheap
|
|
794
|
+
* in-memory default {@link MemoryObservationStore}. Errors are swallowed
|
|
795
|
+
* either way.
|
|
796
|
+
*/
|
|
797
|
+
readonly fireAndForget?: boolean;
|
|
798
|
+
/**
|
|
799
|
+
* Receives a fully-formed observation record.
|
|
800
|
+
* @param record - The observation record.
|
|
801
|
+
* @returns A `Result` whose failure is swallowed (logged to the store's
|
|
802
|
+
* diagnostic logger). A rejected promise is likewise swallowed.
|
|
803
|
+
*/
|
|
804
|
+
observe(record: IMemoryObservationRecord): Promise<Result<unknown>>;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* A retrieval query. Every field is optional; an empty query is the "recency
|
|
809
|
+
* over everything" request. `semantic` and `asOf` are present from day one (the
|
|
810
|
+
* no-resignature guarantee): a backend that adds semantic or temporal recall
|
|
811
|
+
* sets the matching capability flag, with no interface change.
|
|
812
|
+
* @public
|
|
813
|
+
*/
|
|
814
|
+
export declare interface IMemoryQuery {
|
|
815
|
+
/** Restrict to records in this scope. */
|
|
816
|
+
readonly scope?: MemoryScopeKey;
|
|
817
|
+
/** Restrict to records carrying this tag (exact match). */
|
|
818
|
+
readonly tag?: Tag;
|
|
819
|
+
/** Restrict to records of this kind. */
|
|
820
|
+
readonly kind?: Kind;
|
|
821
|
+
/** Restrict to records linked FROM this id (outbound). */
|
|
822
|
+
readonly linkedFrom?: MemoryId;
|
|
823
|
+
/** Restrict to records linked TO this id (inbound / backlinks). */
|
|
824
|
+
readonly linkedTo?: MemoryId;
|
|
825
|
+
/** BFS hop count for link traversal. Default: 1. */
|
|
826
|
+
readonly hops?: number;
|
|
827
|
+
/**
|
|
828
|
+
* Text query for semantic / vector recall. If set and the retriever's
|
|
829
|
+
* `supportsSemanticRecall` is `false`, the retriever returns a loud
|
|
830
|
+
* `Result.fail` ({@link SEMANTIC_UNWIRED_MESSAGE}) — never a silent empty.
|
|
831
|
+
*/
|
|
832
|
+
readonly semantic?: string;
|
|
833
|
+
/** Top-K for semantic recall. Default: 10. */
|
|
834
|
+
readonly topK?: number;
|
|
835
|
+
/**
|
|
836
|
+
* As-of epoch ms for temporal "valid at" queries. If set and the retriever's
|
|
837
|
+
* `supportsTemporalQuery` is `false`, the retriever returns a loud
|
|
838
|
+
* `Result.fail` — never a silent empty.
|
|
839
|
+
*/
|
|
840
|
+
readonly asOf?: number;
|
|
841
|
+
/** Maximum records to return. Applied after all other filters. */
|
|
842
|
+
readonly limit?: number;
|
|
843
|
+
/** Arbitrary predicate applied after the scope / kind / tag pre-filter. */
|
|
844
|
+
readonly filter?: (record: IMemoryRecord<unknown>) => boolean;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* A complete memory record: the invariant {@link IMemoryEnvelope} plus the
|
|
849
|
+
* typed, per-kind body. The store's public surface uses
|
|
850
|
+
* `IMemoryRecord<unknown>`; consumers narrow `TBody` by checking
|
|
851
|
+
* `envelope.kind` and validating through the registered Converter.
|
|
852
|
+
* @public
|
|
853
|
+
*/
|
|
854
|
+
export declare interface IMemoryRecord<TBody = unknown> {
|
|
855
|
+
/** The invariant identity + transaction-time envelope. */
|
|
856
|
+
readonly envelope: IMemoryEnvelope;
|
|
857
|
+
/** The per-kind, Converter-validated body. */
|
|
858
|
+
readonly body: TBody;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* The minimal record-source surface {@link InMemoryCosineIndex.rebuild} reads to
|
|
863
|
+
* re-embed an entire vault. {@link IMemoryStore} satisfies it structurally (its
|
|
864
|
+
* `list` accepts an optional filter, which is assignable to this no-argument
|
|
865
|
+
* shape), so a consumer passes the store directly — without the vector packlet
|
|
866
|
+
* taking a dependency on the store packlet (which depends on the vector packlet
|
|
867
|
+
* for {@link IVectorIndex}, so the reverse import would be a cycle).
|
|
868
|
+
* @public
|
|
869
|
+
*/
|
|
870
|
+
export declare interface IMemoryRecordSource {
|
|
871
|
+
/** List every record in the vault. */
|
|
872
|
+
list(): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* The retrieval contract. A retriever exposes its {@link
|
|
877
|
+
* IMemoryRetrieverCapabilities | capabilities} and answers
|
|
878
|
+
* {@link IMemoryRetriever.retrieve | queries}, degrading loudly (never silently
|
|
879
|
+
* empty) when a requested capability is not wired.
|
|
880
|
+
* @public
|
|
881
|
+
*/
|
|
882
|
+
export declare interface IMemoryRetriever {
|
|
883
|
+
/** The capabilities this retriever exposes. Probe before dispatch. */
|
|
884
|
+
readonly capabilities: IMemoryRetrieverCapabilities;
|
|
885
|
+
/**
|
|
886
|
+
* Retrieve records matching `query`. Returns a `Result.fail` with a
|
|
887
|
+
* diagnostic message when the query requests a capability this retriever does
|
|
888
|
+
* not support (never an empty success).
|
|
889
|
+
*/
|
|
890
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* The capabilities a retriever exposes. A consumer probes these before
|
|
895
|
+
* dispatching a query so it never silently gets an empty result for a
|
|
896
|
+
* capability the retriever does not implement.
|
|
897
|
+
* @public
|
|
898
|
+
*/
|
|
899
|
+
export declare interface IMemoryRetrieverCapabilities {
|
|
900
|
+
/** Semantic / vector recall is operational (an {@link IVectorIndex} is wired). */
|
|
901
|
+
readonly supportsSemanticRecall: boolean;
|
|
902
|
+
/** Temporal "as-of" queries are operational (a temporal index is wired). */
|
|
903
|
+
readonly supportsTemporalQuery: boolean;
|
|
904
|
+
/** Link traversal is supported (an in-memory backlink index is present). */
|
|
905
|
+
readonly supportsLinkTraversal: boolean;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* The writable, FileTree-backed, content-hash-deduped memory store.
|
|
910
|
+
* @public
|
|
911
|
+
*/
|
|
912
|
+
export declare interface IMemoryStore {
|
|
913
|
+
/**
|
|
914
|
+
* Keyed read by entity id. Resolves `entityId` to a storage address via the
|
|
915
|
+
* registered {@link IIdentityCodec} for `kind`. Returns `undefined` when no
|
|
916
|
+
* record exists.
|
|
917
|
+
*/
|
|
918
|
+
get(kind: Kind, entityId: EntityId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
|
|
919
|
+
/**
|
|
920
|
+
* Direct read by `(scope, MemoryId)`. Returns `undefined` when not found.
|
|
921
|
+
*/
|
|
922
|
+
getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
|
|
923
|
+
/**
|
|
924
|
+
* List records, filtered in-memory over the derived index.
|
|
925
|
+
*/
|
|
926
|
+
list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
927
|
+
/**
|
|
928
|
+
* Write a record. Validates the body, computes a content hash, deduplicates
|
|
929
|
+
* (scope-wide, before policy), applies the kind's {@link IWritePolicy}, stamps
|
|
930
|
+
* transaction-time metadata (`created` / `updated` / `seq` / `contentHash`),
|
|
931
|
+
* writes the file, and patches the index. Returns the written record — or the
|
|
932
|
+
* existing record unchanged on a dedup no-op.
|
|
933
|
+
*/
|
|
934
|
+
put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
|
|
935
|
+
/**
|
|
936
|
+
* Delete a record by `(kind, entityId)`. Non-temporal kinds physically delete
|
|
937
|
+
* the file. Returns the {@link MemoryId} of the deleted record.
|
|
938
|
+
*/
|
|
939
|
+
delete(kind: Kind, entityId: EntityId): Promise<Result<MemoryId>>;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Filter for {@link IMemoryStore.list}. All present fields are ANDed together.
|
|
944
|
+
* @public
|
|
945
|
+
*/
|
|
946
|
+
export declare interface IMemoryStoreListFilter {
|
|
947
|
+
/** Restrict to records in this scope. */
|
|
948
|
+
readonly scope?: MemoryScopeKey;
|
|
949
|
+
/** Restrict to records of this kind. */
|
|
950
|
+
readonly kind?: Kind;
|
|
951
|
+
/** Restrict to records carrying this tag (exact match). */
|
|
952
|
+
readonly tag?: Tag;
|
|
953
|
+
/**
|
|
954
|
+
* For temporal kinds: return only records valid at this epoch ms. No-op in
|
|
955
|
+
* B1 (no temporal kinds wired).
|
|
956
|
+
*/
|
|
957
|
+
readonly asOf?: number;
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Strategy for combining the result sets of the retrievers a
|
|
962
|
+
* {@link HybridRetriever} composes. Injectable so a consumer can weight,
|
|
963
|
+
* re-rank, or intersect instead of the default union.
|
|
964
|
+
* @public
|
|
965
|
+
*/
|
|
966
|
+
export declare interface IMergeStrategy {
|
|
967
|
+
/**
|
|
968
|
+
* Merge the per-retriever result sets into a single ordered result.
|
|
969
|
+
* @param resultSets - One entry per composed retriever, in composition order.
|
|
970
|
+
*/
|
|
971
|
+
merge(resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* Whether an indexed entry satisfies a query's scope / kind / tag / predicate
|
|
976
|
+
* pre-filter (the axes shared by every v1 retriever). The `semantic` / `asOf` /
|
|
977
|
+
* link axes are NOT applied here — those are each retriever's own concern.
|
|
978
|
+
* @public
|
|
979
|
+
*/
|
|
980
|
+
export declare function indexedRecordMatchesQuery(entry: IIndexedMemoryRecord, query: IMemoryQuery): boolean;
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* The brute-force, in-memory cosine {@link IVectorIndex}. Stores one
|
|
984
|
+
* `Float32Array` per record and answers a query by computing cosine similarity
|
|
985
|
+
* against every stored vector, returning the top-k by descending score.
|
|
986
|
+
*
|
|
987
|
+
* @remarks
|
|
988
|
+
* This is the **complete** vector implementation for the fgv regime — large-N is
|
|
989
|
+
* explicitly out of scope (the seam stays open for a consumer to swap an external
|
|
990
|
+
* ANN backend once N grows beyond "thousands of records"). No external dependency
|
|
991
|
+
* and no ANN structure: a linear scan over a few thousand vectors is well within
|
|
992
|
+
* an interactive budget.
|
|
993
|
+
*
|
|
994
|
+
* The index has a single dimension established by the first vector added; every
|
|
995
|
+
* subsequent `add` and every `query` vector must match that dimension or fail
|
|
996
|
+
* loudly — a mismatched dimension is an embedder-wiring bug, never a silent
|
|
997
|
+
* zero-similarity result. {@link InMemoryCosineIndex.rebuild | rebuild} clears
|
|
998
|
+
* the index, so a re-embed with a different model (hence dimension) is supported.
|
|
999
|
+
*
|
|
1000
|
+
* Persistence (a JSON sidecar) is deliberately out of scope for this layer — the
|
|
1001
|
+
* index is in-memory and rebuilt from the store via `rebuild`; a sidecar is a
|
|
1002
|
+
* future nicety.
|
|
1003
|
+
* @public
|
|
1004
|
+
*/
|
|
1005
|
+
export declare class InMemoryCosineIndex implements IVectorIndex {
|
|
1006
|
+
private readonly _vectors;
|
|
1007
|
+
/** The dimension of every stored vector; `undefined` until the first `add`. */
|
|
1008
|
+
private _dimension;
|
|
1009
|
+
private constructor();
|
|
1010
|
+
/** The number of vectors currently held. */
|
|
1011
|
+
get size(): number;
|
|
1012
|
+
/** Family-convention factory. */
|
|
1013
|
+
static create(): Result<InMemoryCosineIndex>;
|
|
1014
|
+
/** {@inheritDoc IVectorIndex.add} */
|
|
1015
|
+
add(id: MemoryId, vector: Float32Array): Promise<Result<string>>;
|
|
1016
|
+
/** {@inheritDoc IVectorIndex.remove} */
|
|
1017
|
+
remove(id: MemoryId): Promise<Result<MemoryId>>;
|
|
1018
|
+
/** {@inheritDoc IVectorIndex.query} */
|
|
1019
|
+
query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
|
|
1020
|
+
/**
|
|
1021
|
+
* Re-embed every record from `source` and rebuild the index from scratch.
|
|
1022
|
+
* Clears the current contents (and the established dimension) first, so a
|
|
1023
|
+
* re-embed with a different model is supported. Returns the number of vectors
|
|
1024
|
+
* indexed.
|
|
1025
|
+
*
|
|
1026
|
+
* On any failure (list, embed, or add) the index is rolled back to empty
|
|
1027
|
+
* rather than left in a partially-rebuilt state — a caller that retries a query
|
|
1028
|
+
* after a failed rebuild sees a clean empty index, never a half-populated one.
|
|
1029
|
+
*
|
|
1030
|
+
* @param source - The record source to re-embed (an {@link IMemoryStore}
|
|
1031
|
+
* satisfies this structurally).
|
|
1032
|
+
* @param embed - The embedder applied to each record.
|
|
1033
|
+
*/
|
|
1034
|
+
rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder): Promise<Result<number>>;
|
|
1035
|
+
/** Empty the index and forget the established dimension. */
|
|
1036
|
+
private _reset;
|
|
1037
|
+
/** The Euclidean magnitude (L2 norm) of a vector. */
|
|
1038
|
+
private static _magnitude;
|
|
1039
|
+
/**
|
|
1040
|
+
* Cosine similarity between the query (whose magnitude is precomputed once and
|
|
1041
|
+
* reused across the scan) and a stored vector. A zero-magnitude vector on
|
|
1042
|
+
* either side yields `0` rather than `NaN` — a degenerate vector is simply
|
|
1043
|
+
* maximally dissimilar, not an error.
|
|
1044
|
+
*/
|
|
1045
|
+
private static _cosine;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
/**
|
|
1049
|
+
* Structured provenance for a record or an edge. Never a flat enum — the
|
|
1050
|
+
* `[key: string]: unknown` index signature lets a consumer attach an opaque
|
|
1051
|
+
* domain payload (e.g. PersonAIlity's sentiment / epistemic blocks) without
|
|
1052
|
+
* changing this interface, while still satisfying the no-`any` rule.
|
|
1053
|
+
* @public
|
|
1054
|
+
*/
|
|
1055
|
+
export declare interface IProvenance {
|
|
1056
|
+
/** Where the attribution came from. */
|
|
1057
|
+
readonly source: ProvenanceSource;
|
|
1058
|
+
/** Optional human or agent identifier responsible for the write. */
|
|
1059
|
+
readonly by?: string;
|
|
1060
|
+
/** Optional model identifier, when a model produced the content. */
|
|
1061
|
+
readonly model?: string;
|
|
1062
|
+
/** Optional confidence in `[0, 1]`. */
|
|
1063
|
+
readonly confidence?: number;
|
|
1064
|
+
/** Back-link to the source experience record. Enables the cross-kind provenance spine. */
|
|
1065
|
+
readonly derivedFrom?: MemoryId;
|
|
1066
|
+
/** Opaque extension payload — consumer-owned, never interpreted by the store. */
|
|
1067
|
+
readonly [key: string]: unknown;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* The semantic backend wired into a {@link SemanticRetriever}: the vector index
|
|
1072
|
+
* to query and the embedder that turns the query text into a vector. Both are
|
|
1073
|
+
* required together — a vector index is useless without a way to embed the
|
|
1074
|
+
* query, so {@link SemanticRetriever.create} treats them as one unit.
|
|
1075
|
+
* @public
|
|
1076
|
+
*/
|
|
1077
|
+
export declare interface ISemanticBackend {
|
|
1078
|
+
/** The vector index to query. */
|
|
1079
|
+
readonly vectorIndex: IVectorIndex;
|
|
1080
|
+
/** Turns the query text into a vector. */
|
|
1081
|
+
readonly embedQuery: QueryEmbedder;
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Construction options for {@link SemanticRetriever.create}.
|
|
1086
|
+
* @public
|
|
1087
|
+
*/
|
|
1088
|
+
export declare interface ISemanticRetrieverCreateParams {
|
|
1089
|
+
/** The record index, used to resolve vector hits back to full records. */
|
|
1090
|
+
readonly index: IMemoryIndex;
|
|
1091
|
+
/**
|
|
1092
|
+
* The semantic backend. When absent, the retriever reports
|
|
1093
|
+
* `supportsSemanticRecall: false` and a `query.semantic` request degrades
|
|
1094
|
+
* loudly ({@link SEMANTIC_UNWIRED_MESSAGE}) rather than returning empty.
|
|
1095
|
+
*/
|
|
1096
|
+
readonly backend?: ISemanticBackend;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Optional bi-temporal validity block on an envelope. Present only on
|
|
1101
|
+
* temporal kinds; absent = zero cost for atemporal kinds.
|
|
1102
|
+
* @public
|
|
1103
|
+
*/
|
|
1104
|
+
export declare interface ITemporalBlock {
|
|
1105
|
+
/** World-truth validity start (epoch ms). */
|
|
1106
|
+
readonly valid_at?: number;
|
|
1107
|
+
/** World-truth validity end (epoch ms). `null` = still valid. */
|
|
1108
|
+
readonly invalid_at?: number | null;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
/**
|
|
1112
|
+
* The vector-index seam an embedding backend implements to make
|
|
1113
|
+
* {@link SemanticRetriever | semantic recall} operational.
|
|
1114
|
+
*
|
|
1115
|
+
* @remarks
|
|
1116
|
+
* Vectors cross this seam as `Float32Array` (the in-memory representation an
|
|
1117
|
+
* embedding model produces); `number[]` is reserved for the JSON-wire edges
|
|
1118
|
+
* (e.g. a provider's embedding response). The in-package brute-force cosine
|
|
1119
|
+
* implementation is {@link InMemoryCosineIndex}; a consumer can swap an external
|
|
1120
|
+
* ANN backend behind the same seam once N grows beyond the in-memory regime.
|
|
1121
|
+
*
|
|
1122
|
+
* Every operation returns a `Result` (async, since a real backend does I/O) so
|
|
1123
|
+
* failure is explicit and never throws across the seam.
|
|
1124
|
+
* @public
|
|
1125
|
+
*/
|
|
1126
|
+
export declare interface IVectorIndex {
|
|
1127
|
+
/**
|
|
1128
|
+
* Add (or replace) the embedding for `id`. Returns the opaque
|
|
1129
|
+
* {@link IMemoryEnvelope.embeddingRef | embeddingRef} the store stamps onto
|
|
1130
|
+
* the envelope so a later read knows the record is embedded.
|
|
1131
|
+
*/
|
|
1132
|
+
add(id: MemoryId, vector: Float32Array): Promise<Result<string>>;
|
|
1133
|
+
/**
|
|
1134
|
+
* Remove the embedding for `id`. Returns the removed id. Idempotent — removing
|
|
1135
|
+
* an id with no embedding still succeeds (returns the id).
|
|
1136
|
+
*/
|
|
1137
|
+
remove(id: MemoryId): Promise<Result<MemoryId>>;
|
|
1138
|
+
/**
|
|
1139
|
+
* Return the `topK` nearest records to `vector`, in descending score order.
|
|
1140
|
+
*/
|
|
1141
|
+
query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* A single hit returned by {@link IVectorIndex.query}: the matched record id and
|
|
1146
|
+
* the backend's similarity score (higher = more similar; the exact scale is
|
|
1147
|
+
* backend-defined). Hits are returned in descending score order.
|
|
1148
|
+
* @public
|
|
1149
|
+
*/
|
|
1150
|
+
export declare interface IVectorQueryHit {
|
|
1151
|
+
/** The id of the matched record. */
|
|
1152
|
+
readonly id: MemoryId;
|
|
1153
|
+
/** Backend similarity score; higher is more similar. */
|
|
1154
|
+
readonly score: number;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
/**
|
|
1158
|
+
* A per-kind write policy, injected at store construction. Invoked AFTER
|
|
1159
|
+
* content-hash dedup (dedup is always pre-policy). The policy decides
|
|
1160
|
+
* admission and, on update, applies a JSON Merge Patch (RFC-7386) restricted
|
|
1161
|
+
* to the declared mutable fields.
|
|
1162
|
+
* @public
|
|
1163
|
+
*/
|
|
1164
|
+
export declare interface IWritePolicy {
|
|
1165
|
+
/**
|
|
1166
|
+
* Names of the fields a merge-patch update may touch. Fields outside this
|
|
1167
|
+
* list are immutable; a change to one constitutes a new entity (its
|
|
1168
|
+
* `contentHash` differs). Used by {@link IWritePolicy.applyUpdate} to
|
|
1169
|
+
* restrict which fields the patch applies.
|
|
1170
|
+
*/
|
|
1171
|
+
readonly mutableFields: ReadonlyArray<string>;
|
|
1172
|
+
/**
|
|
1173
|
+
* The granularity at which the store deduplicates an incoming write for this
|
|
1174
|
+
* kind. Optional; when absent the store applies {@link DEFAULT_DEDUP_SCOPE}
|
|
1175
|
+
* (`'entity'`). See {@link DedupScope}.
|
|
1176
|
+
*/
|
|
1177
|
+
readonly dedupScope?: DedupScope;
|
|
1178
|
+
/**
|
|
1179
|
+
* Determine whether the incoming record is admitted.
|
|
1180
|
+
* @param incoming - The record about to be written.
|
|
1181
|
+
* @param existing - The admission cohort the kind's cap applies to: the
|
|
1182
|
+
* records in the same scope of the same kind, EXCLUDING the record at
|
|
1183
|
+
* `incoming`'s target id. Empty on a first write into an empty cohort.
|
|
1184
|
+
* Excluding the target id makes the post-write count uniform
|
|
1185
|
+
* (`existing.length + 1`) across first-writes and same-id updates. A
|
|
1186
|
+
* last-write-wins policy that has no cap ignores this argument.
|
|
1187
|
+
* @returns A {@link AdmissionDecision}.
|
|
1188
|
+
*/
|
|
1189
|
+
admit(incoming: IMemoryRecord<unknown>, existing: ReadonlyArray<IMemoryRecord<unknown>>): Result<AdmissionDecision>;
|
|
1190
|
+
/**
|
|
1191
|
+
* Apply a JSON Merge Patch (RFC-7386) to the mutable fields of an existing
|
|
1192
|
+
* record. Called when admission is `accept` AND a record with the same
|
|
1193
|
+
* `entityId` already exists (an update, not a first write).
|
|
1194
|
+
*
|
|
1195
|
+
* @param existing - The current persisted record.
|
|
1196
|
+
* @param patch - A partial JSON object in Merge Patch format. `null`
|
|
1197
|
+
* deletes the corresponding key; arrays replace wholesale; nested objects
|
|
1198
|
+
* deep-merge. Only keys in {@link IWritePolicy.mutableFields} are applied.
|
|
1199
|
+
* @returns The updated record (envelope + body).
|
|
1200
|
+
*/
|
|
1201
|
+
applyUpdate(existing: IMemoryRecord<unknown>, patch: Record<string, unknown>): Result<IMemoryRecord<unknown>>;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* Join a YAML frontmatter string and a body into the canonical
|
|
1206
|
+
* `---\n<yaml>\n---\n<body>` memory-file format.
|
|
1207
|
+
* @public
|
|
1208
|
+
*/
|
|
1209
|
+
export declare function joinFrontmatter(frontmatter: string, body: string): string;
|
|
1210
|
+
|
|
1211
|
+
/**
|
|
1212
|
+
* Open-vocabulary record classifier. Consumers register one body
|
|
1213
|
+
* {@link https://www.npmjs.com/package/@fgv/ts-utils | Converter} per kind.
|
|
1214
|
+
* @public
|
|
1215
|
+
*/
|
|
1216
|
+
export declare type Kind = Brand<string, 'Kind'>;
|
|
1217
|
+
|
|
1218
|
+
/**
|
|
1219
|
+
/**
|
|
1220
|
+
* Identity codec for the knowledge kind family. A knowledge entity is keyed
|
|
1221
|
+
* by its consumer-supplied `docId`, which is used verbatim as the filename
|
|
1222
|
+
* stem under the flat `knowledge` scope.
|
|
1223
|
+
*
|
|
1224
|
+
* @remarks
|
|
1225
|
+
* - `encode`: scope = `knowledge`, idStem = `docId`, `isVersioned = false`.
|
|
1226
|
+
* - `decode`: brands the stem back to an {@link EntityId}.
|
|
1227
|
+
* - Escaping: the `docId` must match the POSIX portable filename set.
|
|
1228
|
+
* - Layout: `vault/knowledge/<docId>.md`.
|
|
1229
|
+
* @public
|
|
1230
|
+
*/
|
|
1231
|
+
export declare class KnowledgeIdentityCodec implements IIdentityCodec {
|
|
1232
|
+
/** The fixed scope for every knowledge entity. */
|
|
1233
|
+
static readonly scope: MemoryScopeKey;
|
|
1234
|
+
/** {@inheritDoc IIdentityCodec.encode} */
|
|
1235
|
+
encode(entityId: EntityId): Result<IIdentityCodecResult>;
|
|
1236
|
+
/** {@inheritDoc IIdentityCodec.decode} */
|
|
1237
|
+
decode(scope: MemoryScopeKey, encodedStem: string): Result<EntityId>;
|
|
1238
|
+
/** {@inheritDoc IIdentityCodec.verifyRoundTrip} */
|
|
1239
|
+
verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/**
|
|
1243
|
+
* Last-write-wins write policy for the knowledge kind family. Admission
|
|
1244
|
+
* always accepts (no cap, no cull); updates apply an RFC-7386 merge patch
|
|
1245
|
+
* restricted to the knowledge mutable surface.
|
|
1246
|
+
*
|
|
1247
|
+
* @remarks
|
|
1248
|
+
* **Merge-surface pin (resolves design-lock §5.3's body-vs-envelope muddle).**
|
|
1249
|
+
* The declared `mutableFields` span both axes of a record: `body` is the
|
|
1250
|
+
* per-kind body, while `tags` / `links` / `provenance` / `embeddingRef` live
|
|
1251
|
+
* on the {@link IMemoryEnvelope}. `applyUpdate` projects exactly those fields
|
|
1252
|
+
* — each read from its canonical location — into a single record-level JSON
|
|
1253
|
+
* view, runs the merge over that view, then rebuilds a coherent record. The
|
|
1254
|
+
* identity and transaction-time envelope fields (`id`, `entityId`, `kind`,
|
|
1255
|
+
* `created`, `updated`, `seq`, `contentHash`) are NOT mutable and are
|
|
1256
|
+
* preserved verbatim; the store stamps `updated` / `seq` on write.
|
|
1257
|
+
* @public
|
|
1258
|
+
*/
|
|
1259
|
+
export declare class KnowledgeLwwPolicy implements IWritePolicy {
|
|
1260
|
+
/**
|
|
1261
|
+
* The knowledge mutable surface: the body plus the envelope metadata a
|
|
1262
|
+
* consumer may revise without minting a new entity.
|
|
1263
|
+
*/
|
|
1264
|
+
readonly mutableFields: ReadonlyArray<string>;
|
|
1265
|
+
/**
|
|
1266
|
+
* Knowledge dedups scope-wide: an identical `{ kind, body, links }` triple
|
|
1267
|
+
* anywhere in the `knowledge` scope — even under a different `docId` — is a
|
|
1268
|
+
* no-op. Declared explicitly so the B1 content-dedup behavior (and its tests)
|
|
1269
|
+
* are unchanged by the {@link DedupScope} amendment.
|
|
1270
|
+
*/
|
|
1271
|
+
readonly dedupScope: DedupScope;
|
|
1272
|
+
/** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
|
|
1273
|
+
private readonly _cloneEditor;
|
|
1274
|
+
/** Applies the RFC-7386 merge patch. */
|
|
1275
|
+
private readonly _mergeEditor;
|
|
1276
|
+
private constructor();
|
|
1277
|
+
/**
|
|
1278
|
+
* Family-convention factory. Constructs the shared `JsonEditor` instances
|
|
1279
|
+
* (one for cloning, one for the RFC-7386 merge), both with the template /
|
|
1280
|
+
* conditional / multivalue / reference rules disabled (empty rules array).
|
|
1281
|
+
*/
|
|
1282
|
+
static create(): Result<KnowledgeLwwPolicy>;
|
|
1283
|
+
/** {@inheritDoc IWritePolicy.admit} */
|
|
1284
|
+
admit(__incoming: IMemoryRecord<unknown>, __existing: ReadonlyArray<IMemoryRecord<unknown>>): Result<AdmissionDecision>;
|
|
1285
|
+
/** {@inheritDoc IWritePolicy.applyUpdate} */
|
|
1286
|
+
applyUpdate(existing: IMemoryRecord<unknown>, patch: Record<string, unknown>): Result<IMemoryRecord<unknown>>;
|
|
1287
|
+
/**
|
|
1288
|
+
* Reassemble a record from the merged mutable view. `body` / `tags` /
|
|
1289
|
+
* `links` / `provenance` are required and may not be deleted by a patch.
|
|
1290
|
+
* `embeddingRef` is optional: when it is absent from the merged view —
|
|
1291
|
+
* because the existing record never carried it OR a `null` patch deleted it
|
|
1292
|
+
* (RFC-7386) — it is restored as `undefined` (absent), NOT `null`. This keeps
|
|
1293
|
+
* an originally-absent `embeddingRef` from silently flipping to `null` on an
|
|
1294
|
+
* unrelated update, so the field round-trips hash-stably through the store's
|
|
1295
|
+
* content-hash recomputation.
|
|
1296
|
+
*/
|
|
1297
|
+
private _rebuild;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
/**
|
|
1301
|
+
* Truncate to `query.limit` records (a no-op when `limit` is absent). Applied
|
|
1302
|
+
* last, after ordering, so it always takes the top-N of the ordered result. A
|
|
1303
|
+
* non-positive `limit` is public query input and means "no records" — it returns
|
|
1304
|
+
* an empty array rather than letting a negative value slip into `slice`.
|
|
1305
|
+
* @public
|
|
1306
|
+
*/
|
|
1307
|
+
export declare function limitRecords(records: ReadonlyArray<IMemoryRecord<unknown>>, limit?: number): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
1308
|
+
|
|
1309
|
+
/**
|
|
1310
|
+
* The loud-degradation message returned when a link-traversal query supplies no
|
|
1311
|
+
* seed (`linkedFrom` / `linkedTo`).
|
|
1312
|
+
* @public
|
|
1313
|
+
*/
|
|
1314
|
+
export declare const LINK_TRAVERSAL_NO_SEED_MESSAGE: string;
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* The loud-degradation message a retriever returns when a link-traversal axis
|
|
1318
|
+
* (`linkedFrom` / `linkedTo` / `hops`) is requested but no backlink index is
|
|
1319
|
+
* wired.
|
|
1320
|
+
* @public
|
|
1321
|
+
*/
|
|
1322
|
+
export declare const LINK_TRAVERSAL_UNWIRED_MESSAGE: string;
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* Breadth-first link-traversal retriever. From a seed {@link MemoryId} it walks
|
|
1326
|
+
* the link graph up to `query.hops` levels and returns the records reached
|
|
1327
|
+
* (excluding the seed), recency-ordered and limited.
|
|
1328
|
+
*
|
|
1329
|
+
* @remarks
|
|
1330
|
+
* - **Direction.** `linkedFrom` walks OUTBOUND edges (each record's
|
|
1331
|
+
* `envelope.links[].target`); `linkedTo` walks INBOUND edges (the index's
|
|
1332
|
+
* `backlinks`). Exactly one is the seed; `linkedFrom` wins if both are set.
|
|
1333
|
+
* - **Bound + cycle safety.** Traversal is bounded by `hops` (default `1` — a
|
|
1334
|
+
* single hop) and a visited-set guard. The graph is keyed by bare
|
|
1335
|
+
* string {@link MemoryId}s, so a `Set<string>` visited-set is the exact,
|
|
1336
|
+
* collision-free cycle key — no structural hashing (e.g. `Crc32Normalizer`) is
|
|
1337
|
+
* needed. A self-loop or any multi-hop cycle terminates because a revisited id
|
|
1338
|
+
* is never re-expanded.
|
|
1339
|
+
* - **Post-filter.** The scope / kind / tag / predicate axes of the query are
|
|
1340
|
+
* applied to the reached records (the link axes are the traversal itself).
|
|
1341
|
+
* @public
|
|
1342
|
+
*/
|
|
1343
|
+
export declare class LinkTraversalRetriever implements IMemoryRetriever {
|
|
1344
|
+
private readonly _index;
|
|
1345
|
+
private constructor();
|
|
1346
|
+
/** Family-convention factory. */
|
|
1347
|
+
static create(index: IMemoryIndex): Result<LinkTraversalRetriever>;
|
|
1348
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
1349
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
1350
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
1351
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
1352
|
+
/** Run the bounded, cycle-safe BFS and post-filter the reached records. */
|
|
1353
|
+
private _traverse;
|
|
1354
|
+
/**
|
|
1355
|
+
* Group the index's entries by bare {@link MemoryId}. An id can map to more
|
|
1356
|
+
* than one entry when distinct scopes reuse a filename stem (e.g. `turn-0` in
|
|
1357
|
+
* two conversations), so the value is an array.
|
|
1358
|
+
*
|
|
1359
|
+
* @remarks
|
|
1360
|
+
* **Design note (links are globally-scoped identifiers in this phase).** An
|
|
1361
|
+
* {@link IEdge.target} is a bare `MemoryId`, not a `(scope, id)` pair, so
|
|
1362
|
+
* traversal resolves a target across ALL scopes that hold that id. When two
|
|
1363
|
+
* scopes reuse a stem, following an edge to it reaches every match. This
|
|
1364
|
+
* mirrors the `backlinks` index, which is also keyed by bare id. Scope-
|
|
1365
|
+
* qualified link resolution is intentionally out of scope for Phase C and
|
|
1366
|
+
* would be an additive change here (and to {@link IEdge} / the index).
|
|
1367
|
+
*/
|
|
1368
|
+
private _indexById;
|
|
1369
|
+
/** Outbound neighbors: the targets of every edge on the records with this id. */
|
|
1370
|
+
private _outbound;
|
|
1371
|
+
/** Inbound neighbors: the ids whose edges point AT this id (the backlinks). */
|
|
1372
|
+
private _inbound;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/**
|
|
1376
|
+
* Open-vocabulary link-relation type for an attributed {@link IEdge}.
|
|
1377
|
+
* @public
|
|
1378
|
+
*/
|
|
1379
|
+
export declare type LinkType = Brand<string, 'LinkType'>;
|
|
1380
|
+
|
|
1381
|
+
/**
|
|
1382
|
+
* Identity codec for the long-term-memory (LTM) kind family. An LTM entity is
|
|
1383
|
+
* keyed by its `conversationId`, used verbatim as the filename stem under the
|
|
1384
|
+
* flat `conversations` scope.
|
|
1385
|
+
*
|
|
1386
|
+
* @remarks
|
|
1387
|
+
* - `encode`: scope = `conversations`, idStem = `conversationId`, `isVersioned = false`.
|
|
1388
|
+
* - `decode`: brands the stem back to an {@link EntityId}.
|
|
1389
|
+
* - Escaping: the `conversationId` must match the POSIX portable filename set.
|
|
1390
|
+
* - Layout: `vault/conversations/<conversationId>.md`.
|
|
1391
|
+
*
|
|
1392
|
+
* An LTM file (`conversations/<id>.md`) and the MTM subtree
|
|
1393
|
+
* (`conversations/<id>/turn-N.md`) coexist — a file and a same-named directory
|
|
1394
|
+
* are independent on every supported FileTree backend.
|
|
1395
|
+
* @public
|
|
1396
|
+
*/
|
|
1397
|
+
export declare class LtmIdentityCodec implements IIdentityCodec {
|
|
1398
|
+
/** The fixed scope for every LTM entity. */
|
|
1399
|
+
static readonly scope: MemoryScopeKey;
|
|
1400
|
+
/** {@inheritDoc IIdentityCodec.encode} */
|
|
1401
|
+
encode(entityId: EntityId): Result<IIdentityCodecResult>;
|
|
1402
|
+
/** {@inheritDoc IIdentityCodec.decode} */
|
|
1403
|
+
decode(scope: MemoryScopeKey, encodedStem: string): Result<EntityId>;
|
|
1404
|
+
/** {@inheritDoc IIdentityCodec.verifyRoundTrip} */
|
|
1405
|
+
verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/**
|
|
1409
|
+
* Bounded-ring write policy for the experience (memory) kind families.
|
|
1410
|
+
* Admission accepts until `maxRecords` is reached, then evicts the oldest
|
|
1411
|
+
* record(s) by `created` ascending (design-lock §5.3); updates apply the same
|
|
1412
|
+
* RFC-7386 merge patch as {@link KnowledgeLwwPolicy}, restricted to the declared
|
|
1413
|
+
* {@link IMemoryCapCullPolicyParams.mutableFields | mutableFields}.
|
|
1414
|
+
*
|
|
1415
|
+
* @remarks
|
|
1416
|
+
* - **Dedup scope.** Declares `dedupScope: 'entity'` — two distinct memory
|
|
1417
|
+
* entities (e.g. `turn-5` / `turn-9`) with identical `{ kind, body, links }`
|
|
1418
|
+
* never collapse; only an identical re-put of the SAME entity is a no-op.
|
|
1419
|
+
* - **Eviction boundary.** `admit` only DECIDES (returns the `MemoryId`s to
|
|
1420
|
+
* evict); the store executes the file deletions and index patches. The
|
|
1421
|
+
* `existing` cohort the cap counts against is whatever the store supplies to
|
|
1422
|
+
* `admit`.
|
|
1423
|
+
* @public
|
|
1424
|
+
*/
|
|
1425
|
+
export declare class MemoryCapCullPolicy implements IWritePolicy {
|
|
1426
|
+
/** {@inheritDoc IWritePolicy.mutableFields} */
|
|
1427
|
+
readonly mutableFields: ReadonlyArray<string>;
|
|
1428
|
+
/** Experience kinds dedup per-entity (see the class remarks). */
|
|
1429
|
+
readonly dedupScope: DedupScope;
|
|
1430
|
+
/** The admission cap; `undefined` = no cap. */
|
|
1431
|
+
private readonly _maxRecords;
|
|
1432
|
+
/** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
|
|
1433
|
+
private readonly _cloneEditor;
|
|
1434
|
+
/** Applies the RFC-7386 merge patch. */
|
|
1435
|
+
private readonly _mergeEditor;
|
|
1436
|
+
private constructor();
|
|
1437
|
+
/**
|
|
1438
|
+
* Family-convention factory. Constructs the shared `JsonEditor` instances
|
|
1439
|
+
* (one for cloning, one for the RFC-7386 merge) with the same merge config as
|
|
1440
|
+
* {@link KnowledgeLwwPolicy} (`nullAsDelete` true, `arrayMergeBehavior`
|
|
1441
|
+
* `'replace'`, rules disabled).
|
|
1442
|
+
*/
|
|
1443
|
+
static create(params: IMemoryCapCullPolicyParams): Result<MemoryCapCullPolicy>;
|
|
1444
|
+
/** {@inheritDoc IWritePolicy.admit} */
|
|
1445
|
+
admit(__incoming: IMemoryRecord<unknown>, existing: ReadonlyArray<IMemoryRecord<unknown>>): Result<AdmissionDecision>;
|
|
1446
|
+
/** {@inheritDoc IWritePolicy.applyUpdate} */
|
|
1447
|
+
applyUpdate(existing: IMemoryRecord<unknown>, patch: Record<string, unknown>): Result<IMemoryRecord<unknown>>;
|
|
1448
|
+
/**
|
|
1449
|
+
* Reassemble a record from the merged mutable view. Only the declared mutable
|
|
1450
|
+
* fields are taken from the merge; undeclared fields are preserved verbatim
|
|
1451
|
+
* from `existing`. A `null` patch that deletes a *declared mutable* required
|
|
1452
|
+
* field (`body` / `tags` / `links` / `provenance`) is an error — a required
|
|
1453
|
+
* field that is NOT declared mutable simply falls through to its `existing.*`
|
|
1454
|
+
* value and is never at risk. `embeddingRef`, when mutable, is restored as
|
|
1455
|
+
* `undefined` (absent) if the merge dropped it — same hash-stable semantics as
|
|
1456
|
+
* {@link KnowledgeLwwPolicy}.
|
|
1457
|
+
*/
|
|
1458
|
+
private _rebuild;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* Embeds a complete record into a vector for the store's embed-on-write hook.
|
|
1463
|
+
* Async and `Result`-returning, since a real embedder does a network call (cloud
|
|
1464
|
+
* provider) or in-process model inference. The consumer wires this — the core
|
|
1465
|
+
* package never calls an embedding provider directly, staying embedder-agnostic.
|
|
1466
|
+
* @public
|
|
1467
|
+
*/
|
|
1468
|
+
export declare type MemoryEmbedder = (record: IMemoryRecord<unknown>) => Promise<Result<Float32Array>>;
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* Stable file-stem identifier for a memory record. Equals the codec-produced
|
|
1472
|
+
* `idStem` and the on-disk filename stem (`verifyFilenameId` enforces the
|
|
1473
|
+
* round-trip).
|
|
1474
|
+
* @public
|
|
1475
|
+
*/
|
|
1476
|
+
export declare type MemoryId = Brand<string, 'MemoryId'>;
|
|
1477
|
+
|
|
1478
|
+
/**
|
|
1479
|
+
* Default in-memory {@link IMemoryIndex}. Maintains the derived `byKind` /
|
|
1480
|
+
* `byTag` / `byRecency` / `backlinks` views incrementally; a `'put'` for an
|
|
1481
|
+
* already-indexed key first removes the prior entry's associations so a changed
|
|
1482
|
+
* kind / tag / link set never leaves a stale reference behind.
|
|
1483
|
+
* @public
|
|
1484
|
+
*/
|
|
1485
|
+
export declare class MemoryIndex implements IMemoryIndex {
|
|
1486
|
+
/** Primary store: `(scope, id)` composite key → indexed entry. */
|
|
1487
|
+
private readonly _byKey;
|
|
1488
|
+
/** kind → set of composite keys. */
|
|
1489
|
+
private readonly _byKind;
|
|
1490
|
+
/** tag → set of composite keys. */
|
|
1491
|
+
private readonly _byTag;
|
|
1492
|
+
/**
|
|
1493
|
+
* link target id → (source composite key → source id). Keyed by the source's
|
|
1494
|
+
* `(scope, id)` composite — NOT its bare id — so two distinct source records
|
|
1495
|
+
* that share an id across scopes (e.g. `turn-0` in different conversations)
|
|
1496
|
+
* are tracked independently and removing one never drops the other's edge.
|
|
1497
|
+
*/
|
|
1498
|
+
private readonly _backlinks;
|
|
1499
|
+
private constructor();
|
|
1500
|
+
/** Family-convention factory. */
|
|
1501
|
+
static create(): Result<MemoryIndex>;
|
|
1502
|
+
/**
|
|
1503
|
+
* The composite primary key for an entry: scope + id, NUL-separated. NUL
|
|
1504
|
+
* is excluded from both components (scope segments pass
|
|
1505
|
+
* `assertPortableFilenameStem`; `MemoryId` is portable-filename-safe), so it
|
|
1506
|
+
* is a collision-proof separator across every scope/id pair the codecs produce.
|
|
1507
|
+
*/
|
|
1508
|
+
private static _keyOf;
|
|
1509
|
+
/** {@inheritDoc IMemoryIndex.rebuild} */
|
|
1510
|
+
rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;
|
|
1511
|
+
/** {@inheritDoc IMemoryIndex.patch} */
|
|
1512
|
+
patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;
|
|
1513
|
+
/** {@inheritDoc IMemoryIndex.entries} */
|
|
1514
|
+
entries(): ReadonlyArray<IIndexedMemoryRecord>;
|
|
1515
|
+
/** {@inheritDoc IMemoryIndex.byKind} */
|
|
1516
|
+
byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
1517
|
+
/** {@inheritDoc IMemoryIndex.byTag} */
|
|
1518
|
+
byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
1519
|
+
/** {@inheritDoc IMemoryIndex.byRecency} */
|
|
1520
|
+
byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
|
|
1521
|
+
/** {@inheritDoc IMemoryIndex.backlinks} */
|
|
1522
|
+
backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
|
|
1523
|
+
/**
|
|
1524
|
+
* Resolve a set of composite keys to their records, ordered
|
|
1525
|
+
* most-recently-updated first (with a `seq` tiebreak so equal-`updated`
|
|
1526
|
+
* records sort deterministically).
|
|
1527
|
+
*/
|
|
1528
|
+
private _recencyOrdered;
|
|
1529
|
+
/** Insert an entry and register all its derived associations. */
|
|
1530
|
+
private _add;
|
|
1531
|
+
/** Remove the entry at `key` (if present) and all its derived associations. */
|
|
1532
|
+
private _remove;
|
|
1533
|
+
/** Register `sourceId` (keyed by its composite `sourceKey`) as linking at `target`. */
|
|
1534
|
+
private _addBacklink;
|
|
1535
|
+
/** Drop the backlink from `sourceKey` to `target`, removing the target map when empty. */
|
|
1536
|
+
private _removeBacklink;
|
|
1537
|
+
/** Add `member` to the set at `mapKey`, creating the set on first use. */
|
|
1538
|
+
private _addToSetMap;
|
|
1539
|
+
/** Remove `member` from the set at `mapKey`, dropping the set when empty. */
|
|
1540
|
+
private _removeFromSetMap;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* The mutation a {@link IMemoryIndex.patch | patch} applies: a record was
|
|
1545
|
+
* written (`'put'`) or removed (`'delete'`).
|
|
1546
|
+
* @public
|
|
1547
|
+
*/
|
|
1548
|
+
export declare type MemoryIndexPatchOp = 'put' | 'delete';
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* The outcome of the observed operation.
|
|
1552
|
+
* @public
|
|
1553
|
+
*/
|
|
1554
|
+
export declare type MemoryObservationOutcome = 'success' | 'failure';
|
|
1555
|
+
|
|
1556
|
+
/**
|
|
1557
|
+
* The store/retriever operation an observation record describes.
|
|
1558
|
+
*
|
|
1559
|
+
* @remarks
|
|
1560
|
+
* `'read'` / `'write'` / `'delete'` are fired post-op by
|
|
1561
|
+
* {@link FileTreeMemoryStore} (on `get` / `put` / `delete` respectively) when
|
|
1562
|
+
* observers are wired. `'retrieve'` is reserved for retriever-fired
|
|
1563
|
+
* observations; no B2 retriever fires one (a single seq authority per
|
|
1564
|
+
* observation store is the supported topology), so it never appears in B2 but
|
|
1565
|
+
* is part of the vocabulary so a future retriever-firing hook is additive.
|
|
1566
|
+
* @public
|
|
1567
|
+
*/
|
|
1568
|
+
export declare type MemoryObservationPhase = 'read' | 'write' | 'delete' | 'retrieve';
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* The default in-memory observation store: an {@link IMemoryObserver} that
|
|
1572
|
+
* retains the records it observes in a bounded ring and answers schema-aware
|
|
1573
|
+
* {@link MemoryObservationStore.query | queries} over them.
|
|
1574
|
+
*
|
|
1575
|
+
* @remarks
|
|
1576
|
+
* **Privacy posture — this store is most-permissive by design.** It retains
|
|
1577
|
+
* every field of every record verbatim, including any `provenance` and
|
|
1578
|
+
* `querySnapshot`. The library bakes in **no** redaction, retention, or
|
|
1579
|
+
* field-stripping policy — that is deployment policy, not library policy. A
|
|
1580
|
+
* deployment that must redact wraps this store with its own
|
|
1581
|
+
* {@link IMemoryObserver} that transforms records before forwarding, or
|
|
1582
|
+
* substitutes a different observer entirely. Size is the only bounded
|
|
1583
|
+
* dimension, via `maxRecords`.
|
|
1584
|
+
*
|
|
1585
|
+
* The store composes `RetainingRingBuffer` from `@fgv/ts-utils` (it does not
|
|
1586
|
+
* hand-roll a ring) and implements {@link IMemoryObserver} directly — `observe`
|
|
1587
|
+
* (the hook) and `query` (the read surface) live on the same class. Wire it via
|
|
1588
|
+
* {@link IFileTreeMemoryStoreCreateParams.observers}. `seq` and `timestamp` are
|
|
1589
|
+
* assigned by the firing authority (the store) before `observe`, so this store
|
|
1590
|
+
* never mints them.
|
|
1591
|
+
* @public
|
|
1592
|
+
*/
|
|
1593
|
+
export declare class MemoryObservationStore implements IMemoryObserver {
|
|
1594
|
+
/**
|
|
1595
|
+
* The bounded ring of observed records. The firing authority assigns each
|
|
1596
|
+
* record's `seq`, so the ring's monotonic-`seq` cursor contract is satisfied
|
|
1597
|
+
* by that authority's per-instance counter.
|
|
1598
|
+
* @internal
|
|
1599
|
+
*/
|
|
1600
|
+
private readonly _buffer;
|
|
1601
|
+
/**
|
|
1602
|
+
* @param buffer - The pre-constructed backing ring buffer.
|
|
1603
|
+
* @internal
|
|
1604
|
+
*/
|
|
1605
|
+
private constructor();
|
|
1606
|
+
/**
|
|
1607
|
+
* The highest `seq` observed so far. Hold this value and pass it as
|
|
1608
|
+
* `sinceSeq` to {@link MemoryObservationStore.query | query} to page only
|
|
1609
|
+
* records observed afterward. Stable across ring eviction and
|
|
1610
|
+
* {@link MemoryObservationStore.clear | clear}.
|
|
1611
|
+
*/
|
|
1612
|
+
get lastSeq(): number;
|
|
1613
|
+
/**
|
|
1614
|
+
* The number of records currently retained.
|
|
1615
|
+
*/
|
|
1616
|
+
get size(): number;
|
|
1617
|
+
/**
|
|
1618
|
+
* Family-convention factory.
|
|
1619
|
+
* @param params - {@link IMemoryObservationStoreCreateParams | Construction options}.
|
|
1620
|
+
* @returns On success, a new store. Fails if `maxRecords` is supplied and is
|
|
1621
|
+
* not a positive integer.
|
|
1622
|
+
*/
|
|
1623
|
+
static create(params?: IMemoryObservationStoreCreateParams): Result<MemoryObservationStore>;
|
|
1624
|
+
/**
|
|
1625
|
+
* {@inheritDoc IMemoryObserver.observe}
|
|
1626
|
+
*/
|
|
1627
|
+
observe(record: IMemoryObservationRecord): Promise<Result<unknown>>;
|
|
1628
|
+
/**
|
|
1629
|
+
* Returns retained records, oldest-first, narrowed by the supplied criteria.
|
|
1630
|
+
* @param criteria - {@link IMemoryObservationQuery | AND-combined filter criteria}.
|
|
1631
|
+
* @returns The matching records, oldest-first.
|
|
1632
|
+
*/
|
|
1633
|
+
query(criteria?: IMemoryObservationQuery): ReadonlyArray<IMemoryObservationRecord>;
|
|
1634
|
+
/**
|
|
1635
|
+
* Clears all retained records. Does NOT reset
|
|
1636
|
+
* {@link MemoryObservationStore.lastSeq | lastSeq}, so a held `sinceSeq`
|
|
1637
|
+
* cursor never re-sees a sequence number.
|
|
1638
|
+
*/
|
|
1639
|
+
clear(): void;
|
|
1640
|
+
/**
|
|
1641
|
+
* Tests a record against the non-`seq`/`limit` criteria (those are applied by
|
|
1642
|
+
* the ring buffer itself).
|
|
1643
|
+
* @internal
|
|
1644
|
+
*/
|
|
1645
|
+
private static _matches;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
/**
|
|
1649
|
+
* Scope path segment. May be multi-segment (`/`-separated) for kinds whose
|
|
1650
|
+
* codec maps an entity into a sub-tree (e.g. MTM: `conversations/<id>`). The
|
|
1651
|
+
* codec — not this brand — owns filename-safe escaping, so the converter
|
|
1652
|
+
* validates only the non-empty/length/whitespace hygiene shared by every
|
|
1653
|
+
* brand.
|
|
1654
|
+
* @public
|
|
1655
|
+
*/
|
|
1656
|
+
export declare type MemoryScopeKey = Brand<string, 'MemoryScopeKey'>;
|
|
1657
|
+
|
|
1658
|
+
/**
|
|
1659
|
+
* Identity codec for the medium-term-memory (MTM) kind family. An MTM entity is
|
|
1660
|
+
* keyed by the colon-composite `<conversationId>:<turnIndex>` and maps into a
|
|
1661
|
+
* per-conversation subtree.
|
|
1662
|
+
*
|
|
1663
|
+
* @remarks
|
|
1664
|
+
* - `encode`: splits the entity id on `:`; scope =
|
|
1665
|
+
* `conversations/<conversationId>` (multi-segment), idStem = `turn-<turnIndex>`,
|
|
1666
|
+
* `isVersioned = false`.
|
|
1667
|
+
* - `decode`: reverses scope `conversations/<id>` + stem `turn-<N>` to the
|
|
1668
|
+
* composite `<id>:<N>`.
|
|
1669
|
+
* - Escaping: `conversationId` must match the POSIX portable filename set (so it
|
|
1670
|
+
* contains no `/` or `:`); `turnIndex` must be a non-negative integer string,
|
|
1671
|
+
* preserved verbatim so the round-trip is exact.
|
|
1672
|
+
* - Layout: `vault/conversations/<conversationId>/turn-<N>.md`. The `/` in the
|
|
1673
|
+
* scope is handled by the store's multi-segment scope resolver.
|
|
1674
|
+
*
|
|
1675
|
+
* **Verbatim turn index (caller-canonicalization note).** The turn index is
|
|
1676
|
+
* preserved exactly — `conv-1:7` and `conv-1:007` are DISTINCT entities mapping
|
|
1677
|
+
* to distinct files (`turn-7.md` / `turn-007.md`). Verbatim preservation is what
|
|
1678
|
+
* makes the round-trip exact, but it means a caller that formats turn indices
|
|
1679
|
+
* inconsistently (some zero-padded, some not) will silently mint separate
|
|
1680
|
+
* entities. Callers should canonicalize to one form (e.g. no leading zeros).
|
|
1681
|
+
* @public
|
|
1682
|
+
*/
|
|
1683
|
+
export declare class MtmIdentityCodec implements IIdentityCodec {
|
|
1684
|
+
/** The top-level scope segment under which every MTM subtree lives. */
|
|
1685
|
+
static readonly rootScopeSegment: string;
|
|
1686
|
+
/** The fixed filename-stem prefix for a turn record. */
|
|
1687
|
+
static readonly turnStemPrefix: string;
|
|
1688
|
+
/** A non-negative integer string (the turn index), preserved verbatim. */
|
|
1689
|
+
private static readonly _turnIndexRe;
|
|
1690
|
+
/** {@inheritDoc IIdentityCodec.encode} */
|
|
1691
|
+
encode(entityId: EntityId): Result<IIdentityCodecResult>;
|
|
1692
|
+
/** {@inheritDoc IIdentityCodec.decode} */
|
|
1693
|
+
decode(scope: MemoryScopeKey, encodedStem: string): Result<EntityId>;
|
|
1694
|
+
/** {@inheritDoc IIdentityCodec.verifyRoundTrip} */
|
|
1695
|
+
verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
|
|
1696
|
+
/** Validate and extract the `conversationId` from a `conversations/<id>` scope. */
|
|
1697
|
+
private static _conversationIdFromScope;
|
|
1698
|
+
/** Validate and extract the turn-index digits from a `turn-<N>` stem. */
|
|
1699
|
+
private static _turnIndexFromStem;
|
|
1700
|
+
/** Assert a string is a non-negative integer (the turn index). */
|
|
1701
|
+
private static _assertTurnIndex;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/**
|
|
1705
|
+
* The capabilities every non-semantic, non-temporal, non-link v1 retriever
|
|
1706
|
+
* exposes (all three flags `false`).
|
|
1707
|
+
* @public
|
|
1708
|
+
*/
|
|
1709
|
+
export declare const NON_SEMANTIC_CAPABILITIES: IMemoryRetrieverCapabilities;
|
|
1710
|
+
|
|
1711
|
+
/**
|
|
1712
|
+
* Parse a complete memory file into a typed {@link IMemoryRecord}. Splits the
|
|
1713
|
+
* frontmatter, validates the envelope, and dispatches the body through the
|
|
1714
|
+
* registered Converter for the envelope's kind.
|
|
1715
|
+
* @public
|
|
1716
|
+
*/
|
|
1717
|
+
export declare function parseMemoryFile(raw: string, registry: IBodyConverterRegistry): Result<IMemoryRecord<unknown>>;
|
|
1718
|
+
|
|
1719
|
+
/**
|
|
1720
|
+
* Converter for {@link IProvenance}. Validates the known fields and preserves
|
|
1721
|
+
* any extension keys verbatim (the `[key: string]: unknown` opaque payload),
|
|
1722
|
+
* so a round-trip never drops consumer-attached data.
|
|
1723
|
+
* @public
|
|
1724
|
+
*/
|
|
1725
|
+
export declare const provenanceConverter: Converter<IProvenance>;
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Origin of a provenance attribution. Open vocabulary: the three named
|
|
1729
|
+
* sources are conventional, but the `(string & {})` arm admits any other
|
|
1730
|
+
* source string without resignature.
|
|
1731
|
+
* @public
|
|
1732
|
+
*/
|
|
1733
|
+
export declare type ProvenanceSource = 'agent' | 'host-ingest' | 'human' | (string & {});
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Embeds a query string into a vector for {@link IVectorIndex.query}. Async and
|
|
1737
|
+
* `Result`-returning, since a real embedder does a network call.
|
|
1738
|
+
* @public
|
|
1739
|
+
*/
|
|
1740
|
+
export declare type QueryEmbedder = (text: string) => Promise<Result<Float32Array>>;
|
|
1741
|
+
|
|
1742
|
+
/**
|
|
1743
|
+
* Recency comparator: most-recently-updated first, with a `seq` tiebreak so
|
|
1744
|
+
* equal-`updated` records sort deterministically. Mirrors the B1 index ordering.
|
|
1745
|
+
* @public
|
|
1746
|
+
*/
|
|
1747
|
+
export declare function recencyCompare(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number;
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Returns records matching the query's scope / kind / tag / predicate filters,
|
|
1751
|
+
* ordered most-recently-updated first. The universal v1 retriever — an empty
|
|
1752
|
+
* query returns the whole vault in recency order.
|
|
1753
|
+
* @public
|
|
1754
|
+
*/
|
|
1755
|
+
export declare class RecencyRetriever implements IMemoryRetriever {
|
|
1756
|
+
private readonly _index;
|
|
1757
|
+
private constructor();
|
|
1758
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
1759
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
1760
|
+
/** Family-convention factory. */
|
|
1761
|
+
static create(index: IMemoryIndex): Result<RecencyRetriever>;
|
|
1762
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
1763
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
/**
|
|
1767
|
+
* The reference {@link IMergeStrategy}: a score-union. Every record is scored by
|
|
1768
|
+
* the number of composed result sets it appears in (deduplicated by
|
|
1769
|
+
* {@link IMemoryEnvelope.id | id}); the merged result is ordered by descending
|
|
1770
|
+
* score, then by recency. Records surfaced by more retrievers rank higher.
|
|
1771
|
+
*
|
|
1772
|
+
* @remarks
|
|
1773
|
+
* Dedup is by `id` alone, which is unambiguous for the flat (one-file-per-entity)
|
|
1774
|
+
* kinds B2 ships. Phase-C versioned kinds that reuse a stem across scopes will
|
|
1775
|
+
* extend the dedup key — additive, no API change.
|
|
1776
|
+
* @public
|
|
1777
|
+
*/
|
|
1778
|
+
export declare class ScoreUnionMergeStrategy implements IMergeStrategy {
|
|
1779
|
+
private constructor();
|
|
1780
|
+
/** Family-convention factory. */
|
|
1781
|
+
static create(): Result<ScoreUnionMergeStrategy>;
|
|
1782
|
+
/** {@inheritDoc IMergeStrategy.merge} */
|
|
1783
|
+
merge(resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
/**
|
|
1787
|
+
* Apply the shared scope / kind / tag / predicate pre-filter to a set of indexed
|
|
1788
|
+
* entries, returning the surviving records (unordered, unlimited).
|
|
1789
|
+
* @public
|
|
1790
|
+
*/
|
|
1791
|
+
export declare function selectByQuery(entries: ReadonlyArray<IIndexedMemoryRecord>, query: IMemoryQuery): IMemoryRecord<unknown>[];
|
|
1792
|
+
|
|
1793
|
+
/**
|
|
1794
|
+
* The loud-degradation message a retriever returns when `query.semantic` is set
|
|
1795
|
+
* but no {@link IVectorIndex} is wired.
|
|
1796
|
+
* @public
|
|
1797
|
+
*/
|
|
1798
|
+
export declare const SEMANTIC_UNWIRED_MESSAGE: string;
|
|
1799
|
+
|
|
1800
|
+
/**
|
|
1801
|
+
* Vector-recall retriever. When a {@link ISemanticBackend | backend} is wired it
|
|
1802
|
+
* embeds `query.semantic`, queries the vector index, and resolves the hits back
|
|
1803
|
+
* to records (preserving vector score order). When no backend is wired,
|
|
1804
|
+
* `supportsSemanticRecall` is `false` and any `query.semantic` request degrades
|
|
1805
|
+
* loudly — it NEVER returns a silent empty.
|
|
1806
|
+
*
|
|
1807
|
+
* @remarks
|
|
1808
|
+
* A consumer-supplied backend that rejects (throws) is normalized into a
|
|
1809
|
+
* `Failure` — `retrieve` always honors its `Promise<Result<...>>` contract.
|
|
1810
|
+
* @public
|
|
1811
|
+
*/
|
|
1812
|
+
export declare class SemanticRetriever implements IMemoryRetriever {
|
|
1813
|
+
private readonly _index;
|
|
1814
|
+
private readonly _backend;
|
|
1815
|
+
private constructor();
|
|
1816
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
1817
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
1818
|
+
/** Family-convention factory. */
|
|
1819
|
+
static create(params: ISemanticRetrieverCreateParams): Result<SemanticRetriever>;
|
|
1820
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
1821
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
1822
|
+
/**
|
|
1823
|
+
* Invoke a consumer-supplied backend hook, normalizing both a returned `fail`
|
|
1824
|
+
* and a thrown/rejected promise into a single `semantic recall: <label> failed`
|
|
1825
|
+
* `Failure`. Keeps `retrieve` within the `Promise<Result<...>>` contract even
|
|
1826
|
+
* when the injected `embedQuery` / `vectorIndex` misbehaves.
|
|
1827
|
+
*/
|
|
1828
|
+
private static _callBackend;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* Serialize an envelope and its rendered body text into a memory file. The
|
|
1833
|
+
* envelope is emitted as YAML frontmatter; the body is written verbatim after
|
|
1834
|
+
* the closing delimiter.
|
|
1835
|
+
* @public
|
|
1836
|
+
*/
|
|
1837
|
+
export declare function serializeMemoryFile(envelope: IMemoryEnvelope, body: string): Result<string>;
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Split a `---\n<yaml>\n---\n<body>` memory file into its frontmatter and
|
|
1841
|
+
* body parts. Pure string handling — no external dependency.
|
|
1842
|
+
* @public
|
|
1843
|
+
*/
|
|
1844
|
+
export declare function splitFrontmatter(raw: string): Result<IMemoryFileParts>;
|
|
1845
|
+
|
|
1846
|
+
/**
|
|
1847
|
+
* Returns records matching `query.filter`, narrowed by any scope / kind / tag
|
|
1848
|
+
* pre-filter and recency-ordered. The predicate is this retriever's axis: a
|
|
1849
|
+
* query without a `filter` is not its concern and yields an empty success (so it
|
|
1850
|
+
* contributes nothing to a {@link HybridRetriever}, rather than failing).
|
|
1851
|
+
* @public
|
|
1852
|
+
*/
|
|
1853
|
+
export declare class StructuredFilterRetriever implements IMemoryRetriever {
|
|
1854
|
+
private readonly _index;
|
|
1855
|
+
private constructor();
|
|
1856
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
1857
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
1858
|
+
/** Family-convention factory. */
|
|
1859
|
+
static create(index: IMemoryIndex): Result<StructuredFilterRetriever>;
|
|
1860
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
1861
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Open-vocabulary tag label.
|
|
1866
|
+
* @public
|
|
1867
|
+
*/
|
|
1868
|
+
export declare type Tag = Brand<string, 'Tag'>;
|
|
1869
|
+
|
|
1870
|
+
/**
|
|
1871
|
+
* Returns records carrying `query.tag`, recency-ordered within the tag and
|
|
1872
|
+
* narrowed by any scope / kind / predicate filters. Tag is this retriever's
|
|
1873
|
+
* axis: a query without a `tag` is not its concern and yields an empty success
|
|
1874
|
+
* (so it contributes nothing to a {@link HybridRetriever}, rather than failing).
|
|
1875
|
+
* @public
|
|
1876
|
+
*/
|
|
1877
|
+
export declare class TagRetriever implements IMemoryRetriever {
|
|
1878
|
+
private readonly _index;
|
|
1879
|
+
private constructor();
|
|
1880
|
+
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
1881
|
+
get capabilities(): IMemoryRetrieverCapabilities;
|
|
1882
|
+
/** Family-convention factory. */
|
|
1883
|
+
static create(index: IMemoryIndex): Result<TagRetriever>;
|
|
1884
|
+
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
1885
|
+
retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/**
|
|
1889
|
+
* Converter for the optional {@link ITemporalBlock}.
|
|
1890
|
+
* @public
|
|
1891
|
+
*/
|
|
1892
|
+
export declare const temporalConverter: Converter<ITemporalBlock>;
|
|
1893
|
+
|
|
1894
|
+
/**
|
|
1895
|
+
* Build the loud-degradation message a retriever returns when `query.asOf` is
|
|
1896
|
+
* set but no temporal index is wired.
|
|
1897
|
+
* @public
|
|
1898
|
+
*/
|
|
1899
|
+
export declare function temporalUnwiredMessage(kind?: Kind): string;
|
|
1900
|
+
|
|
1901
|
+
export { }
|