@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.
Files changed (120) hide show
  1. package/.rush/temp/{bc5b0f2098f7df2a5c498ea77b0de13316349b96.tar.log → 5667e36f1c0cc503a5c20a3ebff76e7b9a702d41.tar.log} +26 -2
  2. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +2 -2
  3. package/.rush/temp/operation/build/all.log +2 -2
  4. package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/README.md +134 -4
  7. package/dist/packlets/ingest/orchestrator.js +104 -19
  8. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  9. package/dist/packlets/retrieve/fragmentSemanticRetriever.js +10 -7
  10. package/dist/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
  11. package/dist/packlets/store/fileTreeMemoryStore.js +31 -8
  12. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  13. package/dist/packlets/types/writePolicy.js +10 -2
  14. package/dist/packlets/types/writePolicy.js.map +1 -1
  15. package/dist/packlets/vector/fragmentConverters.js +41 -0
  16. package/dist/packlets/vector/fragmentConverters.js.map +1 -0
  17. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +17 -9
  18. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  19. package/dist/packlets/vector/index.js +1 -0
  20. package/dist/packlets/vector/index.js.map +1 -1
  21. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  22. package/dist/test/unit/ingest/dedupScope.test.js +434 -0
  23. package/dist/test/unit/ingest/dedupScope.test.js.map +1 -0
  24. package/dist/test/unit/ingest/orchestrator.test.js +7 -4
  25. package/dist/test/unit/ingest/orchestrator.test.js.map +1 -1
  26. package/dist/test/unit/store/fileTreeMemoryStore.test.js +64 -0
  27. package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  28. package/dist/test/unit/store/indexInjection.test.js +432 -0
  29. package/dist/test/unit/store/indexInjection.test.js.map +1 -0
  30. package/dist/test/unit/tools/memoryTools.test.js +1 -0
  31. package/dist/test/unit/tools/memoryTools.test.js.map +1 -1
  32. package/dist/test/unit/types/temporalPolicy.test.js +21 -0
  33. package/dist/test/unit/types/temporalPolicy.test.js.map +1 -1
  34. package/dist/test/unit/types/writePolicy.test.js +83 -0
  35. package/dist/test/unit/types/writePolicy.test.js.map +1 -1
  36. package/dist/test/unit/vector/fragmentConverters.test.js +58 -0
  37. package/dist/test/unit/vector/fragmentConverters.test.js.map +1 -0
  38. package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js +65 -0
  39. package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +1 -1
  40. package/dist/ts-agent-memory.d.ts +332 -42
  41. package/etc/ts-agent-memory.api.md +12 -1
  42. package/lib/packlets/ingest/orchestrator.d.ts +62 -5
  43. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  44. package/lib/packlets/ingest/orchestrator.js +104 -19
  45. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  46. package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts +10 -7
  47. package/lib/packlets/retrieve/fragmentSemanticRetriever.d.ts.map +1 -1
  48. package/lib/packlets/retrieve/fragmentSemanticRetriever.js +10 -7
  49. package/lib/packlets/retrieve/fragmentSemanticRetriever.js.map +1 -1
  50. package/lib/packlets/store/fileTreeMemoryStore.d.ts +96 -4
  51. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  52. package/lib/packlets/store/fileTreeMemoryStore.js +31 -8
  53. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  54. package/lib/packlets/types/writePolicy.d.ts +40 -2
  55. package/lib/packlets/types/writePolicy.d.ts.map +1 -1
  56. package/lib/packlets/types/writePolicy.js +10 -2
  57. package/lib/packlets/types/writePolicy.js.map +1 -1
  58. package/lib/packlets/vector/fragmentConverters.d.ts +25 -0
  59. package/lib/packlets/vector/fragmentConverters.d.ts.map +1 -0
  60. package/lib/packlets/vector/fragmentConverters.js +44 -0
  61. package/lib/packlets/vector/fragmentConverters.js.map +1 -0
  62. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +5 -3
  63. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
  64. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +17 -9
  65. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  66. package/lib/packlets/vector/index.d.ts +1 -0
  67. package/lib/packlets/vector/index.d.ts.map +1 -1
  68. package/lib/packlets/vector/index.js +1 -0
  69. package/lib/packlets/vector/index.js.map +1 -1
  70. package/lib/packlets/vector/vectorIndex.d.ts +97 -22
  71. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  72. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  73. package/lib/test/unit/ingest/dedupScope.test.d.ts +2 -0
  74. package/lib/test/unit/ingest/dedupScope.test.d.ts.map +1 -0
  75. package/lib/test/unit/ingest/dedupScope.test.js +436 -0
  76. package/lib/test/unit/ingest/dedupScope.test.js.map +1 -0
  77. package/lib/test/unit/ingest/orchestrator.test.js +7 -4
  78. package/lib/test/unit/ingest/orchestrator.test.js.map +1 -1
  79. package/lib/test/unit/store/fileTreeMemoryStore.test.js +64 -0
  80. package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  81. package/lib/test/unit/store/indexInjection.test.d.ts +2 -0
  82. package/lib/test/unit/store/indexInjection.test.d.ts.map +1 -0
  83. package/lib/test/unit/store/indexInjection.test.js +434 -0
  84. package/lib/test/unit/store/indexInjection.test.js.map +1 -0
  85. package/lib/test/unit/tools/memoryTools.test.js +1 -0
  86. package/lib/test/unit/tools/memoryTools.test.js.map +1 -1
  87. package/lib/test/unit/types/temporalPolicy.test.js +21 -0
  88. package/lib/test/unit/types/temporalPolicy.test.js.map +1 -1
  89. package/lib/test/unit/types/writePolicy.test.js +83 -0
  90. package/lib/test/unit/types/writePolicy.test.js.map +1 -1
  91. package/lib/test/unit/vector/fragmentConverters.test.d.ts +2 -0
  92. package/lib/test/unit/vector/fragmentConverters.test.d.ts.map +1 -0
  93. package/lib/test/unit/vector/fragmentConverters.test.js +60 -0
  94. package/lib/test/unit/vector/fragmentConverters.test.js.map +1 -0
  95. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js +65 -0
  96. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +1 -1
  97. package/package.json +7 -7
  98. package/rush-logs/ts-agent-memory.build.cache.log +1 -1
  99. package/rush-logs/ts-agent-memory.build.log +2 -2
  100. package/src/packlets/ingest/orchestrator.ts +116 -21
  101. package/src/packlets/retrieve/fragmentSemanticRetriever.ts +10 -7
  102. package/src/packlets/store/fileTreeMemoryStore.ts +114 -6
  103. package/src/packlets/types/writePolicy.ts +40 -2
  104. package/src/packlets/vector/fragmentConverters.ts +55 -0
  105. package/src/packlets/vector/inMemoryFragmentCosineIndex.ts +35 -8
  106. package/src/packlets/vector/index.ts +1 -0
  107. package/src/packlets/vector/vectorIndex.ts +97 -22
  108. package/src/test/unit/ingest/dedupScope.test.ts +542 -0
  109. package/src/test/unit/ingest/orchestrator.test.ts +4 -0
  110. package/src/test/unit/store/fileTreeMemoryStore.test.ts +75 -0
  111. package/src/test/unit/store/indexInjection.test.ts +550 -0
  112. package/src/test/unit/tools/memoryTools.test.ts +1 -0
  113. package/src/test/unit/types/temporalPolicy.test.ts +28 -0
  114. package/src/test/unit/types/writePolicy.test.ts +112 -0
  115. package/src/test/unit/vector/fragmentConverters.test.ts +80 -0
  116. package/src/test/unit/vector/inMemoryFragmentCosineIndex.test.ts +88 -0
  117. package/temp/build/lint/_eslint-5eVG3S6w.json +30 -14
  118. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  119. package/temp/ts-agent-memory.api.json +274 -21
  120. package/temp/ts-agent-memory.api.md +12 -1
