@fgv/ts-agent-memory 5.1.0-37 → 5.1.0-39

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 (197) hide show
  1. package/.rush/temp/{6dfc331ddf74dc2db0d4483d0c96ea971f351c83.tar.log → b82cf6bdece20481260e6bab946179eeec9d7b46.tar.log} +116 -2
  2. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +4 -4
  3. package/.rush/temp/operation/build/all.log +4 -4
  4. package/.rush/temp/operation/build/log-chunks.jsonl +4 -4
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/.rush/temp/shrinkwrap-deps.json +222 -221
  7. package/config/jest.config.json +1 -1
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -1
  10. package/dist/packlets/ingest/cycleGuard.js +111 -0
  11. package/dist/packlets/ingest/cycleGuard.js.map +1 -0
  12. package/dist/packlets/ingest/hostStages.js +6 -0
  13. package/dist/packlets/ingest/hostStages.js.map +1 -0
  14. package/dist/packlets/ingest/index.js +9 -0
  15. package/dist/packlets/ingest/index.js.map +1 -0
  16. package/dist/packlets/ingest/model.js +6 -0
  17. package/dist/packlets/ingest/model.js.map +1 -0
  18. package/dist/packlets/ingest/orchestrator.js +438 -0
  19. package/dist/packlets/ingest/orchestrator.js.map +1 -0
  20. package/dist/packlets/retrieve/index.js +1 -0
  21. package/dist/packlets/retrieve/index.js.map +1 -1
  22. package/dist/packlets/retrieve/temporalRetrievers.js +172 -0
  23. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -0
  24. package/dist/packlets/store/fileTreeMemoryStore.js +323 -41
  25. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  26. package/dist/packlets/tools/index.js +6 -0
  27. package/dist/packlets/tools/index.js.map +1 -0
  28. package/dist/packlets/tools/memoryTools.js +337 -0
  29. package/dist/packlets/tools/memoryTools.js.map +1 -0
  30. package/dist/packlets/types/identityCodec.js +115 -0
  31. package/dist/packlets/types/identityCodec.js.map +1 -1
  32. package/dist/packlets/types/index.js +1 -0
  33. package/dist/packlets/types/index.js.map +1 -1
  34. package/dist/packlets/types/temporal.js +85 -0
  35. package/dist/packlets/types/temporal.js.map +1 -0
  36. package/dist/packlets/types/writePolicy.js +96 -0
  37. package/dist/packlets/types/writePolicy.js.map +1 -1
  38. package/dist/test/unit/converters/antagonistRoundTrip.test.js +95 -0
  39. package/dist/test/unit/converters/antagonistRoundTrip.test.js.map +1 -0
  40. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js +273 -0
  41. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -0
  42. package/dist/test/unit/ingest/cycleGuard.test.js +54 -0
  43. package/dist/test/unit/ingest/cycleGuard.test.js.map +1 -0
  44. package/dist/test/unit/ingest/orchestrator.test.js +913 -0
  45. package/dist/test/unit/ingest/orchestrator.test.js.map +1 -0
  46. package/dist/test/unit/retrieve/temporalRetrievers.test.js +182 -0
  47. package/dist/test/unit/retrieve/temporalRetrievers.test.js.map +1 -0
  48. package/dist/test/unit/store/antagonistTemporalBoundary.test.js +120 -0
  49. package/dist/test/unit/store/antagonistTemporalBoundary.test.js.map +1 -0
  50. package/dist/test/unit/store/fileTreeMemoryStore.test.js +74 -7
  51. package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  52. package/dist/test/unit/store/temporalStore.test.js +398 -0
  53. package/dist/test/unit/store/temporalStore.test.js.map +1 -0
  54. package/dist/test/unit/tools/memoryTools.test.js +572 -0
  55. package/dist/test/unit/tools/memoryTools.test.js.map +1 -0
  56. package/dist/test/unit/types/temporalCodec.test.js +203 -0
  57. package/dist/test/unit/types/temporalCodec.test.js.map +1 -0
  58. package/dist/test/unit/types/temporalPolicy.test.js +62 -0
  59. package/dist/test/unit/types/temporalPolicy.test.js.map +1 -0
  60. package/dist/ts-agent-memory.d.ts +1042 -10
  61. package/dist/tsdoc-metadata.json +1 -1
  62. package/etc/ts-agent-memory.api.md +284 -0
  63. package/lib/index.d.ts +2 -0
  64. package/lib/index.d.ts.map +1 -1
  65. package/lib/index.js +2 -0
  66. package/lib/index.js.map +1 -1
  67. package/lib/packlets/ingest/cycleGuard.d.ts +41 -0
  68. package/lib/packlets/ingest/cycleGuard.d.ts.map +1 -0
  69. package/lib/packlets/ingest/cycleGuard.js +115 -0
  70. package/lib/packlets/ingest/cycleGuard.js.map +1 -0
  71. package/lib/packlets/ingest/hostStages.d.ts +88 -0
  72. package/lib/packlets/ingest/hostStages.d.ts.map +1 -0
  73. package/lib/packlets/ingest/hostStages.js +7 -0
  74. package/lib/packlets/ingest/hostStages.js.map +1 -0
  75. package/lib/packlets/ingest/index.d.ts +5 -0
  76. package/lib/packlets/ingest/index.d.ts.map +1 -0
  77. package/lib/packlets/ingest/index.js +25 -0
  78. package/lib/packlets/ingest/index.js.map +1 -0
  79. package/lib/packlets/ingest/model.d.ts +177 -0
  80. package/lib/packlets/ingest/model.d.ts.map +1 -0
  81. package/lib/packlets/ingest/model.js +7 -0
  82. package/lib/packlets/ingest/model.js.map +1 -0
  83. package/lib/packlets/ingest/orchestrator.d.ts +206 -0
  84. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -0
  85. package/lib/packlets/ingest/orchestrator.js +442 -0
  86. package/lib/packlets/ingest/orchestrator.js.map +1 -0
  87. package/lib/packlets/retrieve/index.d.ts +1 -0
  88. package/lib/packlets/retrieve/index.d.ts.map +1 -1
  89. package/lib/packlets/retrieve/index.js +1 -0
  90. package/lib/packlets/retrieve/index.js.map +1 -1
  91. package/lib/packlets/retrieve/temporalRetrievers.d.ts +78 -0
  92. package/lib/packlets/retrieve/temporalRetrievers.d.ts.map +1 -0
  93. package/lib/packlets/retrieve/temporalRetrievers.js +178 -0
  94. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -0
  95. package/lib/packlets/store/fileTreeMemoryStore.d.ts +118 -9
  96. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  97. package/lib/packlets/store/fileTreeMemoryStore.js +322 -40
  98. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  99. package/lib/packlets/tools/index.d.ts +2 -0
  100. package/lib/packlets/tools/index.d.ts.map +1 -0
  101. package/lib/packlets/tools/index.js +22 -0
  102. package/lib/packlets/tools/index.js.map +1 -0
  103. package/lib/packlets/tools/memoryTools.d.ts +139 -0
  104. package/lib/packlets/tools/memoryTools.d.ts.map +1 -0
  105. package/lib/packlets/tools/memoryTools.js +341 -0
  106. package/lib/packlets/tools/memoryTools.js.map +1 -0
  107. package/lib/packlets/types/identityCodec.d.ts +86 -0
  108. package/lib/packlets/types/identityCodec.d.ts.map +1 -1
  109. package/lib/packlets/types/identityCodec.js +118 -1
  110. package/lib/packlets/types/identityCodec.js.map +1 -1
  111. package/lib/packlets/types/index.d.ts +1 -0
  112. package/lib/packlets/types/index.d.ts.map +1 -1
  113. package/lib/packlets/types/index.js +1 -0
  114. package/lib/packlets/types/index.js.map +1 -1
  115. package/lib/packlets/types/temporal.d.ts +40 -0
  116. package/lib/packlets/types/temporal.d.ts.map +1 -0
  117. package/lib/packlets/types/temporal.js +92 -0
  118. package/lib/packlets/types/temporal.js.map +1 -0
  119. package/lib/packlets/types/writePolicy.d.ts +49 -0
  120. package/lib/packlets/types/writePolicy.d.ts.map +1 -1
  121. package/lib/packlets/types/writePolicy.js +98 -1
  122. package/lib/packlets/types/writePolicy.js.map +1 -1
  123. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts +10 -0
  124. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts.map +1 -0
  125. package/lib/test/unit/converters/antagonistRoundTrip.test.js +97 -0
  126. package/lib/test/unit/converters/antagonistRoundTrip.test.js.map +1 -0
  127. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts +9 -0
  128. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts.map +1 -0
  129. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js +275 -0
  130. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -0
  131. package/lib/test/unit/ingest/cycleGuard.test.d.ts +2 -0
  132. package/lib/test/unit/ingest/cycleGuard.test.d.ts.map +1 -0
  133. package/lib/test/unit/ingest/cycleGuard.test.js +56 -0
  134. package/lib/test/unit/ingest/cycleGuard.test.js.map +1 -0
  135. package/lib/test/unit/ingest/orchestrator.test.d.ts +2 -0
  136. package/lib/test/unit/ingest/orchestrator.test.d.ts.map +1 -0
  137. package/lib/test/unit/ingest/orchestrator.test.js +915 -0
  138. package/lib/test/unit/ingest/orchestrator.test.js.map +1 -0
  139. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts +2 -0
  140. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts.map +1 -0
  141. package/lib/test/unit/retrieve/temporalRetrievers.test.js +184 -0
  142. package/lib/test/unit/retrieve/temporalRetrievers.test.js.map +1 -0
  143. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts +9 -0
  144. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts.map +1 -0
  145. package/lib/test/unit/store/antagonistTemporalBoundary.test.js +122 -0
  146. package/lib/test/unit/store/antagonistTemporalBoundary.test.js.map +1 -0
  147. package/lib/test/unit/store/fileTreeMemoryStore.test.js +74 -7
  148. package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -1
  149. package/lib/test/unit/store/temporalStore.test.d.ts +2 -0
  150. package/lib/test/unit/store/temporalStore.test.d.ts.map +1 -0
  151. package/lib/test/unit/store/temporalStore.test.js +400 -0
  152. package/lib/test/unit/store/temporalStore.test.js.map +1 -0
  153. package/lib/test/unit/tools/memoryTools.test.d.ts +2 -0
  154. package/lib/test/unit/tools/memoryTools.test.d.ts.map +1 -0
  155. package/lib/test/unit/tools/memoryTools.test.js +574 -0
  156. package/lib/test/unit/tools/memoryTools.test.js.map +1 -0
  157. package/lib/test/unit/types/temporalCodec.test.d.ts +2 -0
  158. package/lib/test/unit/types/temporalCodec.test.d.ts.map +1 -0
  159. package/lib/test/unit/types/temporalCodec.test.js +205 -0
  160. package/lib/test/unit/types/temporalCodec.test.js.map +1 -0
  161. package/lib/test/unit/types/temporalPolicy.test.d.ts +2 -0
  162. package/lib/test/unit/types/temporalPolicy.test.d.ts.map +1 -0
  163. package/lib/test/unit/types/temporalPolicy.test.js +64 -0
  164. package/lib/test/unit/types/temporalPolicy.test.js.map +1 -0
  165. package/package.json +7 -7
  166. package/rush-logs/ts-agent-memory.build.cache.log +1 -1
  167. package/rush-logs/ts-agent-memory.build.log +4 -4
  168. package/src/index.ts +2 -0
  169. package/src/packlets/ingest/cycleGuard.ts +142 -0
  170. package/src/packlets/ingest/hostStages.ts +111 -0
  171. package/src/packlets/ingest/index.ts +9 -0
  172. package/src/packlets/ingest/model.ts +184 -0
  173. package/src/packlets/ingest/orchestrator.ts +797 -0
  174. package/src/packlets/retrieve/index.ts +1 -0
  175. package/src/packlets/retrieve/temporalRetrievers.ts +210 -0
  176. package/src/packlets/store/fileTreeMemoryStore.ts +460 -66
  177. package/src/packlets/tools/index.ts +6 -0
  178. package/src/packlets/tools/memoryTools.ts +579 -0
  179. package/src/packlets/types/identityCodec.ts +184 -0
  180. package/src/packlets/types/index.ts +1 -0
  181. package/src/packlets/types/temporal.ts +96 -0
  182. package/src/packlets/types/writePolicy.ts +127 -0
  183. package/src/test/unit/converters/antagonistRoundTrip.test.ts +110 -0
  184. package/src/test/unit/ingest/antagonistCycleAndParity.test.ts +362 -0
  185. package/src/test/unit/ingest/cycleGuard.test.ts +68 -0
  186. package/src/test/unit/ingest/orchestrator.test.ts +1158 -0
  187. package/src/test/unit/retrieve/temporalRetrievers.test.ts +226 -0
  188. package/src/test/unit/store/antagonistTemporalBoundary.test.ts +158 -0
  189. package/src/test/unit/store/fileTreeMemoryStore.test.ts +98 -7
  190. package/src/test/unit/store/temporalStore.test.ts +469 -0
  191. package/src/test/unit/tools/memoryTools.test.ts +771 -0
  192. package/src/test/unit/types/temporalCodec.test.ts +259 -0
  193. package/src/test/unit/types/temporalPolicy.test.ts +96 -0
  194. package/temp/build/lint/_eslint-5eVG3S6w.json +85 -9
  195. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  196. package/temp/ts-agent-memory.api.json +11984 -6314
  197. package/temp/ts-agent-memory.api.md +284 -0
