@fgv/ts-agent-memory 5.1.0-49 → 5.1.0-51
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/dist/packlets/index/memoryIndex.js +23 -16
- package/dist/packlets/index/memoryIndex.js.map +1 -1
- package/dist/packlets/ingest/orchestrator.js +13 -1
- package/dist/packlets/ingest/orchestrator.js.map +1 -1
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js +94 -5
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
- package/dist/packlets/retrieve/linkTraversalRetriever.js +12 -26
- package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
- package/dist/packlets/retrieve/recencyRetriever.js +7 -7
- package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
- package/dist/packlets/retrieve/retriever.js +91 -10
- package/dist/packlets/retrieve/retriever.js.map +1 -1
- package/dist/packlets/retrieve/semanticRetriever.js +16 -16
- package/dist/packlets/retrieve/semanticRetriever.js.map +1 -1
- package/dist/packlets/retrieve/structuredFilterRetriever.js +7 -7
- package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
- package/dist/packlets/retrieve/tagRetriever.js +7 -7
- package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
- package/dist/packlets/retrieve/temporalRetrievers.js +23 -20
- package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
- package/dist/packlets/store/coverage.js +6 -0
- package/dist/packlets/store/coverage.js.map +1 -0
- package/dist/packlets/store/fileTreeMemoryStore.js +234 -103
- package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
- package/dist/packlets/store/index.js +4 -0
- package/dist/packlets/store/index.js.map +1 -1
- package/dist/packlets/store/listSelection.js +36 -0
- package/dist/packlets/store/listSelection.js.map +1 -0
- package/dist/packlets/store/memoryStore.js +6 -0
- package/dist/packlets/store/memoryStore.js.map +1 -0
- package/dist/packlets/store/reconcile.js +6 -0
- package/dist/packlets/store/reconcile.js.map +1 -0
- package/dist/packlets/store/storeCoverage.js +102 -0
- package/dist/packlets/store/storeCoverage.js.map +1 -0
- package/dist/packlets/store/storeIdentity.js +62 -0
- package/dist/packlets/store/storeIdentity.js.map +1 -0
- package/dist/packlets/store/storeReconcile.js +122 -0
- package/dist/packlets/store/storeReconcile.js.map +1 -0
- package/dist/packlets/store/vectorMaintenance.js +116 -8
- package/dist/packlets/store/vectorMaintenance.js.map +1 -1
- package/dist/packlets/store/vectorRecordSource.js +44 -0
- package/dist/packlets/store/vectorRecordSource.js.map +1 -0
- package/dist/packlets/tools/memoryTools.js +25 -2
- package/dist/packlets/tools/memoryTools.js.map +1 -1
- package/dist/packlets/types/envelope.js +25 -0
- package/dist/packlets/types/envelope.js.map +1 -1
- package/dist/packlets/types/identityResolver.js +6 -0
- package/dist/packlets/types/identityResolver.js.map +1 -0
- package/dist/packlets/types/index.js +2 -0
- package/dist/packlets/types/index.js.map +1 -1
- package/dist/packlets/types/recordResolver.js +6 -0
- package/dist/packlets/types/recordResolver.js.map +1 -0
- package/dist/packlets/types/temporal.js.map +1 -1
- package/dist/packlets/vector/inMemoryCosineIndex.js +39 -18
- package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
- package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +109 -14
- package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
- package/dist/packlets/vector/rebuildHelpers.js +38 -0
- package/dist/packlets/vector/rebuildHelpers.js.map +1 -0
- package/dist/packlets/vector/vectorIndex.js.map +1 -1
- package/dist/ts-agent-memory.d.ts +1226 -114
- package/lib/packlets/index/memoryIndex.d.ts +118 -27
- package/lib/packlets/index/memoryIndex.d.ts.map +1 -1
- package/lib/packlets/index/memoryIndex.js +23 -16
- package/lib/packlets/index/memoryIndex.js.map +1 -1
- package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
- package/lib/packlets/ingest/orchestrator.js +13 -1
- package/lib/packlets/ingest/orchestrator.js.map +1 -1
- package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts +79 -1
- package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/fragmentSemanticRetriever.js +94 -5
- package/lib/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
- package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +3 -10
- package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/linkTraversalRetriever.js +11 -25
- package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
- package/lib/packlets/retrieve/recencyRetriever.d.ts +3 -3
- package/lib/packlets/retrieve/recencyRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/recencyRetriever.js +6 -6
- package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
- package/lib/packlets/retrieve/retriever.d.ts +88 -7
- package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/retriever.js +94 -9
- package/lib/packlets/retrieve/retriever.js.map +1 -1
- package/lib/packlets/retrieve/semanticRetriever.d.ts +3 -5
- package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/semanticRetriever.js +15 -15
- package/lib/packlets/retrieve/semanticRetriever.js.map +1 -1
- package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +3 -3
- package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/structuredFilterRetriever.js +6 -6
- package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
- package/lib/packlets/retrieve/tagRetriever.d.ts +3 -3
- package/lib/packlets/retrieve/tagRetriever.d.ts.map +1 -1
- package/lib/packlets/retrieve/tagRetriever.js +6 -6
- package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
- package/lib/packlets/retrieve/temporalRetrievers.d.ts +7 -5
- package/lib/packlets/retrieve/temporalRetrievers.d.ts.map +1 -1
- package/lib/packlets/retrieve/temporalRetrievers.js +22 -19
- package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
- package/lib/packlets/store/coverage.d.ts +102 -0
- package/lib/packlets/store/coverage.d.ts.map +1 -0
- package/lib/packlets/store/coverage.js +7 -0
- package/lib/packlets/store/coverage.js.map +1 -0
- package/lib/packlets/store/fileTreeMemoryStore.d.ts +56 -167
- package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
- package/lib/packlets/store/fileTreeMemoryStore.js +234 -103
- package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
- package/lib/packlets/store/index.d.ts +4 -0
- package/lib/packlets/store/index.d.ts.map +1 -1
- package/lib/packlets/store/index.js +4 -0
- package/lib/packlets/store/index.js.map +1 -1
- package/lib/packlets/store/listSelection.d.ts +101 -0
- package/lib/packlets/store/listSelection.d.ts.map +1 -0
- package/lib/packlets/store/listSelection.js +40 -0
- package/lib/packlets/store/listSelection.js.map +1 -0
- package/lib/packlets/store/memoryStore.d.ts +237 -0
- package/lib/packlets/store/memoryStore.d.ts.map +1 -0
- package/lib/packlets/store/memoryStore.js +7 -0
- package/lib/packlets/store/memoryStore.js.map +1 -0
- package/lib/packlets/store/reconcile.d.ts +82 -0
- package/lib/packlets/store/reconcile.d.ts.map +1 -0
- package/lib/packlets/store/reconcile.js +7 -0
- package/lib/packlets/store/reconcile.js.map +1 -0
- package/lib/packlets/store/storeCoverage.d.ts +45 -0
- package/lib/packlets/store/storeCoverage.d.ts.map +1 -0
- package/lib/packlets/store/storeCoverage.js +105 -0
- package/lib/packlets/store/storeCoverage.js.map +1 -0
- package/lib/packlets/store/storeIdentity.d.ts +38 -0
- package/lib/packlets/store/storeIdentity.d.ts.map +1 -0
- package/lib/packlets/store/storeIdentity.js +67 -0
- package/lib/packlets/store/storeIdentity.js.map +1 -0
- package/lib/packlets/store/storeReconcile.d.ts +41 -0
- package/lib/packlets/store/storeReconcile.d.ts.map +1 -0
- package/lib/packlets/store/storeReconcile.js +125 -0
- package/lib/packlets/store/storeReconcile.js.map +1 -0
- package/lib/packlets/store/vectorMaintenance.d.ts +74 -0
- package/lib/packlets/store/vectorMaintenance.d.ts.map +1 -1
- package/lib/packlets/store/vectorMaintenance.js +117 -8
- package/lib/packlets/store/vectorMaintenance.js.map +1 -1
- package/lib/packlets/store/vectorRecordSource.d.ts +36 -0
- package/lib/packlets/store/vectorRecordSource.d.ts.map +1 -0
- package/lib/packlets/store/vectorRecordSource.js +47 -0
- package/lib/packlets/store/vectorRecordSource.js.map +1 -0
- package/lib/packlets/tools/memoryTools.d.ts.map +1 -1
- package/lib/packlets/tools/memoryTools.js +25 -2
- package/lib/packlets/tools/memoryTools.js.map +1 -1
- package/lib/packlets/types/envelope.d.ts +24 -2
- package/lib/packlets/types/envelope.d.ts.map +1 -1
- package/lib/packlets/types/envelope.js +26 -0
- package/lib/packlets/types/envelope.js.map +1 -1
- package/lib/packlets/types/identityResolver.d.ts +42 -0
- package/lib/packlets/types/identityResolver.d.ts.map +1 -0
- package/lib/packlets/types/identityResolver.js +7 -0
- package/lib/packlets/types/identityResolver.js.map +1 -0
- package/lib/packlets/types/index.d.ts +2 -0
- package/lib/packlets/types/index.d.ts.map +1 -1
- package/lib/packlets/types/index.js +2 -0
- package/lib/packlets/types/index.js.map +1 -1
- package/lib/packlets/types/recordResolver.d.ts +39 -0
- package/lib/packlets/types/recordResolver.d.ts.map +1 -0
- package/lib/packlets/types/recordResolver.js +7 -0
- package/lib/packlets/types/recordResolver.js.map +1 -0
- package/lib/packlets/types/temporal.d.ts +26 -6
- package/lib/packlets/types/temporal.d.ts.map +1 -1
- package/lib/packlets/types/temporal.js.map +1 -1
- package/lib/packlets/vector/inMemoryCosineIndex.d.ts +9 -2
- package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
- package/lib/packlets/vector/inMemoryCosineIndex.js +40 -19
- package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +18 -4
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +108 -13
- package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
- package/lib/packlets/vector/rebuildHelpers.d.ts +30 -0
- package/lib/packlets/vector/rebuildHelpers.d.ts.map +1 -0
- package/lib/packlets/vector/rebuildHelpers.js +42 -0
- package/lib/packlets/vector/rebuildHelpers.js.map +1 -0
- package/lib/packlets/vector/vectorIndex.d.ts +326 -20
- package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
- package/lib/packlets/vector/vectorIndex.js.map +1 -1
- package/package.json +7 -7
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
import { fail, succeed } from '@fgv/ts-utils';
|
|
6
|
-
import {
|
|
7
|
-
import { SEMANTIC_UNWIRED_MESSAGE, indexedRecordMatchesQuery, limitRecords, temporalUnwiredMessage } from './retriever';
|
|
6
|
+
import { SEMANTIC_UNWIRED_MESSAGE, indexedRecordMatchesQuery, materializePage, temporalUnwiredMessage } from './retriever';
|
|
8
7
|
/**
|
|
9
8
|
* Vector-recall retriever. When a {@link ISemanticBackend | backend} is wired it
|
|
10
9
|
* embeds `query.semantic`, queries the vector index, and resolves the hits back
|
|
@@ -18,9 +17,10 @@ import { SEMANTIC_UNWIRED_MESSAGE, indexedRecordMatchesQuery, limitRecords, temp
|
|
|
18
17
|
* @public
|
|
19
18
|
*/
|
|
20
19
|
export class SemanticRetriever {
|
|
21
|
-
constructor(
|
|
22
|
-
this._index = index;
|
|
23
|
-
this.
|
|
20
|
+
constructor(params) {
|
|
21
|
+
this._index = params.index;
|
|
22
|
+
this._resolver = params.resolver;
|
|
23
|
+
this._backend = params.backend;
|
|
24
24
|
}
|
|
25
25
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
26
26
|
get capabilities() {
|
|
@@ -32,7 +32,7 @@ export class SemanticRetriever {
|
|
|
32
32
|
}
|
|
33
33
|
/** Family-convention factory. */
|
|
34
34
|
static create(params) {
|
|
35
|
-
return succeed(new SemanticRetriever(params
|
|
35
|
+
return succeed(new SemanticRetriever(params));
|
|
36
36
|
}
|
|
37
37
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
38
38
|
async retrieve(query) {
|
|
@@ -59,20 +59,20 @@ export class SemanticRetriever {
|
|
|
59
59
|
if (hits.isFailure()) {
|
|
60
60
|
return fail(hits.message);
|
|
61
61
|
}
|
|
62
|
-
//
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
.map((entry) => [edgeTargetKey({ scope: entry.scope, id: entry.record.envelope.id }), entry]));
|
|
68
|
-
const records = [];
|
|
62
|
+
// Resolve each hit by its canonical scope-qualified target — a bare id would
|
|
63
|
+
// alias two records that share a filename stem across scopes. This used to
|
|
64
|
+
// build a Map over the ENTIRE index to look up at most `topK` of them; the
|
|
65
|
+
// index's own `get` makes it O(hits).
|
|
66
|
+
const selected = [];
|
|
69
67
|
for (const hit of hits.value) {
|
|
70
|
-
const entry =
|
|
68
|
+
const entry = this._index.get(hit.target);
|
|
71
69
|
if (entry !== undefined && indexedRecordMatchesQuery(entry, query)) {
|
|
72
|
-
|
|
70
|
+
selected.push(entry);
|
|
73
71
|
}
|
|
74
72
|
}
|
|
75
|
-
|
|
73
|
+
// Hit order IS the ranking, so the page is taken before materializing and
|
|
74
|
+
// only the returned records are read.
|
|
75
|
+
return materializePage(selected, query, this._resolver);
|
|
76
76
|
}
|
|
77
77
|
/**
|
|
78
78
|
* Invoke a consumer-supplied backend hook, normalizing both a returned `fail`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semanticRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/semanticRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"semanticRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/semanticRetriever.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, IMemoryRecordResolver } from '../types';\nimport { IIndexedMemoryEntry, IMemoryIndex } from '../index';\nimport { IVectorIndex, IVectorQueryHit } from '../vector';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n IRetrieverCreateParams,\n SEMANTIC_UNWIRED_MESSAGE,\n indexedRecordMatchesQuery,\n materializePage,\n temporalUnwiredMessage\n} from './retriever';\n\n/**\n * Embeds a query string into a vector for {@link IVectorIndex.query}. Async and\n * `Result`-returning, since a real embedder does a network call.\n * @public\n */\nexport type QueryEmbedder = (text: string) => Promise<Result<Float32Array>>;\n\n/**\n * The semantic backend wired into a {@link SemanticRetriever}: the vector index\n * to query and the embedder that turns the query text into a vector. Both are\n * required together — a vector index is useless without a way to embed the\n * query, so {@link SemanticRetriever.create} treats them as one unit.\n * @public\n */\nexport interface ISemanticBackend {\n /** The vector index to query. */\n readonly vectorIndex: IVectorIndex;\n /** Turns the query text into a vector. */\n readonly embedQuery: QueryEmbedder;\n}\n\n/**\n * Construction options for {@link SemanticRetriever.create}.\n * @public\n */\nexport interface ISemanticRetrieverCreateParams extends IRetrieverCreateParams {\n /**\n * The semantic backend. When absent, the retriever reports\n * `supportsSemanticRecall: false` and a `query.semantic` request degrades\n * loudly ({@link SEMANTIC_UNWIRED_MESSAGE}) rather than returning empty.\n */\n readonly backend?: ISemanticBackend;\n}\n\n/**\n * Vector-recall retriever. When a {@link ISemanticBackend | backend} is wired it\n * embeds `query.semantic`, queries the vector index, and resolves the hits back\n * to records (preserving vector score order). When no backend is wired,\n * `supportsSemanticRecall` is `false` and any `query.semantic` request degrades\n * loudly — it NEVER returns a silent empty.\n *\n * @remarks\n * A consumer-supplied backend that rejects (throws) is normalized into a\n * `Failure` — `retrieve` always honors its `Promise<Result<...>>` contract.\n * @public\n */\nexport class SemanticRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n private readonly _backend: ISemanticBackend | undefined;\n\n private constructor(params: ISemanticRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n this._backend = params.backend;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return {\n supportsSemanticRecall: this._backend !== undefined,\n supportsTemporalQuery: false,\n supportsLinkTraversal: false\n };\n }\n\n /** Family-convention factory. */\n public static create(params: ISemanticRetrieverCreateParams): Result<SemanticRetriever> {\n return succeed(new SemanticRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public async retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n if (query.asOf !== undefined) {\n return fail(temporalUnwiredMessage(query.kind));\n }\n if (query.semantic === undefined) {\n // The semantic term is this retriever's axis; without it there is nothing\n // to recall (a no-op contribution to a HybridRetriever, not a failure).\n return succeed([]);\n }\n if (this._backend === undefined) {\n return fail(SEMANTIC_UNWIRED_MESSAGE);\n }\n const backend: ISemanticBackend = this._backend;\n // The backend hooks are consumer-supplied; a rejecting (throwing) impl must\n // still surface as a `Failure`, never escape `retrieve` as a rejected\n // promise. `_callBackend` normalizes both a returned `fail` and a rejection.\n const embedded: Result<Float32Array> = await SemanticRetriever._callBackend('query embedding', () =>\n backend.embedQuery(query.semantic as string)\n );\n if (embedded.isFailure()) {\n return fail(embedded.message);\n }\n const hits: Result<ReadonlyArray<IVectorQueryHit>> = await SemanticRetriever._callBackend(\n 'vector query',\n () => backend.vectorIndex.query(embedded.value, query.topK ?? 10)\n );\n if (hits.isFailure()) {\n return fail(hits.message);\n }\n // Resolve each hit by its canonical scope-qualified target — a bare id would\n // alias two records that share a filename stem across scopes. This used to\n // build a Map over the ENTIRE index to look up at most `topK` of them; the\n // index's own `get` makes it O(hits).\n const selected: IIndexedMemoryEntry[] = [];\n for (const hit of hits.value) {\n const entry: IIndexedMemoryEntry | undefined = this._index.get(hit.target);\n if (entry !== undefined && indexedRecordMatchesQuery(entry, query)) {\n selected.push(entry);\n }\n }\n // Hit order IS the ranking, so the page is taken before materializing and\n // only the returned records are read.\n return materializePage(selected, query, this._resolver);\n }\n\n /**\n * Invoke a consumer-supplied backend hook, normalizing both a returned `fail`\n * and a thrown/rejected promise into a single `semantic recall: <label> failed`\n * `Failure`. Keeps `retrieve` within the `Promise<Result<...>>` contract even\n * when the injected `embedQuery` / `vectorIndex` misbehaves.\n */\n private static async _callBackend<T>(label: string, op: () => Promise<Result<T>>): Promise<Result<T>> {\n try {\n return (await op()).withErrorFormat((msg) => `semantic recall: ${label} failed: ${msg}`);\n } catch (err) {\n return fail(`semantic recall: ${label} failed: ${String(err)}`);\n }\n }\n}\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
import { succeed } from '@fgv/ts-utils';
|
|
6
|
-
import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities,
|
|
6
|
+
import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities, resolveQuery } from './retriever';
|
|
7
7
|
/**
|
|
8
8
|
* Returns records matching `query.filter` or `query.provenanceSource`, narrowed
|
|
9
9
|
* by any scope / kind / tag / provenance-source pre-filter and recency-ordered.
|
|
@@ -34,16 +34,17 @@ import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities, limitRecords, or
|
|
|
34
34
|
* @public
|
|
35
35
|
*/
|
|
36
36
|
export class StructuredFilterRetriever {
|
|
37
|
-
constructor(
|
|
38
|
-
this._index = index;
|
|
37
|
+
constructor(params) {
|
|
38
|
+
this._index = params.index;
|
|
39
|
+
this._resolver = params.resolver;
|
|
39
40
|
}
|
|
40
41
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
41
42
|
get capabilities() {
|
|
42
43
|
return NON_SEMANTIC_CAPABILITIES;
|
|
43
44
|
}
|
|
44
45
|
/** Family-convention factory. */
|
|
45
|
-
static create(
|
|
46
|
-
return succeed(new StructuredFilterRetriever(
|
|
46
|
+
static create(params) {
|
|
47
|
+
return succeed(new StructuredFilterRetriever(params));
|
|
47
48
|
}
|
|
48
49
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
49
50
|
retrieve(query) {
|
|
@@ -51,8 +52,7 @@ export class StructuredFilterRetriever {
|
|
|
51
52
|
if (query.filter === undefined && query.provenanceSource === undefined) {
|
|
52
53
|
return succeed([]);
|
|
53
54
|
}
|
|
54
|
-
|
|
55
|
-
return succeed(limitRecords(ordered, query.limit, query.offset));
|
|
55
|
+
return resolveQuery(this._index.entries(), query, this._resolver);
|
|
56
56
|
}));
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structuredFilterRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/structuredFilterRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,
|
|
1
|
+
{"version":3,"file":"structuredFilterRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/structuredFilterRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAKL,yBAAyB,EACzB,0BAA0B,EAC1B,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,OAAO,yBAAyB;IAIpC,YAAoB,MAA8B;QAChD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;IACnC,CAAC;IAED,kDAAkD;IAClD,IAAW,YAAY;QACrB,OAAO,yBAAyB,CAAC;IACnC,CAAC;IAED,iCAAiC;IAC1B,MAAM,CAAC,MAAM,CAAC,MAA8B;QACjD,OAAO,OAAO,CAAC,IAAI,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,8CAA8C;IACvC,QAAQ,CAAC,KAAmB;QACjC,OAAO,OAAO,CAAC,OAAO,CACpB,0BAA0B,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAClE,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACvE,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpE,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, IMemoryRecordResolver } from '../types';\nimport { IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n IRetrieverCreateParams,\n NON_SEMANTIC_CAPABILITIES,\n guardRetrieverCapabilities,\n resolveQuery\n} from './retriever';\n\n/**\n * Returns records matching `query.filter` or `query.provenanceSource`, narrowed\n * by any scope / kind / tag / provenance-source pre-filter and recency-ordered.\n * Those two are this retriever's axes: a query carrying neither is not its\n * concern and yields an empty success (so it contributes nothing to a\n * {@link HybridRetriever}, rather than failing).\n *\n * Note `provenanceSource` appears on both sides of that sentence, and the\n * duplication is real rather than sloppy: it is one of this retriever's two\n * *dispatch* axes (it decides whether the query is this retriever's concern at\n * all) **and** a member of the shared *pre-filter* (it narrows the result set,\n * as it does for every other retriever). `filter` is likewise both.\n *\n * @remarks\n * `provenanceSource` is *applied* by the shared pre-filter, so every retriever\n * narrows by it. What this retriever adds is *answering* a query whose only axis\n * is `provenanceSource` — the \"show me everything this source produced\" request,\n * which would otherwise fall through the `filter`-absent guard and come back\n * empty.\n *\n * Consequently, inside a {@link HybridRetriever} composed with the universal\n * {@link RecencyRetriever}, a `provenanceSource`-only query is answered by both\n * children and every matching record scores twice under a score-union merge.\n * That is the established behavior for a dedicated-axis retriever composed with\n * the universal one — {@link TagRetriever} double-scores a `tag`-only query the\n * same way — and is intentional here, not an artifact of grafting a second axis\n * onto a retriever whose original concern was arbitrary predicates.\n * @public\n */\nexport class StructuredFilterRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n\n private constructor(params: IRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return NON_SEMANTIC_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(params: IRetrieverCreateParams): Result<StructuredFilterRetriever> {\n return succeed(new StructuredFilterRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n if (query.filter === undefined && query.provenanceSource === undefined) {\n return succeed([]);\n }\n return resolveQuery(this._index.entries(), query, this._resolver);\n })\n );\n }\n}\n"]}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPDX-License-Identifier: MIT
|
|
4
4
|
*/
|
|
5
5
|
import { succeed } from '@fgv/ts-utils';
|
|
6
|
-
import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities,
|
|
6
|
+
import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities, resolveQuery } from './retriever';
|
|
7
7
|
/**
|
|
8
8
|
* Returns records carrying `query.tag`, recency-ordered within the tag and
|
|
9
9
|
* narrowed by any scope / kind / provenance-source / predicate filters. Tag is this retriever's
|
|
@@ -12,16 +12,17 @@ import { NON_SEMANTIC_CAPABILITIES, guardRetrieverCapabilities, limitRecords, or
|
|
|
12
12
|
* @public
|
|
13
13
|
*/
|
|
14
14
|
export class TagRetriever {
|
|
15
|
-
constructor(
|
|
16
|
-
this._index = index;
|
|
15
|
+
constructor(params) {
|
|
16
|
+
this._index = params.index;
|
|
17
|
+
this._resolver = params.resolver;
|
|
17
18
|
}
|
|
18
19
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
19
20
|
get capabilities() {
|
|
20
21
|
return NON_SEMANTIC_CAPABILITIES;
|
|
21
22
|
}
|
|
22
23
|
/** Family-convention factory. */
|
|
23
|
-
static create(
|
|
24
|
-
return succeed(new TagRetriever(
|
|
24
|
+
static create(params) {
|
|
25
|
+
return succeed(new TagRetriever(params));
|
|
25
26
|
}
|
|
26
27
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
27
28
|
retrieve(query) {
|
|
@@ -29,8 +30,7 @@ export class TagRetriever {
|
|
|
29
30
|
if (query.tag === undefined) {
|
|
30
31
|
return succeed([]);
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
return succeed(limitRecords(ordered, query.limit, query.offset));
|
|
33
|
+
return resolveQuery(this._index.entries(), query, this._resolver);
|
|
34
34
|
}));
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tagRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/tagRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,
|
|
1
|
+
{"version":3,"file":"tagRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/tagRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,OAAO,EAAE,MAAM,eAAe,CAAC;AAGhD,OAAO,EAKL,yBAAyB,EACzB,0BAA0B,EAC1B,YAAY,EACb,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IAIvB,YAAoB,MAA8B;QAChD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;IACnC,CAAC;IAED,kDAAkD;IAClD,IAAW,YAAY;QACrB,OAAO,yBAAyB,CAAC;IACnC,CAAC;IAED,iCAAiC;IAC1B,MAAM,CAAC,MAAM,CAAC,MAA8B;QACjD,OAAO,OAAO,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,8CAA8C;IACvC,QAAQ,CAAC,KAAmB;QACjC,OAAO,OAAO,CAAC,OAAO,CACpB,0BAA0B,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAClE,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO,OAAO,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QACpE,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, IMemoryRecordResolver } from '../types';\nimport { IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n IRetrieverCreateParams,\n NON_SEMANTIC_CAPABILITIES,\n guardRetrieverCapabilities,\n resolveQuery\n} from './retriever';\n\n/**\n * Returns records carrying `query.tag`, recency-ordered within the tag and\n * narrowed by any scope / kind / provenance-source / predicate filters. Tag is this retriever's\n * axis: a query without a `tag` is not its concern and yields an empty success\n * (so it contributes nothing to a {@link HybridRetriever}, rather than failing).\n * @public\n */\nexport class TagRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n\n private constructor(params: IRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return NON_SEMANTIC_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(params: IRetrieverCreateParams): Result<TagRetriever> {\n return succeed(new TagRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n if (query.tag === undefined) {\n return succeed([]);\n }\n return resolveQuery(this._index.entries(), query, this._resolver);\n })\n );\n }\n}\n"]}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { succeed } from '@fgv/ts-utils';
|
|
6
6
|
import { isTemporalRecord, selectCurrentVersion, selectVersionAsOf } from '../types';
|
|
7
|
-
import { guardRetrieverCapabilities, indexedRecordMatchesQuery,
|
|
7
|
+
import { guardRetrieverCapabilities, indexedRecordMatchesQuery, materializePage, recencyCompare } from './retriever';
|
|
8
8
|
/**
|
|
9
9
|
* The capabilities every temporal retriever exposes: temporal "as-of" queries
|
|
10
10
|
* are operational (semantic recall and link traversal are not this retriever's
|
|
@@ -25,16 +25,16 @@ export const TEMPORAL_CAPABILITIES = {
|
|
|
25
25
|
function groupTemporalVersionsByEntity(index, query) {
|
|
26
26
|
const groups = new Map();
|
|
27
27
|
for (const entry of index.entries()) {
|
|
28
|
-
if (!isTemporalRecord(entry
|
|
28
|
+
if (!isTemporalRecord(entry) || !indexedRecordMatchesQuery(entry, query)) {
|
|
29
29
|
continue;
|
|
30
30
|
}
|
|
31
|
-
const key = `${entry.
|
|
31
|
+
const key = `${entry.envelope.kind}\0${entry.envelope.entityId}`;
|
|
32
32
|
const existing = groups.get(key);
|
|
33
33
|
if (existing === undefined) {
|
|
34
|
-
groups.set(key, [entry
|
|
34
|
+
groups.set(key, [entry]);
|
|
35
35
|
}
|
|
36
36
|
else {
|
|
37
|
-
existing.push(entry
|
|
37
|
+
existing.push(entry);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
return groups;
|
|
@@ -47,16 +47,17 @@ function groupTemporalVersionsByEntity(index, query) {
|
|
|
47
47
|
* @public
|
|
48
48
|
*/
|
|
49
49
|
export class CurrentValidRetriever {
|
|
50
|
-
constructor(
|
|
51
|
-
this._index = index;
|
|
50
|
+
constructor(params) {
|
|
51
|
+
this._index = params.index;
|
|
52
|
+
this._resolver = params.resolver;
|
|
52
53
|
}
|
|
53
54
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
54
55
|
get capabilities() {
|
|
55
56
|
return TEMPORAL_CAPABILITIES;
|
|
56
57
|
}
|
|
57
58
|
/** Family-convention factory. */
|
|
58
|
-
static create(
|
|
59
|
-
return succeed(new CurrentValidRetriever(
|
|
59
|
+
static create(params) {
|
|
60
|
+
return succeed(new CurrentValidRetriever(params));
|
|
60
61
|
}
|
|
61
62
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
62
63
|
retrieve(query) {
|
|
@@ -69,7 +70,7 @@ export class CurrentValidRetriever {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
selected.sort(recencyCompare);
|
|
72
|
-
return
|
|
73
|
+
return materializePage(selected, query, this._resolver);
|
|
73
74
|
}));
|
|
74
75
|
}
|
|
75
76
|
}
|
|
@@ -82,16 +83,17 @@ export class CurrentValidRetriever {
|
|
|
82
83
|
* @public
|
|
83
84
|
*/
|
|
84
85
|
export class AsOfRetriever {
|
|
85
|
-
constructor(
|
|
86
|
-
this._index = index;
|
|
86
|
+
constructor(params) {
|
|
87
|
+
this._index = params.index;
|
|
88
|
+
this._resolver = params.resolver;
|
|
87
89
|
}
|
|
88
90
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
89
91
|
get capabilities() {
|
|
90
92
|
return TEMPORAL_CAPABILITIES;
|
|
91
93
|
}
|
|
92
94
|
/** Family-convention factory. */
|
|
93
|
-
static create(
|
|
94
|
-
return succeed(new AsOfRetriever(
|
|
95
|
+
static create(params) {
|
|
96
|
+
return succeed(new AsOfRetriever(params));
|
|
95
97
|
}
|
|
96
98
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
97
99
|
retrieve(query) {
|
|
@@ -108,7 +110,7 @@ export class AsOfRetriever {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
selected.sort(recencyCompare);
|
|
111
|
-
return
|
|
113
|
+
return materializePage(selected, query, this._resolver);
|
|
112
114
|
}));
|
|
113
115
|
}
|
|
114
116
|
}
|
|
@@ -126,16 +128,17 @@ export class AsOfRetriever {
|
|
|
126
128
|
* @public
|
|
127
129
|
*/
|
|
128
130
|
export class HistoryRetriever {
|
|
129
|
-
constructor(
|
|
130
|
-
this._index = index;
|
|
131
|
+
constructor(params) {
|
|
132
|
+
this._index = params.index;
|
|
133
|
+
this._resolver = params.resolver;
|
|
131
134
|
}
|
|
132
135
|
/** {@inheritDoc IMemoryRetriever.capabilities} */
|
|
133
136
|
get capabilities() {
|
|
134
137
|
return TEMPORAL_CAPABILITIES;
|
|
135
138
|
}
|
|
136
139
|
/** Family-convention factory. */
|
|
137
|
-
static create(
|
|
138
|
-
return succeed(new HistoryRetriever(
|
|
140
|
+
static create(params) {
|
|
141
|
+
return succeed(new HistoryRetriever(params));
|
|
139
142
|
}
|
|
140
143
|
/** {@inheritDoc IMemoryRetriever.retrieve} */
|
|
141
144
|
retrieve(query) {
|
|
@@ -145,7 +148,7 @@ export class HistoryRetriever {
|
|
|
145
148
|
history.push(...versions);
|
|
146
149
|
}
|
|
147
150
|
history.sort(HistoryRetriever._byValidAtAscending);
|
|
148
|
-
return
|
|
151
|
+
return materializePage(history, query, this._resolver);
|
|
149
152
|
}));
|
|
150
153
|
}
|
|
151
154
|
/** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temporalRetrievers.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/temporalRetrievers.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, isTemporalRecord, selectCurrentVersion, selectVersionAsOf } from '../types';\nimport { IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n guardRetrieverCapabilities,\n indexedRecordMatchesQuery,\n limitRecords,\n recencyCompare\n} from './retriever';\n\n/**\n * The capabilities every temporal retriever exposes: temporal \"as-of\" queries\n * are operational (semantic recall and link traversal are not this retriever's\n * concern).\n * @public\n */\nexport const TEMPORAL_CAPABILITIES: IMemoryRetrieverCapabilities = {\n supportsSemanticRecall: false,\n supportsTemporalQuery: true,\n supportsLinkTraversal: false\n};\n\n/**\n * Group the temporal records surviving a query's scope / kind / tag / provenance-source / predicate\n * pre-filter by entity (`kind` + `entityId`). Non-temporal records are excluded —\n * the temporal retrievers operate only over versioned entities. The map values\n * are each entity's versions (unordered).\n */\nfunction groupTemporalVersionsByEntity(\n index: IMemoryIndex,\n query: IMemoryQuery\n): Map<string, IMemoryRecord<unknown>[]> {\n const groups: Map<string, IMemoryRecord<unknown>[]> = new Map<string, IMemoryRecord<unknown>[]>();\n for (const entry of index.entries()) {\n if (!isTemporalRecord(entry.record) || !indexedRecordMatchesQuery(entry, query)) {\n continue;\n }\n const key: string = `${entry.record.envelope.kind}\\0${entry.record.envelope.entityId}`;\n const existing: IMemoryRecord<unknown>[] | undefined = groups.get(key);\n if (existing === undefined) {\n groups.set(key, [entry.record]);\n } else {\n existing.push(entry.record);\n }\n }\n return groups;\n}\n\n/**\n * Temporal retriever returning the **current** version of each temporal entity\n * matching the query (the newest version whose `invalid_at` is null/absent),\n * recency-ordered and limited. A fully-invalidated (soft-deleted) entity\n * contributes nothing. Non-temporal records are not this retriever's concern.\n * @public\n */\nexport class CurrentValidRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n\n private constructor(index: IMemoryIndex) {\n this._index = index;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(index: IMemoryIndex): Result<CurrentValidRetriever> {\n return succeed(new CurrentValidRetriever(index));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n const selected: IMemoryRecord<unknown>[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n const current: IMemoryRecord<unknown> | undefined = selectCurrentVersion(versions);\n if (current !== undefined) {\n selected.push(current);\n }\n }\n selected.sort(recencyCompare);\n return succeed(limitRecords(selected, query.limit, query.offset));\n })\n );\n }\n}\n\n/**\n * Temporal retriever returning, for each temporal entity matching the query, the\n * version valid at `query.asOf` (epoch ms). `asOf` is this retriever's axis: a\n * query without it yields an empty success (a no-op contribution to a\n * {@link HybridRetriever}, not a failure). Results are recency-ordered and\n * limited.\n * @public\n */\nexport class AsOfRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n\n private constructor(index: IMemoryIndex) {\n this._index = index;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(index: IMemoryIndex): Result<AsOfRetriever> {\n return succeed(new AsOfRetriever(index));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n if (query.asOf === undefined) {\n return succeed([]);\n }\n const asOf: number = query.asOf;\n const selected: IMemoryRecord<unknown>[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n const valid: IMemoryRecord<unknown> | undefined = selectVersionAsOf(versions, asOf);\n if (valid !== undefined) {\n selected.push(valid);\n }\n }\n selected.sort(recencyCompare);\n return succeed(limitRecords(selected, query.limit, query.offset));\n })\n );\n }\n}\n\n/**\n * Temporal retriever returning **every** version of each temporal entity matching\n * the query, ordered ascending by `valid_at` (then `seq` as a stable tiebreak) —\n * the entity's full history. Limited after ordering.\n *\n * @remarks\n * When the query matches more than one entity, the result is a single\n * CROSS-entity list globally sorted by `valid_at` — versions of different\n * entities interleave, NOT grouped per entity — so `limit` truncates that\n * globally-sorted list. Constrain to one entity (e.g. via `query.scope`) for a\n * single entity's contiguous history.\n * @public\n */\nexport class HistoryRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n\n private constructor(index: IMemoryIndex) {\n this._index = index;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(index: IMemoryIndex): Result<HistoryRetriever> {\n return succeed(new HistoryRetriever(index));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n const history: IMemoryRecord<unknown>[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n history.push(...versions);\n }\n history.sort(HistoryRetriever._byValidAtAscending);\n return succeed(limitRecords(history, query.limit, query.offset));\n })\n );\n }\n\n /** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */\n private static _startOf(record: IMemoryRecord<unknown>): number {\n const temporal: IMemoryRecord<unknown>['envelope']['temporal'] = record.envelope.temporal;\n /* c8 ignore next 3 -- unreachable: HistoryRetriever orders only temporal records (pre-filtered by isTemporalRecord), so `temporal` is always present; the guard keeps the type honest */\n if (temporal === undefined) {\n return record.envelope.created;\n }\n return temporal.valid_at ?? record.envelope.created;\n }\n\n /**\n * Ascending-by-`valid_at` comparator (a version's `valid_at` defaults to its\n * `created` when absent), with `seq` as a stable ascending tiebreak so\n * same-instant versions order by write sequence.\n */\n private static _byValidAtAscending(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number {\n const aStart: number = HistoryRetriever._startOf(a);\n const bStart: number = HistoryRetriever._startOf(b);\n return aStart !== bStart ? aStart - bStart : a.envelope.seq - b.envelope.seq;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"temporalRetrievers.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/temporalRetrievers.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport {\n IMemoryRecord,\n IMemoryRecordResolver,\n isTemporalRecord,\n selectCurrentVersion,\n selectVersionAsOf\n} from '../types';\nimport { IIndexedMemoryEntry, IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n IRetrieverCreateParams,\n guardRetrieverCapabilities,\n indexedRecordMatchesQuery,\n materializePage,\n recencyCompare\n} from './retriever';\n\n/**\n * The capabilities every temporal retriever exposes: temporal \"as-of\" queries\n * are operational (semantic recall and link traversal are not this retriever's\n * concern).\n * @public\n */\nexport const TEMPORAL_CAPABILITIES: IMemoryRetrieverCapabilities = {\n supportsSemanticRecall: false,\n supportsTemporalQuery: true,\n supportsLinkTraversal: false\n};\n\n/**\n * Group the temporal records surviving a query's scope / kind / tag / provenance-source / predicate\n * pre-filter by entity (`kind` + `entityId`). Non-temporal records are excluded —\n * the temporal retrievers operate only over versioned entities. The map values\n * are each entity's versions (unordered).\n */\nfunction groupTemporalVersionsByEntity(\n index: IMemoryIndex,\n query: IMemoryQuery\n): Map<string, IIndexedMemoryEntry[]> {\n const groups: Map<string, IIndexedMemoryEntry[]> = new Map<string, IIndexedMemoryEntry[]>();\n for (const entry of index.entries()) {\n if (!isTemporalRecord(entry) || !indexedRecordMatchesQuery(entry, query)) {\n continue;\n }\n const key: string = `${entry.envelope.kind}\\0${entry.envelope.entityId}`;\n const existing: IIndexedMemoryEntry[] | undefined = groups.get(key);\n if (existing === undefined) {\n groups.set(key, [entry]);\n } else {\n existing.push(entry);\n }\n }\n return groups;\n}\n\n/**\n * Temporal retriever returning the **current** version of each temporal entity\n * matching the query (the newest version whose `invalid_at` is null/absent),\n * recency-ordered and limited. A fully-invalidated (soft-deleted) entity\n * contributes nothing. Non-temporal records are not this retriever's concern.\n * @public\n */\nexport class CurrentValidRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n\n private constructor(params: IRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(params: IRetrieverCreateParams): Result<CurrentValidRetriever> {\n return succeed(new CurrentValidRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n const selected: IIndexedMemoryEntry[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n const current: IIndexedMemoryEntry | undefined = selectCurrentVersion(versions);\n if (current !== undefined) {\n selected.push(current);\n }\n }\n selected.sort(recencyCompare);\n return materializePage(selected, query, this._resolver);\n })\n );\n }\n}\n\n/**\n * Temporal retriever returning, for each temporal entity matching the query, the\n * version valid at `query.asOf` (epoch ms). `asOf` is this retriever's axis: a\n * query without it yields an empty success (a no-op contribution to a\n * {@link HybridRetriever}, not a failure). Results are recency-ordered and\n * limited.\n * @public\n */\nexport class AsOfRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n\n private constructor(params: IRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(params: IRetrieverCreateParams): Result<AsOfRetriever> {\n return succeed(new AsOfRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n if (query.asOf === undefined) {\n return succeed([]);\n }\n const asOf: number = query.asOf;\n const selected: IIndexedMemoryEntry[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n const valid: IIndexedMemoryEntry | undefined = selectVersionAsOf(versions, asOf);\n if (valid !== undefined) {\n selected.push(valid);\n }\n }\n selected.sort(recencyCompare);\n return materializePage(selected, query, this._resolver);\n })\n );\n }\n}\n\n/**\n * Temporal retriever returning **every** version of each temporal entity matching\n * the query, ordered ascending by `valid_at` (then `seq` as a stable tiebreak) —\n * the entity's full history. Limited after ordering.\n *\n * @remarks\n * When the query matches more than one entity, the result is a single\n * CROSS-entity list globally sorted by `valid_at` — versions of different\n * entities interleave, NOT grouped per entity — so `limit` truncates that\n * globally-sorted list. Constrain to one entity (e.g. via `query.scope`) for a\n * single entity's contiguous history.\n * @public\n */\nexport class HistoryRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n private readonly _resolver: IMemoryRecordResolver;\n\n private constructor(params: IRetrieverCreateParams) {\n this._index = params.index;\n this._resolver = params.resolver;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return TEMPORAL_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(params: IRetrieverCreateParams): Result<HistoryRetriever> {\n return succeed(new HistoryRetriever(params));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n const history: IIndexedMemoryEntry[] = [];\n for (const versions of groupTemporalVersionsByEntity(this._index, query).values()) {\n history.push(...versions);\n }\n history.sort(HistoryRetriever._byValidAtAscending);\n return materializePage(history, query, this._resolver);\n })\n );\n }\n\n /** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */\n private static _startOf(record: IIndexedMemoryEntry): number {\n const temporal: IMemoryRecord<unknown>['envelope']['temporal'] = record.envelope.temporal;\n /* c8 ignore next 3 -- unreachable: HistoryRetriever orders only temporal records (pre-filtered by isTemporalRecord), so `temporal` is always present; the guard keeps the type honest */\n if (temporal === undefined) {\n return record.envelope.created;\n }\n return temporal.valid_at ?? record.envelope.created;\n }\n\n /**\n * Ascending-by-`valid_at` comparator (a version's `valid_at` defaults to its\n * `created` when absent), with `seq` as a stable ascending tiebreak so\n * same-instant versions order by write sequence.\n */\n private static _byValidAtAscending(a: IIndexedMemoryEntry, b: IIndexedMemoryEntry): number {\n const aStart: number = HistoryRetriever._startOf(a);\n const bStart: number = HistoryRetriever._startOf(b);\n return aStart !== bStart ? aStart - bStart : a.envelope.seq - b.envelope.seq;\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../../src/packlets/store/coverage.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Kind } from '../types';\n\n/**\n * How much of one derived artifact exists, for one {@link Kind}.\n *\n * @remarks\n * Two numbers rather than a percentage, deliberately: a ratio cannot express\n * *\"nothing was expected\"*, and a kind that is intentionally not derived (an\n * excluded kind, a kind with no projector) is a normal and healthy state that a\n * `0%` would render as an alarm.\n * @public\n */\nexport interface IArtifactCoverage {\n /**\n * Records of this kind the store would derive this artifact for, **after** any\n * exclusion. Read against {@link IDerivedStateCoverage.records} for the same\n * kind: `records: 40, expected: 0` is the exclusion story, stated rather than\n * inferred.\n */\n readonly expected: number;\n /**\n * Of those, how many the **store believes** are covered. See\n * {@link IIndexCoverage.indexSize} for why the word \"believes\" is load-bearing.\n */\n readonly covered: number;\n}\n\n/**\n * Coverage of the record-granular vector index.\n * @public\n */\nexport interface IIndexCoverage {\n /** Per-kind expected/covered, derived from the envelope walk. */\n readonly perKind: ReadonlyMap<Kind, IArtifactCoverage>;\n /**\n * What the index **actually holds**, whole-index.\n *\n * @remarks\n * **This is a fact and {@link IArtifactCoverage.covered} is a belief, and the\n * two are reported separately because their disagreement is the only free\n * signal that distinguishes a persistent index from a fresh one.** `covered`\n * counts envelopes carrying an `embeddingRef`; `indexSize` counts vectors. With\n * a persistent index they agree. With an in-memory index at open they do not —\n * the envelopes still claim references from previous sessions while the index\n * holds nothing, so `covered` **lies, in the confident direction**.\n *\n * Collapsing them into one \"coverage %\" would destroy that signal, which is why\n * this type does not offer one.\n */\n readonly indexSize: number;\n}\n\n/**\n * Coverage of the fragment-granular vector index.\n *\n * @remarks\n * **Aggregate only, and the reason is structural rather than an omission.** The\n * record lane has a per-record marker on the envelope (`embeddingRef`), so its\n * numerator falls out of the same free walk that produces the denominator. The\n * fragment lane has **no envelope marker at all** — nothing on a record says\n * whether it has fragments — so a per-kind numerator would cost one\n * `IFragmentVectorIndex.has` call per record, and coverage is contractually\n * cheap (see {@link IMemoryStore.coverage}).\n *\n * The per-kind **denominator** is still available on\n * {@link IDerivedStateCoverage.records}. A caller who needs the per-kind\n * numerator runs `reconcile(kind, 'fragment-vector')`, which reports it because\n * it is already paying for the walk.\n * @public\n */\nexport interface IFragmentIndexCoverage {\n /** Records with at least one fragment held. Mirrors `IFragmentVectorIndex.recordCount`. */\n readonly indexRecordCount: number;\n /** Total fragments held — the fan-out. Mirrors `IFragmentVectorIndex.fragmentCount`. */\n readonly indexFragmentCount: number;\n}\n\n/**\n * A snapshot of how much of the store's **derived state** exists, resolved by\n * {@link Kind} — the answer to *\"is my derived state consistent with my\n * records?\"*.\n *\n * @remarks\n * **Absent is not zero.** Each artifact member is optional, and `undefined` means\n * *this artifact is not derived here at all* — no rank projector is registered,\n * or that index lane is not wired. It never means *nothing is covered*. Folding\n * the two would make a health surface render a confident `0%` for a feature the\n * deployment deliberately did not turn on, which is the same defect as\n * `embeddingRef`'s three-way ambiguity one level up.\n *\n * So: `fragmentVectors: undefined` is a store with no fragment index and is not a\n * problem. `fragmentVectors: { indexRecordCount: 0, indexFragmentCount: 0 }` is a\n * wired fragment index holding nothing, and probably is.\n * @public\n */\nexport interface IDerivedStateCoverage {\n /** Records per kind — the denominator every other number is read against. */\n readonly records: ReadonlyMap<Kind, number>;\n /** Absent when no kind has a registered {@link RankProjector}. */\n readonly rank?: ReadonlyMap<Kind, IArtifactCoverage>;\n /** Absent when the record-vector lane is not wired. */\n readonly recordVectors?: IIndexCoverage;\n /** Absent when the fragment lane is not wired. */\n readonly fragmentVectors?: IFragmentIndexCoverage;\n}\n"]}
|