@fgv/ts-agent-memory 5.1.0-45 → 5.1.0-47
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/{bc5b0f2098f7df2a5c498ea77b0de13316349b96.tar.log → 5667e36f1c0cc503a5c20a3ebff76e7b9a702d41.tar.log} +26 -2
- package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +2 -2
- package/.rush/temp/operation/build/all.log +2 -2
- package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
- package/.rush/temp/operation/build/state.json +1 -1
- package/README.md +134 -4
- package/dist/packlets/ingest/orchestrator.js +104 -19
- package/dist/packlets/ingest/orchestrator.js.map +1 -1
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js +10 -7
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
- package/dist/packlets/store/fileTreeMemoryStore.js +31 -8
- package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
- package/dist/packlets/types/writePolicy.js +10 -2
- package/dist/packlets/types/writePolicy.js.map +1 -1
- package/dist/packlets/vector/fragmentConverters.js +41 -0
- package/dist/packlets/vector/fragmentConverters.js.map +1 -0
- package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +17 -9
- package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
- package/dist/packlets/vector/index.js +1 -0
- package/dist/packlets/vector/index.js.map +1 -1
- package/dist/packlets/vector/vectorIndex.js.map +1 -1
- package/dist/test/unit/ingest/dedupScope.test.js +434 -0
- package/dist/test/unit/ingest/dedupScope.test.js.map +1 -0
- package/dist/test/unit/ingest/orchestrator.test.js +7 -4
- package/dist/test/unit/ingest/orchestrator.test.js.map +1 -1
- package/dist/test/unit/store/fileTreeMemoryStore.test.js +64 -0
- package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
- package/dist/test/unit/store/indexInjection.test.js +432 -0
- package/dist/test/unit/store/indexInjection.test.js.map +1 -0
- package/dist/test/unit/tools/memoryTools.test.js +1 -0
- package/dist/test/unit/tools/memoryTools.test.js.map +1 -1
- package/dist/test/unit/types/temporalPolicy.test.js +21 -0
- package/dist/test/unit/types/temporalPolicy.test.js.map +1 -1
- package/dist/test/unit/types/writePolicy.test.js +83 -0
- package/dist/test/unit/types/writePolicy.test.js.map +1 -1
- package/dist/test/unit/vector/fragmentConverters.test.js +58 -0
- package/dist/test/unit/vector/fragmentConverters.test.js.map +1 -0
- package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js +65 -0
- package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +1 -1
- package/dist/ts-agent-memory.d.ts +332 -42
- package/etc/ts-agent-memory.api.md +12 -1
- package/lib/packlets/ingest/orchestrator.d.ts +62 -5
- package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
- package/lib/packlets/ingest/orchestrator.js +104 -19
- package/lib/packlets/ingest/orchestrator.js.map +1 -1
- package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts +10 -7
- package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/fragmentSemanticRetriever.js +10 -7
- package/lib/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
- package/lib/packlets/store/fileTreeMemoryStore.d.ts +96 -4
- package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
- package/lib/packlets/store/fileTreeMemoryStore.js +31 -8
- package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
- package/lib/packlets/types/writePolicy.d.ts +40 -2
- package/lib/packlets/types/writePolicy.d.ts.map +1 -1
- package/lib/packlets/types/writePolicy.js +10 -2
- package/lib/packlets/types/writePolicy.js.map +1 -1
- package/lib/packlets/vector/fragmentConverters.d.ts +25 -0
- package/lib/packlets/vector/fragmentConverters.d.ts.map +1 -0
- package/lib/packlets/vector/fragmentConverters.js +44 -0
- package/lib/packlets/vector/fragmentConverters.js.map +1 -0
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +5 -3
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +17 -9
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
- package/lib/packlets/vector/index.d.ts +1 -0
- package/lib/packlets/vector/index.d.ts.map +1 -1
- package/lib/packlets/vector/index.js +1 -0
- package/lib/packlets/vector/index.js.map +1 -1
- package/lib/packlets/vector/vectorIndex.d.ts +97 -22
- package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
- package/lib/packlets/vector/vectorIndex.js.map +1 -1
- package/lib/test/unit/ingest/dedupScope.test.d.ts +2 -0
- package/lib/test/unit/ingest/dedupScope.test.d.ts.map +1 -0
- package/lib/test/unit/ingest/dedupScope.test.js +436 -0
- package/lib/test/unit/ingest/dedupScope.test.js.map +1 -0
- package/lib/test/unit/ingest/orchestrator.test.js +7 -4
- package/lib/test/unit/ingest/orchestrator.test.js.map +1 -1
- package/lib/test/unit/store/fileTreeMemoryStore.test.js +64 -0
- package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
- package/lib/test/unit/store/indexInjection.test.d.ts +2 -0
- package/lib/test/unit/store/indexInjection.test.d.ts.map +1 -0
- package/lib/test/unit/store/indexInjection.test.js +434 -0
- package/lib/test/unit/store/indexInjection.test.js.map +1 -0
- package/lib/test/unit/tools/memoryTools.test.js +1 -0
- package/lib/test/unit/tools/memoryTools.test.js.map +1 -1
- package/lib/test/unit/types/temporalPolicy.test.js +21 -0
- package/lib/test/unit/types/temporalPolicy.test.js.map +1 -1
- package/lib/test/unit/types/writePolicy.test.js +83 -0
- package/lib/test/unit/types/writePolicy.test.js.map +1 -1
- package/lib/test/unit/vector/fragmentConverters.test.d.ts +2 -0
- package/lib/test/unit/vector/fragmentConverters.test.d.ts.map +1 -0
- package/lib/test/unit/vector/fragmentConverters.test.js +60 -0
- package/lib/test/unit/vector/fragmentConverters.test.js.map +1 -0
- package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js +65 -0
- package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +1 -1
- package/package.json +7 -7
- package/rush-logs/ts-agent-memory.build.cache.log +1 -1
- package/rush-logs/ts-agent-memory.build.log +2 -2
- package/src/packlets/ingest/orchestrator.ts +116 -21
- package/src/packlets/retrieve/fragmentSemanticRetriever.ts +10 -7
- package/src/packlets/store/fileTreeMemoryStore.ts +114 -6
- package/src/packlets/types/writePolicy.ts +40 -2
- package/src/packlets/vector/fragmentConverters.ts +55 -0
- package/src/packlets/vector/inMemoryFragmentCosineIndex.ts +35 -8
- package/src/packlets/vector/index.ts +1 -0
- package/src/packlets/vector/vectorIndex.ts +97 -22
- package/src/test/unit/ingest/dedupScope.test.ts +542 -0
- package/src/test/unit/ingest/orchestrator.test.ts +4 -0
- package/src/test/unit/store/fileTreeMemoryStore.test.ts +75 -0
- package/src/test/unit/store/indexInjection.test.ts +550 -0
- package/src/test/unit/tools/memoryTools.test.ts +1 -0
- package/src/test/unit/types/temporalPolicy.test.ts +28 -0
- package/src/test/unit/types/writePolicy.test.ts +112 -0
- package/src/test/unit/vector/fragmentConverters.test.ts +80 -0
- package/src/test/unit/vector/inMemoryFragmentCosineIndex.test.ts +88 -0
- package/temp/build/lint/_eslint-5eVG3S6w.json +30 -14
- package/temp/build/typescript/ts_8nwakTlr.json +1 -1
- package/temp/ts-agent-memory.api.json +274 -21
- package/temp/ts-agent-memory.api.md +12 -1
|
@@ -17,16 +17,19 @@ exports.FRAGMENT_SEMANTIC_UNWIRED_MESSAGE = 'fragment recall: no fragment index
|
|
|
17
17
|
* The sub-document semantic-search retriever — the "discovery" half of a
|
|
18
18
|
* search-then-read contract. It embeds a fragment query, queries the
|
|
19
19
|
* {@link IFragmentVectorIndex}, and returns the raw per-fragment
|
|
20
|
-
* {@link IVectorQueryHit | hits} (each carrying a record `target` AND
|
|
21
|
-
* `locator`
|
|
22
|
-
*
|
|
20
|
+
* {@link IVectorQueryHit | hits} (each carrying a record `target` AND whichever of
|
|
21
|
+
* `locator` / `fragmentId` the stored fragment was indexed with), NOT resolved
|
|
22
|
+
* records: the consumer re-reads each record and resolves the fragment on its own
|
|
23
|
+
* read side. Note the `locator` span is advisory — see {@link IFragmentLocator}; it
|
|
24
|
+
* is not a slice guaranteed to reproduce the fragment's text.
|
|
23
25
|
*
|
|
24
26
|
* @remarks
|
|
25
27
|
* Deliberately NOT an {@link IMemoryRetriever}: memory recall is record-granular and
|
|
26
|
-
* returns records; fragment discovery is
|
|
27
|
-
* it a distinct surface matches the consumer contract (memory
|
|
28
|
-
* sub-document knowledge uses a separate fragment index) and
|
|
29
|
-
* record retriever's return type with
|
|
28
|
+
* returns records; fragment discovery is fragment-granular and returns fragment
|
|
29
|
+
* identities. Keeping it a distinct surface matches the consumer contract (memory
|
|
30
|
+
* stays record-granular; sub-document knowledge uses a separate fragment index) and
|
|
31
|
+
* avoids overloading the record retriever's return type with identity fields that
|
|
32
|
+
* only make sense here.
|
|
30
33
|
*
|
|
31
34
|
* When no backend is wired, `supportsFragmentRecall` is `false` and any fragment
|
|
32
35
|
* query degrades loudly ({@link FRAGMENT_SEMANTIC_UNWIRED_MESSAGE}) — it NEVER
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fragmentSemanticRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/fragmentSemanticRetriever.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,4CAAsD;AAItD;;;;;GAKG;AACU,QAAA,iCAAiC,GAC5C,6GAA6G,CAAC;AA0ChH
|
|
1
|
+
{"version":3,"file":"fragmentSemanticRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/fragmentSemanticRetriever.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,4CAAsD;AAItD;;;;;GAKG;AACU,QAAA,iCAAiC,GAC5C,6GAA6G,CAAC;AA0ChH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,yBAAyB;IAGpC,YAAoB,OAA6C;QAC/D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,mDAAmD;IACnD,IAAW,YAAY;QACrB,OAAO,EAAE,sBAAsB,EAAE,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;IACjE,CAAC;IAED,iCAAiC;IAC1B,MAAM,CAAC,MAAM,CAAC,MAEpB;QACC,OAAO,IAAA,kBAAO,EAAC,IAAI,yBAAyB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,QAAQ,CAAC,KAAqB;QACzC,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,IAAA,eAAI,EAAC,yCAAiC,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,OAAO,GAA6B,IAAI,CAAC,QAAQ,CAAC;QACxD,4EAA4E;QAC5E,uEAAuE;QACvE,gEAAgE;QAChE,MAAM,QAAQ,GAAyB,MAAM,yBAAyB,CAAC,YAAY,CACjF,iBAAiB,EACjB,GAAG,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,CACzC,CAAC;QACF,IAAI,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC;YACzB,OAAO,IAAA,eAAI,EAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,yBAAyB,CAAC,YAAY,CAAC,gBAAgB,EAAE,GAAG,EAAE,WACnE,OAAA,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,MAAA,KAAK,CAAC,IAAI,mCAAI,EAAE,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA,EAAA,CAClF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACK,MAAM,CAAC,KAAK,CAAC,YAAY,CAAI,KAAa,EAAE,EAA4B;QAC9E,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,KAAK,YAAY,GAAG,EAAE,CAAC,CAAC;QAC3F,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,IAAA,eAAI,EAAC,oBAAoB,KAAK,YAAY,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;CACF;AAvDD,8DAuDC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { IFragmentVectorIndex, IVectorQueryHit } from '../vector';\nimport { QueryEmbedder } from './semanticRetriever';\n\n/**\n * The loud-degradation message a {@link FragmentSemanticRetriever} returns when a\n * fragment query is issued but no {@link IFragmentSemanticBackend | backend} is\n * wired — the discovery surface NEVER answers a fragment query with a silent empty.\n * @public\n */\nexport const FRAGMENT_SEMANTIC_UNWIRED_MESSAGE: string =\n 'fragment recall: no fragment index is wired; wire an IFragmentSemanticBackend to enable sub-document search';\n\n/**\n * The fragment backend wired into a {@link FragmentSemanticRetriever}: the fragment\n * index to query and the embedder that turns the query text into a vector. Both are\n * required together — a fragment index is useless without a way to embed the query.\n * @public\n */\nexport interface IFragmentSemanticBackend {\n /** The fragment-granular vector index to query. */\n readonly fragmentIndex: IFragmentVectorIndex;\n /** Turns the query text into a vector. */\n readonly embedQuery: QueryEmbedder;\n}\n\n/**\n * A sub-document semantic-search request: the natural-language `semantic` text to\n * match, an optional `topK` result cap (default 10), and an optional\n * `maxPerRecord` cap that keeps one long document from monopolizing the result.\n * @public\n */\nexport interface IFragmentQuery {\n /** The natural-language text to embed and match against stored fragments. */\n readonly semantic: string;\n /** Maximum number of fragment hits to return. Defaults to 10. */\n readonly topK?: number;\n /**\n * Maximum number of fragments any single record may contribute to the result.\n * Applied during selection (before the `topK` cut). Omit for uncapped.\n */\n readonly maxPerRecord?: number;\n}\n\n/**\n * What a {@link FragmentSemanticRetriever} can do given its wiring.\n * @public\n */\nexport interface IFragmentRetrieverCapabilities {\n /** `true` when a fragment backend is wired and fragment recall is operational. */\n readonly supportsFragmentRecall: boolean;\n}\n\n/**\n * The sub-document semantic-search retriever — the \"discovery\" half of a\n * search-then-read contract. It embeds a fragment query, queries the\n * {@link IFragmentVectorIndex}, and returns the raw per-fragment\n * {@link IVectorQueryHit | hits} (each carrying a record `target` AND whichever of\n * `locator` / `fragmentId` the stored fragment was indexed with), NOT resolved\n * records: the consumer re-reads each record and resolves the fragment on its own\n * read side. Note the `locator` span is advisory — see {@link IFragmentLocator}; it\n * is not a slice guaranteed to reproduce the fragment's text.\n *\n * @remarks\n * Deliberately NOT an {@link IMemoryRetriever}: memory recall is record-granular and\n * returns records; fragment discovery is fragment-granular and returns fragment\n * identities. Keeping it a distinct surface matches the consumer contract (memory\n * stays record-granular; sub-document knowledge uses a separate fragment index) and\n * avoids overloading the record retriever's return type with identity fields that\n * only make sense here.\n *\n * When no backend is wired, `supportsFragmentRecall` is `false` and any fragment\n * query degrades loudly ({@link FRAGMENT_SEMANTIC_UNWIRED_MESSAGE}) — it NEVER\n * returns a silent empty. A consumer-supplied backend that rejects (throws) is\n * normalized into a `Failure`.\n * @public\n */\nexport class FragmentSemanticRetriever {\n private readonly _backend: IFragmentSemanticBackend | undefined;\n\n private constructor(backend: IFragmentSemanticBackend | undefined) {\n this._backend = backend;\n }\n\n /** What this retriever can do given its wiring. */\n public get capabilities(): IFragmentRetrieverCapabilities {\n return { supportsFragmentRecall: this._backend !== undefined };\n }\n\n /** Family-convention factory. */\n public static create(params: {\n readonly backend?: IFragmentSemanticBackend;\n }): Result<FragmentSemanticRetriever> {\n return succeed(new FragmentSemanticRetriever(params.backend));\n }\n\n /**\n * Embed `query.semantic`, query the fragment index, and return the per-fragment\n * hits in descending score order. Fails loudly when no backend is wired.\n */\n public async retrieve(query: IFragmentQuery): Promise<Result<ReadonlyArray<IVectorQueryHit>>> {\n if (this._backend === undefined) {\n return fail(FRAGMENT_SEMANTIC_UNWIRED_MESSAGE);\n }\n const backend: IFragmentSemanticBackend = this._backend;\n // Consumer-supplied hooks may throw; normalize both a returned `fail` and a\n // rejection into a single `fragment recall: <label> failed` Failure so\n // `retrieve` always honors its `Promise<Result<...>>` contract.\n const embedded: Result<Float32Array> = await FragmentSemanticRetriever._callBackend(\n 'query embedding',\n () => backend.embedQuery(query.semantic)\n );\n if (embedded.isFailure()) {\n return fail(embedded.message);\n }\n return FragmentSemanticRetriever._callBackend('fragment query', () =>\n backend.fragmentIndex.query(embedded.value, query.topK ?? 10, query.maxPerRecord)\n );\n }\n\n /**\n * Invoke a consumer-supplied backend hook, normalizing both a returned `fail`\n * and a thrown/rejected promise into a single `fragment recall: <label> failed`\n * `Failure`.\n */\n private static async _callBackend<T>(label: string, op: () => Promise<Result<T>>): Promise<Result<T>> {\n try {\n return (await op()).withErrorFormat((msg) => `fragment recall: ${label} failed: ${msg}`);\n } catch (err) {\n return fail(`fragment recall: ${label} failed: ${String(err)}`);\n }\n }\n}\n"]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Logging, Result } from '@fgv/ts-utils';
|
|
2
2
|
import { FileTree } from '@fgv/ts-json-base';
|
|
3
|
-
import { EntityId, IIdentityCodec, IMemoryRecord, IWritePolicy, Kind, MemoryId, MemoryScopeKey, RankProjector, Tag } from '../types';
|
|
3
|
+
import { DedupScope, EntityId, IIdentityCodec, IMemoryRecord, IWritePolicy, Kind, MemoryId, MemoryScopeKey, RankProjector, Tag } from '../types';
|
|
4
4
|
import { IBodyConverterRegistry as IRegistry } from '../converters';
|
|
5
|
+
import { IMemoryIndex } from '../index';
|
|
5
6
|
import { IMemoryObserver } from '../observe';
|
|
6
7
|
import { FragmentEmbedder, IFragmentVectorIndex, IMemoryRecordSource, IScopedMemoryRecord, IVectorIndex, MemoryEmbedder } from '../vector';
|
|
7
8
|
/**
|
|
@@ -92,6 +93,34 @@ export interface IMemoryStore {
|
|
|
92
93
|
* seam's `list()` returns scope-qualified records.
|
|
93
94
|
*/
|
|
94
95
|
asRecordSource(): IMemoryRecordSource;
|
|
96
|
+
/**
|
|
97
|
+
* The EFFECTIVE {@link DedupScope} for `kind` — the granularity at which a
|
|
98
|
+
* write for this kind deduplicates against the existing vault.
|
|
99
|
+
*
|
|
100
|
+
* @remarks
|
|
101
|
+
* This is a **read accessor over the store's already-injected write policies**,
|
|
102
|
+
* and it is the single place any caller — the store's own write path included —
|
|
103
|
+
* asks what a kind's dedup granularity is. It resolves the full chain the store
|
|
104
|
+
* applies on write: the kind's registered {@link IWritePolicy}, falling back to
|
|
105
|
+
* the store's default policy, then that policy's
|
|
106
|
+
* {@link IWritePolicy.dedupScope | dedupScope}, falling back to
|
|
107
|
+
* {@link DEFAULT_DEDUP_SCOPE}. Note the store's default policy is a
|
|
108
|
+
* {@link KnowledgeLwwPolicy}, which declares `'content'` — so a kind with NO
|
|
109
|
+
* registered policy resolves to `'content'`, not to `DEFAULT_DEDUP_SCOPE`.
|
|
110
|
+
*
|
|
111
|
+
* It exists so a caller that must agree with the store about dedup granularity
|
|
112
|
+
* — notably the ingest orchestrator's stage-4 layer-1 exact match — can read the
|
|
113
|
+
* declaration through this seam instead of being handed a second copy of the
|
|
114
|
+
* policy map. A second declaration site is precisely the defect this accessor
|
|
115
|
+
* was added to remove.
|
|
116
|
+
*
|
|
117
|
+
* Deliberately synchronous, total, and NOT `Result`-returning: it reads
|
|
118
|
+
* constructor-injected configuration, touches no I/O, and cannot fail (every
|
|
119
|
+
* link in the fallback chain has a total default). It exposes only the scope,
|
|
120
|
+
* never the {@link IWritePolicy} itself, so it can never become a back door for
|
|
121
|
+
* invoking admission or merge logic out of band.
|
|
122
|
+
*/
|
|
123
|
+
dedupScopeFor(kind: Kind): DedupScope;
|
|
95
124
|
/**
|
|
96
125
|
* Write a record. Validates the body, computes a content hash, deduplicates
|
|
97
126
|
* (scope-wide, before policy), applies the kind's {@link IWritePolicy}, stamps
|
|
@@ -137,6 +166,59 @@ export interface IFileTreeMemoryStoreCreateParams {
|
|
|
137
166
|
readonly rankProjectors?: ReadonlyMap<Kind, RankProjector>;
|
|
138
167
|
/** Default codec for kinds without an explicit entry. */
|
|
139
168
|
readonly defaultCodec?: IIdentityCodec;
|
|
169
|
+
/**
|
|
170
|
+
* Optional derived-index implementation. Defaults to a fresh {@link MemoryIndex} —
|
|
171
|
+
* omitting this parameter is byte-identical to the store's behavior before the
|
|
172
|
+
* parameter existed. When supplied, the store uses it for EVERY index operation
|
|
173
|
+
* it performs and never holds a second index, so an injected index is the store's
|
|
174
|
+
* only view of its own records:
|
|
175
|
+
*
|
|
176
|
+
* - `rebuild` — once, from the initial vault walk in `create()`.
|
|
177
|
+
* - `patch` — on every persisted write, delete, version invalidation, and
|
|
178
|
+
* cap-cull eviction.
|
|
179
|
+
* - `entries` — behind {@link IMemoryStore.list | list} /
|
|
180
|
+
* {@link IMemoryStore.listScoped | listScoped}, the keyed temporal reads, the
|
|
181
|
+
* write path's content-hash dedup and write-policy admission cohort, AND the
|
|
182
|
+
* temporal (versioned) write and delete paths, which resolve an entity's
|
|
183
|
+
* version history entirely from the index.
|
|
184
|
+
*
|
|
185
|
+
* That last group is the one to weigh before injecting anything other than a
|
|
186
|
+
* pass-through decorator: an index that filters, reorders, or otherwise reshapes
|
|
187
|
+
* `entries()` changes WRITE semantics, not just what reads return. Concretely,
|
|
188
|
+
* on a versioned kind the store derives an entity's whole version history from
|
|
189
|
+
* `entries()` filtered by scope, and that derivation decides which version a
|
|
190
|
+
* `put` treats as current (so what it dedups against and what it merges its
|
|
191
|
+
* patch over), which prior versions it stamps `invalid_at` on, what the
|
|
192
|
+
* admission cohort is, and which versions a `delete` tombstones. An index that
|
|
193
|
+
* hides a version makes it invisible to all of those — the FileTree still holds
|
|
194
|
+
* it, but the store will not supersede, invalidate, or tombstone it. On flat
|
|
195
|
+
* kinds the same reshaping changes what dedups and what a cap-cull policy
|
|
196
|
+
* evicts. A faithful delegating decorator — the intended use below — has no such
|
|
197
|
+
* effect. Note the store's keyed reads ({@link IMemoryStore.get | get} on a flat
|
|
198
|
+
* kind, and {@link IMemoryStore.getById | getById}) go to the FileTree, not the
|
|
199
|
+
* index; the FileTree remains the source of truth and the index stays a derived
|
|
200
|
+
* view.
|
|
201
|
+
*
|
|
202
|
+
* @remarks
|
|
203
|
+
* **This is an instrumentation seam, NOT a resident-memory fix.** The intended
|
|
204
|
+
* use is wrapping the shipped {@link MemoryIndex} in a decorator that counts and
|
|
205
|
+
* times the calls the store makes — resident bytes by kind, open cost against
|
|
206
|
+
* vault size, where the curve actually bends — so a decision about a partial-read
|
|
207
|
+
* redesign can be driven by measurements instead of estimates.
|
|
208
|
+
*
|
|
209
|
+
* It does NOT lower the store's resident-memory ceiling, and injecting a
|
|
210
|
+
* "persisted" or "lazy" index will not change that. {@link IMemoryIndex}'s read
|
|
211
|
+
* surface returns whole records by construction: `entries()` yields
|
|
212
|
+
* {@link IIndexedMemoryRecord}s and `byKind` / `byTag` / `byRecency` / `byRank`
|
|
213
|
+
* yield `IMemoryRecord<unknown>` — `{ envelope, body }` pairs with the body
|
|
214
|
+
* materialized. Any implementation satisfying the current contract must therefore
|
|
215
|
+
* be able to produce every body on demand. An injected index changes WHERE records
|
|
216
|
+
* come from; it does not change WHETHER bodies are held. Lowering the ceiling
|
|
217
|
+
* requires a partial-read (id-or-envelope-only) redesign of `IMemoryIndex` itself,
|
|
218
|
+
* which is separate, breaking, design-first work and is deliberately not part of
|
|
219
|
+
* this seam.
|
|
220
|
+
*/
|
|
221
|
+
readonly index?: IMemoryIndex;
|
|
140
222
|
/** Scope encoding. Defaults to {@link defaultMemoryScopeEncoding}. */
|
|
141
223
|
readonly scopeEncoding?: (scope: MemoryScopeKey) => Result<string>;
|
|
142
224
|
/**
|
|
@@ -283,11 +365,19 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
|
|
|
283
365
|
*/
|
|
284
366
|
get skippedRecords(): ReadonlyArray<ISkippedRecord>;
|
|
285
367
|
/**
|
|
286
|
-
* Family-convention factory.
|
|
287
|
-
*
|
|
288
|
-
*
|
|
368
|
+
* Family-convention factory. Resolves the derived index (the caller's
|
|
369
|
+
* {@link IFileTreeMemoryStoreCreateParams.index | index} when supplied, a fresh
|
|
370
|
+
* {@link MemoryIndex} otherwise) and a default LWW policy, then performs an
|
|
371
|
+
* initial FileTree walk so an existing vault is indexed (and the `seq` counter
|
|
372
|
+
* resumes past the highest persisted `seq`).
|
|
289
373
|
*/
|
|
290
374
|
static create(params: IFileTreeMemoryStoreCreateParams): Result<FileTreeMemoryStore>;
|
|
375
|
+
/**
|
|
376
|
+
* Resolve the derived index for a `create()`: the caller's injected
|
|
377
|
+
* {@link IMemoryIndex} verbatim, or a fresh {@link MemoryIndex} when none was
|
|
378
|
+
* supplied (the default that keeps an omitting caller byte-identical).
|
|
379
|
+
*/
|
|
380
|
+
private static _resolveIndex;
|
|
291
381
|
/** {@inheritDoc IMemoryStore.get} */
|
|
292
382
|
get(kind: Kind, entityId: EntityId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
|
|
293
383
|
/** {@inheritDoc IMemoryStore.getById} */
|
|
@@ -548,6 +638,8 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
|
|
|
548
638
|
private _stampRank;
|
|
549
639
|
private _codecFor;
|
|
550
640
|
private _policyFor;
|
|
641
|
+
/** {@inheritDoc IMemoryStore.dedupScopeFor} */
|
|
642
|
+
dedupScopeFor(kind: Kind): DedupScope;
|
|
551
643
|
/**
|
|
552
644
|
* Read and validate the record at `<scope>/<idStem>.md`, returning `undefined`
|
|
553
645
|
* when the scope directory or file is absent. Verifies the on-disk id ↔
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileTreeMemoryStore.d.ts","sourceRoot":"","sources":["../../../src/packlets/store/fileTreeMemoryStore.ts"],"names":[],"mappings":"AAKA,OAAO,EAAQ,OAAO,EAAE,MAAM,EAAyC,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"fileTreeMemoryStore.d.ts","sourceRoot":"","sources":["../../../src/packlets/store/fileTreeMemoryStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -63,12 +63,14 @@ class FileTreeMemoryStore {
|
|
|
63
63
|
return this._skippedRecords;
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
* Family-convention factory.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
66
|
+
* Family-convention factory. Resolves the derived index (the caller's
|
|
67
|
+
* {@link IFileTreeMemoryStoreCreateParams.index | index} when supplied, a fresh
|
|
68
|
+
* {@link MemoryIndex} otherwise) and a default LWW policy, then performs an
|
|
69
|
+
* initial FileTree walk so an existing vault is indexed (and the `seq` counter
|
|
70
|
+
* resumes past the highest persisted `seq`).
|
|
69
71
|
*/
|
|
70
72
|
static create(params) {
|
|
71
|
-
return types_1.KnowledgeLwwPolicy.create().onSuccess((defaultPolicy) =>
|
|
73
|
+
return types_1.KnowledgeLwwPolicy.create().onSuccess((defaultPolicy) => FileTreeMemoryStore._resolveIndex(params.index).onSuccess((index) => {
|
|
72
74
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
73
75
|
const store = new FileTreeMemoryStore({
|
|
74
76
|
root: params.root,
|
|
@@ -91,6 +93,20 @@ class FileTreeMemoryStore {
|
|
|
91
93
|
return store._initialIndex((_h = params.onRecordError) !== null && _h !== void 0 ? _h : 'fail').onSuccess(() => (0, ts_utils_1.succeed)(store));
|
|
92
94
|
}));
|
|
93
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolve the derived index for a `create()`: the caller's injected
|
|
98
|
+
* {@link IMemoryIndex} verbatim, or a fresh {@link MemoryIndex} when none was
|
|
99
|
+
* supplied (the default that keeps an omitting caller byte-identical).
|
|
100
|
+
*/
|
|
101
|
+
static _resolveIndex(index) {
|
|
102
|
+
// Nullish rather than strictly-undefined, matching how every sibling optional
|
|
103
|
+
// param in `create()` handles absence (`params.codecs ?? new Map()`, and so on).
|
|
104
|
+
// A JS caller — or a TS caller arriving through an `unknown` escape hatch —
|
|
105
|
+
// passing `null` otherwise gets `null` installed as the store's index and fails
|
|
106
|
+
// later inside `entries()` with a message that names neither the param nor the
|
|
107
|
+
// cause.
|
|
108
|
+
return index ? (0, ts_utils_1.succeed)(index) : index_1.MemoryIndex.create();
|
|
109
|
+
}
|
|
94
110
|
/** {@inheritDoc IMemoryStore.get} */
|
|
95
111
|
async get(kind, entityId) {
|
|
96
112
|
var _a;
|
|
@@ -335,9 +351,11 @@ class FileTreeMemoryStore {
|
|
|
335
351
|
* vector index, the on-disk file, and the derived index never interleave.
|
|
336
352
|
*/
|
|
337
353
|
async _writeResolved(record, body, scope, idStem, hash) {
|
|
338
|
-
var _a;
|
|
339
354
|
const policy = this._policyFor(record.envelope.kind);
|
|
340
|
-
|
|
355
|
+
// Read through the public accessor so the write path and every external
|
|
356
|
+
// caller (the ingest orchestrator's layer-1) resolve dedup granularity from
|
|
357
|
+
// ONE place — there is no second declaration site.
|
|
358
|
+
const dedupScope = this.dedupScopeFor(record.envelope.kind);
|
|
341
359
|
// Content-hash dedup runs BEFORE policy. Its granularity is the kind's
|
|
342
360
|
// `dedupScope`:
|
|
343
361
|
// - 'content': an identical { kind, body, links } triple ANYWHERE in the
|
|
@@ -661,7 +679,6 @@ class FileTreeMemoryStore {
|
|
|
661
679
|
* version's `valid_at` lower-bounds its interval.
|
|
662
680
|
*/
|
|
663
681
|
async _putVersioned(record, body, codec, scope) {
|
|
664
|
-
var _a;
|
|
665
682
|
const envelope = record.envelope;
|
|
666
683
|
const entityId = envelope.entityId;
|
|
667
684
|
const kind = envelope.kind;
|
|
@@ -673,7 +690,8 @@ class FileTreeMemoryStore {
|
|
|
673
690
|
const priorCurrents = versions.filter(types_1.isVersionCurrent);
|
|
674
691
|
const current = (0, types_1.selectCurrentVersion)(versions);
|
|
675
692
|
const policy = this._policyFor(kind);
|
|
676
|
-
|
|
693
|
+
// Same single-owner read as the flat path (see `_writeResolved`).
|
|
694
|
+
const dedupScope = this.dedupScopeFor(kind);
|
|
677
695
|
return this._contentHash(kind, body, envelope.links).thenOnSuccess((hash) => {
|
|
678
696
|
// Entity-scoped dedup: a re-put is a no-op only when the CURRENT content AND
|
|
679
697
|
// its mutable metadata are unchanged (does not spawn a redundant version).
|
|
@@ -921,6 +939,11 @@ class FileTreeMemoryStore {
|
|
|
921
939
|
var _a;
|
|
922
940
|
return (_a = this._writePolicies.get(kind)) !== null && _a !== void 0 ? _a : this._defaultPolicy;
|
|
923
941
|
}
|
|
942
|
+
/** {@inheritDoc IMemoryStore.dedupScopeFor} */
|
|
943
|
+
dedupScopeFor(kind) {
|
|
944
|
+
var _a;
|
|
945
|
+
return (_a = this._policyFor(kind).dedupScope) !== null && _a !== void 0 ? _a : types_1.DEFAULT_DEDUP_SCOPE;
|
|
946
|
+
}
|
|
924
947
|
/**
|
|
925
948
|
* Read and validate the record at `<scope>/<idStem>.md`, returning `undefined`
|
|
926
949
|
* when the scope directory or file is absent. Verifies the on-disk id ↔
|