@@ -1,3 +1,4 @@
1
+ import { AiAssist } from '@fgv/ts-extras';
1
2
  import { Brand } from '@fgv/ts-utils';
2
3
  import { Converter } from '@fgv/ts-utils';
3
4
  import { FileTree } from '@fgv/ts-json-base';
@@ -20,6 +21,46 @@ export declare type AdmissionDecision = {
20
21
  readonly evict: ReadonlyArray<MemoryId>;
21
22
  };
22
23
 
24
+ /**
25
+ * Temporal retriever returning, for each temporal entity matching the query, the
26
+ * version valid at `query.asOf` (epoch ms). `asOf` is this retriever's axis: a
27
+ * query without it yields an empty success (a no-op contribution to a
28
+ * {@link HybridRetriever}, not a failure). Results are recency-ordered and
29
+ * limited.
30
+ * @public
31
+ */
32
+ export declare class AsOfRetriever implements IMemoryRetriever {
33
+ private readonly _index;
34
+ private constructor();
35
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
36
+ get capabilities(): IMemoryRetrieverCapabilities;
37
+ /** Family-convention factory. */
38
+ static create(index: IMemoryIndex): Result<AsOfRetriever>;
39
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
40
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
41
+ }
42
+
43
+ /**
44
+ * Write-time cycle guard. Given the graph's EXISTING directed edges plus a set of
45
+ * PROPOSED edges, verify that adding the proposed edges keeps the union graph a
46
+ * DAG. Fails loudly, naming the first proposed edge that would close a directed
47
+ * cycle.
48
+ *
49
+ * @remarks
50
+ * Distinct from the read-time BFS in `LinkTraversalRetriever` (which tolerates
51
+ * cycles via a visited-set): this is a WRITE-time admission check. Cycle
52
+ * detection is DFS reachability — a proposed `source -> target` edge closes a
53
+ * cycle iff `target` already reaches `source` in the graph built so far. Proposed
54
+ * edges are folded in one at a time (in order), so an intra-batch cycle
55
+ * (`A->B`, `B->A` both proposed) is caught as well as a batch-vs-existing cycle.
56
+ *
57
+ * See the design note §4: this enforces GLOBAL directed-acyclicity over all link
58
+ * types (conservative — a mutual associative pair is rejected). Callers that need
59
+ * mutual links disable the guard.
60
+ * @public
61
+ */
62
+ export declare function assertNoCycles(existing: ReadonlyArray<ICycleGuardEdge>, proposed: ReadonlyArray<ICycleGuardEdge>): Result<true>;
63
+
23
64
  /**
24
65
  * Validates a single filename stem against the POSIX portable filename set
25
66
  * (`[A-Za-z0-9._-]`), rejecting a leading or trailing `.` and reserved Windows
@@ -64,6 +105,22 @@ export declare class BodyConverterRegistry implements IBodyConverterRegistry {
64
105
  convert(kind: Kind, body: unknown): Result<unknown>;
65
106
  }
66
107
 
108
+ /**
109
+ * Build the design's `buildCycleKey` — a deterministic, canonical (RFC-8785,
110
+ * via `Crc32Normalizer` from `@fgv/ts-utils`) identity for a directed edge. Used
111
+ * to de-duplicate proposed edges so a repeated `(source, target, type)` proposal
112
+ * contributes a single graph edge (and never spuriously "re-closes" a cycle).
113
+ * @public
114
+ */
115
+ export declare function buildCycleKey(edge: ICycleGuardEdge): Result<string>;
116
+
117
+ /**
118
+ * The link type whose presence on a temporal-kind candidate fires the
119
+ * contradicts→temporal-versioned interlock.
120
+ * @public
121
+ */
122
+ export declare const CONTRADICTS_LINK_TYPE: LinkType;
123
+
67
124
  /**
68
125
  * Converters for the branded identifier scalars. Each validates an `unknown`
69
126
  * value into the corresponding brand, enforcing the shared hygiene
@@ -79,6 +136,51 @@ export declare const Convert: {
79
136
  readonly linkType: Converter<LinkType>;
80
137
  };
81
138
 
139
+ /**
140
+ * Build the selected suite of memory `AiAssist.IAiClientTool`s over a
141
+ * pre-scoped store — ready to hand to `AiAssist.executeClientToolTurn` (and, via
142
+ * the shared `JsonSchema.object(...)` schemas, `@fgv/ts-extras-mcp`).
143
+ *
144
+ * @remarks
145
+ * **Scope isolation is make-or-break.** The returned tools close over the
146
+ * pre-scoped {@link ICreateMemoryToolsParams.store | store}; no tool's
147
+ * `parametersSchema` declares a `scope` (or any scope-widening) property, so an
148
+ * LLM cannot steer a tool at another actor's memory. The store instance is the
149
+ * sole scope authority.
150
+ *
151
+ * The default selection is {@link DEFAULT_MEMORY_TOOLS} (the read-only set) —
152
+ * `memory_write` / `memory_delete` are included only when named in
153
+ * {@link ICreateMemoryToolsParams.tools | tools}.
154
+ * @public
155
+ */
156
+ export declare function createMemoryTools(params: ICreateMemoryToolsParams): ReadonlyArray<AiAssist.IAiClientTool>;
157
+
158
+ /**
159
+ * Temporal retriever returning the **current** version of each temporal entity
160
+ * matching the query (the newest version whose `invalid_at` is null/absent),
161
+ * recency-ordered and limited. A fully-invalidated (soft-deleted) entity
162
+ * contributes nothing. Non-temporal records are not this retriever's concern.
163
+ * @public
164
+ */
165
+ export declare class CurrentValidRetriever implements IMemoryRetriever {
166
+ private readonly _index;
167
+ private constructor();
168
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
169
+ get capabilities(): IMemoryRetrieverCapabilities;
170
+ /** Family-convention factory. */
171
+ static create(index: IMemoryIndex): Result<CurrentValidRetriever>;
172
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
173
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
174
+ }
175
+
176
+ /**
177
+ * How the write-time cycle guard behaves: `'reject'` (default — a cycle-inducing
178
+ * edge fails the ingest) or `'off'` (no acyclicity constraint, for deployments
179
+ * whose link graphs are legitimately cyclic — e.g. mutual associative links).
180
+ * @public
181
+ */
182
+ export declare type CycleGuardMode = 'reject' | 'off';
183
+
82
184
  /**
83
185
  * The granularity at which the store deduplicates an incoming write against the
84
186
  * existing vault, declared per kind via {@link IWritePolicy.dedupScope}.
@@ -103,6 +205,29 @@ export declare type DedupScope = 'content' | 'entity';
103
205
  */
