@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 @@
|
|
|
1
|
+
{"version":3,"file":"writePolicy.test.js","sourceRoot":"","sources":["../../../../src/test/unit/types/writePolicy.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\nimport {\n EntityId,\n IMemoryEnvelope,\n IMemoryRecord,\n IProvenance,\n Kind,\n KnowledgeLwwPolicy,\n MemoryCapCullPolicy,\n MemoryId,\n Tag\n} from '../../../packlets/types';\n\ninterface IKnowledgeBody {\n readonly title: string;\n readonly text: string;\n}\n\nfunction makeRecord(overrides?: Partial<IMemoryEnvelope>, body?: unknown): IMemoryRecord<unknown> {\n const envelope: IMemoryEnvelope = {\n id: 'intro-to-rust' as MemoryId,\n entityId: 'intro-to-rust' as EntityId,\n kind: 'knowledge' as Kind,\n tags: ['rust'] as Tag[],\n links: [],\n created: 1000,\n updated: 1000,\n seq: 1,\n contentHash: 'hash-1',\n provenance: { source: 'agent' } as IProvenance,\n ...overrides\n };\n return {\n envelope,\n body: body ?? ({ title: 'Intro', text: 'original' } as IKnowledgeBody)\n };\n}\n\ndescribe('KnowledgeLwwPolicy', () => {\n let policy: KnowledgeLwwPolicy;\n\n beforeEach(() => {\n policy = KnowledgeLwwPolicy.create().orThrow();\n });\n\n test('create succeeds and exposes the knowledge mutable surface', () => {\n expect(policy.mutableFields).toEqual(['body', 'tags', 'links', 'provenance', 'embeddingRef']);\n });\n\n test('declares content-scoped dedup (knowledge collapses identical content across ids)', () => {\n expect(policy.dedupScope).toBe('content');\n });\n\n describe('admit', () => {\n test('always accepts (last-write-wins, no cap)', () => {\n expect(policy.admit(makeRecord(), [])).toSucceedWith({ decision: 'accept' });\n expect(policy.admit(makeRecord(), [makeRecord(), makeRecord()])).toSucceedWith({\n decision: 'accept'\n });\n });\n });\n\n describe('applyUpdate', () => {\n test('deep-merges a body change', () => {\n const existing = makeRecord(undefined, { title: 'Intro', text: 'original' });\n expect(policy.applyUpdate(existing, { body: { text: 'revised' } })).toSucceedAndSatisfy((updated) => {\n expect(updated.body).toEqual({ title: 'Intro', text: 'revised' });\n });\n });\n\n test('replaces arrays wholesale (RFC-7386 array semantics)', () => {\n const existing = makeRecord({ tags: ['rust', 'systems'] as Tag[] });\n expect(policy.applyUpdate(existing, { tags: ['rust'] })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.tags).toEqual(['rust']);\n });\n });\n\n test('deep-merges provenance and preserves untouched keys', () => {\n const existing = makeRecord({ provenance: { source: 'agent', by: 'curator' } as IProvenance });\n expect(policy.applyUpdate(existing, { provenance: { model: 'gpt' } })).toSucceedAndSatisfy(\n (updated) => {\n expect(updated.envelope.provenance).toEqual({ source: 'agent', by: 'curator', model: 'gpt' });\n }\n );\n });\n\n test('sets embeddingRef when present in the patch', () => {\n const existing = makeRecord({ embeddingRef: 'vec-1' });\n expect(policy.applyUpdate(existing, { embeddingRef: 'vec-2' })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBe('vec-2');\n });\n });\n\n test('clears embeddingRef (to absent) when the patch deletes it (null = delete, RFC-7386)', () => {\n const existing = makeRecord({ embeddingRef: 'vec-1' });\n expect(policy.applyUpdate(existing, { embeddingRef: null })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBeUndefined();\n });\n });\n\n test('preserves an absent embeddingRef as absent on an unrelated update (hash-stable)', () => {\n const existing = makeRecord(); // no embeddingRef\n expect(policy.applyUpdate(existing, { body: { text: 'revised' } })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBeUndefined();\n });\n });\n\n test('preserves an explicit null embeddingRef when the patch does not touch it', () => {\n const existing = makeRecord({ embeddingRef: null });\n expect(policy.applyUpdate(existing, { body: { text: 'revised' } })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBeNull();\n });\n });\n\n test('ignores patch keys outside the mutable surface', () => {\n const existing = makeRecord();\n expect(\n policy.applyUpdate(existing, { id: 'tampered', created: 9999, body: { text: 'revised' } })\n ).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.id).toBe('intro-to-rust');\n expect(updated.envelope.created).toBe(1000);\n expect(updated.body).toEqual({ title: 'Intro', text: 'revised' });\n });\n });\n\n test('preserves identity and transaction-time envelope fields', () => {\n const existing = makeRecord();\n expect(policy.applyUpdate(existing, { tags: ['updated'] })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.id).toBe('intro-to-rust');\n expect(updated.envelope.entityId).toBe('intro-to-rust');\n expect(updated.envelope.kind).toBe('knowledge');\n expect(updated.envelope.created).toBe(1000);\n expect(updated.envelope.seq).toBe(1);\n expect(updated.envelope.contentHash).toBe('hash-1');\n });\n });\n\n test('fails when a patch deletes a required mutable field', () => {\n const existing = makeRecord();\n expect(policy.applyUpdate(existing, { body: null })).toFailWith(\n /may not delete required field\\(s\\): body/i\n );\n });\n });\n});\n\ndescribe('MemoryCapCullPolicy', () => {\n const memoryFields: ReadonlyArray<string> = ['body', 'tags', 'links', 'provenance', 'embeddingRef'];\n\n /** Build a memory record with a distinct id and creation time. */\n function memoryRecord(id: string, created: number, body?: unknown): IMemoryRecord<unknown> {\n return makeRecord(\n {\n id: id as MemoryId,\n entityId: id as EntityId,\n kind: 'summarized-turn' as Kind,\n created,\n updated: created\n },\n body\n );\n }\n\n test('create succeeds and exposes the supplied mutable surface + entity dedup scope', () => {\n const policy = MemoryCapCullPolicy.create({ maxRecords: 3, mutableFields: memoryFields }).orThrow();\n expect(policy.mutableFields).toEqual(memoryFields);\n expect(policy.dedupScope).toBe('entity');\n });\n\n describe('admit', () => {\n test('accepts unconditionally when no cap is configured', () => {\n const policy = MemoryCapCullPolicy.create({ mutableFields: memoryFields }).orThrow();\n const cohort = [memoryRecord('turn-0', 1), memoryRecord('turn-1', 2), memoryRecord('turn-2', 3)];\n expect(policy.admit(memoryRecord('turn-3', 4), cohort)).toSucceedWith({ decision: 'accept' });\n });\n\n test('accepts while the cohort is below the cap', () => {\n const policy = MemoryCapCullPolicy.create({ maxRecords: 3, mutableFields: memoryFields }).orThrow();\n const cohort = [memoryRecord('turn-0', 1), memoryRecord('turn-1', 2)];\n expect(policy.admit(memoryRecord('turn-2', 3), cohort)).toSucceedWith({ decision: 'accept' });\n });\n\n test('culls the single oldest (by created ascending) when the cohort is at the cap', () => {\n const policy = MemoryCapCullPolicy.create({ maxRecords: 3, mutableFields: memoryFields }).orThrow();\n // Deliberately out of created order to prove the sort, not the input order.\n const cohort = [memoryRecord('turn-c', 30), memoryRecord('turn-a', 10), memoryRecord('turn-b', 20)];\n expect(policy.admit(memoryRecord('turn-new', 40), cohort)).toSucceedWith({\n decision: 'cull-oldest',\n evict: ['turn-a' as MemoryId]\n });\n });\n\n test('culls enough oldest records to land exactly at the cap when over it', () => {\n const policy = MemoryCapCullPolicy.create({ maxRecords: 2, mutableFields: memoryFields }).orThrow();\n const cohort = [\n memoryRecord('turn-a', 10),\n memoryRecord('turn-b', 20),\n memoryRecord('turn-c', 30),\n memoryRecord('turn-d', 40)\n ];\n // cohort 4, cap 2 → evict 4 - 2 + 1 = 3 oldest, leaving 1 + the incoming = 2.\n expect(policy.admit(memoryRecord('turn-new', 50), cohort)).toSucceedWith({\n decision: 'cull-oldest',\n evict: ['turn-a' as MemoryId, 'turn-b' as MemoryId, 'turn-c' as MemoryId]\n });\n });\n });\n\n describe('applyUpdate', () => {\n let policy: MemoryCapCullPolicy;\n\n beforeEach(() => {\n policy = MemoryCapCullPolicy.create({ maxRecords: 5, mutableFields: memoryFields }).orThrow();\n });\n\n test('deep-merges a body change over the declared mutable surface', () => {\n const existing = memoryRecord('turn-0', 1, { summary: 'first draft', turn: 0 });\n expect(policy.applyUpdate(existing, { body: { summary: 'revised' } })).toSucceedAndSatisfy(\n (updated) => {\n expect(updated.body).toEqual({ summary: 'revised', turn: 0 });\n }\n );\n });\n\n test('replaces arrays wholesale (RFC-7386 array semantics)', () => {\n const existing = makeRecord({\n id: 'turn-0' as MemoryId,\n kind: 'summarized-turn' as Kind,\n tags: ['draft', 'pending'] as Tag[]\n });\n expect(policy.applyUpdate(existing, { tags: ['final'] })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.tags).toEqual(['final']);\n });\n });\n\n test('sets embeddingRef when present in the patch', () => {\n const existing = makeRecord({\n id: 'turn-0' as MemoryId,\n kind: 'summarized-turn' as Kind,\n embeddingRef: 'vec-1'\n });\n expect(policy.applyUpdate(existing, { embeddingRef: 'vec-2' })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBe('vec-2');\n });\n });\n\n test('clears embeddingRef (to absent) when the patch deletes it (null = delete)', () => {\n const existing = makeRecord({\n id: 'turn-0' as MemoryId,\n kind: 'summarized-turn' as Kind,\n embeddingRef: 'vec-1'\n });\n expect(policy.applyUpdate(existing, { embeddingRef: null })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBeUndefined();\n });\n });\n\n test('fails when a patch deletes a required mutable field', () => {\n const existing = memoryRecord('turn-0', 1);\n expect(policy.applyUpdate(existing, { tags: null })).toFailWith(\n /may not delete required field\\(s\\): tags/i\n );\n });\n\n test('preserves a field that is not declared mutable', () => {\n // mutableFields omits 'links', so a links patch is ignored and the\n // existing links are preserved verbatim.\n const narrow = MemoryCapCullPolicy.create({ maxRecords: 5, mutableFields: ['body'] }).orThrow();\n const existing = makeRecord(\n {\n id: 'turn-0' as MemoryId,\n kind: 'summarized-turn' as Kind,\n links: [{ type: 'derived-from' as never, target: 'doc-1' as MemoryId }]\n },\n { summary: 'old' }\n );\n expect(narrow.applyUpdate(existing, { body: { summary: 'x' }, links: [] })).toSucceedAndSatisfy(\n (updated) => {\n expect(updated.envelope.links).toEqual([{ type: 'derived-from', target: 'doc-1' }]);\n expect(updated.body).toEqual({ summary: 'x' });\n }\n );\n });\n\n test('preserves the body when body is not a declared mutable field', () => {\n // mutableFields is tags-only: a body patch is ignored and the existing\n // body is preserved verbatim (the `else existing.body` rebuild branch).\n const tagsOnly = MemoryCapCullPolicy.create({ maxRecords: 5, mutableFields: ['tags'] }).orThrow();\n const existing = makeRecord(\n { id: 'turn-0' as MemoryId, kind: 'summarized-turn' as Kind, tags: ['old'] as Tag[] },\n { summary: 'keep me' }\n );\n expect(\n tagsOnly.applyUpdate(existing, { tags: ['new'], body: { summary: 'ignored' } })\n ).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.tags).toEqual(['new']);\n expect(updated.body).toEqual({ summary: 'keep me' });\n });\n });\n\n test('preserves an absent embeddingRef as absent on an unrelated update (hash-stable)', () => {\n const existing = memoryRecord('turn-0', 1, { summary: 's' });\n expect(policy.applyUpdate(existing, { body: { summary: 't' } })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBeUndefined();\n });\n });\n\n test('preserves embeddingRef when it is not in the declared mutable surface', () => {\n const narrow = MemoryCapCullPolicy.create({ maxRecords: 5, mutableFields: ['body'] }).orThrow();\n const existing = makeRecord({\n id: 'turn-0' as MemoryId,\n kind: 'summarized-turn' as Kind,\n embeddingRef: 'vec-keep'\n });\n expect(narrow.applyUpdate(existing, { body: { summary: 'x' } })).toSucceedAndSatisfy((updated) => {\n expect(updated.envelope.embeddingRef).toBe('vec-keep');\n });\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inMemoryCosineIndex.test.d.ts","sourceRoot":"","sources":["../../../../src/test/unit/vector/inMemoryCosineIndex.test.ts"],"names":[],"mappings":"AAKA,OAAO,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
require("@fgv/ts-utils-jest");
|
|
8
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
9
|
+
const index_1 = require("../../../index");
|
|
10
|
+
/** A trivial record carrying just the id + a marker body, for rebuild tests. */
|
|
11
|
+
function record(id, body = `body-${id}`) {
|
|
12
|
+
return {
|
|
13
|
+
// The cosine index only reads `envelope.id`; a minimal envelope suffices.
|
|
14
|
+
envelope: { id: id },
|
|
15
|
+
body
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** A scripted record source for `rebuild`. */
|
|
19
|
+
class FakeSource {
|
|
20
|
+
constructor(result) {
|
|
21
|
+
this._result = result;
|
|
22
|
+
}
|
|
23
|
+
list() {
|
|
24
|
+
return Promise.resolve(this._result);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
describe('InMemoryCosineIndex', () => {
|
|
28
|
+
describe('add', () => {
|
|
29
|
+
test('returns the id as the embedding ref and tracks size', async () => {
|
|
30
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
31
|
+
expect(index.size).toBe(0);
|
|
32
|
+
expect(await index.add('a', Float32Array.from([1, 0]))).toSucceedWith('a');
|
|
33
|
+
expect(index.size).toBe(1);
|
|
34
|
+
});
|
|
35
|
+
test('replaces the vector on a same-id add', async () => {
|
|
36
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
37
|
+
expect(await index.add('a', Float32Array.from([1, 0]))).toSucceed();
|
|
38
|
+
expect(await index.add('a', Float32Array.from([0, 1]))).toSucceed();
|
|
39
|
+
expect(index.size).toBe(1);
|
|
40
|
+
// The query now matches the replacement vector ([0,1]) not the original.
|
|
41
|
+
expect(await index.query(Float32Array.from([0, 1]), 1)).toSucceedAndSatisfy((hits) => {
|
|
42
|
+
expect(hits[0].score).toBeCloseTo(1);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
test('fails loudly on an empty vector', async () => {
|
|
46
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
47
|
+
expect(await index.add('a', new Float32Array(0))).toFailWith(/empty vector/i);
|
|
48
|
+
});
|
|
49
|
+
test('stores a defensive copy — mutating the caller buffer after add does not corrupt the index', async () => {
|
|
50
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
51
|
+
const buffer = Float32Array.from([1, 0]);
|
|
52
|
+
(await index.add('a', buffer)).orThrow();
|
|
53
|
+
// Mutate the caller's buffer after the add; the stored vector must be unaffected.
|
|
54
|
+
buffer[0] = 0;
|
|
55
|
+
buffer[1] = 1;
|
|
56
|
+
expect(await index.query(Float32Array.from([1, 0]), 1)).toSucceedAndSatisfy((hits) => {
|
|
57
|
+
// Still maximally similar to [1,0] — proves the index kept its own copy.
|
|
58
|
+
expect(hits[0].score).toBeCloseTo(1);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
test('fails loudly on a dimension mismatch against the established dimension', async () => {
|
|
62
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
63
|
+
expect(await index.add('a', Float32Array.from([1, 0]))).toSucceed();
|
|
64
|
+
expect(await index.add('b', Float32Array.from([1, 0, 0]))).toFailWith(/dimension 3 does not match index dimension 2/i);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('query', () => {
|
|
68
|
+
async function seeded() {
|
|
69
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
70
|
+
(await index.add('a', Float32Array.from([1, 0]))).orThrow();
|
|
71
|
+
(await index.add('b', Float32Array.from([0, 1]))).orThrow();
|
|
72
|
+
(await index.add('c', Float32Array.from([1, 1]))).orThrow();
|
|
73
|
+
return index;
|
|
74
|
+
}
|
|
75
|
+
test('returns hits in descending cosine-similarity order', async () => {
|
|
76
|
+
const index = await seeded();
|
|
77
|
+
expect(await index.query(Float32Array.from([1, 0]), 3)).toSucceedAndSatisfy((hits) => {
|
|
78
|
+
expect(hits.map((h) => h.id)).toEqual(['a', 'c', 'b']);
|
|
79
|
+
expect(hits[0].score).toBeCloseTo(1);
|
|
80
|
+
expect(hits[1].score).toBeCloseTo(1 / Math.sqrt(2));
|
|
81
|
+
expect(hits[2].score).toBeCloseTo(0);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
test('truncates to topK', async () => {
|
|
85
|
+
const index = await seeded();
|
|
86
|
+
expect(await index.query(Float32Array.from([1, 0]), 2)).toSucceedAndSatisfy((hits) => {
|
|
87
|
+
expect(hits.map((h) => h.id)).toEqual(['a', 'c']);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
test('returns empty for a non-positive topK', async () => {
|
|
91
|
+
const index = await seeded();
|
|
92
|
+
expect(await index.query(Float32Array.from([1, 0]), 0)).toSucceedWith([]);
|
|
93
|
+
expect(await index.query(Float32Array.from([1, 0]), -1)).toSucceedWith([]);
|
|
94
|
+
});
|
|
95
|
+
test('returns empty when the index is empty (no dimension check)', async () => {
|
|
96
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
97
|
+
expect(await index.query(Float32Array.from([1, 2, 3, 4]), 5)).toSucceedWith([]);
|
|
98
|
+
});
|
|
99
|
+
test('fails loudly on a query-dimension mismatch', async () => {
|
|
100
|
+
const index = await seeded();
|
|
101
|
+
expect(await index.query(Float32Array.from([1, 0, 0]), 3)).toFailWith(/query dimension 3 does not match index dimension 2/i);
|
|
102
|
+
});
|
|
103
|
+
test('scores a degenerate (zero-magnitude) stored vector as 0 rather than NaN', async () => {
|
|
104
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
105
|
+
(await index.add('zero', Float32Array.from([0, 0]))).orThrow();
|
|
106
|
+
(await index.add('unit', Float32Array.from([1, 0]))).orThrow();
|
|
107
|
+
expect(await index.query(Float32Array.from([1, 0]), 2)).toSucceedAndSatisfy((hits) => {
|
|
108
|
+
expect(hits.map((h) => h.id)).toEqual(['unit', 'zero']);
|
|
109
|
+
expect(hits[1].score).toBe(0);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
test('scores against a degenerate (zero-magnitude) query as 0 rather than NaN', async () => {
|
|
113
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
114
|
+
(await index.add('unit', Float32Array.from([1, 0]))).orThrow();
|
|
115
|
+
expect(await index.query(Float32Array.from([0, 0]), 1)).toSucceedAndSatisfy((hits) => {
|
|
116
|
+
expect(hits[0].score).toBe(0);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('remove', () => {
|
|
121
|
+
test('removes a vector and is reflected in subsequent queries', async () => {
|
|
122
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
123
|
+
(await index.add('a', Float32Array.from([1, 0]))).orThrow();
|
|
124
|
+
(await index.add('b', Float32Array.from([0, 1]))).orThrow();
|
|
125
|
+
expect(await index.remove('a')).toSucceedWith('a');
|
|
126
|
+
expect(index.size).toBe(1);
|
|
127
|
+
expect(await index.query(Float32Array.from([1, 0]), 5)).toSucceedAndSatisfy((hits) => {
|
|
128
|
+
expect(hits.map((h) => h.id)).toEqual(['b']);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
test('is idempotent — removing an absent id still succeeds', async () => {
|
|
132
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
133
|
+
expect(await index.remove('missing')).toSucceedWith('missing');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('rebuild', () => {
|
|
137
|
+
const embed = (r) => {
|
|
138
|
+
// Deterministic: encode the id's first char code into a 2-vector.
|
|
139
|
+
const code = r.envelope.id.charCodeAt(0);
|
|
140
|
+
return Promise.resolve((0, ts_utils_1.succeed)(Float32Array.from([code, 1])));
|
|
141
|
+
};
|
|
142
|
+
test('re-embeds every record from the source and reports the count', async () => {
|
|
143
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
144
|
+
const source = new FakeSource((0, ts_utils_1.succeed)([record('a'), record('b'), record('c')]));
|
|
145
|
+
expect(await index.rebuild(source, embed)).toSucceedWith(3);
|
|
146
|
+
expect(index.size).toBe(3);
|
|
147
|
+
expect(await index.query(Float32Array.from([99, 1]), 1)).toSucceedAndSatisfy((hits) => {
|
|
148
|
+
// 'c' = char code 99, the closest to the query vector.
|
|
149
|
+
expect(hits[0].id).toBe('c');
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
test('clears prior contents and re-establishes the dimension', async () => {
|
|
153
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
154
|
+
// Seed a 3-dim vector, then rebuild with a 2-dim embedder.
|
|
155
|
+
(await index.add('old', Float32Array.from([1, 2, 3]))).orThrow();
|
|
156
|
+
const source = new FakeSource((0, ts_utils_1.succeed)([record('a')]));
|
|
157
|
+
expect(await index.rebuild(source, embed)).toSucceedWith(1);
|
|
158
|
+
expect(index.size).toBe(1);
|
|
159
|
+
// The 2-dim query now succeeds (dimension was reset by rebuild).
|
|
160
|
+
expect(await index.query(Float32Array.from([97, 1]), 1)).toSucceed();
|
|
161
|
+
});
|
|
162
|
+
test('fails loudly when the source list fails', async () => {
|
|
163
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
164
|
+
// Seed an entry so the reset-before-list rollback is observable, not just "stayed empty".
|
|
165
|
+
(await index.add('seed', Float32Array.from([1, 1]))).orThrow();
|
|
166
|
+
const source = new FakeSource((0, ts_utils_1.fail)('disk gone'));
|
|
167
|
+
expect(await index.rebuild(source, embed)).toFailWith(/failed to list records: disk gone/i);
|
|
168
|
+
expect(index.size).toBe(0);
|
|
169
|
+
});
|
|
170
|
+
test('fails loudly and rolls back to empty when an embedding fails mid-rebuild', async () => {
|
|
171
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
172
|
+
// Seed an existing entry so the rollback (not merely "stayed empty") is observable.
|
|
173
|
+
(await index.add('seed', Float32Array.from([1, 1]))).orThrow();
|
|
174
|
+
// First record embeds fine, the second fails — so a naive impl would leave
|
|
175
|
+
// record 'a' indexed; the rollback must clear it.
|
|
176
|
+
let calls = 0;
|
|
177
|
+
const flakyEmbed = (r) => {
|
|
178
|
+
calls += 1;
|
|
179
|
+
return Promise.resolve(calls === 1 ? (0, ts_utils_1.succeed)(Float32Array.from([1, 1])) : (0, ts_utils_1.fail)('no model'));
|
|
180
|
+
};
|
|
181
|
+
const source = new FakeSource((0, ts_utils_1.succeed)([record('a'), record('b')]));
|
|
182
|
+
expect(await index.rebuild(source, flakyEmbed)).toFailWith(/embedding 'b' failed: no model/i);
|
|
183
|
+
expect(index.size).toBe(0);
|
|
184
|
+
});
|
|
185
|
+
test('fails loudly and rolls back to empty when adding an embedded vector fails', async () => {
|
|
186
|
+
const index = index_1.InMemoryCosineIndex.create().orThrow();
|
|
187
|
+
const source = new FakeSource((0, ts_utils_1.succeed)([record('a')]));
|
|
188
|
+
const emptyEmbed = () => Promise.resolve((0, ts_utils_1.succeed)(new Float32Array(0)));
|
|
189
|
+
expect(await index.rebuild(source, emptyEmbed)).toFailWith(/empty vector/i);
|
|
190
|
+
expect(index.size).toBe(0);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=inMemoryCosineIndex.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inMemoryCosineIndex.test.js","sourceRoot":"","sources":["../../../../src/test/unit/vector/inMemoryCosineIndex.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport {\n IMemoryRecord,\n IMemoryRecordSource,\n IVectorQueryHit,\n InMemoryCosineIndex,\n MemoryId\n} from '../../../index';\n\n/** A trivial record carrying just the id + a marker body, for rebuild tests. */\nfunction record(id: string, body: string = `body-${id}`): IMemoryRecord<unknown> {\n return {\n // The cosine index only reads `envelope.id`; a minimal envelope suffices.\n envelope: { id: id as MemoryId } as IMemoryRecord<unknown>['envelope'],\n body\n };\n}\n\n/** A scripted record source for `rebuild`. */\nclass FakeSource implements IMemoryRecordSource {\n private readonly _result: Result<ReadonlyArray<IMemoryRecord<unknown>>>;\n public constructor(result: Result<ReadonlyArray<IMemoryRecord<unknown>>>) {\n this._result = result;\n }\n public list(): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(this._result);\n }\n}\n\ndescribe('InMemoryCosineIndex', () => {\n describe('add', () => {\n test('returns the id as the embedding ref and tracks size', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(index.size).toBe(0);\n expect(await index.add('a' as MemoryId, Float32Array.from([1, 0]))).toSucceedWith('a');\n expect(index.size).toBe(1);\n });\n\n test('replaces the vector on a same-id add', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(await index.add('a' as MemoryId, Float32Array.from([1, 0]))).toSucceed();\n expect(await index.add('a' as MemoryId, Float32Array.from([0, 1]))).toSucceed();\n expect(index.size).toBe(1);\n // The query now matches the replacement vector ([0,1]) not the original.\n expect(await index.query(Float32Array.from([0, 1]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits[0].score).toBeCloseTo(1);\n }\n );\n });\n\n test('fails loudly on an empty vector', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(await index.add('a' as MemoryId, new Float32Array(0))).toFailWith(/empty vector/i);\n });\n\n test('stores a defensive copy — mutating the caller buffer after add does not corrupt the index', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n const buffer = Float32Array.from([1, 0]);\n (await index.add('a' as MemoryId, buffer)).orThrow();\n // Mutate the caller's buffer after the add; the stored vector must be unaffected.\n buffer[0] = 0;\n buffer[1] = 1;\n expect(await index.query(Float32Array.from([1, 0]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n // Still maximally similar to [1,0] — proves the index kept its own copy.\n expect(hits[0].score).toBeCloseTo(1);\n }\n );\n });\n\n test('fails loudly on a dimension mismatch against the established dimension', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(await index.add('a' as MemoryId, Float32Array.from([1, 0]))).toSucceed();\n expect(await index.add('b' as MemoryId, Float32Array.from([1, 0, 0]))).toFailWith(\n /dimension 3 does not match index dimension 2/i\n );\n });\n });\n\n describe('query', () => {\n async function seeded(): Promise<InMemoryCosineIndex> {\n const index = InMemoryCosineIndex.create().orThrow();\n (await index.add('a' as MemoryId, Float32Array.from([1, 0]))).orThrow();\n (await index.add('b' as MemoryId, Float32Array.from([0, 1]))).orThrow();\n (await index.add('c' as MemoryId, Float32Array.from([1, 1]))).orThrow();\n return index;\n }\n\n test('returns hits in descending cosine-similarity order', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 3)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.id)).toEqual(['a', 'c', 'b']);\n expect(hits[0].score).toBeCloseTo(1);\n expect(hits[1].score).toBeCloseTo(1 / Math.sqrt(2));\n expect(hits[2].score).toBeCloseTo(0);\n }\n );\n });\n\n test('truncates to topK', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 2)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.id)).toEqual(['a', 'c']);\n }\n );\n });\n\n test('returns empty for a non-positive topK', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0]), 0)).toSucceedWith([]);\n expect(await index.query(Float32Array.from([1, 0]), -1)).toSucceedWith([]);\n });\n\n test('returns empty when the index is empty (no dimension check)', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(await index.query(Float32Array.from([1, 2, 3, 4]), 5)).toSucceedWith([]);\n });\n\n test('fails loudly on a query-dimension mismatch', async () => {\n const index = await seeded();\n expect(await index.query(Float32Array.from([1, 0, 0]), 3)).toFailWith(\n /query dimension 3 does not match index dimension 2/i\n );\n });\n\n test('scores a degenerate (zero-magnitude) stored vector as 0 rather than NaN', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n (await index.add('zero' as MemoryId, Float32Array.from([0, 0]))).orThrow();\n (await index.add('unit' as MemoryId, Float32Array.from([1, 0]))).orThrow();\n expect(await index.query(Float32Array.from([1, 0]), 2)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.id)).toEqual(['unit', 'zero']);\n expect(hits[1].score).toBe(0);\n }\n );\n });\n\n test('scores against a degenerate (zero-magnitude) query as 0 rather than NaN', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n (await index.add('unit' as MemoryId, Float32Array.from([1, 0]))).orThrow();\n expect(await index.query(Float32Array.from([0, 0]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits[0].score).toBe(0);\n }\n );\n });\n });\n\n describe('remove', () => {\n test('removes a vector and is reflected in subsequent queries', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n (await index.add('a' as MemoryId, Float32Array.from([1, 0]))).orThrow();\n (await index.add('b' as MemoryId, Float32Array.from([0, 1]))).orThrow();\n expect(await index.remove('a' as MemoryId)).toSucceedWith('a' as MemoryId);\n expect(index.size).toBe(1);\n expect(await index.query(Float32Array.from([1, 0]), 5)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits.map((h) => h.id)).toEqual(['b']);\n }\n );\n });\n\n test('is idempotent — removing an absent id still succeeds', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n expect(await index.remove('missing' as MemoryId)).toSucceedWith('missing' as MemoryId);\n });\n });\n\n describe('rebuild', () => {\n const embed = (r: IMemoryRecord<unknown>): Promise<Result<Float32Array>> => {\n // Deterministic: encode the id's first char code into a 2-vector.\n const code: number = (r.envelope.id as string).charCodeAt(0);\n return Promise.resolve(succeed(Float32Array.from([code, 1])));\n };\n\n test('re-embeds every record from the source and reports the count', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n const source = new FakeSource(succeed([record('a'), record('b'), record('c')]));\n expect(await index.rebuild(source, embed)).toSucceedWith(3);\n expect(index.size).toBe(3);\n expect(await index.query(Float32Array.from([99, 1]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n // 'c' = char code 99, the closest to the query vector.\n expect(hits[0].id).toBe('c');\n }\n );\n });\n\n test('clears prior contents and re-establishes the dimension', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n // Seed a 3-dim vector, then rebuild with a 2-dim embedder.\n (await index.add('old' as MemoryId, Float32Array.from([1, 2, 3]))).orThrow();\n const source = new FakeSource(succeed([record('a')]));\n expect(await index.rebuild(source, embed)).toSucceedWith(1);\n expect(index.size).toBe(1);\n // The 2-dim query now succeeds (dimension was reset by rebuild).\n expect(await index.query(Float32Array.from([97, 1]), 1)).toSucceed();\n });\n\n test('fails loudly when the source list fails', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n // Seed an entry so the reset-before-list rollback is observable, not just \"stayed empty\".\n (await index.add('seed' as MemoryId, Float32Array.from([1, 1]))).orThrow();\n const source = new FakeSource(fail('disk gone'));\n expect(await index.rebuild(source, embed)).toFailWith(/failed to list records: disk gone/i);\n expect(index.size).toBe(0);\n });\n\n test('fails loudly and rolls back to empty when an embedding fails mid-rebuild', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n // Seed an existing entry so the rollback (not merely \"stayed empty\") is observable.\n (await index.add('seed' as MemoryId, Float32Array.from([1, 1]))).orThrow();\n // First record embeds fine, the second fails — so a naive impl would leave\n // record 'a' indexed; the rollback must clear it.\n let calls: number = 0;\n const flakyEmbed = (r: IMemoryRecord<unknown>): Promise<Result<Float32Array>> => {\n calls += 1;\n return Promise.resolve(calls === 1 ? succeed(Float32Array.from([1, 1])) : fail('no model'));\n };\n const source = new FakeSource(succeed([record('a'), record('b')]));\n expect(await index.rebuild(source, flakyEmbed)).toFailWith(/embedding 'b' failed: no model/i);\n expect(index.size).toBe(0);\n });\n\n test('fails loudly and rolls back to empty when adding an embedded vector fails', async () => {\n const index = InMemoryCosineIndex.create().orThrow();\n const source = new FakeSource(succeed([record('a')]));\n const emptyEmbed = (): Promise<Result<Float32Array>> => Promise.resolve(succeed(new Float32Array(0)));\n expect(await index.rebuild(source, emptyEmbed)).toFailWith(/empty vector/i);\n expect(index.size).toBe(0);\n });\n });\n});\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectorIndex.test.d.ts","sourceRoot":"","sources":["../../../../src/test/unit/vector/vectorIndex.test.ts"],"names":[],"mappings":"AAKA,OAAO,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2026 Erik Fortune
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
require("@fgv/ts-utils-jest");
|
|
8
|
+
const ts_utils_1 = require("@fgv/ts-utils");
|
|
9
|
+
/**
|
|
10
|
+
* A trivial conforming implementation, present to verify the {@link IVectorIndex}
|
|
11
|
+
* seam is usable end-to-end (the in-package cosine impl is a post-v1 fast-follow).
|
|
12
|
+
*/
|
|
13
|
+
class StubVectorIndex {
|
|
14
|
+
constructor() {
|
|
15
|
+
this._vectors = new Map();
|
|
16
|
+
}
|
|
17
|
+
add(id, vector) {
|
|
18
|
+
this._vectors.set(id, vector);
|
|
19
|
+
return Promise.resolve((0, ts_utils_1.succeed)(`ref-${id}`));
|
|
20
|
+
}
|
|
21
|
+
remove(id) {
|
|
22
|
+
this._vectors.delete(id);
|
|
23
|
+
return Promise.resolve((0, ts_utils_1.succeed)(id));
|
|
24
|
+
}
|
|
25
|
+
query(vector, topK) {
|
|
26
|
+
const hits = Array.from(this._vectors.keys())
|
|
27
|
+
.map((id) => ({ id, score: vector.length }))
|
|
28
|
+
.slice(0, topK);
|
|
29
|
+
return Promise.resolve((0, ts_utils_1.succeed)(hits));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
describe('IVectorIndex seam', () => {
|
|
33
|
+
test('a conforming implementation supports add / query / remove', async () => {
|
|
34
|
+
const index = new StubVectorIndex();
|
|
35
|
+
expect(await index.add('a', Float32Array.from([1, 2, 3]))).toSucceedWith('ref-a');
|
|
36
|
+
expect(await index.add('b', Float32Array.from([4, 5, 6]))).toSucceedWith('ref-b');
|
|
37
|
+
expect(await index.query(Float32Array.from([0.1, 0.2]), 1)).toSucceedAndSatisfy((hits) => {
|
|
38
|
+
expect(hits).toHaveLength(1);
|
|
39
|
+
expect(hits[0].score).toBe(2);
|
|
40
|
+
});
|
|
41
|
+
expect(await index.remove('a')).toSucceedWith('a');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=vectorIndex.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vectorIndex.test.js","sourceRoot":"","sources":["../../../../src/test/unit/vector/vectorIndex.test.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAEH,8BAA4B;AAC5B,4CAAgD;AAGhD;;;GAGG;AACH,MAAM,eAAe;IAArB;QACmB,aAAQ,GAAgC,IAAI,GAAG,EAAE,CAAC;IAkBrE,CAAC;IAhBQ,GAAG,CAAC,EAAY,EAAE,MAAoB;QAC3C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,IAAA,kBAAO,EAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IAEM,MAAM,CAAC,EAAY;QACxB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAA,kBAAO,EAAC,EAAE,CAAC,CAAC,CAAC;IACtC,CAAC;IAEM,KAAK,CAAC,MAAoB,EAAE,IAAY;QAC7C,MAAM,IAAI,GAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;aAC7D,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;aAC3C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAClB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAA,kBAAO,EAAC,IAAI,CAAC,CAAC,CAAC;IACxC,CAAC;CACF;AAED,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;QACpC,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAe,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9F,MAAM,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAe,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC9F,MAAM,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAC7E,CAAC,IAAoC,EAAE,EAAE;YACvC,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CACF,CAAC;QACF,MAAM,CAAC,MAAM,KAAK,CAAC,MAAM,CAAC,GAAe,CAAC,CAAC,CAAC,aAAa,CAAC,GAAe,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport '@fgv/ts-utils-jest';\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IVectorIndex, IVectorQueryHit, MemoryId } from '../../../index';\n\n/**\n * A trivial conforming implementation, present to verify the {@link IVectorIndex}\n * seam is usable end-to-end (the in-package cosine impl is a post-v1 fast-follow).\n */\nclass StubVectorIndex implements IVectorIndex {\n private readonly _vectors: Map<MemoryId, Float32Array> = new Map();\n\n public add(id: MemoryId, vector: Float32Array): Promise<Result<string>> {\n this._vectors.set(id, vector);\n return Promise.resolve(succeed(`ref-${id}`));\n }\n\n public remove(id: MemoryId): Promise<Result<MemoryId>> {\n this._vectors.delete(id);\n return Promise.resolve(succeed(id));\n }\n\n public query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>> {\n const hits: IVectorQueryHit[] = Array.from(this._vectors.keys())\n .map((id) => ({ id, score: vector.length }))\n .slice(0, topK);\n return Promise.resolve(succeed(hits));\n }\n}\n\ndescribe('IVectorIndex seam', () => {\n test('a conforming implementation supports add / query / remove', async () => {\n const index = new StubVectorIndex();\n expect(await index.add('a' as MemoryId, Float32Array.from([1, 2, 3]))).toSucceedWith('ref-a');\n expect(await index.add('b' as MemoryId, Float32Array.from([4, 5, 6]))).toSucceedWith('ref-b');\n expect(await index.query(Float32Array.from([0.1, 0.2]), 1)).toSucceedAndSatisfy(\n (hits: ReadonlyArray<IVectorQueryHit>) => {\n expect(hits).toHaveLength(1);\n expect(hits[0].score).toBe(2);\n }\n );\n expect(await index.remove('a' as MemoryId)).toSucceedWith('a' as MemoryId);\n });\n});\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fgv/ts-agent-memory",
|
|
3
|
+
"version": "5.1.0-36",
|
|
4
|
+
"description": "FileTree-backed storage and retrieval substrate for agent memory and knowledge",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "dist/ts-agent-memory.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"node": {
|
|
10
|
+
"import": "./lib/index.js",
|
|
11
|
+
"require": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"default": "./lib/index.js",
|
|
14
|
+
"types": "./dist/ts-agent-memory.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "heft build --clean",
|
|
19
|
+
"clean": "heft clean",
|
|
20
|
+
"test": "heft test --clean",
|
|
21
|
+
"test-handles": "jest --runInBand --detectOpenHandles",
|
|
22
|
+
"clean-jest": "jest --clear-cache",
|
|
23
|
+
"coverage": "jest --coverage",
|
|
24
|
+
"lint": "eslint src --ext .ts",
|
|
25
|
+
"fixlint": "eslint src --ext .ts --fix"
|
|
26
|
+
},
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"keywords": [
|
|
29
|
+
"typescript",
|
|
30
|
+
"memory",
|
|
31
|
+
"knowledge",
|
|
32
|
+
"agent",
|
|
33
|
+
"filetree",
|
|
34
|
+
"vault"
|
|
35
|
+
],
|
|
36
|
+
"author": "Erik Fortune",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/ErikFortune/fgv/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/ErikFortune/fgv/tree/main/libraries/ts-agent-memory#readme",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/ErikFortune/fgv.git"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"tslib": "^2.8.1",
|
|
48
|
+
"@fgv/ts-extras": "workspace:*",
|
|
49
|
+
"@fgv/ts-json": "workspace:*",
|
|
50
|
+
"@fgv/ts-json-base": "workspace:*",
|
|
51
|
+
"@fgv/ts-utils": "workspace:*"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@fgv/heft-dual-rig": "workspace:*",
|
|
55
|
+
"@fgv/ts-utils-jest": "workspace:*",
|
|
56
|
+
"@microsoft/api-extractor": "^7.55.2",
|
|
57
|
+
"@types/jest": "^29.5.14",
|
|
58
|
+
"@types/node": "^20.14.9",
|
|
59
|
+
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
|
60
|
+
"@typescript-eslint/parser": "^8.52.0",
|
|
61
|
+
"eslint": "^9.39.2",
|
|
62
|
+
"eslint-config-prettier": "^10.1.8",
|
|
63
|
+
"eslint-plugin-import": "^2.32.0",
|
|
64
|
+
"eslint-plugin-node": "^11.1.0",
|
|
65
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
66
|
+
"eslint-plugin-promise": "^7.2.1",
|
|
67
|
+
"jest": "^29.7.0",
|
|
68
|
+
"prettier": "^3.7.4",
|
|
69
|
+
"rimraf": "^6.1.2",
|
|
70
|
+
"ts-jest": "^29.4.6",
|
|
71
|
+
"ts-node": "^10.9.2",
|
|
72
|
+
"typescript": "5.9.3",
|
|
73
|
+
"eslint-plugin-n": "^17.23.1",
|
|
74
|
+
"@rushstack/eslint-config": "4.6.4",
|
|
75
|
+
"@rushstack/heft": "1.2.7",
|
|
76
|
+
"@rushstack/heft-jest-plugin": "1.2.6",
|
|
77
|
+
"@rushstack/heft-node-rig": "2.11.27",
|
|
78
|
+
"@types/heft-jest": "1.0.6",
|
|
79
|
+
"eslint-plugin-tsdoc": "~0.5.2"
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Invoking: heft build --clean
|
|
2
|
+
---- build started ----
|
|
3
|
+
[build:typescript] The TypeScript compiler version 5.9.3 is newer than the latest version that was tested with Heft (5.8); it may not work correctly.
|
|
4
|
+
[build:typescript] Using TypeScript version 5.9.3
|
|
5
|
+
[build:lint] Using ESLint version 9.39.4
|
|
6
|
+
[build:api-extractor] Using API Extractor version 7.58.7
|
|
7
|
+
[build:api-extractor] Analysis will use the bundled TypeScript version 5.9.3
|
|
8
|
+
---- build finished (5.889s) ----
|
|
9
|
+
-------------------- Finished (5.89s) --------------------
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Erik Fortune
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from './packlets/types';
|
|
7
|
+
export * from './packlets/converters';
|
|
8
|
+
export * from './packlets/store';
|
|
9
|
+
export * from './packlets/index';
|
|
10
|
+
export * from './packlets/retrieve';
|
|
11
|
+
export * from './packlets/observe';
|
|
12
|
+
export * from './packlets/vector';
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2026 Erik Fortune
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Converter, Converters, Result, fail, succeed } from '@fgv/ts-utils';
|
|
7
|
+
import { JsonSchema } from '@fgv/ts-json-base';
|
|
8
|
+
import { Kind } from '../types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Registry of per-kind body Converters. Each memory {@link Kind} registers the
|
|
12
|
+
* validated shape of its body; the store dispatches an `unknown` body through
|
|
13
|
+
* the registered Converter on every write. Type-safe — no `any`.
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export interface IBodyConverterRegistry {
|
|
17
|
+
/**
|
|
18
|
+
* Register a Converter for a kind. Replaces any prior registration for the
|
|
19
|
+
* same kind.
|
|
20
|
+
*/
|
|
21
|
+
register<T>(kind: Kind, converter: Converter<T>): void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register a `JsonSchema` validator (from `@fgv/ts-json-base`) for a kind —
|
|
25
|
+
* the schema IS a Validator. Replaces any prior registration for the same
|
|
26
|
+
* kind.
|
|
27
|
+
*/
|
|
28
|
+
registerSchema<T>(kind: Kind, schema: JsonSchema.ISchemaValidator<T>): void;
|
|
29
|
+
|
|
30
|
+
/** Returns `true` if a converter is registered for the kind. */
|
|
31
|
+
has(kind: Kind): boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the raw converter registered for a kind. Fails if the kind is
|
|
35
|
+
* unregistered. Exposed for implementors that need to re-validate a patched
|
|
36
|
+
* body (e.g. a temporal-versioned policy's `applyUpdate`).
|
|
37
|
+
*/
|
|
38
|
+
getConverter(kind: Kind): Result<Converter<unknown>>;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert an `unknown` body value for the given kind. Fails with
|
|
42
|
+
* `no converter registered for kind '<k>'` when unregistered.
|
|
43
|
+
*/
|
|
44
|
+
convert(kind: Kind, body: unknown): Result<unknown>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Default in-memory {@link IBodyConverterRegistry}.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* Both `register` and `registerSchema` adapt the supplied converter/validator
|
|
52
|
+
* into a `Converter<unknown>` via `Converters.generic`, so the heterogeneous
|
|
53
|
+
* per-kind types are stored uniformly with no cast and no `any`. A
|
|
54
|
+
* `Converter<T>` is not structurally a `Converter<unknown>` (the `map` callback
|
|
55
|
+
* makes the type invariant), so the `generic` wrapper — whose callback returns
|
|
56
|
+
* `Result<T>` (assignable to `Result<unknown>`) — is the type-safe bridge.
|
|
57
|
+
* @public
|
|
58
|
+
*/
|
|
59
|
+
export class BodyConverterRegistry implements IBodyConverterRegistry {
|
|
60
|
+
private readonly _converters: Map<Kind, Converter<unknown>>;
|
|
61
|
+
|
|
62
|
+
private constructor() {
|
|
63
|
+
this._converters = new Map<Kind, Converter<unknown>>();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Family-convention factory. */
|
|
67
|
+
public static create(): Result<BodyConverterRegistry> {
|
|
68
|
+
return succeed(new BodyConverterRegistry());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** {@inheritDoc IBodyConverterRegistry.register} */
|
|
72
|
+
public register<T>(kind: Kind, converter: Converter<T>): void {
|
|
73
|
+
this._converters.set(
|
|
74
|
+
kind,
|
|
75
|
+
Converters.generic<unknown>((from: unknown) => converter.convert(from))
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** {@inheritDoc IBodyConverterRegistry.registerSchema} */
|
|
80
|
+
public registerSchema<T>(kind: Kind, schema: JsonSchema.ISchemaValidator<T>): void {
|
|
81
|
+
this._converters.set(
|
|
82
|
+
kind,
|
|
83
|
+
Converters.generic<unknown>((from: unknown) => schema.convert(from))
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** {@inheritDoc IBodyConverterRegistry.has} */
|
|
88
|
+
public has(kind: Kind): boolean {
|
|
89
|
+
return this._converters.has(kind);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** {@inheritDoc IBodyConverterRegistry.getConverter} */
|
|
93
|
+
public getConverter(kind: Kind): Result<Converter<unknown>> {
|
|
94
|
+
const converter: Converter<unknown> | undefined = this._converters.get(kind);
|
|
95
|
+
if (converter === undefined) {
|
|
96
|
+
return fail(`no converter registered for kind '${kind}'`);
|
|
97
|
+
}
|
|
98
|
+
return succeed(converter);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** {@inheritDoc IBodyConverterRegistry.convert} */
|
|
102
|
+
public convert(kind: Kind, body: unknown): Result<unknown> {
|
|
103
|
+
return this.getConverter(kind).onSuccess((converter) => converter.convert(body));
|
|
104
|
+
}
|
|
105
|
+
}
|