@@ -2,10 +2,24 @@ import { Result } from '@fgv/ts-utils';
2
2
  import { IEdgeTarget, IMemoryRecord } from '../types';
3
3
  /**
4
4
  * A half-open `[start, end)` span into a record's body — the in-record locator a
5
- * {@link IFragmentVectorIndex} carries on each fragment hit. `start` is inclusive,
5
+ * {@link IFragmentVectorIndex} may carry on a fragment. `start` is inclusive,
6
6
  * `end` exclusive. The unit (character / byte / token offsets) is the consumer's
7
7
  * choice: the index stores the two integers opaquely and never interprets them,
8
8
  * so they line up with whatever locator the consumer's own read side uses.
9
+ *
10
+ * @remarks
11
+ * **The span is advisory.** It names the region of the body a fragment was
12
+ * *derived from*; it is NOT a slice guaranteed to reproduce the fragment's text.
13
+ * `body.slice(start, end)` round-trips only under a segmenter that merely chooses
14
+ * boundaries. Under a **rewriting** segmenter — one that turns a span into a
15
+ * curated block, an increasingly common ingestion shape when a model both selects
16
+ * and rewrites — the fragment text is not a substring of the body at all, and the
17
+ * fragmentation is not re-derivable from the body. Treat the span as a pointer for
18
+ * locating context, never as an extraction recipe.
19
+ *
20
+ * A fragment whose provenance cannot honestly be expressed as a body span should
21
+ * omit the locator entirely and carry an {@link IEmbeddedFragment.fragmentId}
22
+ * instead.
9
23
  * @public
10
24
  */