104
206
  export declare const DEFAULT_DEDUP_SCOPE: DedupScope;
105
207
 
208
+ /**
209
+ * The default tool subset when {@link ICreateMemoryToolsParams.tools | tools} is
210
+ * omitted: the read-only set. Mutating tools (`memory_write` / `memory_delete`)
211
+ * are **off by default** and must be named explicitly — writes stay
212
+ * curation-mediated unless the host opts in.
213
+ * @public
214
+ */
215
+ export declare const DEFAULT_MEMORY_TOOLS: ReadonlyArray<MemoryToolName>;
216
+
217
+ /**
218
+ * The default stage-4 layer-2 cosine similarity threshold (design note §2). A
219
+ * near-duplicate candidate is surfaced to the {@link IEntityResolver} only when
220
+ * its nearest neighbor scores `>= 0.85`.
221
+ * @public
222
+ */
223
+ export declare const DEFAULT_SIMILARITY_THRESHOLD: number;
224
+
225
+ /**
226
+ * The default top-K for stage-4 layer-2 similarity candidate generation.
227
+ * @public
228
+ */
229
+ export declare const DEFAULT_SIMILARITY_TOP_K: number;
230
+
106
231
  /**
107
232
  * Encode a {@link MemoryScopeKey} to its on-disk directory path. The scope may
108
233
  * be multi-segment (`/`-separated) — each component is validated independently
@@ -155,9 +280,11 @@ export declare const envelopeYamlConverter: Converter<IMemoryEnvelope>;
155
280
  * write-lock so the index and the on-disk files never interleave.
156
281
  *
157
282
  * @remarks
158
- * B1 supports flat (non-versioned) layout only and string (markdown) bodies.
159
- * A codec reporting `isVersioned: true`, or a non-string body, fails loudly —
160
- * the versioned/temporal write path is a fast-follow.
283
+ * Bodies are string (markdown). Both layouts are supported: flat
284
+ * (one-file-per-entity, non-versioned) and versioned (subtree-per-entity,
285
+ * invalidate-don't-delete), dispatched per kind by the codec's `isVersioned`
286
+ * flag. A kind is flat with zero behavioral impact unless it opts into a
287
+ * {@link ITemporalIdentityCodec}.
161
288
  * @public
162
289
  */
