@fgv/ts-agent-memory 5.1.0-45 → 5.1.0-46
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 → 6e1299d1cebb080772163a532c221cd181b54f9f.tar.log} +20 -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 +79 -4
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js +10 -7
- package/dist/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
- package/dist/packlets/store/fileTreeMemoryStore.js +20 -4
- 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/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/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 +240 -37
- package/etc/ts-agent-memory.api.md +10 -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 +65 -3
- package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
- package/lib/packlets/store/fileTreeMemoryStore.js +20 -4
- 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/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/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/retrieve/fragmentSemanticRetriever.ts +10 -7
- package/src/packlets/store/fileTreeMemoryStore.ts +74 -4
- 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/store/fileTreeMemoryStore.test.ts +75 -0
- package/src/test/unit/store/indexInjection.test.ts +550 -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 +23 -11
- package/temp/build/typescript/ts_8nwakTlr.json +1 -1
- package/temp/ts-agent-memory.api.json +177 -21
- package/temp/ts-agent-memory.api.md +10 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Start time:
|
|
2
|
-
Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/
|
|
1
|
+
Start time: Sat Aug 01 2026 02:41:15 GMT+0000 (Coordinated Universal Time)
|
|
2
|
+
Invoking "/usr/bin/tar -c -f /home/runner/work/fgv/fgv/common/temp/build-cache/6e1299d1cebb080772163a532c221cd181b54f9f-dad54f1e2d176bf4.temp -z --files-from=-"
|
|
3
3
|
|
|
4
4
|
======= BEGIN PROCESS INPUT ======
|
|
5
5
|
.rush/temp/operation/build/all.log
|
|
@@ -77,6 +77,8 @@ dist/packlets/types/temporal.js
|
|
|
77
77
|
dist/packlets/types/temporal.js.map
|
|
78
78
|
dist/packlets/types/writePolicy.js
|
|
79
79
|
dist/packlets/types/writePolicy.js.map
|
|
80
|
+
dist/packlets/vector/fragmentConverters.js
|
|
81
|
+
dist/packlets/vector/fragmentConverters.js.map
|
|
80
82
|
dist/packlets/vector/inMemoryCosineIndex.js
|
|
81
83
|
dist/packlets/vector/inMemoryCosineIndex.js.map
|
|
82
84
|
dist/packlets/vector/inMemoryFragmentCosineIndex.js
|
|
@@ -117,6 +119,8 @@ dist/test/unit/store/fileTreeMemoryStore.test.js
|
|
|
117
119
|
dist/test/unit/store/fileTreeMemoryStore.test.js.map
|
|
118
120
|
dist/test/unit/store/fragmentEmbedOnWrite.test.js
|
|
119
121
|
dist/test/unit/store/fragmentEmbedOnWrite.test.js.map
|
|
122
|
+
dist/test/unit/store/indexInjection.test.js
|
|
123
|
+
dist/test/unit/store/indexInjection.test.js.map
|
|
120
124
|
dist/test/unit/store/lenientOpen.test.js
|
|
121
125
|
dist/test/unit/store/lenientOpen.test.js.map
|
|
122
126
|
dist/test/unit/store/listScoped.test.js
|
|
@@ -141,6 +145,8 @@ dist/test/unit/types/temporalPolicy.test.js
|
|
|
141
145
|
dist/test/unit/types/temporalPolicy.test.js.map
|
|
142
146
|
dist/test/unit/types/writePolicy.test.js
|
|
143
147
|
dist/test/unit/types/writePolicy.test.js.map
|
|
148
|
+
dist/test/unit/vector/fragmentConverters.test.js
|
|
149
|
+
dist/test/unit/vector/fragmentConverters.test.js.map
|
|
144
150
|
dist/test/unit/vector/inMemoryCosineIndex.test.js
|
|
145
151
|
dist/test/unit/vector/inMemoryCosineIndex.test.js.map
|
|
146
152
|
dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js
|
|
@@ -293,6 +299,10 @@ lib/packlets/types/writePolicy.d.ts
|
|
|
293
299
|
lib/packlets/types/writePolicy.d.ts.map
|
|
294
300
|
lib/packlets/types/writePolicy.js
|
|
295
301
|
lib/packlets/types/writePolicy.js.map
|
|
302
|
+
lib/packlets/vector/fragmentConverters.d.ts
|
|
303
|
+
lib/packlets/vector/fragmentConverters.d.ts.map
|
|
304
|
+
lib/packlets/vector/fragmentConverters.js
|
|
305
|
+
lib/packlets/vector/fragmentConverters.js.map
|
|
296
306
|
lib/packlets/vector/inMemoryCosineIndex.d.ts
|
|
297
307
|
lib/packlets/vector/inMemoryCosineIndex.d.ts.map
|
|
298
308
|
lib/packlets/vector/inMemoryCosineIndex.js
|
|
@@ -373,6 +383,10 @@ lib/test/unit/store/fragmentEmbedOnWrite.test.d.ts
|
|
|
373
383
|
lib/test/unit/store/fragmentEmbedOnWrite.test.d.ts.map
|
|
374
384
|
lib/test/unit/store/fragmentEmbedOnWrite.test.js
|
|
375
385
|
lib/test/unit/store/fragmentEmbedOnWrite.test.js.map
|
|
386
|
+
lib/test/unit/store/indexInjection.test.d.ts
|
|
387
|
+
lib/test/unit/store/indexInjection.test.d.ts.map
|
|
388
|
+
lib/test/unit/store/indexInjection.test.js
|
|
389
|
+
lib/test/unit/store/indexInjection.test.js.map
|
|
376
390
|
lib/test/unit/store/lenientOpen.test.d.ts
|
|
377
391
|
lib/test/unit/store/lenientOpen.test.d.ts.map
|
|
378
392
|
lib/test/unit/store/lenientOpen.test.js
|
|
@@ -421,6 +435,10 @@ lib/test/unit/types/writePolicy.test.d.ts
|
|
|
421
435
|
lib/test/unit/types/writePolicy.test.d.ts.map
|
|
422
436
|
lib/test/unit/types/writePolicy.test.js
|
|
423
437
|
lib/test/unit/types/writePolicy.test.js.map
|
|
438
|
+
lib/test/unit/vector/fragmentConverters.test.d.ts
|
|
439
|
+
lib/test/unit/vector/fragmentConverters.test.d.ts.map
|
|
440
|
+
lib/test/unit/vector/fragmentConverters.test.js
|
|
441
|
+
lib/test/unit/vector/fragmentConverters.test.js.map
|
|
424
442
|
lib/test/unit/vector/inMemoryCosineIndex.test.d.ts
|
|
425
443
|
lib/test/unit/vector/inMemoryCosineIndex.test.d.ts.map
|
|
426
444
|
lib/test/unit/vector/inMemoryCosineIndex.test.js
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
{"kind":"O","text":"[build:lint] Using ESLint version 9.39.5\n"}
|
|
6
6
|
{"kind":"O","text":"[build:api-extractor] Using API Extractor version 7.58.9\n"}
|
|
7
7
|
{"kind":"O","text":"[build:api-extractor] Analysis will use the bundled TypeScript version 5.9.3\n"}
|
|
8
|
-
{"kind":"O","text":" ---- build finished (
|
|
9
|
-
{"kind":"O","text":"-------------------- Finished (
|
|
8
|
+
{"kind":"O","text":" ---- build finished (28.708s) ---- \n"}
|
|
9
|
+
{"kind":"O","text":"-------------------- Finished (28.714s) --------------------\n"}
|
|
@@ -5,5 +5,5 @@ Invoking: heft build --clean
|
|
|
5
5
|
[build:lint] Using ESLint version 9.39.5
|
|
6
6
|
[build:api-extractor] Using API Extractor version 7.58.9
|
|
7
7
|
[build:api-extractor] Analysis will use the bundled TypeScript version 5.9.3
|
|
8
|
-
---- build finished (
|
|
9
|
-
-------------------- Finished (
|
|
8
|
+
---- build finished (28.708s) ----
|
|
9
|
+
-------------------- Finished (28.714s) --------------------
|
|
@@ -5,5 +5,5 @@
|
|
|
5
5
|
{"kind":"O","text":"[build:lint] Using ESLint version 9.39.5\n"}
|
|
6
6
|
{"kind":"O","text":"[build:api-extractor] Using API Extractor version 7.58.9\n"}
|
|
7
7
|
{"kind":"O","text":"[build:api-extractor] Analysis will use the bundled TypeScript version 5.9.3\n"}
|
|
8
|
-
{"kind":"O","text":" ---- build finished (
|
|
9
|
-
{"kind":"O","text":"-------------------- Finished (
|
|
8
|
+
{"kind":"O","text":" ---- build finished (28.708s) ---- \n"}
|
|
9
|
+
{"kind":"O","text":"-------------------- Finished (28.714s) --------------------\n"}
|
package/README.md
CHANGED
|
@@ -6,11 +6,12 @@ edges, content-hash dedup, and the optional-layer seams (vector / temporal /
|
|
|
6
6
|
observe). It owns none of a consumer's processing, transformation, or
|
|
7
7
|
composition logic.
|
|
8
8
|
|
|
9
|
-
> **Status:**
|
|
10
|
-
> knowledge-first.
|
|
11
|
-
> converters
|
|
9
|
+
> **Status:** under active development. The package is being built
|
|
10
|
+
> knowledge-first. The surface listing below covers the foundational type model
|
|
11
|
+
> and converters; the store, index, retrieval, observe, and vector layers ship
|
|
12
|
+
> alongside them and are not itemized here.
|
|
12
13
|
|
|
13
|
-
##
|
|
14
|
+
## Foundational surface
|
|
14
15
|
|
|
15
16
|
### `types` packlet
|
|
16
17
|
|
|
@@ -34,6 +35,80 @@ composition logic.
|
|
|
34
35
|
`joinFrontmatter` / `parseMemoryFile` / `serializeMemoryFile` helpers for
|
|
35
36
|
the `---\n<yaml>\n---\n<body>` memory-file format.
|
|
36
37
|
|
|
38
|
+
## Record updates — the merge contract
|
|
39
|
+
|
|
40
|
+
When a `put` targets an `entityId` that already exists, the write is an
|
|
41
|
+
**update**, not a replace. The store projects the incoming record's mutable
|
|
42
|
+
fields into a patch and hands it to the kind's `IWritePolicy.applyUpdate`, which
|
|
43
|
+
applies it as an **RFC-7386 JSON Merge Patch** over the policy's declared
|
|
44
|
+
mutable surface. This is a **pinned contract**, not an artifact of the current
|
|
45
|
+
merge configuration — the shipped policies compose `@fgv/ts-json`'s `JsonEditor`
|
|
46
|
+
with `{ nullAsDelete: true, arrayMergeBehavior: 'replace' }` specifically to get
|
|
47
|
+
RFC-7386 semantics, and `applyUpdate` documents them as the interface contract.
|
|
48
|
+
|
|
49
|
+
Within the policy's declared mutable surface:
|
|
50
|
+
|
|
51
|
+
- **Objects merge per key.** Keys you supply overwrite; keys you omit are
|
|
52
|
+
**preserved**, not dropped. An update that sets one provenance field leaves
|
|
53
|
+
every sibling field intact.
|
|
54
|
+
- **An explicit `null` on a sub-key clears that sub-key.** This is the
|
|
55
|
+
sanctioned way to remove a single key from a nested object such as
|
|
56
|
+
`provenance`. The cleared key is gone from the persisted record and does not
|
|
57
|
+
reappear on reload.
|
|
58
|
+
- **Arrays replace wholesale.** `tags` and `links` are not element-merged.
|
|
59
|
+
- **A whole-block `null` on a required field is rejected loudly**, never
|
|
60
|
+
silently accepted. `body`, `tags`, `links`, and `provenance` are required; a
|
|
61
|
+
patch that deletes one fails with
|
|
62
|
+
`... merge patch may not delete required field(s): <names>`. (`embeddingRef`
|
|
63
|
+
is optional and *may* be cleared by a `null`, which restores it to absent
|
|
64
|
+
rather than to `null`.)
|
|
65
|
+
- **Keys outside the mutable surface are ignored.** Identity and
|
|
66
|
+
transaction-time envelope fields (`id`, `entityId`, `kind`, `created`,
|
|
67
|
+
`updated`, `seq`, `contentHash`) are preserved verbatim; the store stamps
|
|
68
|
+
`updated` / `seq` / `contentHash` on write.
|
|
69
|
+
|
|
70
|
+
### `provenance` is on the pinned surface
|
|
71
|
+
|
|
72
|
+
`KnowledgeLwwPolicy` and `TemporalVersionedPolicy` both hard-code their mutable
|
|
73
|
+
surface as `['body', 'tags', 'links', 'provenance', 'embeddingRef']`. For those
|
|
74
|
+
policies the guarantees above apply to `provenance` specifically: per-key merge,
|
|
75
|
+
sub-key clearing via `null`, and loud rejection of a whole-block delete are all
|
|
76
|
+
part of the contract and are pinned by tests.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
// existing.envelope.provenance: { source: 'agent', confidence: 0.9, note: 'stale' }
|
|
80
|
+
|
|
81
|
+
// clear one sub-key; siblings survive
|
|
82
|
+
policy.applyUpdate(existing, { provenance: { note: null } });
|
|
83
|
+
// => provenance: { source: 'agent', confidence: 0.9 }
|
|
84
|
+
|
|
85
|
+
// revise one sub-key; siblings survive
|
|
86
|
+
policy.applyUpdate(existing, { provenance: { confidence: 0.5 } });
|
|
87
|
+
// => provenance: { source: 'agent', confidence: 0.5, note: 'stale' }
|
|
88
|
+
|
|
89
|
+
// delete the whole block — rejected, not silently accepted
|
|
90
|
+
policy.applyUpdate(existing, { provenance: null });
|
|
91
|
+
// => Result.fail('knowledge LWW: merge patch may not delete required field(s): provenance')
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Going through the store rather than calling a policy directly, the same three
|
|
95
|
+
cases are expressed as the `provenance` value on the record you `put`: include a
|
|
96
|
+
key to set it, set a key to `null` to clear it, and omit a key to leave it
|
|
97
|
+
alone. A whole-block delete is not expressible there at all —
|
|
98
|
+
`IMemoryEnvelope.provenance` is non-nullable and `envelopeConverter` rejects
|
|
99
|
+
`null` — so that case fails at the converter instead of at the policy. Either
|
|
100
|
+
way it fails loudly.
|
|
101
|
+
|
|
102
|
+
### The surface is policy-dependent
|
|
103
|
+
|
|
104
|
+
`MemoryCapCullPolicy` takes its `mutableFields` from the caller, so **what it
|
|
105
|
+
guarantees depends on what you declared**. A field you did not declare mutable
|
|
106
|
+
is not merged at all: patch keys naming it are dropped before the merge runs, so
|
|
107
|
+
a `null` on it neither clears anything nor raises an error — it is simply inert,
|
|
108
|
+
and the existing value is preserved verbatim. If you want the guarantees above
|
|
109
|
+
for `provenance` under a cap-cull policy, `provenance` must appear in the
|
|
110
|
+
`mutableFields` you pass to `MemoryCapCullPolicy.create`.
|
|
111
|
+
|
|
37
112
|
## Conventions
|
|
38
113
|
|
|
39
114
|
`Result<T>` on every fallible operation; no `any`; Converters/Validators for
|
|
@@ -14,16 +14,19 @@ export const FRAGMENT_SEMANTIC_UNWIRED_MESSAGE = 'fragment recall: no fragment i
|
|
|
14
14
|
* The sub-document semantic-search retriever — the "discovery" half of a
|
|
15
15
|
* search-then-read contract. It embeds a fragment query, queries the
|
|
16
16
|
* {@link IFragmentVectorIndex}, and returns the raw per-fragment
|
|
17
|
-
* {@link IVectorQueryHit | hits} (each carrying a record `target` AND
|
|
18
|
-
* `locator`
|
|
19
|
-
*
|
|
17
|
+
* {@link IVectorQueryHit | hits} (each carrying a record `target` AND whichever of
|
|
18
|
+
* `locator` / `fragmentId` the stored fragment was indexed with), NOT resolved
|
|
19
|
+
* records: the consumer re-reads each record and resolves the fragment on its own
|
|
20
|
+
* read side. Note the `locator` span is advisory — see {@link IFragmentLocator}; it
|
|
21
|
+
* is not a slice guaranteed to reproduce the fragment's text.
|
|
20
22
|
*
|
|
21
23
|
* @remarks
|
|
22
24
|
* Deliberately NOT an {@link IMemoryRetriever}: memory recall is record-granular and
|
|
23
|
-
* returns records; fragment discovery is
|
|
24
|
-
* it a distinct surface matches the consumer contract (memory
|
|
25
|
-
* sub-document knowledge uses a separate fragment index) and
|
|
26
|
-
* record retriever's return type with
|
|
25
|
+
* returns records; fragment discovery is fragment-granular and returns fragment
|
|
26
|
+
* identities. Keeping it a distinct surface matches the consumer contract (memory
|
|
27
|
+
* stays record-granular; sub-document knowledge uses a separate fragment index) and
|
|
28
|
+
* avoids overloading the record retriever's return type with identity fields that
|
|
29
|
+
* only make sense here.
|
|
27
30
|
*
|
|
28
31
|
* When no backend is wired, `supportsFragmentRecall` is `false` and any fragment
|
|
29
32
|
* 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,OAAO,EAAU,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAItD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAC5C,6GAA6G,CAAC;AA0ChH
|
|
1
|
+
{"version":3,"file":"fragmentSemanticRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/fragmentSemanticRetriever.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAItD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAC5C,6GAA6G,CAAC;AA0ChH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,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,OAAO,CAAC,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,IAAI,CAAC,iCAAiC,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,IAAI,CAAC,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,IAAI,CAAC,oBAAoB,KAAK,YAAY,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;CACF","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"]}
|
|
@@ -60,12 +60,14 @@ export class FileTreeMemoryStore {
|
|
|
60
60
|
return this._skippedRecords;
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
|
-
* Family-convention factory.
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* Family-convention factory. Resolves the derived index (the caller's
|
|
64
|
+
* {@link IFileTreeMemoryStoreCreateParams.index | index} when supplied, a fresh
|
|
65
|
+
* {@link MemoryIndex} otherwise) and a default LWW policy, then performs an
|
|
66
|
+
* initial FileTree walk so an existing vault is indexed (and the `seq` counter
|
|
67
|
+
* resumes past the highest persisted `seq`).
|
|
66
68
|
*/
|
|
67
69
|
static create(params) {
|
|
68
|
-
return KnowledgeLwwPolicy.create().onSuccess((defaultPolicy) =>
|
|
70
|
+
return KnowledgeLwwPolicy.create().onSuccess((defaultPolicy) => FileTreeMemoryStore._resolveIndex(params.index).onSuccess((index) => {
|
|
69
71
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
70
72
|
const store = new FileTreeMemoryStore({
|
|
71
73
|
root: params.root,
|
|
@@ -88,6 +90,20 @@ export class FileTreeMemoryStore {
|
|
|
88
90
|
return store._initialIndex((_h = params.onRecordError) !== null && _h !== void 0 ? _h : 'fail').onSuccess(() => succeed(store));
|
|
89
91
|
}));
|
|
90
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve the derived index for a `create()`: the caller's injected
|
|
95
|
+
* {@link IMemoryIndex} verbatim, or a fresh {@link MemoryIndex} when none was
|
|
96
|
+
* supplied (the default that keeps an omitting caller byte-identical).
|
|
97
|
+
*/
|
|
98
|
+
static _resolveIndex(index) {
|
|
99
|
+
// Nullish rather than strictly-undefined, matching how every sibling optional
|
|
100
|
+
// param in `create()` handles absence (`params.codecs ?? new Map()`, and so on).
|
|
101
|
+
// A JS caller — or a TS caller arriving through an `unknown` escape hatch —
|
|
102
|
+
// passing `null` otherwise gets `null` installed as the store's index and fails
|
|
103
|
+
// later inside `entries()` with a message that names neither the param nor the
|
|
104
|
+
// cause.
|
|
105
|
+
return index ? succeed(index) : MemoryIndex.create();
|
|
106
|
+
}
|
|
91
107
|
/** {@inheritDoc IMemoryStore.get} */
|
|
92
108
|
async get(kind, entityId) {
|
|
93
109
|
var _a;
|