@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,346 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Erik Fortune
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import '@fgv/ts-utils-jest';
|
|
7
|
+
import { Converters, Logging, Result, fail, succeed } from '@fgv/ts-utils';
|
|
8
|
+
import { FileTree } from '@fgv/ts-json-base';
|
|
9
|
+
import {
|
|
10
|
+
BodyConverterRegistry,
|
|
11
|
+
FileTreeMemoryStore,
|
|
12
|
+
IBodyConverterRegistry,
|
|
13
|
+
IIdentityCodec,
|
|
14
|
+
IIndexedMemoryRecord,
|
|
15
|
+
IMemoryRecord,
|
|
16
|
+
IVectorIndex,
|
|
17
|
+
IVectorQueryHit,
|
|
18
|
+
InMemoryCosineIndex,
|
|
19
|
+
Kind,
|
|
20
|
+
KnowledgeIdentityCodec,
|
|
21
|
+
MemoryCapCullPolicy,
|
|
22
|
+
EntityId,
|
|
23
|
+
MemoryId,
|
|
24
|
+
MemoryIndex,
|
|
25
|
+
MemoryScopeKey,
|
|
26
|
+
MtmIdentityCodec,
|
|
27
|
+
IWritePolicy,
|
|
28
|
+
SemanticRetriever,
|
|
29
|
+
envelopeConverter
|
|
30
|
+
} from '../../../index';
|
|
31
|
+
|
|
32
|
+
const knowledgeKind: Kind = 'knowledge' as Kind;
|
|
33
|
+
const mtmKind: Kind = 'mtm' as Kind;
|
|
34
|
+
|
|
35
|
+
function mutableRoot(): FileTree.IMutableFileTreeDirectoryItem {
|
|
36
|
+
const tree = FileTree.inMemory([], { mutable: true }).orThrow();
|
|
37
|
+
const root = tree.getDirectory('/').orThrow();
|
|
38
|
+
if (!FileTree.isMutableDirectoryItem(root)) {
|
|
39
|
+
throw new Error('expected a mutable root directory');
|
|
40
|
+
}
|
|
41
|
+
return root;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function makeRecord(
|
|
45
|
+
id: string,
|
|
46
|
+
body: string,
|
|
47
|
+
kind: string = 'knowledge',
|
|
48
|
+
entityId?: string
|
|
49
|
+
): IMemoryRecord<unknown> {
|
|
50
|
+
return {
|
|
51
|
+
envelope: envelopeConverter
|
|
52
|
+
.convert({
|
|
53
|
+
id,
|
|
54
|
+
entityId: entityId ?? id,
|
|
55
|
+
kind,
|
|
56
|
+
tags: [],
|
|
57
|
+
links: [],
|
|
58
|
+
created: 0,
|
|
59
|
+
updated: 0,
|
|
60
|
+
seq: 0,
|
|
61
|
+
contentHash: '',
|
|
62
|
+
provenance: { source: 'agent' }
|
|
63
|
+
})
|
|
64
|
+
.orThrow(),
|
|
65
|
+
body
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Deterministic keyword-count embedder (no network). Counts occurrences of three
|
|
71
|
+
* marker words, so a record's vector and a query's vector live in the same space
|
|
72
|
+
* and cosine ordering is fully predictable.
|
|
73
|
+
*/
|
|
74
|
+
const MARKERS: ReadonlyArray<string> = ['cat', 'dog', 'fish'];
|
|
75
|
+
function featureVector(text: string): Float32Array {
|
|
76
|
+
const lower: string = text.toLowerCase();
|
|
77
|
+
return Float32Array.from(MARKERS.map((m) => lower.split(m).length - 1));
|
|
78
|
+
}
|
|
79
|
+
const recordEmbed = (r: IMemoryRecord<unknown>): Promise<Result<Float32Array>> =>
|
|
80
|
+
// A real embedder receives an `IMemoryRecord<unknown>` and must validate the
|
|
81
|
+
// body through a Converter rather than casting — this models that idiom.
|
|
82
|
+
Promise.resolve(
|
|
83
|
+
Converters.string
|
|
84
|
+
.convert(r.body)
|
|
85
|
+
.withErrorFormat((msg) => `cannot embed: body is not a string: ${msg}`)
|
|
86
|
+
.onSuccess((body) => succeed(featureVector(body)))
|
|
87
|
+
);
|
|
88
|
+
const queryEmbed = (text: string): Promise<Result<Float32Array>> =>
|
|
89
|
+
Promise.resolve(succeed(featureVector(text)));
|
|
90
|
+
|
|
91
|
+
/** Records add/remove call order and can be configured to fail either op. */
|
|
92
|
+
class SpyVectorIndex implements IVectorIndex {
|
|
93
|
+
public readonly calls: string[] = [];
|
|
94
|
+
public failAdd: boolean = false;
|
|
95
|
+
public failRemove: boolean = false;
|
|
96
|
+
private readonly _inner: InMemoryCosineIndex = InMemoryCosineIndex.create().orThrow();
|
|
97
|
+
|
|
98
|
+
public async add(id: MemoryId, vector: Float32Array): Promise<Result<string>> {
|
|
99
|
+
this.calls.push(`add:${id}`);
|
|
100
|
+
return this.failAdd ? fail('add boom') : this._inner.add(id, vector);
|
|
101
|
+
}
|
|
102
|
+
public async remove(id: MemoryId): Promise<Result<MemoryId>> {
|
|
103
|
+
this.calls.push(`remove:${id}`);
|
|
104
|
+
return this.failRemove ? fail('remove boom') : this._inner.remove(id);
|
|
105
|
+
}
|
|
106
|
+
public query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>> {
|
|
107
|
+
return this._inner.query(vector, topK);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function knowledgeStore(
|
|
112
|
+
vectorIndex?: IVectorIndex,
|
|
113
|
+
embed?: (r: IMemoryRecord<unknown>) => Promise<Result<Float32Array>>,
|
|
114
|
+
logger?: Logging.ILogger
|
|
115
|
+
): FileTreeMemoryStore {
|
|
116
|
+
const registry: IBodyConverterRegistry = BodyConverterRegistry.create().orThrow();
|
|
117
|
+
registry.register(knowledgeKind, Converters.string);
|
|
118
|
+
return FileTreeMemoryStore.create({
|
|
119
|
+
root: mutableRoot(),
|
|
120
|
+
registry,
|
|
121
|
+
codecs: new Map<Kind, IIdentityCodec>([[knowledgeKind, new KnowledgeIdentityCodec()]]),
|
|
122
|
+
vectorIndex,
|
|
123
|
+
embed,
|
|
124
|
+
logger
|
|
125
|
+
}).orThrow();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
describe('FileTreeMemoryStore embed-on-write', () => {
|
|
129
|
+
describe('when wired', () => {
|
|
130
|
+
test('embeds on write, stamps embeddingRef, and indexes the vector', async () => {
|
|
131
|
+
const index = InMemoryCosineIndex.create().orThrow();
|
|
132
|
+
const store = knowledgeStore(index, recordEmbed);
|
|
133
|
+
expect(await store.put(makeRecord('doc-a', 'cat cat'))).toSucceedAndSatisfy(
|
|
134
|
+
(record: IMemoryRecord<unknown>) => {
|
|
135
|
+
// InMemoryCosineIndex keys entries by id, so the ref IS the id.
|
|
136
|
+
expect(record.envelope.embeddingRef).toBe('doc-a');
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
expect(index.size).toBe(1);
|
|
140
|
+
// The persisted file carries the embeddingRef (round-trips through disk).
|
|
141
|
+
expect(await store.getById('knowledge' as MemoryScopeKey, 'doc-a' as MemoryId)).toSucceedAndSatisfy(
|
|
142
|
+
(record: IMemoryRecord<unknown> | undefined) => {
|
|
143
|
+
expect(record?.envelope.embeddingRef).toBe('doc-a');
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
// The vector is queryable.
|
|
147
|
+
expect(await index.query(featureVector('cat'), 1)).toSucceedAndSatisfy(
|
|
148
|
+
(hits: ReadonlyArray<IVectorQueryHit>) => {
|
|
149
|
+
expect(hits[0].id).toBe('doc-a');
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('re-embeds on a content change by re-adding (replace semantics, no eager remove)', async () => {
|
|
155
|
+
const spy = new SpyVectorIndex();
|
|
156
|
+
const store = knowledgeStore(spy, recordEmbed);
|
|
157
|
+
(await store.put(makeRecord('doc-a', 'cat'))).orThrow();
|
|
158
|
+
(await store.put(makeRecord('doc-a', 'dog'))).orThrow();
|
|
159
|
+
// The content change re-adds (IVectorIndex.add replaces for a same id) —
|
|
160
|
+
// no redundant eager remove.
|
|
161
|
+
expect(spy.calls).toEqual(['add:doc-a', 'add:doc-a']);
|
|
162
|
+
// The index now reflects the new content ('dog'), not the original.
|
|
163
|
+
expect(await spy.query(featureVector('dog'), 1)).toSucceedAndSatisfy(
|
|
164
|
+
(hits: ReadonlyArray<IVectorQueryHit>) => {
|
|
165
|
+
expect(hits[0].score).toBeCloseTo(1);
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('does not re-embed a dedup no-op (identical content)', async () => {
|
|
171
|
+
const spy = new SpyVectorIndex();
|
|
172
|
+
const store = knowledgeStore(spy, recordEmbed);
|
|
173
|
+
(await store.put(makeRecord('doc-a', 'cat'))).orThrow();
|
|
174
|
+
(await store.put(makeRecord('doc-a', 'cat'))).orThrow();
|
|
175
|
+
// The second identical put is a content-dedup no-op — no second embedding.
|
|
176
|
+
expect(spy.calls).toEqual(['add:doc-a']);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('removes the vector on delete', async () => {
|
|
180
|
+
const spy = new SpyVectorIndex();
|
|
181
|
+
const store = knowledgeStore(spy, recordEmbed);
|
|
182
|
+
(await store.put(makeRecord('doc-a', 'cat'))).orThrow();
|
|
183
|
+
expect(await store.delete(knowledgeKind, 'doc-a' as unknown as EntityId)).toSucceedWith(
|
|
184
|
+
'doc-a' as MemoryId
|
|
185
|
+
);
|
|
186
|
+
expect(spy.calls).toEqual(['add:doc-a', 'remove:doc-a']);
|
|
187
|
+
expect(await spy.query(featureVector('cat'), 5)).toSucceedWith([]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('removes evicted vectors on cap-cull eviction', async () => {
|
|
191
|
+
const index = InMemoryCosineIndex.create().orThrow();
|
|
192
|
+
const capCull: IWritePolicy = MemoryCapCullPolicy.create({
|
|
193
|
+
maxRecords: 2,
|
|
194
|
+
mutableFields: ['body', 'tags', 'links', 'provenance', 'embeddingRef']
|
|
195
|
+
}).orThrow();
|
|
196
|
+
const registry: IBodyConverterRegistry = BodyConverterRegistry.create().orThrow();
|
|
197
|
+
registry.register(mtmKind, Converters.string);
|
|
198
|
+
const store = FileTreeMemoryStore.create({
|
|
199
|
+
root: mutableRoot(),
|
|
200
|
+
registry,
|
|
201
|
+
codecs: new Map<Kind, IIdentityCodec>([[mtmKind, new MtmIdentityCodec()]]),
|
|
202
|
+
writePolicies: new Map<Kind, IWritePolicy>([[mtmKind, capCull]]),
|
|
203
|
+
vectorIndex: index,
|
|
204
|
+
embed: recordEmbed
|
|
205
|
+
}).orThrow();
|
|
206
|
+
|
|
207
|
+
// Distinct bodies so each gets a distinct vector; maxRecords=2 evicts the
|
|
208
|
+
// oldest on the third write.
|
|
209
|
+
(await store.put(makeRecord('turn-0', 'cat', 'mtm', 'conv-1:0'))).orThrow();
|
|
210
|
+
(await store.put(makeRecord('turn-1', 'dog', 'mtm', 'conv-1:1'))).orThrow();
|
|
211
|
+
(await store.put(makeRecord('turn-2', 'fish', 'mtm', 'conv-1:2'))).orThrow();
|
|
212
|
+
// turn-0 was evicted from the store AND the vector index.
|
|
213
|
+
expect(index.size).toBe(2);
|
|
214
|
+
expect(await index.query(featureVector('cat'), 5)).toSucceedAndSatisfy(
|
|
215
|
+
(hits: ReadonlyArray<IVectorQueryHit>) => {
|
|
216
|
+
expect(hits.map((h) => h.id)).not.toContain('turn-0');
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('persists the record (best-effort) and logs when embedding fails', async () => {
|
|
222
|
+
const index = InMemoryCosineIndex.create().orThrow();
|
|
223
|
+
const logger = new Logging.InMemoryLogger();
|
|
224
|
+
const failEmbed = (): Promise<Result<Float32Array>> => Promise.resolve(fail('no model'));
|
|
225
|
+
const store = knowledgeStore(index, failEmbed, logger);
|
|
226
|
+
// The durable write succeeds; only the derived index is skipped (rebuildable).
|
|
227
|
+
expect(await store.put(makeRecord('doc-a', 'cat'))).toSucceedAndSatisfy(
|
|
228
|
+
(record: IMemoryRecord<unknown>) => {
|
|
229
|
+
expect(record.envelope.embeddingRef).toBeUndefined();
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
expect(await store.getById('knowledge' as MemoryScopeKey, 'doc-a' as MemoryId)).toSucceedAndSatisfy(
|
|
233
|
+
(record: IMemoryRecord<unknown> | undefined) => {
|
|
234
|
+
expect(record?.envelope.id).toBe('doc-a');
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
expect(index.size).toBe(0);
|
|
238
|
+
expect(logger.logged.some((m) => /embedding 'doc-a' failed.*no model/i.test(m))).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test('persists the record (best-effort) and logs when the vector add fails', async () => {
|
|
242
|
+
const spy = new SpyVectorIndex();
|
|
243
|
+
spy.failAdd = true;
|
|
244
|
+
const logger = new Logging.InMemoryLogger();
|
|
245
|
+
const store = knowledgeStore(spy, recordEmbed, logger);
|
|
246
|
+
expect(await store.put(makeRecord('doc-a', 'cat'))).toSucceedAndSatisfy(
|
|
247
|
+
(record: IMemoryRecord<unknown>) => {
|
|
248
|
+
expect(record.envelope.embeddingRef).toBeUndefined();
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
expect(await store.getById('knowledge' as MemoryScopeKey, 'doc-a' as MemoryId)).toSucceedAndSatisfy(
|
|
252
|
+
(record: IMemoryRecord<unknown> | undefined) => {
|
|
253
|
+
expect(record?.envelope.id).toBe('doc-a');
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
expect(logger.logged.some((m) => /vector add for 'doc-a' failed.*add boom/i.test(m))).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test('best-effort embed survives a throwing/rejecting embedder', async () => {
|
|
260
|
+
const index = InMemoryCosineIndex.create().orThrow();
|
|
261
|
+
const logger = new Logging.InMemoryLogger();
|
|
262
|
+
const throwingEmbed = (): Promise<Result<Float32Array>> => Promise.reject(new Error('embedder kaboom'));
|
|
263
|
+
const store = knowledgeStore(index, throwingEmbed, logger);
|
|
264
|
+
expect(await store.put(makeRecord('doc-a', 'cat'))).toSucceed();
|
|
265
|
+
expect(index.size).toBe(0);
|
|
266
|
+
expect(logger.logged.some((m) => /embedding 'doc-a' threw.*embedder kaboom/i.test(m))).toBe(true);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test('a committed delete succeeds (best-effort) and logs when vector removal fails', async () => {
|
|
270
|
+
const spy = new SpyVectorIndex();
|
|
271
|
+
const logger = new Logging.InMemoryLogger();
|
|
272
|
+
const store = knowledgeStore(spy, recordEmbed, logger);
|
|
273
|
+
(await store.put(makeRecord('doc-a', 'cat'))).orThrow();
|
|
274
|
+
spy.failRemove = true;
|
|
275
|
+
// The record is already gone; vector cleanup failure must NOT fail the delete.
|
|
276
|
+
expect(await store.delete(knowledgeKind, 'doc-a' as unknown as EntityId)).toSucceedWith(
|
|
277
|
+
'doc-a' as MemoryId
|
|
278
|
+
);
|
|
279
|
+
expect(await store.getById('knowledge' as MemoryScopeKey, 'doc-a' as MemoryId)).toSucceedWith(
|
|
280
|
+
undefined
|
|
281
|
+
);
|
|
282
|
+
expect(logger.logged.some((m) => /vector removal for 'doc-a' failed.*remove boom/i.test(m))).toBe(true);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe('when unwired', () => {
|
|
287
|
+
test('a store without a vector index or embedder leaves embeddingRef unset', async () => {
|
|
288
|
+
const store = knowledgeStore();
|
|
289
|
+
expect(await store.put(makeRecord('doc-a', 'cat'))).toSucceedAndSatisfy(
|
|
290
|
+
(record: IMemoryRecord<unknown>) => {
|
|
291
|
+
expect(record.envelope.embeddingRef).toBeUndefined();
|
|
292
|
+
}
|
|
293
|
+
);
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test('a vector index without an embedder is fully inert (no add or remove)', async () => {
|
|
297
|
+
const spy = new SpyVectorIndex();
|
|
298
|
+
const store = knowledgeStore(spy, undefined);
|
|
299
|
+
expect(await store.put(makeRecord('doc-a', 'cat'))).toSucceedAndSatisfy(
|
|
300
|
+
(record: IMemoryRecord<unknown>) => {
|
|
301
|
+
expect(record.envelope.embeddingRef).toBeUndefined();
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
// The vector lifecycle is wholly off without an embedder: neither put nor
|
|
305
|
+
// delete touches the index (a remove would target a record never embedded).
|
|
306
|
+
(await store.delete(knowledgeKind, 'doc-a' as unknown as EntityId)).orThrow();
|
|
307
|
+
expect(spy.calls).toEqual([]);
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
describe('semantic recall end-to-end', () => {
|
|
312
|
+
test('a wired store + InMemoryCosineIndex + SemanticRetriever returns cosine top-k', async () => {
|
|
313
|
+
const index = InMemoryCosineIndex.create().orThrow();
|
|
314
|
+
const store = knowledgeStore(index, recordEmbed);
|
|
315
|
+
(await store.put(makeRecord('cats', 'cat cat cat'))).orThrow();
|
|
316
|
+
(await store.put(makeRecord('dogs', 'dog dog dog'))).orThrow();
|
|
317
|
+
(await store.put(makeRecord('fishes', 'fish fish fish'))).orThrow();
|
|
318
|
+
|
|
319
|
+
// SemanticRetriever takes an IMemoryIndex separate from the store (the
|
|
320
|
+
// store's derived index is private and the two are intentionally
|
|
321
|
+
// decoupled), so build a record index mirroring the store — keyed by the
|
|
322
|
+
// shared vectorIndex instance — for hit hydration.
|
|
323
|
+
const listed: ReadonlyArray<IMemoryRecord<unknown>> = (await store.list()).orThrow();
|
|
324
|
+
const recordIndex = MemoryIndex.create().orThrow();
|
|
325
|
+
recordIndex
|
|
326
|
+
.rebuild(
|
|
327
|
+
listed.map((record): IIndexedMemoryRecord => ({ scope: 'knowledge' as MemoryScopeKey, record }))
|
|
328
|
+
)
|
|
329
|
+
.orThrow();
|
|
330
|
+
|
|
331
|
+
const retriever = SemanticRetriever.create({
|
|
332
|
+
index: recordIndex,
|
|
333
|
+
backend: { vectorIndex: index, embedQuery: queryEmbed }
|
|
334
|
+
}).orThrow();
|
|
335
|
+
expect(retriever.capabilities.supportsSemanticRecall).toBe(true);
|
|
336
|
+
|
|
337
|
+
expect(await retriever.retrieve({ semantic: 'cat', topK: 2 })).toSucceedAndSatisfy(
|
|
338
|
+
(records: ReadonlyArray<IMemoryRecord<unknown>>) => {
|
|
339
|
+
// 'cats' is the closest; 'dogs'/'fishes' are orthogonal (cosine 0) so
|
|
340
|
+
// only the cat record ranks above them — it is first.
|
|
341
|
+
expect(records[0].envelope.id).toBe('cats');
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
});
|