163
290
  export declare class FileTreeMemoryStore implements IMemoryStore {
@@ -207,6 +334,14 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
207
334
  getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
208
335
  /** {@inheritDoc IMemoryStore.list} */
209
336
  list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
337
+ /**
338
+ * Collapse temporal records to the single version valid at `asOf` per entity;
339
+ * non-temporal records are timeless and pass through unchanged (valid-time
340
+ * `asOf` applies only to versioned kinds; transaction-time / full bi-temporal
341
+ * filtering is deferred — OQ-9). An entity with no version valid at `asOf`
342
+ * contributes nothing.
343
+ */
344
+ private static _projectAsOf;
210
345
  /** {@inheritDoc IMemoryStore.put} */
211
346
  put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
212
347
  /** {@inheritDoc IMemoryStore.delete} */
@@ -308,6 +443,73 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
308
443
  /** Serialize and write a fully-stamped record, then patch the index. */
309
444
  private _persist;
310
445
  private _deleteLocked;
446
+ /**
447
+ * Flat (non-versioned) delete: physically remove the record file + index
448
+ * entry, then prune the vector best-effort. Structurally unchanged from the
449
+ * pre-temporal delete path.
450
+ */
451
+ private _deleteFlat;
452
+ /**
453
+ * Resolve the current version of a temporal entity from the derived index: the
454
+ * highest-`seq` version under the entity subtree `scope` whose `invalid_at` is
455
+ * null/absent. `undefined` when the entity has no current version (never
456
+ * written, or fully invalidated / soft-deleted).
457
+ */
458
+ private _readVersionedCurrent;
459
+ /**
460
+ * Every persisted version of the entity whose subtree is `scope`. All version
461
+ * files for one entity live under exactly that scope (which encodes the
462
+ * entityId), so a scope filter over the index isolates one entity's versions.
463
+ */
464
+ private _versionsForEntity;
465
+ /**
466
+ * Versioned write (invalidate-don't-delete). Builds the new version's content
467
+ * (a first version from the incoming record, or a merge of the incoming patch
468
+ * over the current version), persists it as a NEW version file, then sets
469
+ * `invalid_at` on the prior current version.
470
+ *
471
+ * Durability order: the new version is persisted FIRST. It carries the highest
472
+ * `seq`, so a crash before the prior-version invalidation completes still
473
+ * resolves the new version as current (`selectCurrentVersion` breaks a
474
+ * two-current tie by highest `seq`), and `asOf` reads stay correct because each
475
+ * version's `valid_at` lower-bounds its interval.
476
+ */
477
+ private _putVersioned;
478
+ /**
479
+ * Invalidate a set of still-current versions (close each world-truth interval at
480
+ * `invalidAt`; stamp transaction-time `updated` = `now`). Chained so a mid-list
481
+ * failure propagates. Invalidating every prior current — not just the
482
+ * highest-`seq` pick — self-heals a state where a previous write's invalidation
483
+ * only partially completed (P2-7).
484
+ */
485
+ private _invalidateCurrents;
486
+ /**
487
+ * Build the new version to persist. A first version takes the incoming
488
+ * content verbatim (its dedup `hash` is reused); a subsequent version projects
489
+ * the incoming record's mutable fields into a merge-patch and lets the policy
490
+ * merge them over the CURRENT version (the merge-patch-under-versioning
491
+ * contract), recomputing the content hash from the policy's output. Each
492
+ * version is its own record with its own transaction time (`created` = `now`)
493
+ * and a store-minted `id` = the version stem, so `id === filename stem` holds.
494
+ */
495
+ private _buildVersionedRecord;
496
+ /**
497
+ * Set `invalid_at` on a prior current version (invalidate-don't-delete) and
498
+ * rewrite its file + index entry. The content hash is unchanged — `invalid_at`
499
+ * is temporal metadata, not part of `{ kind, body, links }` — so the version's
500
+ * identity is stable.
501
+ */
502
+ private _invalidateVersion;
503
+ /**
504
+ * Versioned delete: SOFT delete (invalidate-don't-delete). Sets `invalid_at` on
505
+ * the current version, leaving the entity's history intact and the entity with
506
+ * no current version. Returns the invalidated version's {@link MemoryId}. Fails
507
+ * with "no record found" when there is no current version — matching the flat
508
+ * delete's not-found semantics. History is retained deliberately: temporal
509
+ * kinds exist to preserve the audit trail (and the L3 `contradicts` interlock
510
+ * builds on it), so a hard delete would defeat the purpose.
511
+ */
512
+ private _deleteVersioned;
311
513
  /** Evict (physically delete) a single record file by id, patching the index. */
312
514
  private _evict;
313
515
  /** Project the incoming record's mutable fields into a merge-patch. */
@@ -321,8 +523,26 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
321
523
  * across first-writes and updates.
322
524
  */
323
525
  private _admissionCohort;
324
- /** Find a record in `scope` whose `contentHash` equals `hash`, if any. */
526
+ /**
527
+ * Find a record in `scope` whose `contentHash` equals `hash`, if any,
528
+ * optionally excluding a specific id. `excludeId` lets the content-scoped
529
+ * dedup skip the same-id record so it does not shadow the LWW update path.
530
+ */
325
531
  private _findByContentHash;
532
+ /**
533
+ * True when `incoming`'s caller-authored mutable metadata (`tags` / `provenance`)
534
+ * canonically equals `existing`'s. `body` and `links` are covered by the content
535
+ * hash; `embeddingRef` is store-derived (not caller metadata) and is deliberately
536
+ * excluded so a same-content re-put is not treated as changed merely because the
537
+ * store already stamped an embedding. Used to keep an identical re-put a no-op
538
+ * without swallowing a genuine metadata revision.
539
+ *
540
+ * Canonicalization never fails for a validated record (`tags`/`provenance` are
541
+ * always plain JSON); a failure is defaulted to a non-matching sentinel so the
542
+ * write flows to `applyUpdate` (which re-validates) rather than silently
543
+ * no-op-ing on an un-canonicalizable value.
544
+ */
545
+ private _isMutableMetadataUnchanged;
326
546
  private _contentHash;
327
547
  private _codecFor;
328
548
  private _policyFor;
@@ -332,7 +552,15 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
332
552
  * filename round-trip on every load.
333
553
  */
334
554
  private _readRecord;
335
- /** Enforce `envelope.id === filename stem` and the codec round-trip on load. */
555
+ /**
556
+ * Enforce `envelope.id === filename stem`, the codec round-trip, AND that the
557
+ * envelope's own `entityId` agrees with the id decoded from the subtree scope.
558
+ * The round-trip only validates (scope, stem) consistency; the codec derives
559
+ * `entityId` from the scope path and never reads the envelope's `entityId`
560
+ * field, so a tampered/corrupt file whose frontmatter declares a foreign
561
+ * `entityId` would otherwise load undetected — and `entityId` is trusted
562
+ * verbatim downstream (e.g. merge-into re-addressing). Cross-check it here.
563
+ */
336
564
  private _verifyLoaded;
337
565
  /**
338
566
  * Resolve the directory for a scope, returning `undefined` when it does not
@@ -368,6 +596,44 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
368
596
  */
369
597
  export declare function guardRetrieverCapabilities(query: IMemoryQuery, capabilities: IMemoryRetrieverCapabilities): Result<true>;
370
598
 
599
+ /**
600
+ * Temporal retriever returning **every** version of each temporal entity matching
601
+ * the query, ordered ascending by `valid_at` (then `seq` as a stable tiebreak) —
602
+ * the entity's full history. Limited after ordering.
603
+ *
604
+ * @remarks
605
+ * When the query matches more than one entity, the result is a single
606
+ * CROSS-entity list globally sorted by `valid_at` — versions of different
607
+ * entities interleave, NOT grouped per entity — so `limit` truncates that
608
+ * globally-sorted list. Constrain to one entity (e.g. via `query.scope`) for a
609
+ * single entity's contiguous history.
610
+ * @public
611
+ */
612
+ export declare class HistoryRetriever implements IMemoryRetriever {
613
+ private readonly _index;
614
+ private constructor();
615
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
616
+ get capabilities(): IMemoryRetrieverCapabilities;
617
+ /** Family-convention factory. */
618
+ static create(index: IMemoryIndex): Result<HistoryRetriever>;
619
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
620
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
621
+ /** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */
622
+ private static _startOf;
623
+ /**
624
+ * Ascending-by-`valid_at` comparator (a version's `valid_at` defaults to its
625
+ * `created` when absent), with `seq` as a stable ascending tiebreak so
626
+ * same-instant versions order by write sequence.
627
+ */
628
+ private static _byValidAtAscending;
629
+ }
630
+
631
+ /**
632
+ * The provenance source stamped on every record the ingest pipeline writes.
633
+ * @public
634
+ */
635
+ export declare const HOST_INGEST_PROVENANCE_SOURCE: string;
636
+
371
637
  /**
372
638
  * Composes several retrievers, dispatches a query to each, and merges their
373
639
  * results via an injectable {@link IMergeStrategy}. Its capabilities are the
@@ -445,6 +711,100 @@ export declare interface IBodyConverterRegistry {
445
711
  convert(kind: Kind, body: unknown): Result<unknown>;
446
712
  }
447
713
 
714
+ /**
715
+ * A stage-5 attributed edge proposal: the {@link ICandidateEdge.edge | edge} to
716
+ * add, sourced from {@link ICandidateEdge.source | source}. The source MUST be a
717
+ * candidate being written in this ingest (edges land on the source record's
718
+ * `envelope.links`); the edge's `target` must resolve to a sibling candidate or
719
+ * an existing store record.
720
+ * @public
721
+ */
722
+ export declare interface ICandidateEdge {
723
+ /** The reference id (codec `idStem`) of the candidate the edge originates from. */
724
+ readonly source: MemoryId;
725
+ /** The attributed edge (type / target / optional confidence / provenance). */
726
+ readonly edge: IEdge;
727
+ }
728
+
729
+ /**
730
+ * A host-extracted candidate record (stage 3 output). The host supplies the
731
+ * full envelope MINUS the {@link StoreStampedEnvelopeField | store-stamped
732
+ * fields} (fgv derives `id` from the codec; the store stamps the rest) plus the
733
+ * typed body. Every candidate body is validated against the kind's registered
734
+ * Converter before it can reach the store (the typed validation boundary).
735
+ * @public
736
+ */
737
+ export declare interface ICandidateRecord {
738
+ /** The host-supplied envelope, minus the store-stamped fields. */
739
+ readonly envelope: Omit<IMemoryEnvelope, StoreStampedEnvelopeField>;
740
+ /** The per-kind body (a markdown string in v1), validated on ingest. */
741
+ readonly body: unknown;
742
+ }
743
+
744
+ /**
745
+ * Parameters for {@link createMemoryTools}.
746
+ *
747
+ * @remarks
748
+ * **Scope isolation is constructor-fixed.** The {@link
749
+ * ICreateMemoryToolsParams.store | store} is the sole scope authority — it is the
750
+ * actor's own, pre-scoped memory root. No tool's `parametersSchema` declares a
751
+ * `scope` (or any scope-widening) property, so an LLM cannot steer a tool at
752
+ * another actor's memory.
753
+ * @public
754
+ */
755
+ export declare interface ICreateMemoryToolsParams {
756
+ /**
757
+ * The pre-scoped memory store (the actor's own memory root). Sole scope
758
+ * authority — backs `memory_write` / `memory_read` / `memory_delete`.
759
+ */
760
+ readonly store: IMemoryStore;
761
+ /** Retriever backing `memory_search` (and `memory_context` via link traversal). */
762
+ readonly retriever: IMemoryRetriever;
763
+ /** Body converter registry — gates the toolable kinds via `has(kind)`. */
764
+ readonly registry: IBodyConverterRegistry;
765
+ /**
766
+ * The per-tool enable subset. Defaults to {@link DEFAULT_MEMORY_TOOLS} (the
767
+ * read-only set). Name `memory_write` / `memory_delete` here to opt into the
768
+ * mutating tools.
769
+ */
770
+ readonly tools?: ReadonlyArray<MemoryToolName>;
771
+ /**
772
+ * Optional whitelist of toolable kinds. When present, a tool `kind` argument
773
+ * outside this set is rejected. When absent, {@link
774
+ * IBodyConverterRegistry.has | registry.has} is the sole kind gate.
775
+ */
776
+ readonly kinds?: ReadonlyArray<Kind>;
777
+ /**
778
+ * Per-kind identity codecs, used by `memory_write` to map the domain
779
+ * {@link EntityId} to the record's storage id (the store resolves codecs
780
+ * internally for `get` / `delete`, so read / delete do not need them). Absent
781
+ * → `memory_write` uses {@link ICreateMemoryToolsParams.defaultCodec |
782
+ * defaultCodec}, and fails loudly for a kind with no resolvable codec. The
783
+ * read-only default tool set needs no codecs.
784
+ */
785
+ readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;
786
+ /** Default identity codec for kinds without an explicit {@link ICreateMemoryToolsParams.codecs | codecs} entry. */
787
+ readonly defaultCodec?: IIdentityCodec;
788
+ /**
789
+ * Optional host hook mapping a record to its agent-visible handle (an evocative
790
+ * mnemonic tag). When supplied, `memory_search` / `memory_context` results use
791
+ * the returned handle as the agent-visible key; when absent the raw
792
+ * {@link MemoryId} is used.
793
+ */
794
+ readonly handleFor?: (record: IMemoryRecord<unknown>) => string;
795
+ }
796
+
797
+ /**
798
+ * A directed edge in the link graph the cycle guard reasons over: `source` links
799
+ * to `target` under relation `type`.
800
+ * @public
801
+ */
802
+ export declare interface ICycleGuardEdge {
803
+ readonly source: MemoryId;
804
+ readonly target: MemoryId;
805
+ readonly type: LinkType;
806
+ }
807
+
448
808
  /**
449
809
  * An attributed link between two records. Carries the relation type, the
450
810
  * target id, and optional confidence / provenance / world-truth validity.
@@ -470,6 +830,54 @@ export declare interface IEdge {
470
830
  readonly invalid_at?: number | null;
471
831
  }
472
832
 
833
+ /**
834
+ * A near-duplicate candidate surfaced to the {@link IEntityResolver} by stage-4
835
+ * layer-2 similarity search: an existing record whose embedding is within the
836
+ * similarity threshold of the incoming candidate.
837
+ * @public
838
+ */
839
+ export declare interface IEntityResolutionCandidate {
840
+ /** The existing record's id. */
841
+ readonly id: MemoryId;
842
+ /** The existing record. */
843
+ readonly record: IMemoryRecord<unknown>;
844
+ /** The backend similarity score (higher = more similar). */
845
+ readonly score: number;
846
+ }
847
+
848
+ /**
849
+ * Stage 4 (optional) — the host's entity resolver (OQ-13, LOCKED OPTIONAL). When
850
+ * supplied, fgv surfaces near-duplicate {@link IEntityResolutionCandidate}s (from
851
+ * layer-2 similarity search) and the resolver returns a
852
+ * {@link ResolutionVerdict}. When ABSENT, stage-4 dedup falls back to
853
+ * exact-`{ kind, body }`-hash only — the deterministic-identity host path.
854
+ * @public
855
+ */
856
+ export declare interface IEntityResolver {
857
+ /**
858
+ * Decide whether `candidate` is new, a duplicate of / supersedes / merges into
859
+ * one of the surfaced `similar` records. `similar` is non-empty and ordered by
860
+ * descending score when the resolver is invoked (fgv only calls it when
861
+ * layer-2 surfaces at least one over-threshold neighbor).
862
+ */
863
+ resolve(candidate: ICandidateRecord, similar: ReadonlyArray<IEntityResolutionCandidate>): Promise<Result<ResolutionVerdict>>;
864
+ }
865
+
866
+ /**
867
+ * Stage 3 — the host's fact extractor. Turns a classified item into zero or more
868
+ * {@link ICandidateRecord}s. Each candidate's body is validated against the
869
+ * kind's registered Converter by fgv before it can reach the store (the typed
870
+ * validation boundary — no unchecked host data is persisted).
871
+ * @public
872
+ */
873
+ export declare interface IFactExtractor {
874
+ /**
875
+ * Extract candidate records from a classified item. An empty array is a valid
876
+ * result (the item yielded nothing memorable).
877
+ */
878
+ extract(item: IIngestItem, classification: IMemoryClassification): Promise<Result<ReadonlyArray<ICandidateRecord>>>;
879
+ }
880
+
473
881
  /**
474
882
  * Parameters for {@link FileTreeMemoryStore.create}.
475
883
  * @public
@@ -587,6 +995,87 @@ export declare interface IIndexedMemoryRecord {
587
995
  readonly record: IMemoryRecord<unknown>;
588
996
  }
589
997
 
998
+ /**
999
+ * Per-candidate outcome of an ingest run.
1000
+ * @public
1001
+ */
1002
+ export declare interface IIngestedRecordResult {
1003
+ /** The candidate the outcome is for. */
1004
+ readonly candidate: ICandidateRecord;
1005
+ /** The resolution verdict fgv reached (or the resolver returned). */
1006
+ readonly resolution: ResolutionVerdict;
1007
+ /** What the write ultimately did. */
1008
+ readonly disposition: IngestDisposition;
1009
+ /**
1010
+ * The stored record's id: the newly-written id (`written` / `merged`), or the
1011
+ * existing target's id (`deduped`).
1012
+ */
1013
+ readonly id: MemoryId;
1014
+ /** The persisted record, when a write happened (`written` / `merged`). */
1015
+ readonly record?: IMemoryRecord<unknown>;
1016
+ /** The stage-5 edges attached to this candidate before the write. */
1017
+ readonly edges: ReadonlyArray<ICandidateEdge>;
1018
+ /**
1019
+ * Informational diagnostic, set to `'temporal-versioned'` when the
1020
+ * contradicts→temporal interlock is recognized: a `contradicts` edge was
1021
+ * attached to a candidate of a temporal kind.
1022
+ *
1023
+ * @remarks
1024
+ * A temporal kind ALWAYS writes through the store's versioned put path (that is
1025
+ * the codec's `isVersioned` behavior — the prior version is invalidated and a
1026
+ * new version written on every write, contradicts edge or not). This flag does
1027
+ * NOT cause that routing; it is a diagnostic marker that the contradicts-driven
1028
+ * scenario occurred, so callers can distinguish a contradiction-superseding
1029
+ * version from an ordinary revision.
1030
+ */
1031
+ readonly interlock?: 'temporal-versioned';
1032
+ }
1033
+
1034
+ /**
1035
+ * A single unit of raw source material handed to the ingest pipeline. The host
1036
+ * owns the shape of {@link IIngestItem.content | content} — fgv never interprets
1037
+ * it; it flows opaquely into the host's classifier and extractor (stages 2-3).
1038
+ *
1039
+ * Single-item ingest is FIRST-CLASS: the orchestrator's primary entry point
1040
+ * takes one `IIngestItem` (per-turn streaming), and the batch entry point is a
1041
+ * convenience loop over it.
1042
+ * @public
1043
+ */
1044
+ export declare interface IIngestItem {
1045
+ /**
1046
+ * Host-owned identity for this source item. Opaque to fgv; used only in
1047
+ * diagnostics and echoed back on the {@link IIngestItemResult}.
1048
+ */
1049
+ readonly id: string;
1050
+ /**
1051
+ * The opaque source payload the host's classifier / extractor understand
1052
+ * (e.g. a raw turn, a document, a tool-call transcript). Never interpreted by
1053
+ * fgv.
1054
+ */
1055
+ readonly content: unknown;
1056
+ /**
1057
+ * Optional back-link to the memory record this item was derived from (e.g. the
1058
+ * MTM turn an extracted fact came from). When present, fgv stamps it as
1059
+ * {@link IProvenance.derivedFrom | provenance.derivedFrom} on every record
1060
+ * ingested from this item (stage 6) — the cross-kind provenance spine.
1061
+ */
1062
+ readonly sourceId?: MemoryId;
1063
+ /** Optional opaque metadata carried alongside the item; never interpreted by fgv. */
1064
+ readonly metadata?: Record<string, unknown>;
1065
+ }
1066
+
1067
+ /**
1068
+ * The result of ingesting a single {@link IIngestItem}: the item plus the
1069
+ * per-candidate outcomes (one item can yield zero or many candidate records).
1070
+ * @public
1071
+ */
1072
+ export declare interface IIngestItemResult {
1073
+ /** The item that was ingested. */
1074
+ readonly item: IIngestItem;
1075
+ /** The per-candidate outcomes, in extraction order. */
1076
+ readonly records: ReadonlyArray<IIngestedRecordResult>;
1077
+ }
1078
+
590
1079
  /**
591
1080
  * Parameters for {@link MemoryCapCullPolicy.create}.
592
1081
  * @public
@@ -606,6 +1095,41 @@ export declare interface IMemoryCapCullPolicyParams {
606
1095
  readonly mutableFields: ReadonlyArray<string>;
607
1096
  }
608
1097
 
1098
+ /**
1099
+ * The host classifier's verdict for an {@link IIngestItem} (stage 2). Guides the
1100
+ * host's own extractor (stage 3) and supplies the default `kind` / `tags` /
1101
+ * `confidence` fgv stamps when the extractor does not override them.
1102
+ *
1103
+ * Extensible: the `[key: string]: unknown` arm lets the host attach an opaque
1104
+ * classification payload without changing this interface.
1105
+ * @public
1106
+ */
1107
+ export declare interface IMemoryClassification {
1108
+ /** The record kind the item classifies as. */
1109
+ readonly kind: Kind;
1110
+ /** Optional tags the classifier assigns. */
1111
+ readonly tags?: ReadonlyArray<Tag>;
1112
+ /** Optional classifier confidence in `[0, 1]`; flows to `provenance.confidence`. */
1113
+ readonly confidence?: number;
1114
+ /** Opaque, host-owned extension payload — never interpreted by fgv. */
1115
+ readonly [key: string]: unknown;
1116
+ }
1117
+
1118
+ /**
1119
+ * Stage 2 — the host's classifier. Decides what {@link IIngestItem} maps to
1120
+ * which memory {@link Kind} (and optional tags / confidence). LOCKED as a
1121
+ * separate staged interface (OQ-10): the host plugs its existing classifier
1122
+ * machinery in here rather than surrendering to an opaque ingestor.
1123
+ * @public
1124
+ */
1125
+ export declare interface IMemoryClassifier {
1126
+ /**
1127
+ * Classify one item. A `fail` aborts the item's ingest loudly (fgv never
1128
+ * guesses a kind).
1129
+ */
1130
+ classify(item: IIngestItem): Promise<Result<IMemoryClassification>>;
1131
+ }
1132
+
609
1133
  /**
610
1134
  * The invariant identity + transaction-time envelope carried by every memory
611
1135
  * record, independent of the per-kind body.
@@ -700,6 +1224,75 @@ export declare interface IMemoryIndex {
700
1224
  backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
701
1225
  }
702
1226
 
1227
+ /**
1228
+ * The fgv-owned six-stage ingest orchestrator. Composes the host's staged
1229
+ * classify / extract / (optional) resolve / relate machinery around fgv's owned
1230
+ * validation boundary, dedup, edge + cycle safety, provenance stamping, and the
1231
+ * contradicts→temporal interlock.
1232
+ * @public
1233
+ */
1234
+ export declare interface IMemoryIngestOrchestrator {
1235
+ /**
1236
+ * Ingest a SINGLE item end-to-end (the first-class per-turn streaming path).
1237
+ */
1238
+ ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>>;
1239
+ /**
1240
+ * Ingest a batch of items. A convenience loop over {@link
1241
+ * IMemoryIngestOrchestrator.ingestItem | ingestItem} — items are processed in
1242
+ * order and the first failure aborts the batch.
1243
+ */
1244
+ ingestBatch(items: ReadonlyArray<IIngestItem>): Promise<Result<ReadonlyArray<IIngestItemResult>>>;
1245
+ }
1246
+
1247
+ /**
1248
+ * Parameters for {@link MemoryIngestOrchestrator.create}.
1249
+ * @public
1250
+ */
1251
+ export declare interface IMemoryIngestOrchestratorCreateParams {
1252
+ /** The store every write bottoms out in (stage 6). */
1253
+ readonly store: IMemoryStore;
1254
+ /** Per-kind body converter registry — the stage-3 typed validation boundary. */
1255
+ readonly registry: IBodyConverterRegistry;
1256
+ /** Per-kind identity codecs (maps a candidate `entityId` to its storage address). */
1257
+ readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;
1258
+ /** Default identity codec for kinds without an explicit entry. */
1259
+ readonly defaultCodec?: IIdentityCodec;
1260
+ /** Stage 2 — the host's classifier. */
1261
+ readonly classifier: IMemoryClassifier;
1262
+ /** Stage 3 — the host's fact extractor. */
1263
+ readonly extractor: IFactExtractor;
1264
+ /** Stage 5 — the host's relation extractor. */
1265
+ readonly relationExtractor: IRelationExtractor;
1266
+ /**
1267
+ * Stage 4 (optional, OQ-13) — the host's entity resolver. Absent → stage-4
1268
+ * dedup is exact-`{ kind, body }`-hash only (the deterministic-identity path).
1269
+ */
1270
+ readonly entityResolver?: IEntityResolver;
1271
+ /**
1272
+ * Optional vector index for stage-4 layer-2 similarity candidate-gen. Wired
1273
+ * together with {@link IMemoryIngestOrchestratorCreateParams.embed | embed} AND
1274
+ * {@link IMemoryIngestOrchestratorCreateParams.entityResolver | entityResolver};
1275
+ * absent (or either co-requisite absent) → layer-2 is skipped and dedup is
1276
+ * exact-only.
1277
+ */
1278
+ readonly vectorIndex?: IVectorIndex;
1279
+ /** Optional embedder used to embed a candidate for layer-2 similarity search. */
1280
+ readonly embed?: MemoryEmbedder;
1281
+ /**
1282
+ * Stage-4 layer-2 cosine threshold. Defaults to {@link
1283
+ * DEFAULT_SIMILARITY_THRESHOLD} (`0.85`).
1284
+ */
1285
+ readonly similarityThreshold?: number;
1286
+ /**
1287
+ * Stage-4 layer-2 top-K. Defaults to {@link DEFAULT_SIMILARITY_TOP_K} (`5`).
1288
+ */
1289
+ readonly similarityTopK?: number;
1290
+ /** Write-time cycle guard mode. Defaults to `'reject'`. */
1291
+ readonly cycleGuard?: CycleGuardMode;
1292
+ /** Diagnostic logger (defaults to a no-op). */
1293
+ readonly logger?: Logging.ILogger;
1294
+ }
1295
+
703
1296
  /**
704
1297
  * Query criteria for {@link MemoryObservationStore.query}. All supplied criteria
705
1298
  * are AND-combined.
@@ -913,7 +1506,9 @@ export declare interface IMemoryStore {
913
1506
  /**
914
1507
  * Keyed read by entity id. Resolves `entityId` to a storage address via the
915
1508
  * registered {@link IIdentityCodec} for `kind`. Returns `undefined` when no
916
- * record exists.
1509
+ * record exists. For a versioned (temporal) kind this returns the current
1510
+ * version, resolved from the derived in-memory index (not re-read/re-verified
1511
+ * from disk per call — the index is kept in sync with every write).
917
1512
  */
918
1513
  get(kind: Kind, entityId: EntityId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
919
1514
  /**
@@ -934,7 +1529,10 @@ export declare interface IMemoryStore {
934
1529
  put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
935
1530
  /**
936
1531
  * Delete a record by `(kind, entityId)`. Non-temporal kinds physically delete
937
- * the file. Returns the {@link MemoryId} of the deleted record.
1532
+ * the file and return the deleted record's {@link MemoryId}. Temporal
1533
+ * (versioned) kinds SOFT-delete: the current version is invalidated
1534
+ * (`invalid_at` set), history is retained, and the invalidated version's
1535
+ * {@link MemoryId} is returned.
938
1536
  */
939
1537
  delete(kind: Kind, entityId: EntityId): Promise<Result<MemoryId>>;
940
1538
  }
@@ -951,12 +1549,48 @@ export declare interface IMemoryStoreListFilter {
951
1549
  /** Restrict to records carrying this tag (exact match). */
952
1550
  readonly tag?: Tag;
953
1551
  /**
954
- * For temporal kinds: return only records valid at this epoch ms. No-op in
955
- * B1 (no temporal kinds wired).
1552
+ * For temporal (versioned) kinds: collapse each entity to the single version
1553
+ * valid at this epoch ms. Non-temporal records are timeless and pass through
1554
+ * unchanged. Absent = no temporal projection (every version is returned).
956
1555
  */
957
1556
  readonly asOf?: number;
958
1557
  }
959
1558
 
1559
+ /**
1560
+ * A single agent-visible search / context result item. The agent-facing key is
1561
+ * {@link IMemoryToolResultItem.handle | handle}: the host mnemonic when a
1562
+ * {@link ICreateMemoryToolsParams.handleFor | handleFor} hook is supplied, else
1563
+ * the raw {@link MemoryId}.
1564
+ * @public
1565
+ */
1566
+ export declare interface IMemoryToolResultItem {
1567
+ /** The agent-visible key (host handle when supplied, raw {@link MemoryId} otherwise). */
1568
+ readonly handle: string;
1569
+ /** The record's {@link Kind}. */
1570
+ readonly kind: Kind;
1571
+ /** The record's domain {@link EntityId}. */
1572
+ readonly entityId: EntityId;
1573
+ /** The record's tags. */
1574
+ readonly tags: ReadonlyArray<string>;
1575
+ /** The record body (a markdown string in B1). */
1576
+ readonly body: unknown;
1577
+ }
1578
+
1579
+ /**
1580
+ * The success value returned by `memory_write.execute`.
1581
+ * @public
1582
+ */
1583
+ export declare interface IMemoryWriteResult {
1584
+ /** What the write did — see {@link MemoryWriteOutcome}. */
1585
+ readonly outcome: MemoryWriteOutcome;
1586
+ /** The stored record's {@link MemoryId}. */
1587
+ readonly id: MemoryId;
1588
+ /** The domain {@link EntityId} the write targeted. */
1589
+ readonly entityId: EntityId;
1590
+ /** The record's {@link Kind}. */
1591
+ readonly kind: Kind;
1592
+ }
1593
+
960
1594
  /**
961
1595
  * Strategy for combining the result sets of the retrievers a
962
1596
  * {@link HybridRetriever} composes. Injectable so a consumer can weight,
@@ -979,6 +1613,18 @@ export declare interface IMergeStrategy {
979
1613
  */
980
1614
  export declare function indexedRecordMatchesQuery(entry: IIndexedMemoryRecord, query: IMemoryQuery): boolean;
981
1615
 
1616
+ /**
1617
+ * How a candidate was ultimately written (or not) after resolution.
1618
+ *
1619
+ * - `written` — persisted as a fresh record (verdict `new`), or as a superseding
1620
+ * record (verdict `supersede`).
1621
+ * - `deduped` — not written; an existing record satisfied it (verdict
1622
+ * `duplicate-of`, incl. every layer-1 exact `{ kind, body }` match).
1623
+ * - `merged` — merged into an existing target entity (verdict `merge-into`).
1624
+ * @public
1625
+ */
1626
+ export declare type IngestDisposition = 'written' | 'deduped' | 'merged';
1627
+
982
1628
  /**
983
1629
  * The brute-force, in-memory cosine {@link IVectorIndex}. Stores one
984
1630
  * `Float32Array` per record and answers a query by computing cosine similarity
@@ -1067,6 +1713,48 @@ export declare interface IProvenance {
1067
1713
  readonly [key: string]: unknown;
1068
1714
  }
1069
1715
 
1716
+ /**
1717
+ * A candidate paired with its resolved reference id, handed to the relation
1718
+ * extractor so it can source edges from it.
1719
+ * @public
1720
+ */
1721
+ export declare interface IRelationCandidate {
1722
+ /** The candidate about to be written. */
1723
+ readonly candidate: ICandidateRecord;
1724
+ /** Its resolved reference id (codec `idStem` — the stable entity reference). */
1725
+ readonly id: MemoryId;
1726
+ }
1727
+
1728
+ /**
1729
+ * The context fgv hands the host's relation extractor (stage 5): the source item
1730
+ * plus the candidates fgv is about to write, each paired with its resolved
1731
+ * reference id (the codec `idStem`). The extractor proposes attributed edges over
1732
+ * these ids and existing store records.
1733
+ * @public
1734
+ */
1735
+ export declare interface IRelationContext {
1736
+ /** The item being ingested. */
1737
+ readonly item: IIngestItem;
1738
+ /** The candidates fgv is about to write, each with its resolved reference id. */
1739
+ readonly candidates: ReadonlyArray<IRelationCandidate>;
1740
+ }
1741
+
1742
+ /**
1743
+ * Stage 5 — the host's relation extractor. Proposes attributed edges among the
1744
+ * candidates and existing records. fgv owns the validation, the write-time cycle
1745
+ * guard, and the actual persistence of the edges (the host brings only the
1746
+ * relationship judgment).
1747
+ * @public
1748
+ */
1749
+ export declare interface IRelationExtractor {
1750
+ /**
1751
+ * Propose the edges to attach for this ingest. An empty array is valid (no
1752
+ * relations). Every proposed {@link ICandidateEdge.source | source} must be one
1753
+ * of the context's candidate reference ids.
1754
+ */
1755
+ relate(context: IRelationContext): Promise<Result<ReadonlyArray<ICandidateEdge>>>;
1756
+ }
1757
+
1070
1758
  /**
1071
1759
  * The semantic backend wired into a {@link SemanticRetriever}: the vector index
1072
1760
  * to query and the embedder that turns the query text into a vector. Both are
@@ -1096,6 +1784,41 @@ export declare interface ISemanticRetrieverCreateParams {
1096
1784
  readonly backend?: ISemanticBackend;
1097
1785
  }
1098
1786
 
1787
+ /**
1788
+ * Narrow an {@link IIdentityCodec} to {@link ITemporalIdentityCodec} by probing
1789
+ * for the versioned methods. Used by the store when an `encode` result reports
1790
+ * `isVersioned: true`.
1791
+ * @public
1792
+ */
1793
+ export declare function isTemporalIdentityCodec(codec: IIdentityCodec): codec is ITemporalIdentityCodec;
1794
+
1795
+ /**
1796
+ * Whether a record participates in the versioned (temporal) layout. A temporal
1797
+ * record always carries a {@link ITemporalBlock | temporal} block (the store
1798
+ * stamps `valid_at` on every versioned write); an atemporal record never does,
1799
+ * so presence of `temporal` is the discriminator. Independent of `id`/`entityId`
1800
+ * divergence (MTM is flat yet has `entityId !== id`).
1801
+ * @public
1802
+ */
1803
+ export declare function isTemporalRecord(record: IMemoryRecord<unknown>): boolean;
1804
+
1805
+ /**
1806
+ * Whether a temporal record is a *current* version — its `temporal.invalid_at`
1807
+ * is `null` or absent (the still-valid sentinel). A non-temporal record is never
1808
+ * current in this sense (returns `false`).
1809
+ * @public
1810
+ */
1811
+ export declare function isVersionCurrent(record: IMemoryRecord<unknown>): boolean;
1812
+
1813
+ /**
1814
+ * Whether a temporal record's validity interval contains `asOf` (epoch ms):
1815
+ * `valid_at <= asOf` and (`invalid_at` is null/absent OR `asOf < invalid_at`).
1816
+ * The version's `valid_at` defaults to its `created` when absent; a non-temporal
1817
+ * record is never "valid at" a point (returns `false`).
1818
+ * @public
1819
+ */
1820
+ export declare function isVersionValidAt(record: IMemoryRecord<unknown>, asOf: number): boolean;
1821
+
1099
1822
  /**
1100
1823
  * Optional bi-temporal validity block on an envelope. Present only on
1101
1824
  * temporal kinds; absent = zero cost for atemporal kinds.
@@ -1108,6 +1831,42 @@ export declare interface ITemporalBlock {
1108
1831
  readonly invalid_at?: number | null;
1109
1832
  }
1110
1833
 
1834
+ /**
1835
+ * Additive extension of {@link IIdentityCodec} for versioned (temporal) kinds. A
1836
+ * codec whose {@link IIdentityCodec.encode | encode} reports `isVersioned: true`
1837
+ * implements this so the store can form and parse per-version filenames without
1838
+ * knowing the layout. Non-versioned codecs do NOT implement it (probe with
1839
+ * {@link isTemporalIdentityCodec}).
1840
+ * @public
1841
+ */
1842
+ export declare interface ITemporalIdentityCodec extends IIdentityCodec {
1843
+ /**
1844
+ * Form the version filename stem for a specific `(entityId, seq)`. The store
1845
+ * appends the extension and writes it under the entity subtree returned by
1846
+ * {@link IIdentityCodec.encode | encode}.
1847
+ */
1848
+ encodeVersion(entityId: EntityId, seq: number): Result<string>;
1849
+ /**
1850
+ * Parse a `(subtree scope, version stem)` back to its
1851
+ * {@link ITemporalVersionAddress}. The subtree scope is authoritative for the
1852
+ * `entityId`, disambiguating a stem whose `entityId` itself ends in
1853
+ * `-v<digits>`.
1854
+ */
1855
+ decodeVersion(scope: MemoryScopeKey, stem: string): Result<ITemporalVersionAddress>;
1856
+ }
1857
+
1858
+ /**
1859
+ * The `(entityId, version seq)` an {@link ITemporalIdentityCodec.decodeVersion}
1860
+ * recovers from a version file address.
1861
+ * @public
1862
+ */
1863
+ export declare interface ITemporalVersionAddress {
1864
+ /** The stable consumer-supplied domain key. */
1865
+ readonly entityId: EntityId;
1866
+ /** The version's monotonic `seq` (the `v<seq>` component of the file stem). */
1867
+ readonly seq: number;
1868
+ }
1869
+
1111
1870
  /**
1112
1871
  * The vector-index seam an embedding backend implements to make
1113
1872
  * {@link SemanticRetriever | semantic recall} operational.
@@ -1216,7 +1975,6 @@ export declare function joinFrontmatter(frontmatter: string, body: string): stri
1216
1975
  export declare type Kind = Brand<string, 'Kind'>;
1217
1976
 
1218
1977
  /**
1219
- /**
1220
1978
  * Identity codec for the knowledge kind family. A knowledge entity is keyed
1221
1979
  * by its consumer-supplied `docId`, which is used verbatim as the filename
1222
1980
  * stem under the flat `knowledge` scope.
@@ -1547,6 +2305,108 @@ export declare class MemoryIndex implements IMemoryIndex {
1547
2305
  */
1548
2306
  export declare type MemoryIndexPatchOp = 'put' | 'delete';
1549
2307
 
2308
+ /**
2309
+ * Default {@link IMemoryIngestOrchestrator}.
2310
+ * @public
2311
+ */
2312
+ export declare class MemoryIngestOrchestrator implements IMemoryIngestOrchestrator {
2313
+ private readonly _store;
2314
+ private readonly _registry;
2315
+ private readonly _codecs;
2316
+ private readonly _defaultCodec;
2317
+ private readonly _classifier;
2318
+ private readonly _extractor;
2319
+ private readonly _relationExtractor;
2320
+ private readonly _similarity;
2321
+ private readonly _similarityThreshold;
2322
+ private readonly _similarityTopK;
2323
+ private readonly _cycleGuard;
2324
+ private readonly _logger;
2325
+ private readonly _hasher;
2326
+ private constructor();
2327
+ /** Family-convention factory. */
2328
+ static create(params: IMemoryIngestOrchestratorCreateParams): Result<MemoryIngestOrchestrator>;
2329
+ /** {@inheritDoc IMemoryIngestOrchestrator.ingestItem} */
2330
+ ingestItem(item: IIngestItem): Promise<Result<IIngestItemResult>>;
2331
+ /** {@inheritDoc IMemoryIngestOrchestrator.ingestBatch} */
2332
+ ingestBatch(items: ReadonlyArray<IIngestItem>): Promise<Result<ReadonlyArray<IIngestItemResult>>>;
2333
+ /** Stage 2 — classify (host), normalizing a rejected promise into a Failure. */
2334
+ private _classify;
2335
+ /** Stage 3 — extract (host), normalizing a rejected promise into a Failure. */
2336
+ private _extract;
2337
+ /**
2338
+ * Stages 3b-6 over the extracted candidates: validate bodies, resolve/dedup
2339
+ * (stage 4), relate + cycle guard (stage 5), and load-with-provenance (stage 6).
2340
+ */
2341
+ private _processCandidates;
2342
+ /** Stage 3b + 4 for one candidate: validate body, resolve address, resolve verdict. */
2343
+ private _planCandidate;
2344
+ /**
2345
+ * Turn a resolved verdict into a candidate plan. A `new` verdict writes under the
2346
+ * candidate's own address. Every TARGET-bearing verdict
2347
+ * (`duplicate-of` / `supersede` / `merge-into`) is validated uniformly: the
2348
+ * target must be a real store record (fgv owns validation — a non-compliant host
2349
+ * resolver never smuggles a bogus id through) AND its kind must equal the
2350
+ * candidate's kind (a cross-kind target would write to the wrong scope). Only
2351
+ * `merge-into` re-addresses the write to the target's entity.
2352
+ */
2353
+ private _planFromVerdict;
2354
+ /**
2355
+ * Stage 4 — resolve a dedup verdict. Layer 1: an exact `{ kind, body }` match in
2356
+ * the candidate's scope is a `duplicate-of` (design note §1). Layer 2 (only when
2357
+ * a resolver + vector index + embedder are all wired): embed the candidate,
2358
+ * surface over-threshold neighbors, and dispatch to the {@link IEntityResolver}.
2359
+ * Otherwise the verdict is `new` (the exact-only fall-back path).
2360
+ */
2361
+ private _resolveVerdict;
2362
+ /** Layer-2 similarity candidate-gen + resolver dispatch. */
2363
+ private _resolveViaSimilarity;
2364
+ /** Layer-2 continuation once the candidate has a provisional record to embed. */
2365
+ private _resolveViaSimilarityEmbedded;
2366
+ /**
2367
+ * Find an existing record in `scope` whose `{ kind, body }` hash matches the
2368
+ * candidate's (layer-1 exact dedup). Invalidated temporal versions are excluded
2369
+ * — only a live (non-temporal or current) record deduplicates a candidate.
2370
+ */
2371
+ private _findExactMatch;
2372
+ /**
2373
+ * The body of a persisted record, required to be a string (the store persists
2374
+ * only string bodies). Fails loudly rather than blind-casting an `unknown` body
2375
+ * into the exact-dedup hash — a non-string existing body is a store-integrity
2376
+ * fault, surfaced with context, not a silent miscompute.
2377
+ */
2378
+ private static _recordBodyString;
2379
+ /** The stage-4 exact-dedup key over `{ kind, body }` (design note §1). */
2380
+ private _exactKey;
2381
+ /** Stage 5 — relate (host), validate edges, and run the write-time cycle guard. */
2382
+ private _relate;
2383
+ /**
2384
+ * Validate stage-5 edges: each `source` must be a candidate being written; each
2385
+ * `target` must resolve to a sibling candidate or an existing store record.
2386
+ */
2387
+ private _validateEdges;
2388
+ /** Stage 6 — stamp provenance + edges, admit through the store, record the outcome. */
2389
+ private _loadCandidate;
2390
+ /** Build the fully-stamped record to persist (provenance + edges + placeholder txn fields). */
2391
+ private _buildRecord;
2392
+ /** Union two tag lists, de-duplicated, preserving first-occurrence order. */
2393
+ private static _unionTags;
2394
+ /** De-duplicate a link list by canonical edge hash, preserving first-occurrence order. */
2395
+ private _dedupEdges;
2396
+ /** Resolve a `(kind, entityId)` to its storage address via the registered codec. */
2397
+ private _resolveAddress;
2398
+ /** Run a host hook, normalizing a thrown/rejected hook into a Failure (never throws across the seam). */
2399
+ private _capture;
2400
+ /** Index a record snapshot by id (last write wins on an id collision across scopes). */
2401
+ private static _indexById;
2402
+ /** Every existing outbound edge in the snapshot, as cycle-guard edges. */
2403
+ private static _existingEdges;
2404
+ /** A provisional record for embedding a candidate (placeholder txn-time fields). */
2405
+ private static _provisionalRecord;
2406
+ /** Require a candidate body to be a string (the store persists only string bodies). */
2407
+ private static _asStringBody;
2408
+ }
2409
+
1550
2410
  /**
1551
2411
  * The outcome of the observed operation.
1552
2412
  * @public
@@ -1655,6 +2515,30 @@ export declare class MemoryObservationStore implements IMemoryObserver {
1655
2515
  */
1656
2516
  export declare type MemoryScopeKey = Brand<string, 'MemoryScopeKey'>;
1657
2517
 
2518
+ /**
2519
+ * The names of the five proof-set memory tools. A caller selects a subset via
2520
+ * {@link ICreateMemoryToolsParams.tools | tools}.
2521
+ * @public
2522
+ */
2523
+ export declare type MemoryToolName = 'memory_write' | 'memory_read' | 'memory_search' | 'memory_context' | 'memory_delete';
2524
+
2525
+ /**
2526
+ * Discriminates the outcome of a {@link createMemoryTools | memory_write} call so
2527
+ * the agent can reason about what its write actually did.
2528
+ *
2529
+ * @remarks
2530
+ * - `written` — a new record was persisted, or an existing entity was updated.
2531
+ * - `deduped` — the content already existed (content-hash dedup no-op); the
2532
+ * store returned the existing record unchanged.
2533
+ *
2534
+ * The store's public `put` return does not surface cap-cull evictions, so a
2535
+ * `culled` outcome is not distinguishable at this layer without an L1 change or
2536
+ * observer wiring (both out of scope for L2). The writer's own record is always
2537
+ * `written` even when the write triggers a cull of older siblings.
2538
+ * @public
2539
+ */
2540
+ export declare type MemoryWriteOutcome = 'written' | 'deduped';
2541
+
1658
2542
  /**
1659
2543
  * Identity codec for the medium-term-memory (MTM) kind family. An MTM entity is
1660
2544
  * keyed by the colon-composite `<conversationId>:<turnIndex>` and maps into a
@@ -1763,6 +2647,25 @@ export declare class RecencyRetriever implements IMemoryRetriever {
1763
2647
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
1764
2648
  }
1765
2649
 
2650
+ /**
2651
+ * The four dedup verdicts a {@link IEntityResolver} (or fgv's exact-match layer)
2652
+ * returns for a candidate. See the design note §3 for the verdict → write
2653
+ * disposition mapping.
2654
+ * @public
2655
+ */
2656
+ export declare type ResolutionVerdict = {
2657
+ readonly verdict: 'new';
2658
+ } | {
2659
+ readonly verdict: 'duplicate-of';
2660
+ readonly target: MemoryId;
2661
+ } | {
2662
+ readonly verdict: 'supersede';
2663
+ readonly target: MemoryId;
2664
+ } | {
2665
+ readonly verdict: 'merge-into';
2666
+ readonly target: MemoryId;
2667
+ };
2668
+
1766
2669
  /**
1767
2670
  * The reference {@link IMergeStrategy}: a score-union. Every record is scored by
1768
2671
  * the number of composed result sets it appears in (deduplicated by
@@ -1790,6 +2693,22 @@ export declare class ScoreUnionMergeStrategy implements IMergeStrategy {
1790
2693
  */
1791
2694
  export declare function selectByQuery(entries: ReadonlyArray<IIndexedMemoryRecord>, query: IMemoryQuery): IMemoryRecord<unknown>[];
1792
2695
 
2696
+ /**
2697
+ * Select the current version from a set of an entity's versions: the newest
2698
+ * (highest `seq`) version whose `invalid_at` is null/absent. `undefined` when the
2699
+ * entity has no current version (fully invalidated / soft-deleted, or empty).
2700
+ * @public
2701
+ */
2702
+ export declare function selectCurrentVersion(versions: ReadonlyArray<IMemoryRecord<unknown>>): IMemoryRecord<unknown> | undefined;
2703
+
2704
+ /**
2705
+ * Select the version of an entity valid at `asOf` (epoch ms): the newest
2706
+ * (highest `seq`) version whose validity interval contains `asOf`. `undefined`
2707
+ * when no version was valid at that instant.
2708
+ * @public
2709
+ */
2710
+ export declare function selectVersionAsOf(versions: ReadonlyArray<IMemoryRecord<unknown>>, asOf: number): IMemoryRecord<unknown> | undefined;
2711
+
1793
2712
  /**
1794
2713
  * The loud-degradation message a retriever returns when `query.semantic` is set
1795
2714
  * but no {@link IVectorIndex} is wired.
@@ -1843,6 +2762,14 @@ export declare function serializeMemoryFile(envelope: IMemoryEnvelope, body: str
1843
2762
  */
1844
2763
  export declare function splitFrontmatter(raw: string): Result<IMemoryFileParts>;
1845
2764
 
2765
+ /**
2766
+ * The store-owned envelope fields fgv derives or stamps: `id` (from the codec),
2767
+ * and the transaction-time metadata (`seq` / `contentHash` / `created` /
2768
+ * `updated`). A {@link ICandidateRecord} supplies everything EXCEPT these.
2769
+ * @public
2770
+ */
2771
+ export declare type StoreStampedEnvelopeField = 'id' | 'seq' | 'contentHash' | 'created' | 'updated';
2772
+
1846
2773
  /**
1847
2774
  * Returns records matching `query.filter`, narrowed by any scope / kind / tag
1848
2775
  * pre-filter and recency-ordered. The predicate is this retriever's axis: a
@@ -1885,12 +2812,67 @@ export declare class TagRetriever implements IMemoryRetriever {
1885
2812
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
1886
2813
  }
1887
2814
 
2815
+ /**
2816
+ * The capabilities every temporal retriever exposes: temporal "as-of" queries
2817
+ * are operational (semantic recall and link traversal are not this retriever's
2818
+ * concern).
2819
+ * @public
2820
+ */
2821
+ export declare const TEMPORAL_CAPABILITIES: IMemoryRetrieverCapabilities;
2822
+
1888
2823
  /**
1889
2824
  * Converter for the optional {@link ITemporalBlock}.
1890
2825
  * @public
1891
2826
  */
1892
2827
  export declare const temporalConverter: Converter<ITemporalBlock>;
1893
2828
 
2829
+ /**
2830
+ * Identity codec for a versioned (temporal) kind family, resolving OQ-11 to the
2831
+ * subtree-per-entity layout: every version of an entity is a distinct file under
2832
+ * a per-entity subtree.
2833
+ *
2834
+ * @remarks
2835
+ * - `encode`: scope = `<baseScope>/entities/<entityId>`, idStem = `<entityId>`,
2836
+ * `isVersioned = true`. The idStem is the stable entity prefix; the store forms
2837
+ * each version's filename via {@link TemporalIdentityCodec.encodeVersion}.
2838
+ * - `encodeVersion`: version stem = `<entityId>-v<seq>`.
2839
+ * - `decode` / `decodeVersion`: recover `entityId` (and `seq`) from a
2840
+ * `(subtree scope, version stem)` pair; the scope is authoritative for the
2841
+ * `entityId`.
2842
+ * - Escaping: `baseScope` and `entityId` must each match the POSIX portable
2843
+ * filename set (they become path segments); `seq` is a non-negative integer.
2844
+ * - Layout: `vault/<baseScope>/entities/<entityId>/<entityId>-v<seq>.md`.
2845
+ * @public
2846
+ */
2847
+ export declare class TemporalIdentityCodec implements ITemporalIdentityCodec {
2848
+ /** The fixed subtree segment separating an entity's versions from its scope. */
2849
+ static readonly entitiesSegment: string;
2850
+ /** The version-stem infix: `<entityId>` + this + `<seq>`. */
2851
+ static readonly versionInfix: string;
2852
+ /** A non-negative integer string (the version seq). */
2853
+ private static readonly _versionSeqRe;
2854
+ /** The base scope segment this codec's entities live under. */
2855
+ readonly baseScope: string;
2856
+ private constructor();
2857
+ /**
2858
+ * Family-convention factory. Validates that `baseScope` is a single portable
2859
+ * filename segment (it becomes the top-level path component).
2860
+ */
2861
+ static create(baseScope: string): Result<TemporalIdentityCodec>;
2862
+ /** {@inheritDoc IIdentityCodec.encode} */
2863
+ encode(entityId: EntityId): Result<IIdentityCodecResult>;
2864
+ /** {@inheritDoc ITemporalIdentityCodec.encodeVersion} */
2865
+ encodeVersion(entityId: EntityId, seq: number): Result<string>;
2866
+ /** {@inheritDoc IIdentityCodec.decode} */
2867
+ decode(scope: MemoryScopeKey, encodedStem: string): Result<EntityId>;
2868
+ /** {@inheritDoc ITemporalIdentityCodec.decodeVersion} */
2869
+ decodeVersion(scope: MemoryScopeKey, stem: string): Result<ITemporalVersionAddress>;
2870
+ /** {@inheritDoc IIdentityCodec.verifyRoundTrip} */
2871
+ verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
2872
+ /** Validate and extract the `entityId` from a `<baseScope>/entities/<entityId>` scope. */
2873
+ private _entityIdFromScope;
2874
+ }
2875
+
1894
2876
  /**
1895
2877
  * Build the loud-degradation message a retriever returns when `query.asOf` is
1896
2878
  * set but no temporal index is wired.
@@ -1898,4 +2880,54 @@ export declare const temporalConverter: Converter<ITemporalBlock>;
1898
2880
  */
1899
2881
  export declare function temporalUnwiredMessage(kind?: Kind): string;
1900
2882
 
2883
+ /**
2884
+ * Write policy for a versioned (temporal) kind family, implementing
2885
+ * invalidate-don't-delete. Admission always accepts — history is retained, never
2886
+ * culled — and updates apply the same RFC-7386 merge patch as
2887
+ * {@link KnowledgeLwwPolicy}, restricted to the temporal mutable surface.
2888
+ *
2889
+ * @remarks
2890
+ * The policy does NOT perform the version file writes or set `invalid_at` — that
2891
+ * is the store's versioned write branch, driven by the kind's
2892
+ * {@link ITemporalIdentityCodec}. The policy's role is limited to admission and
2893
+ * the merge that forms the **new version's** content from the **current**
2894
+ * version plus the incoming patch (the merge-patch-under-versioning contract).
2895
+ *
2896
+ * - **Dedup scope.** `'entity'` — an identical re-put of the current content is a
2897
+ * no-op (the store compares the incoming content hash against the current
2898
+ * version), so identical writes do not spawn redundant versions.
2899
+ * - **Mutable surface.** `body` + the envelope metadata a consumer may revise
2900
+ * (`tags` / `links` / `provenance` / `embeddingRef`). `temporal` is NOT mutable
2901
+ * here — `valid_at` / `invalid_at` are set by the store's versioned branch.
2902
+ * @public
2903
+ */
2904
+ export declare class TemporalVersionedPolicy implements IWritePolicy {
2905
+ /** The temporal mutable surface (mirrors {@link KnowledgeLwwPolicy}). */
2906
+ readonly mutableFields: ReadonlyArray<string>;
2907
+ /** Versioned kinds dedup per-entity against the current version (see the class remarks). */
2908
+ readonly dedupScope: DedupScope;
2909
+ /** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
2910
+ private readonly _cloneEditor;
2911
+ /** Applies the RFC-7386 merge patch. */
2912
+ private readonly _mergeEditor;
2913
+ private constructor();
2914
+ /**
2915
+ * Family-convention factory. Constructs the shared `JsonEditor` instances (one
2916
+ * for cloning, one for the RFC-7386 merge), rules disabled — the same merge
2917
+ * config as the shipped policies.
2918
+ */
2919
+ static create(): Result<TemporalVersionedPolicy>;
2920
+ /** {@inheritDoc IWritePolicy.admit} */
2921
+ admit(__incoming: IMemoryRecord<unknown>, __existing: ReadonlyArray<IMemoryRecord<unknown>>): Result<AdmissionDecision>;
2922
+ /** {@inheritDoc IWritePolicy.applyUpdate} */
2923
+ applyUpdate(existing: IMemoryRecord<unknown>, patch: Record<string, unknown>): Result<IMemoryRecord<unknown>>;
2924
+ /**
2925
+ * Reassemble a record from the merged mutable view. `body` / `tags` / `links` /
2926
+ * `provenance` are required and may not be deleted by a patch; `embeddingRef`,
2927
+ * when dropped by the merge, is restored as `undefined` (absent) — the same
2928
+ * hash-stable semantics as {@link KnowledgeLwwPolicy}.
2929
+ */
2930
+ private _rebuild;
2931
+ }
2932
+
1901
2933
  export { }