11
25
  export interface IFragmentLocator {
@@ -28,9 +42,27 @@ export interface IFragmentLocator {
28
42
  * that share a stem. The caller re-resolves the hit against the record index by
29
43
  * the same scoped address.
30
44
  *
31
- * `locator` is present only on hits from a {@link IFragmentVectorIndex} — it
32
- * identifies WHICH fragment of the record matched. Record-granular
33
- * {@link IVectorIndex} hits omit it.
45
+ * **No single field discriminates a fragment hit from a record-granular hit.** A
46
+ * record hit carries neither `locator` nor `fragmentId`; a fragment hit carries at
47
+ * least one of the two, but not necessarily any particular one — a fragment with a
48
+ * body span but no consumer-minted id, and a fragment with an id but no honest span,
49
+ * are both legal. Testing one field for presence therefore cannot tell you which
50
+ * kind of hit you hold.
51
+ *
52
+ * That "at least one" requirement is enforced on the upsert side by
53
+ * {@link embeddedFragmentConverter} — a different boundary from this type — and is
54
+ * deliberately NOT offered here as a discriminator either. A caller keyed off it
55
+ * would be coupled to an invariant this type does not own, and would fail silently
56
+ * if the invariant were ever relaxed.
57
+ *
58
+ * **The robust rule is that fragment-ness is determined by which index produced the
59
+ * hit**: {@link IFragmentVectorIndex.query} returns fragment hits and
60
+ * {@link IVectorIndex.query} returns record hits. The caller chose the index it
61
+ * queried, so it already knows which kind it is holding.
62
+ *
63
+ * Note in particular that an absent `locator` now carries **two** distinct meanings
64
+ * — a record-granular hit, or a fragment with no honest body span (see
65
+ * {@link IFragmentLocator}) — which is precisely why presence-branching is unsafe.
34
66
  * @public
35
67
  */
36
68
  export interface IVectorQueryHit {
@@ -38,8 +70,18 @@ export interface IVectorQueryHit {
38
70
  readonly target: IEdgeTarget;
39
71
  /** Backend similarity score; higher is more similar. */
40
72
  readonly score: number;
41
- /** The matched fragment's in-record span; present only for fragment-index hits. */
73
+ /**
74
+ * The advisory in-record span the matched fragment was derived from, when the
75
+ * producing fragment carried one. Absent on record-granular hits AND on fragment
76
+ * hits with no honest span — see the remarks above; do not branch on its presence.
77
+ */
42
78
  readonly locator?: IFragmentLocator;
79
+ /**
80
+ * The opaque identity the producing fragment was stored with, carried back
81
+ * verbatim. Absent on record-granular hits AND on fragment hits stored without
82
+ * one — see the remarks above; do not branch on its presence.
83
+ */
84
+ readonly fragmentId?: string;
43
85
  }
44
86
  /**
45
87
  * The vector-index seam an embedding backend implements to make
@@ -77,31 +119,63 @@ export interface IVectorIndex {
77
119
  query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
78
120
  }
79
121
  /**
80
- * One embedded fragment of a record: its in-record {@link IFragmentLocator | span}
81
- * and the vector for that span. Produced by a {@link FragmentEmbedder} and stored
82
- * via {@link IFragmentVectorIndex.addFragments}.
122
+ * One embedded fragment of a record: the fragment's vector, plus at least one of the
123
+ * two ways to identify it its advisory in-record {@link IFragmentLocator | span}
124
+ * and/or an opaque consumer-minted {@link IEmbeddedFragment.fragmentId | fragmentId}.
125
+ * Produced by a {@link FragmentEmbedder} and stored via
126
+ * {@link IFragmentVectorIndex.addFragments}.
127
+ *
128
+ * @remarks
129
+ * Both identity fields are optional **in the type**, but the "at least one"
130
+ * requirement is real — a fragment carrying neither is unidentifiable at the read
131
+ * side. It is enforced by {@link embeddedFragmentConverter} (and re-checked by the
132
+ * in-package index implementations) rather than by a conditional-required union
133
+ * (`{ locator; fragmentId? } | { locator?; fragmentId }`), which was considered and
134
+ * declined: the union costs at every construction site and buys nothing at the read
135
+ * site, where each field reads as `… | undefined` either way.
83
136
  * @public
84
137
  */
85
138
  export interface IEmbeddedFragment {
86
- /** The fragment's in-record span. */
87
- readonly locator: IFragmentLocator;
88
- /** The embedding vector for that span. */
139
+ /**
140
+ * The region of the record body this fragment was derived from, when one can be
141
+ * stated honestly. Advisory see {@link IFragmentLocator}; it is NOT a slice that
142
+ * reproduces the fragment text. Omit it for a fragment with no honest body span (a
143
+ * rewriting segmenter), in which case `fragmentId` must be supplied.
144
+ */
145
+ readonly locator?: IFragmentLocator;
146
+ /**
147
+ * An opaque, consumer-minted identity for this fragment, carried verbatim through
148
+ * the index and returned on the corresponding {@link IVectorQueryHit}. The index
149
+ * **never parses it, never filters on it, and never assigns meaning to it** — it is
150
+ * a bytestring, not part of the query path. It exists so a fragment stays
151
+ * identifiable when its text is not re-derivable from the record body.
152
+ *
153
+ * The guarantee is "we never parse it", NOT "we keep it stable". Because
154
+ * `addFragments` is whole-record-replace, an updated record re-emits its entire
155
+ * fragment set, so **any stability of a fragment id across re-embeds is the
156
+ * consumer's responsibility**, not the index's.
157
+ */
158
+ readonly fragmentId?: string;
159
+ /** The embedding vector for this fragment. */
89
160
  readonly vector: Float32Array;
90
161
  }
91
162
  /**
92
163
  * The fragment-granular sibling of {@link IVectorIndex}: instead of one vector per
93
- * record it holds many vectors per record, each tagged with an in-record
94
- * {@link IFragmentLocator}, and its `query` returns per-fragment hits carrying that
95
- * locator. This is the seam behind sub-document semantic search — the "discovery"
96
- * half of a search-then-read contract, where a hit's `(target, locator)` tells the
97
- * consumer which record AND which span to read.
164
+ * record it holds many vectors per record, each tagged with the identity its
165
+ * {@link IEmbeddedFragment} carried, and its `query` returns per-fragment hits
166
+ * carrying that identity back. This is the seam behind sub-document semantic search
167
+ * — the "discovery" half of a search-then-read contract, where a hit tells the
168
+ * consumer which record AND which fragment of it to read.
98
169
  *
99
170
  * @remarks
100
- * Deliberately NOT `extends IVectorIndex`: an index keyed by `(target, locator)`
101
- * has no well-defined single-vector `add(target, vector)`. It is a parallel
102
- * contract with three operations — `addFragments`, `remove`, `query` — reusing
103
- * {@link IVectorQueryHit} (whose `locator` is always populated here). Kept distinct
104
- * from the record-granular index per the consumer contract: memory recall stays
171
+ * Deliberately NOT `extends IVectorIndex`: an index holding many vectors per record
172
+ * has no well-defined single-vector `add(target, vector)`. It is a parallel contract
173
+ * with three operations — `addFragments`, `remove`, `query` — reusing
174
+ * {@link IVectorQueryHit}, on which both `locator` and `fragmentId` are optional. A
175
+ * fragment hit populates whichever of the two its stored fragment carried; see
176
+ * {@link IVectorQueryHit} for why that is not a discriminator and why fragment-ness
177
+ * is determined by the index queried, not by field presence. Kept distinct from the
178
+ * record-granular index per the consumer contract: memory recall stays
105
179
  * record-granular; sub-document knowledge uses a separate fragment index.
106
180
  * @public
107
181
  */
@@ -120,7 +194,8 @@ export interface IFragmentVectorIndex {
120
194
  remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
121
195
  /**
122
196
  * Return the `topK` nearest fragments to `vector`, in descending score order,
123
- * each hit carrying its record `target` and fragment `locator`. When
197
+ * each hit carrying its record `target` plus whichever of `locator` /
198
+ * `fragmentId` the stored fragment was added with. When
124
199
  * `maxPerRecord` is supplied, no more than that many fragments of any single
125
200
  * record appear in the result — the cap is applied during selection (before the
126
201
  * `topK` cut) so one long document cannot crowd out others.
@@ -1 +1 @@
1
- {"version":3,"file":"vectorIndex.d.ts","sourceRoot":"","sources":["../../../src/packlets/vector/vectorIndex.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;OAMG;IACH,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAExE;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAE1D;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CAC5F;AAED;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,CAAC,iBAAiB,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAExG;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;IAE1D;;;;;;OAMG;IACH,KAAK,CACH,MAAM,EAAE,YAAY,EACpB,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;CACpD;AAED;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAE/F;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,KAC3B,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACzC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;CAC7D"}
1
+ {"version":3,"file":"vectorIndex.d.ts","sourceRoot":"","sources":["../../../src/packlets/vector/vectorIndex.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"vectorIndex.js","sourceRoot":"","sources":["../../../src/packlets/vector/vectorIndex.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result } from '@fgv/ts-utils';\nimport { IEdgeTarget, IMemoryRecord } from '../types';\n\n/**\n * A half-open `[start, end)` span into a record's body — the in-record locator a\n * {@link IFragmentVectorIndex} carries on each fragment hit. `start` is inclusive,\n * `end` exclusive. The unit (character / byte / token offsets) is the consumer's\n * choice: the index stores the two integers opaquely and never interprets them,\n * so they line up with whatever locator the consumer's own read side uses.\n * @public\n */\nexport interface IFragmentLocator {\n /** Inclusive start offset into the record body. */\n readonly start: number;\n /** Exclusive end offset into the record body. */\n readonly end: number;\n}\n\n/**\n * A single hit returned by {@link IVectorIndex.query} (or\n * {@link IFragmentVectorIndex.query}): the matched record's scope-qualified\n * {@link IEdgeTarget | address} and the backend's similarity score (higher = more\n * similar; the exact scale is backend-defined). Hits are returned in descending\n * score order.\n *\n * @remarks\n * The address is a `(scope, id)` pair, NOT a bare {@link MemoryId} — per-scope\n * codecs (e.g. the medium-term codec's `turn-<n>` stems) legally mint the same\n * stem under different scopes, so a bare id could not disambiguate two records\n * that share a stem. The caller re-resolves the hit against the record index by\n * the same scoped address.\n *\n * `locator` is present only on hits from a {@link IFragmentVectorIndex} — it\n * identifies WHICH fragment of the record matched. Record-granular\n * {@link IVectorIndex} hits omit it.\n * @public\n */\nexport interface IVectorQueryHit {\n /** The scope-qualified address of the matched record. */\n readonly target: IEdgeTarget;\n /** Backend similarity score; higher is more similar. */\n readonly score: number;\n /** The matched fragment's in-record span; present only for fragment-index hits. */\n readonly locator?: IFragmentLocator;\n}\n\n/**\n * The vector-index seam an embedding backend implements to make\n * {@link SemanticRetriever | semantic recall} operational.\n *\n * @remarks\n * Vectors cross this seam as `Float32Array` (the in-memory representation an\n * embedding model produces); `number[]` is reserved for the JSON-wire edges\n * (e.g. a provider's embedding response). The in-package brute-force cosine\n * implementation is {@link InMemoryCosineIndex}; a consumer can swap an external\n * ANN backend behind the same seam once N grows beyond the in-memory regime.\n *\n * Every operation returns a `Result` (async, since a real backend does I/O) so\n * failure is explicit and never throws across the seam.\n * @public\n */\nexport interface IVectorIndex {\n /**\n * Add (or replace) the embedding for the scope-qualified `target`. Returns the\n * opaque {@link IMemoryEnvelope.embeddingRef | embeddingRef} the store stamps\n * onto the envelope so a later read knows the record is embedded. Keying on the\n * `(scope, id)` address (not a bare id) is load-bearing: two records that share\n * a filename stem across scopes must not clobber each other's embedding.\n */\n add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;\n\n /**\n * Remove the embedding for the scope-qualified `target`. Returns the removed\n * target. Idempotent — removing a target with no embedding still succeeds\n * (returns the target).\n */\n remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;\n\n /**\n * Return the `topK` nearest records to `vector`, in descending score order.\n */\n query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;\n}\n\n/**\n * One embedded fragment of a record: its in-record {@link IFragmentLocator | span}\n * and the vector for that span. Produced by a {@link FragmentEmbedder} and stored\n * via {@link IFragmentVectorIndex.addFragments}.\n * @public\n */\nexport interface IEmbeddedFragment {\n /** The fragment's in-record span. */\n readonly locator: IFragmentLocator;\n /** The embedding vector for that span. */\n readonly vector: Float32Array;\n}\n\n/**\n * The fragment-granular sibling of {@link IVectorIndex}: instead of one vector per\n * record it holds many vectors per record, each tagged with an in-record\n * {@link IFragmentLocator}, and its `query` returns per-fragment hits carrying that\n * locator. This is the seam behind sub-document semantic search — the \"discovery\"\n * half of a search-then-read contract, where a hit's `(target, locator)` tells the\n * consumer which record AND which span to read.\n *\n * @remarks\n * Deliberately NOT `extends IVectorIndex`: an index keyed by `(target, locator)`\n * has no well-defined single-vector `add(target, vector)`. It is a parallel\n * contract with three operations — `addFragments`, `remove`, `query` — reusing\n * {@link IVectorQueryHit} (whose `locator` is always populated here). Kept distinct\n * from the record-granular index per the consumer contract: memory recall stays\n * record-granular; sub-document knowledge uses a separate fragment index.\n * @public\n */\nexport interface IFragmentVectorIndex {\n /**\n * Add (or replace) all fragments for the scope-qualified `target`. Whole-record\n * semantics: every fragment previously held for `target` is dropped and replaced\n * by `fragments`, so a re-authored document never leaves stale fragments behind.\n * Returns the number of fragments now held for the record.\n */\n addFragments(target: IEdgeTarget, fragments: ReadonlyArray<IEmbeddedFragment>): Promise<Result<number>>;\n\n /**\n * Remove every fragment for the scope-qualified `target`. Returns the removed\n * target. Idempotent — removing a target with no fragments still succeeds.\n */\n remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;\n\n /**\n * Return the `topK` nearest fragments to `vector`, in descending score order,\n * each hit carrying its record `target` and fragment `locator`. When\n * `maxPerRecord` is supplied, no more than that many fragments of any single\n * record appear in the result — the cap is applied during selection (before the\n * `topK` cut) so one long document cannot crowd out others.\n */\n query(\n vector: Float32Array,\n topK: number,\n maxPerRecord?: number\n ): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;\n}\n\n/**\n * Embeds a complete record into a vector for the store's embed-on-write hook.\n * Async and `Result`-returning, since a real embedder does a network call (cloud\n * provider) or in-process model inference. The consumer wires this — the core\n * package never calls an embedding provider directly, staying embedder-agnostic.\n * @public\n */\nexport type MemoryEmbedder = (record: IMemoryRecord<unknown>) => Promise<Result<Float32Array>>;\n\n/**\n * The fragment-granular sibling of {@link MemoryEmbedder}: chunks a record's body\n * and embeds each chunk, returning one {@link IEmbeddedFragment} per chunk. The\n * chunking policy (window size, overlap) lives entirely in the consumer's embedder\n * — the core stays chunking-agnostic, exactly as it stays embedder-agnostic for\n * the record-granular path. Used by the store's fragment-embed-on-write hook.\n * @public\n */\nexport type FragmentEmbedder = (\n record: IMemoryRecord<unknown>\n) => Promise<Result<ReadonlyArray<IEmbeddedFragment>>>;\n\n/**\n * A record paired with its scope-qualified {@link IEdgeTarget | address}, as\n * yielded by {@link IMemoryRecordSource.list}. The address is required because\n * {@link InMemoryCosineIndex.rebuild} keys each re-embedded entry on the\n * scope-qualified target, not a bare {@link MemoryId} — two records that share a\n * filename stem across scopes must not collide when the whole vault is re-indexed.\n * @public\n */\nexport interface IScopedMemoryRecord {\n /** The record's scope-qualified `(scope, id)` address. */\n readonly target: IEdgeTarget;\n /** The record itself, passed to the embedder. */\n readonly record: IMemoryRecord<unknown>;\n}\n\n/**\n * The minimal record-source surface {@link InMemoryCosineIndex.rebuild} reads to\n * re-embed an entire vault. Each entry carries the record's scope-qualified\n * address (see {@link IScopedMemoryRecord}) so the rebuild keys the vector index\n * exactly as the incremental embed-on-write path does. A consumer backs this with\n * the store's scoped index — the vector packlet does not import the store packlet\n * (which depends on the vector packlet for {@link IVectorIndex}, so the reverse\n * import would be a cycle).\n * @public\n */\nexport interface IMemoryRecordSource {\n /** List every record in the vault, each paired with its scoped address. */\n list(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;\n}\n"]}
1
+ {"version":3,"file":"vectorIndex.js","sourceRoot":"","sources":["../../../src/packlets/vector/vectorIndex.ts"],"names":[],"mappings":";AAAA;;;GAGG","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result } from '@fgv/ts-utils';\nimport { IEdgeTarget, IMemoryRecord } from '../types';\n\n/**\n * A half-open `[start, end)` span into a record's body — the in-record locator a\n * {@link IFragmentVectorIndex} may carry on a fragment. `start` is inclusive,\n * `end` exclusive. The unit (character / byte / token offsets) is the consumer's\n * choice: the index stores the two integers opaquely and never interprets them,\n * so they line up with whatever locator the consumer's own read side uses.\n *\n * @remarks\n * **The span is advisory.** It names the region of the body a fragment was\n * *derived from*; it is NOT a slice guaranteed to reproduce the fragment's text.\n * `body.slice(start, end)` round-trips only under a segmenter that merely chooses\n * boundaries. Under a **rewriting** segmenter — one that turns a span into a\n * curated block, an increasingly common ingestion shape when a model both selects\n * and rewrites — the fragment text is not a substring of the body at all, and the\n * fragmentation is not re-derivable from the body. Treat the span as a pointer for\n * locating context, never as an extraction recipe.\n *\n * A fragment whose provenance cannot honestly be expressed as a body span should\n * omit the locator entirely and carry an {@link IEmbeddedFragment.fragmentId}\n * instead.\n * @public\n */\nexport interface IFragmentLocator {\n /** Inclusive start offset into the record body. */\n readonly start: number;\n /** Exclusive end offset into the record body. */\n readonly end: number;\n}\n\n/**\n * A single hit returned by {@link IVectorIndex.query} (or\n * {@link IFragmentVectorIndex.query}): the matched record's scope-qualified\n * {@link IEdgeTarget | address} and the backend's similarity score (higher = more\n * similar; the exact scale is backend-defined). Hits are returned in descending\n * score order.\n *\n * @remarks\n * The address is a `(scope, id)` pair, NOT a bare {@link MemoryId} — per-scope\n * codecs (e.g. the medium-term codec's `turn-<n>` stems) legally mint the same\n * stem under different scopes, so a bare id could not disambiguate two records\n * that share a stem. The caller re-resolves the hit against the record index by\n * the same scoped address.\n *\n * **No single field discriminates a fragment hit from a record-granular hit.** A\n * record hit carries neither `locator` nor `fragmentId`; a fragment hit carries at\n * least one of the two, but not necessarily any particular one — a fragment with a\n * body span but no consumer-minted id, and a fragment with an id but no honest span,\n * are both legal. Testing one field for presence therefore cannot tell you which\n * kind of hit you hold.\n *\n * That \"at least one\" requirement is enforced on the upsert side by\n * {@link embeddedFragmentConverter} — a different boundary from this type — and is\n * deliberately NOT offered here as a discriminator either. A caller keyed off it\n * would be coupled to an invariant this type does not own, and would fail silently\n * if the invariant were ever relaxed.\n *\n * **The robust rule is that fragment-ness is determined by which index produced the\n * hit**: {@link IFragmentVectorIndex.query} returns fragment hits and\n * {@link IVectorIndex.query} returns record hits. The caller chose the index it\n * queried, so it already knows which kind it is holding.\n *\n * Note in particular that an absent `locator` now carries **two** distinct meanings\n * — a record-granular hit, or a fragment with no honest body span (see\n * {@link IFragmentLocator}) — which is precisely why presence-branching is unsafe.\n * @public\n */\nexport interface IVectorQueryHit {\n /** The scope-qualified address of the matched record. */\n readonly target: IEdgeTarget;\n /** Backend similarity score; higher is more similar. */\n readonly score: number;\n /**\n * The advisory in-record span the matched fragment was derived from, when the\n * producing fragment carried one. Absent on record-granular hits AND on fragment\n * hits with no honest span — see the remarks above; do not branch on its presence.\n */\n readonly locator?: IFragmentLocator;\n /**\n * The opaque identity the producing fragment was stored with, carried back\n * verbatim. Absent on record-granular hits AND on fragment hits stored without\n * one — see the remarks above; do not branch on its presence.\n */\n readonly fragmentId?: string;\n}\n\n/**\n * The vector-index seam an embedding backend implements to make\n * {@link SemanticRetriever | semantic recall} operational.\n *\n * @remarks\n * Vectors cross this seam as `Float32Array` (the in-memory representation an\n * embedding model produces); `number[]` is reserved for the JSON-wire edges\n * (e.g. a provider's embedding response). The in-package brute-force cosine\n * implementation is {@link InMemoryCosineIndex}; a consumer can swap an external\n * ANN backend behind the same seam once N grows beyond the in-memory regime.\n *\n * Every operation returns a `Result` (async, since a real backend does I/O) so\n * failure is explicit and never throws across the seam.\n * @public\n */\nexport interface IVectorIndex {\n /**\n * Add (or replace) the embedding for the scope-qualified `target`. Returns the\n * opaque {@link IMemoryEnvelope.embeddingRef | embeddingRef} the store stamps\n * onto the envelope so a later read knows the record is embedded. Keying on the\n * `(scope, id)` address (not a bare id) is load-bearing: two records that share\n * a filename stem across scopes must not clobber each other's embedding.\n */\n add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;\n\n /**\n * Remove the embedding for the scope-qualified `target`. Returns the removed\n * target. Idempotent — removing a target with no embedding still succeeds\n * (returns the target).\n */\n remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;\n\n /**\n * Return the `topK` nearest records to `vector`, in descending score order.\n */\n query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;\n}\n\n/**\n * One embedded fragment of a record: the fragment's vector, plus at least one of the\n * two ways to identify it — its advisory in-record {@link IFragmentLocator | span}\n * and/or an opaque consumer-minted {@link IEmbeddedFragment.fragmentId | fragmentId}.\n * Produced by a {@link FragmentEmbedder} and stored via\n * {@link IFragmentVectorIndex.addFragments}.\n *\n * @remarks\n * Both identity fields are optional **in the type**, but the \"at least one\"\n * requirement is real — a fragment carrying neither is unidentifiable at the read\n * side. It is enforced by {@link embeddedFragmentConverter} (and re-checked by the\n * in-package index implementations) rather than by a conditional-required union\n * (`{ locator; fragmentId? } | { locator?; fragmentId }`), which was considered and\n * declined: the union costs at every construction site and buys nothing at the read\n * site, where each field reads as `… | undefined` either way.\n * @public\n */\nexport interface IEmbeddedFragment {\n /**\n * The region of the record body this fragment was derived from, when one can be\n * stated honestly. Advisory — see {@link IFragmentLocator}; it is NOT a slice that\n * reproduces the fragment text. Omit it for a fragment with no honest body span (a\n * rewriting segmenter), in which case `fragmentId` must be supplied.\n */\n readonly locator?: IFragmentLocator;\n /**\n * An opaque, consumer-minted identity for this fragment, carried verbatim through\n * the index and returned on the corresponding {@link IVectorQueryHit}. The index\n * **never parses it, never filters on it, and never assigns meaning to it** — it is\n * a bytestring, not part of the query path. It exists so a fragment stays\n * identifiable when its text is not re-derivable from the record body.\n *\n * The guarantee is \"we never parse it\", NOT \"we keep it stable\". Because\n * `addFragments` is whole-record-replace, an updated record re-emits its entire\n * fragment set, so **any stability of a fragment id across re-embeds is the\n * consumer's responsibility**, not the index's.\n */\n readonly fragmentId?: string;\n /** The embedding vector for this fragment. */\n readonly vector: Float32Array;\n}\n\n/**\n * The fragment-granular sibling of {@link IVectorIndex}: instead of one vector per\n * record it holds many vectors per record, each tagged with the identity its\n * {@link IEmbeddedFragment} carried, and its `query` returns per-fragment hits\n * carrying that identity back. This is the seam behind sub-document semantic search\n * — the \"discovery\" half of a search-then-read contract, where a hit tells the\n * consumer which record AND which fragment of it to read.\n *\n * @remarks\n * Deliberately NOT `extends IVectorIndex`: an index holding many vectors per record\n * has no well-defined single-vector `add(target, vector)`. It is a parallel contract\n * with three operations — `addFragments`, `remove`, `query` — reusing\n * {@link IVectorQueryHit}, on which both `locator` and `fragmentId` are optional. A\n * fragment hit populates whichever of the two its stored fragment carried; see\n * {@link IVectorQueryHit} for why that is not a discriminator and why fragment-ness\n * is determined by the index queried, not by field presence. Kept distinct from the\n * record-granular index per the consumer contract: memory recall stays\n * record-granular; sub-document knowledge uses a separate fragment index.\n * @public\n */\nexport interface IFragmentVectorIndex {\n /**\n * Add (or replace) all fragments for the scope-qualified `target`. Whole-record\n * semantics: every fragment previously held for `target` is dropped and replaced\n * by `fragments`, so a re-authored document never leaves stale fragments behind.\n * Returns the number of fragments now held for the record.\n */\n addFragments(target: IEdgeTarget, fragments: ReadonlyArray<IEmbeddedFragment>): Promise<Result<number>>;\n\n /**\n * Remove every fragment for the scope-qualified `target`. Returns the removed\n * target. Idempotent — removing a target with no fragments still succeeds.\n */\n remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;\n\n /**\n * Return the `topK` nearest fragments to `vector`, in descending score order,\n * each hit carrying its record `target` plus whichever of `locator` /\n * `fragmentId` the stored fragment was added with. When\n * `maxPerRecord` is supplied, no more than that many fragments of any single\n * record appear in the result — the cap is applied during selection (before the\n * `topK` cut) so one long document cannot crowd out others.\n */\n query(\n vector: Float32Array,\n topK: number,\n maxPerRecord?: number\n ): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;\n}\n\n/**\n * Embeds a complete record into a vector for the store's embed-on-write hook.\n * Async and `Result`-returning, since a real embedder does a network call (cloud\n * provider) or in-process model inference. The consumer wires this — the core\n * package never calls an embedding provider directly, staying embedder-agnostic.\n * @public\n */\nexport type MemoryEmbedder = (record: IMemoryRecord<unknown>) => Promise<Result<Float32Array>>;\n\n/**\n * The fragment-granular sibling of {@link MemoryEmbedder}: chunks a record's body\n * and embeds each chunk, returning one {@link IEmbeddedFragment} per chunk. The\n * chunking policy (window size, overlap) lives entirely in the consumer's embedder\n * — the core stays chunking-agnostic, exactly as it stays embedder-agnostic for\n * the record-granular path. Used by the store's fragment-embed-on-write hook.\n * @public\n */\nexport type FragmentEmbedder = (\n record: IMemoryRecord<unknown>\n) => Promise<Result<ReadonlyArray<IEmbeddedFragment>>>;\n\n/**\n * A record paired with its scope-qualified {@link IEdgeTarget | address}, as\n * yielded by {@link IMemoryRecordSource.list}. The address is required because\n * {@link InMemoryCosineIndex.rebuild} keys each re-embedded entry on the\n * scope-qualified target, not a bare {@link MemoryId} — two records that share a\n * filename stem across scopes must not collide when the whole vault is re-indexed.\n * @public\n */\nexport interface IScopedMemoryRecord {\n /** The record's scope-qualified `(scope, id)` address. */\n readonly target: IEdgeTarget;\n /** The record itself, passed to the embedder. */\n readonly record: IMemoryRecord<unknown>;\n}\n\n/**\n * The minimal record-source surface {@link InMemoryCosineIndex.rebuild} reads to\n * re-embed an entire vault. Each entry carries the record's scope-qualified\n * address (see {@link IScopedMemoryRecord}) so the rebuild keys the vector index\n * exactly as the incremental embed-on-write path does. A consumer backs this with\n * the store's scoped index — the vector packlet does not import the store packlet\n * (which depends on the vector packlet for {@link IVectorIndex}, so the reverse\n * import would be a cycle).\n * @public\n */\nexport interface IMemoryRecordSource {\n /** List every record in the vault, each paired with its scoped address. */\n list(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import '@fgv/ts-utils-jest';
2
+ //# sourceMappingURL=dedupScope.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedupScope.test.d.ts","sourceRoot":"","sources":["../../../../src/test/unit/ingest/dedupScope.test.ts"],"names":[],"mappings":"AAKA,OAAO,oBAAoB,CAAC"}