@avantf/dsh-mem 0.6.4 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -10
- package/lib/client.js +1665 -3466
- package/lib/dsh-build.json +6 -5
- package/lib/engine/graph-recall/in-memory.d.ts +93 -0
- package/lib/engine/graph-recall/index.d.ts +22 -0
- package/lib/engine/graph-recall/traverse.d.ts +119 -0
- package/lib/engine/graph-recall/types.d.ts +188 -0
- package/lib/engine/mem/config/config_files.d.ts +1 -1
- package/lib/engine/mem/config/loader.d.ts +2 -3
- package/lib/engine/mem/config/paths.d.ts +2 -13
- package/lib/engine/mem/db/dao/contradictions.d.ts +4 -22
- package/lib/engine/mem/db/dao/entities.d.ts +80 -7
- package/lib/engine/mem/db/dao/fact_sources.d.ts +3 -13
- package/lib/engine/mem/db/dao/facts.d.ts +311 -56
- package/lib/engine/mem/db/dao/triples.d.ts +1 -26
- package/lib/engine/mem/db/graph.d.ts +249 -0
- package/lib/engine/mem/db/journal_notes.d.ts +35 -0
- package/lib/engine/mem/db/migrations.d.ts +9 -0
- package/lib/engine/mem/db/port.d.ts +22 -3
- package/lib/engine/mem/db/schema.d.ts +119 -0
- package/lib/engine/mem/db/tokenizer.d.ts +9 -0
- package/lib/engine/mem/dbdrivers/fake/driver.d.ts +75 -0
- package/lib/engine/mem/dbdrivers/fake/index.d.ts +7 -0
- package/lib/engine/mem/dbdrivers/fake/surface.d.ts +34 -0
- package/lib/engine/mem/dbdrivers/fake/transport.d.ts +61 -0
- package/lib/engine/mem/dbdrivers/sqlite/connection.d.ts +14 -0
- package/lib/engine/mem/dbdrivers/sqlite/contradictions.d.ts +35 -0
- package/lib/engine/mem/dbdrivers/sqlite/dao/index.d.ts +16 -0
- package/lib/engine/mem/dbdrivers/sqlite/driver.d.ts +54 -0
- package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +117 -0
- package/lib/engine/mem/dbdrivers/sqlite/index.d.ts +6 -0
- package/lib/engine/mem/dbdrivers/sqlite/migrations.d.ts +15 -0
- package/lib/engine/mem/dbdrivers/sqlite/relations.d.ts +62 -0
- package/lib/engine/mem/dbdrivers/sqlite/search.d.ts +56 -0
- package/lib/engine/mem/dbdrivers/sqlite/sources.d.ts +37 -0
- package/lib/engine/mem/defaults.d.ts +3 -4
- package/lib/engine/mem/dispatch.d.ts +7 -32
- package/lib/engine/mem/entities/extract.d.ts +44 -47
- package/lib/engine/mem/eval/derived_runner.d.ts +6 -1
- package/lib/engine/mem/eval/relation_gold.d.ts +251 -0
- package/lib/engine/mem/eval/write_metrics.d.ts +1 -1
- package/lib/engine/mem/facts_io.d.ts +328 -0
- package/lib/engine/mem/index.d.ts +10 -2
- package/lib/engine/mem/interfaces/capabilities.d.ts +30 -0
- package/lib/engine/mem/interfaces/common.d.ts +11 -0
- package/lib/engine/mem/interfaces/errors.d.ts +65 -0
- package/lib/engine/mem/interfaces/index.d.ts +18 -0
- package/lib/engine/mem/interfaces/migrations.d.ts +21 -0
- package/lib/engine/mem/interfaces/repositories.d.ts +471 -0
- package/lib/engine/mem/interfaces/search_index.d.ts +113 -0
- package/lib/engine/mem/interfaces/storage_backend.d.ts +84 -0
- package/lib/engine/mem/interfaces/storage_driver.d.ts +43 -0
- package/lib/engine/mem/interfaces/version.d.ts +34 -0
- package/lib/engine/mem/lifecycle/contradiction.d.ts +2 -2
- package/lib/engine/mem/maintenance_paths.d.ts +41 -0
- package/lib/engine/mem/modelBootstrap.d.ts +1 -1
- package/lib/engine/mem/relations/confidence.d.ts +39 -0
- package/lib/engine/mem/relations/extract.d.ts +128 -0
- package/lib/engine/mem/relations/index.d.ts +32 -0
- package/lib/engine/mem/relations/name-quality.d.ts +78 -0
- package/lib/engine/mem/relations/normalize.d.ts +275 -0
- package/lib/engine/mem/relations/patterns.d.ts +67 -0
- package/lib/engine/mem/relations/statement.d.ts +88 -0
- package/lib/engine/mem/relations/types.d.ts +79 -0
- package/lib/engine/mem/runtime.d.ts +117 -90
- package/lib/engine/mem/storage/create.d.ts +42 -0
- package/lib/engine/mem/storage/errors.d.ts +65 -0
- package/lib/engine/mem/storage/index.d.ts +15 -0
- package/lib/engine/mem/storage/ports.d.ts +472 -0
- package/lib/engine/mem/storage/registry.d.ts +37 -0
- package/lib/engine/mem/storage/sqlite.d.ts +28 -0
- package/lib/engine/mem/store/common.d.ts +1 -1
- package/lib/engine/mem/store/entity_leg.d.ts +59 -2
- package/lib/engine/mem/store/entity_sweep.d.ts +4 -3
- package/lib/engine/mem/store/explicit_relations.d.ts +52 -0
- package/lib/engine/mem/store/facts_restore.d.ts +124 -0
- package/lib/engine/mem/store/floors.d.ts +4 -1
- package/lib/engine/mem/store/graph_reader.d.ts +146 -0
- package/lib/engine/mem/store/hybrid.d.ts +50 -13
- package/lib/engine/mem/store/kinds.d.ts +47 -0
- package/lib/engine/mem/store/legs.d.ts +24 -29
- package/lib/engine/mem/store/lexical.d.ts +36 -45
- package/lib/engine/mem/store/memory.d.ts +486 -82
- package/lib/engine/mem/store/recall_policy.d.ts +49 -0
- package/lib/engine/mem/store/relation_backfill.d.ts +210 -0
- package/lib/engine/mem/store/relation_planner.d.ts +191 -0
- package/lib/engine/mem/store/secret_guard.d.ts +0 -1
- package/lib/engine/mem/store/self_query.d.ts +4 -2
- package/lib/engine/mem/store/time_filter.d.ts +121 -0
- package/lib/engine/mem/store/titles.d.ts +52 -0
- package/lib/engine/mem/store/vector_repair.d.ts +44 -54
- package/lib/engine/mem-contract/config.d.ts +44 -38
- package/lib/engine/mem-contract/text.d.ts +1 -1
- package/lib/engine/mem-contract/tools.d.ts +184 -117
- package/lib/engine/mem-contract/types.d.ts +364 -288
- package/lib/index.js +13898 -11500
- package/lib/interface-version.json +1 -1
- package/lib/types/client/index.d.ts +2 -2
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/paging.d.ts +3 -12
- package/lib/types/client/paging.d.ts.map +1 -1
- package/lib/types/client/summarize.d.ts.map +1 -1
- package/lib/types/command.d.ts +61 -0
- package/lib/types/command.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +1 -1
- package/lib/types/envinit.d.ts +2 -6
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +28 -24
- package/lib/types/hints.d.ts.map +1 -1
- package/lib/types/index.d.ts +21 -40
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/inject.d.ts +3 -2
- package/lib/types/inject.d.ts.map +1 -1
- package/lib/types/interface_gate.d.ts +9 -9
- package/lib/types/prompt.d.ts +17 -41
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/provision.d.ts +3 -0
- package/lib/types/provision.d.ts.map +1 -1
- package/lib/types/remote.d.ts +23 -9
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts +3 -3
- package/lib/types/tool_schema.d.ts +4 -0
- package/lib/types/tool_schema.d.ts.map +1 -1
- package/lib/types/vectorMigration.d.ts +5 -7
- package/lib/types/vectorMigration.d.ts.map +1 -1
- package/package.json +16 -15
- package/lib/engine/mem/config/domains.d.ts +0 -20
- package/lib/engine/mem/db/dao/chunks.d.ts +0 -231
- package/lib/engine/mem/db/dao/documents.d.ts +0 -51
- package/lib/engine/mem/db/hash.d.ts +0 -2
- package/lib/engine/mem/db/knowledge.d.ts +0 -17
- package/lib/engine/mem/eval/index.d.ts +0 -4
- package/lib/engine/mem/git.d.ts +0 -73
- package/lib/engine/mem/router.d.ts +0 -51
- package/lib/engine/mem/store/doc_files.d.ts +0 -125
- package/lib/engine/mem/store/document_text.d.ts +0 -47
- package/lib/engine/mem/store/ingest_guard.d.ts +0 -25
- package/lib/engine/mem/store/knowledge.d.ts +0 -598
- package/lib/engine/mem/store/source_picker.d.ts +0 -43
- package/lib/engine/mem-retrieval/adapters/local_reranker.d.ts +0 -52
- package/lib/engine/mem-retrieval/adapters/none_reranker.d.ts +0 -10
- package/lib/engine/mem-retrieval/rerank.d.ts +0 -13
- package/lib/types/client/domains.d.ts +0 -53
- package/lib/types/client/domains.d.ts.map +0 -1
- package/lib/types/client/ingest.d.ts +0 -24
- package/lib/types/client/ingest.d.ts.map +0 -1
- package/lib/types/open.d.ts +0 -77
- package/lib/types/open.d.ts.map +0 -1
- package/lib/types/reconcile.d.ts +0 -64
- package/lib/types/reconcile.d.ts.map +0 -1
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import { type Config, type ContradictionRecord, type FactDetail, type FactPage, type FloorProfile, type RecallResult, type RememberResult, type SourceCoverage, type TrustDiagnostic, type ValidityCoverage, type VectorMigrationOutcome, type VectorMigrationProgress, type VectorSpaceHealth, type VectorsDiagnostic, type VectorsFixStoreReport } from '../../mem-contract/index.js';
|
|
1
|
+
import { type Config, type ContradictionRecord, type FactDetail, type FactPage, type MemoryKind, type FloorProfile, type RecallResult, type RememberResult, type SourceCoverage, type StatusFilter, type TimeFilter, type MemorySearchResult, type RelationInput, type TitlesResult, type TrustDiagnostic, type ValidityCoverage, type VectorMigrationOutcome, type VectorMigrationProgress, type VectorSpaceHealth, type VectorsDiagnostic, type VectorsFixStoreReport } from '../../mem-contract/index.js';
|
|
2
2
|
import type { SemanticBackend, VectorStore } from '../../mem-retrieval/index.js';
|
|
3
3
|
import { ENTITY_SWEEP_BATCH, type EntitySweepReport } from './entity_sweep.js';
|
|
4
|
+
import type { RestoreOptions, RestoreReport } from './facts_restore.js';
|
|
5
|
+
import type { FactsExportDocument } from '../facts_io.js';
|
|
4
6
|
import { DEFAULT_VECTOR_MIGRATION_BATCH, type VectorRepairTarget } from './vector_repair.js';
|
|
5
|
-
import type { Db } from '../db/conn.js';
|
|
6
7
|
import { type LexicalProbe } from './lexical.js';
|
|
7
8
|
import { type FloorLeg } from './floors.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import type { GraphReader } from '../../graph-recall/index.js';
|
|
10
|
+
import type { RelationVocabulary, SurfaceMatch } from './relation_planner.js';
|
|
11
|
+
import type { EdgeReextractReport, GraphCoverageReport } from '../interfaces/repositories.js';
|
|
12
|
+
import type { Sig } from '../lifecycle/contradiction.js';
|
|
13
|
+
import type { MaintenanceResult } from '../lifecycle/maintenance.js';
|
|
14
|
+
import type { TickResult } from '../lifecycle/tick.js';
|
|
15
|
+
import type { StorageBackend } from '../interfaces/storage_backend.js';
|
|
12
16
|
/**
|
|
13
17
|
* Result shapes live in `@avantf/mem-contract` (single source for tool/UI payloads); these
|
|
14
18
|
* aliases keep the long-standing `@avantf/mem` export names working for consumers.
|
|
@@ -76,7 +80,7 @@ export interface ContradictionResolution {
|
|
|
76
80
|
}
|
|
77
81
|
export interface SearchInput {
|
|
78
82
|
query: string;
|
|
79
|
-
|
|
83
|
+
kind?: MemoryKind;
|
|
80
84
|
/**
|
|
81
85
|
* P-08: restrict every leg to facts whose `fact_sources.ref` equals this value. Applied INSIDE
|
|
82
86
|
* each leg's SQL (an `EXISTS` predicate, before its `LIMIT cap`), never as a post-fusion filter:
|
|
@@ -132,6 +136,24 @@ export interface SearchInput {
|
|
|
132
136
|
recordStats?: boolean;
|
|
133
137
|
/** P-01: attach per-leg evidence (`scores` / `final`) to the returned hits. Default false. */
|
|
134
138
|
includeScores?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* Override `retriever.min_score` (`0` = off). The unified cross-store entry passes `0` and applies
|
|
141
|
+
* the floor once on the NORMALIZED merged pool (DESIGN §7); a direct store search leaves it absent.
|
|
142
|
+
*/
|
|
143
|
+
minScore?: number;
|
|
144
|
+
/** Read-only probe (`AvantfRuntime.wouldFind`): no reinforcement, stats or model warm. */
|
|
145
|
+
probe?: boolean;
|
|
146
|
+
/**
|
|
147
|
+
* The caller's EXPLICIT time filter (D1-D5), normalized by `store/time_filter.ts` and applied
|
|
148
|
+
* INSIDE every leg's SQL. A HARD filter: a fact outside the window is not a candidate at all, so
|
|
149
|
+
* the leg caps and the fusion pool are filled from the window only. No `COALESCE` fallback across
|
|
150
|
+
* axes — an `event` filter never reads `created_at`.
|
|
151
|
+
*
|
|
152
|
+
* When present it REPLACES the implicit `retriever.time_window` leg (the text-parsed one): the
|
|
153
|
+
* caller has said exactly which window it means, and stacking a second, phrase-derived window on
|
|
154
|
+
* top would silently widen an explicitly narrowed question.
|
|
155
|
+
*/
|
|
156
|
+
time?: TimeFilter;
|
|
135
157
|
}
|
|
136
158
|
/**
|
|
137
159
|
* P-08 / P-13 write-side extras on `add` / `update`.
|
|
@@ -147,25 +169,97 @@ export interface FactWriteOptions {
|
|
|
147
169
|
eventDate?: string;
|
|
148
170
|
/** P-13: known end → `valid_to`, WITHOUT archiving the row. */
|
|
149
171
|
validUntil?: string;
|
|
172
|
+
/**
|
|
173
|
+
* The journal title (JOURNAL_TITLE_SEARCH §2.2/§11.1). `undefined` = not given, `null` = "no
|
|
174
|
+
* title", a string = the (already normalized) title. On a revision an omitted title INHERITS the
|
|
175
|
+
* replaced row's, and an explicitly DIFFERENT one is refused — except for a row whose title is
|
|
176
|
+
* still NULL, which may be backfilled once, and except under {@link retitle}.
|
|
177
|
+
*/
|
|
178
|
+
title?: string | null;
|
|
179
|
+
/**
|
|
180
|
+
* v17 / R3: the CONTROLLED way to correct a title inside a revision chain.
|
|
181
|
+
*
|
|
182
|
+
* With this flag the immutability rule steps aside for ONE write, and the title it replaces is
|
|
183
|
+
* recorded as an alias of the revision (`title_aliases`), so searches for the old handle still find
|
|
184
|
+
* the transaction. It requires an explicit new `title` and an existing fact to rewrite — a first
|
|
185
|
+
* `add` has no chain to correct — and it changes nothing about the rule afterwards: the next update
|
|
186
|
+
* that names a different title WITHOUT this flag is refused exactly as before.
|
|
187
|
+
*
|
|
188
|
+
* Deliberately not a free rename: the caller must say "I am correcting a handle", which is the
|
|
189
|
+
* difference between fixing a typo and silently renaming a transaction the caller merely thinks it
|
|
190
|
+
* is continuing (the merge branch keeps refusing a conflicting title for the same reason).
|
|
191
|
+
*/
|
|
192
|
+
retitle?: true;
|
|
193
|
+
/**
|
|
194
|
+
* Enforce the MODEL-facing rule "a journal needs a title" (JOURNAL_TITLE_SEARCH §2.3/§11.2).
|
|
195
|
+
*
|
|
196
|
+
* Set ONLY by the model-facing tool boundary (`@avantf/mem`'s `runToolSpec`). The CLI, an import,
|
|
197
|
+
* a merge and the UI leave it off: legacy journals, old exports and scripts must keep working, and
|
|
198
|
+
* a store-wide rule would break them.
|
|
199
|
+
*/
|
|
200
|
+
requireJournalTitle?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* STRUCTURED_RELATIONS §2: relations the CALLER states, written as edges of this revision.
|
|
203
|
+
*
|
|
204
|
+
* `journal` refuses a non-empty list outright ({@link RELATIONS_NEED_NOTE}). The list is passed to
|
|
205
|
+
* `persistFact`, which writes the explicit edges BEFORE the extracted ones so a relation the caller
|
|
206
|
+
* stated wins the four-tuple dedupe against the extractor's guess at the same relation.
|
|
207
|
+
*
|
|
208
|
+
* OMITTED VERSUS EMPTY. Omitting the field means "this write says nothing about relations" — a
|
|
209
|
+
* write that lands on an existing row (duplicate `add`, revive, unchanged-content `update`) leaves
|
|
210
|
+
* that row's explicit edges untouched. An EMPTY ARRAY means "this write declares none" and clears
|
|
211
|
+
* them, which is the only way to remove the last relation from a fact whose content did not
|
|
212
|
+
* change (no new revision is created, so `supersede` cannot delete the edge).
|
|
213
|
+
*/
|
|
214
|
+
relations?: readonly RelationInput[];
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* The `mem_recall.titles` request (JOURNAL_TITLE_SEARCH §4/§11.4, RECALL_INTERFACE §2.3).
|
|
218
|
+
*
|
|
219
|
+
* Defaults are the action's documented ones — `status: 'active'`, `kind: 'journal'` — applied HERE
|
|
220
|
+
* rather than in the contract, because the contract leaves an omitted field absent and the engine
|
|
221
|
+
* owns what absence means. `offset`/`limit` page the EXACT (title-equal) result only; the fuzzy
|
|
222
|
+
* fallback is the engine's fixed shortlist.
|
|
223
|
+
*/
|
|
224
|
+
export interface TitleInput {
|
|
225
|
+
query: string;
|
|
226
|
+
kind?: MemoryKind;
|
|
227
|
+
status?: StatusFilter;
|
|
228
|
+
time?: TimeFilter;
|
|
229
|
+
offset?: number;
|
|
230
|
+
limit?: number;
|
|
231
|
+
includeExcerpt?: boolean;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The `mem_recall.search` request, after the engine's two-stage policy is applied to it
|
|
235
|
+
* (docs/RECALL_INTERFACE.md): the caller's `limit`/`offset` page the EXACT result, while the fuzzy
|
|
236
|
+
* fallback is fixed by the engine (`FUZZY_TOP_K` / `FUZZY_MIN_SCORE`), never by the caller.
|
|
237
|
+
*
|
|
238
|
+
* `query` is OPTIONAL (RECALL_INTERFACE §2.4): a call with only `time` is a PURE TIME window
|
|
239
|
+
* (`mode:"time"`), which never runs the literal/relation/fuzzy stages at all. An omitted `query`
|
|
240
|
+
* without a `time` is the caller's error, refused in {@link MemoryStore.searchMemory}.
|
|
241
|
+
*/
|
|
242
|
+
export interface MemorySearchInput extends Omit<SearchInput, 'query'> {
|
|
243
|
+
/** The literal query. Omitted = a pure time window (then `time` is required). */
|
|
244
|
+
query?: string;
|
|
245
|
+
/** The exact page's first row (default 0). Only read when the literal pass matches. */
|
|
246
|
+
offset?: number;
|
|
150
247
|
}
|
|
151
248
|
export declare class MemoryStore {
|
|
152
|
-
private readonly db;
|
|
153
249
|
private readonly config;
|
|
154
250
|
private readonly semantic;
|
|
155
251
|
private readonly vstore;
|
|
156
252
|
private readonly contradictions;
|
|
157
253
|
private readonly facts;
|
|
254
|
+
private readonly relations;
|
|
158
255
|
private readonly entities;
|
|
159
256
|
private readonly triples;
|
|
160
257
|
private readonly sources;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
* and building MATCH expressions for the wrong one is silent zero recall (see `db/tokenizer.ts`).
|
|
167
|
-
*/
|
|
168
|
-
private readonly ftsTokenizer;
|
|
258
|
+
private readonly searchIndex;
|
|
259
|
+
private readonly keyValue;
|
|
260
|
+
private readonly lifecycle;
|
|
261
|
+
/** The store's database path, used ONLY to name the vector index snapshot beside it. */
|
|
262
|
+
private readonly dbPath;
|
|
169
263
|
/**
|
|
170
264
|
* Identity object for the shared entity sweep's in-flight guard (`store/entity_sweep.ts`).
|
|
171
265
|
*
|
|
@@ -179,9 +273,18 @@ export declare class MemoryStore {
|
|
|
179
273
|
* derived state: nothing is lost when it resets, and the queue itself is in the database.
|
|
180
274
|
*/
|
|
181
275
|
private conflictDrainCursor;
|
|
182
|
-
constructor(
|
|
276
|
+
constructor(backend: StorageBackend, config: Config, semantic: SemanticBackend, vstore: VectorStore,
|
|
183
277
|
/** The store's database path, used ONLY to name the vector index snapshot beside it. */
|
|
184
278
|
dbPath?: string);
|
|
279
|
+
/**
|
|
280
|
+
* The open-time pass, now an explicit step (DB-2).
|
|
281
|
+
*
|
|
282
|
+
* It used to run inside the constructor. A constructor cannot `await` a repository call, and
|
|
283
|
+
* silently skipping the pass on a backend that answers asynchronously would leave the clock
|
|
284
|
+
* unrepaired and the semantic index empty — so the composition root wires the store first and
|
|
285
|
+
* starts it here, in the order the comments below explain.
|
|
286
|
+
*/
|
|
287
|
+
start(): Promise<void>;
|
|
185
288
|
/**
|
|
186
289
|
* One automatic trust/lifecycle pass (spec §5): advance the active-day clock
|
|
187
290
|
* (when trust is enabled), run the tick, and evict archived/purged vectors from
|
|
@@ -189,7 +292,7 @@ export declare class MemoryStore {
|
|
|
189
292
|
*/
|
|
190
293
|
trustTick(opts?: {
|
|
191
294
|
budget?: number | null;
|
|
192
|
-
}): TickResult
|
|
295
|
+
}): Promise<TickResult>;
|
|
193
296
|
/**
|
|
194
297
|
* Identity of the vector space this store currently writes into — the REPRESENTATION fingerprint
|
|
195
298
|
* (`vectorSpaceId` + the backend's declared pooling/normalize/window/revision), not just the
|
|
@@ -221,14 +324,14 @@ export declare class MemoryStore {
|
|
|
221
324
|
* The counts are the same two `VectorsDiagnostic` already separates (`stale` / `space_stale`); this
|
|
222
325
|
* is the read model those surfaces use instead of the full diagnose pass.
|
|
223
326
|
*/
|
|
224
|
-
vectorSpaceHealth(): VectorSpaceHealth
|
|
327
|
+
vectorSpaceHealth(): Promise<VectorSpaceHealth>;
|
|
225
328
|
/**
|
|
226
329
|
* {@link vectorSpaceHealth} plus the internal `legacy` split (how many `space_stale` rows predate
|
|
227
330
|
* the representation fingerprint). Kept off the contract type so the public detection surface —
|
|
228
331
|
* and every existing assertion on it — is unchanged; only the warning's wording uses it.
|
|
229
332
|
*/
|
|
230
333
|
private vectorSpaceCounts;
|
|
231
|
-
add(content: string,
|
|
334
|
+
add(content: string, kind?: MemoryKind, ttlDays?: number, opts?: FactWriteOptions): Promise<RememberResult>;
|
|
232
335
|
/**
|
|
233
336
|
* Write-path contradiction detection (DESIGN §11): check the fact that was just
|
|
234
337
|
* written against the corpus, then report its FULL open-conflict set.
|
|
@@ -255,18 +358,18 @@ export declare class MemoryStore {
|
|
|
255
358
|
private warnIndexFailure;
|
|
256
359
|
/** Best-effort semantic indexing: encode the fact and add it to the vstore. */
|
|
257
360
|
private maybeIndexSemantic;
|
|
258
|
-
update(fact_id: number, content: string,
|
|
259
|
-
remove(fact_id: number, reason?: string): boolean
|
|
260
|
-
archive(fact_id: number, reason?: string): boolean
|
|
361
|
+
update(fact_id: number, content: string, kind?: MemoryKind, ttlDays?: number, opts?: FactWriteOptions): Promise<RememberResult>;
|
|
362
|
+
remove(fact_id: number, reason?: string): Promise<boolean>;
|
|
363
|
+
archive(fact_id: number, reason?: string): Promise<boolean>;
|
|
261
364
|
/**
|
|
262
365
|
* Bring an archived fact back (spec §2.6): a `forgot` fact is lifted to
|
|
263
366
|
* `recall_floor` (otherwise the very next tick would re-archive it, R4), the
|
|
264
367
|
* idle clock is refreshed (otherwise an `idle`-archived fact dies again, R18),
|
|
265
368
|
* and `settle_clock` moves to now so the downtime is not charged.
|
|
266
369
|
*/
|
|
267
|
-
restore(fact_id: number): boolean
|
|
268
|
-
helpful(fact_id: number): number | null
|
|
269
|
-
unhelpful(fact_id: number): number | null
|
|
370
|
+
restore(fact_id: number): Promise<boolean>;
|
|
371
|
+
helpful(fact_id: number): Promise<number | null>;
|
|
372
|
+
unhelpful(fact_id: number): Promise<number | null>;
|
|
270
373
|
/**
|
|
271
374
|
* Explicit feedback (spec §2.4). Pinned/archived rows only move `helpful_count`
|
|
272
375
|
* (R7); otherwise the decay is settled first, then `±feedback_delta`, pin at the
|
|
@@ -274,40 +377,46 @@ export declare class MemoryStore {
|
|
|
274
377
|
*/
|
|
275
378
|
private applyFeedback;
|
|
276
379
|
/** Permanent-memory administration (spec §6). */
|
|
277
|
-
pin(fact_id: number): boolean
|
|
278
|
-
unpin(fact_id: number): boolean
|
|
380
|
+
pin(fact_id: number): Promise<boolean>;
|
|
381
|
+
unpin(fact_id: number): Promise<boolean>;
|
|
279
382
|
/**
|
|
280
383
|
* Trust/forgetting diagnostics (spec §6, R17/R24): the quota is PER FACT, so the
|
|
281
384
|
* report exposes "facts that consumed quota today" and "total effective gains"
|
|
282
385
|
* rather than a meaningless global remaining.
|
|
283
386
|
*/
|
|
284
|
-
trustDiagnose(): TrustDiagnostic
|
|
285
|
-
get(fact_id: number): FactDetail | null
|
|
387
|
+
trustDiagnose(): Promise<TrustDiagnostic>;
|
|
388
|
+
get(fact_id: number): Promise<FactDetail | null>;
|
|
286
389
|
private triplesOf;
|
|
287
390
|
/**
|
|
288
391
|
* One page of facts, newest first.
|
|
289
|
-
* @param
|
|
392
|
+
* @param kind - optional kind filter.
|
|
290
393
|
* @param status - `active` (default) or `archived`.
|
|
291
394
|
* @param limit - page size.
|
|
292
395
|
* @param offset - page offset (0-based).
|
|
293
396
|
* @returns the page, the page size (`count`), the matching total, and whether
|
|
294
397
|
* more rows remain after this page.
|
|
295
398
|
*/
|
|
296
|
-
list(
|
|
399
|
+
list(kind?: MemoryKind, status?: string, limit?: number, offset?: number,
|
|
297
400
|
/** P-08: only facts whose `fact_sources.ref` equals this value (reverse provenance lookup). */
|
|
298
|
-
source?: string
|
|
401
|
+
source?: string,
|
|
402
|
+
/**
|
|
403
|
+
* The caller's EXPLICIT time window (the same normalized filter `search` takes). It NARROWS the
|
|
404
|
+
* page: `total`/`truncated` count the window, and the page order stays `created_at DESC` (a list
|
|
405
|
+
* page is a listing, not a chronological walk along whichever axis was filtered).
|
|
406
|
+
*/
|
|
407
|
+
time?: TimeFilter): Promise<FactPage>;
|
|
299
408
|
/**
|
|
300
409
|
* P-08 coverage over the ACTIVE corpus, for `admin stats`: the share of facts that can answer
|
|
301
410
|
* "why do I remember this". Reported from day one so the field cannot quietly become a second
|
|
302
411
|
* `mirror_source` (filled but meaningless).
|
|
303
412
|
*/
|
|
304
|
-
sourceCoverage(): SourceCoverage
|
|
413
|
+
sourceCoverage(): Promise<SourceCoverage>;
|
|
305
414
|
/** P-13 coverage over the ACTIVE corpus: the share of facts carrying an event time. */
|
|
306
|
-
validityCoverage(): ValidityCoverage
|
|
307
|
-
countByStatus(): {
|
|
415
|
+
validityCoverage(): Promise<ValidityCoverage>;
|
|
416
|
+
countByStatus(): Promise<{
|
|
308
417
|
active: number;
|
|
309
418
|
archived: number;
|
|
310
|
-
}
|
|
419
|
+
}>;
|
|
311
420
|
/** Run one incremental contradiction-detection pass; returns newly logged pairs. */
|
|
312
421
|
/**
|
|
313
422
|
* Drain the DURABLE pending queue, bounded by `budget`.
|
|
@@ -331,9 +440,9 @@ export declare class MemoryStore {
|
|
|
331
440
|
*
|
|
332
441
|
* A throw keeps the batch queued too — `checkMany` never returns, so nothing is stamped.
|
|
333
442
|
*/
|
|
334
|
-
drainConflicts(budget?: number): ConflictDrainReport
|
|
443
|
+
drainConflicts(budget?: number): Promise<ConflictDrainReport>;
|
|
335
444
|
/** {@link drainConflicts}, reduced to the pairs it logged (the `contradict_check` payload). */
|
|
336
|
-
checkContradictions(budget?: number): Sig[]
|
|
445
|
+
checkContradictions(budget?: number): Promise<Sig[]>;
|
|
337
446
|
/**
|
|
338
447
|
* One bounded conflict pass for {@link maintenance}, reduced to COUNTS.
|
|
339
448
|
*
|
|
@@ -349,10 +458,10 @@ export declare class MemoryStore {
|
|
|
349
458
|
/** List contradiction candidates (`resolved` 0 = open, 1 = closed with a recorded verdict). */
|
|
350
459
|
listContradictions(opts?: {
|
|
351
460
|
resolved?: number;
|
|
352
|
-
|
|
461
|
+
kind?: MemoryKind;
|
|
353
462
|
threshold?: number;
|
|
354
463
|
limit?: number;
|
|
355
|
-
}): ContradictionRecord[]
|
|
464
|
+
}): Promise<ContradictionRecord[]>;
|
|
356
465
|
/**
|
|
357
466
|
* Adjudicate one logged conflict — the outlet the detector never had.
|
|
358
467
|
*
|
|
@@ -369,7 +478,7 @@ export declare class MemoryStore {
|
|
|
369
478
|
* The verdict is recorded on this row either way: `archive()` short-circuits for a fact that
|
|
370
479
|
* is already archived, and the row would otherwise stay open on a technicality.
|
|
371
480
|
*/
|
|
372
|
-
resolveContradiction(id: number, resolution: 'true_positive' | 'false_positive', loserFactId?: number): ContradictionResolution
|
|
481
|
+
resolveContradiction(id: number, resolution: 'true_positive' | 'false_positive', loserFactId?: number): Promise<ContradictionResolution>;
|
|
373
482
|
/**
|
|
374
483
|
* Semantic-index health for the ACTIVE corpus (the only thing the index serves):
|
|
375
484
|
* - `missing` active facts with no persisted vector at all;
|
|
@@ -383,7 +492,7 @@ export declare class MemoryStore {
|
|
|
383
492
|
* upgraded past the space id) — usable bytes, incomparable scores.
|
|
384
493
|
* `total`/`with_semantic` still describe the whole table (including archived).
|
|
385
494
|
*/
|
|
386
|
-
vectorsDiagnose(): VectorsDiagnostic
|
|
495
|
+
vectorsDiagnose(): Promise<VectorsDiagnostic>;
|
|
387
496
|
/**
|
|
388
497
|
* ONE read+decode pass over the persisted vectors, shared by diagnose and fix
|
|
389
498
|
* (the two methods used to re-scan and re-decode the same blobs 4-5× per call).
|
|
@@ -395,7 +504,7 @@ export declare class MemoryStore {
|
|
|
395
504
|
* `semantic.auto_migrate`, the dry-run split and the drop-before-encode order — lives there once,
|
|
396
505
|
* for both stores.
|
|
397
506
|
*/
|
|
398
|
-
vectorRepairTarget(): VectorRepairTarget
|
|
507
|
+
vectorRepairTarget(): Promise<VectorRepairTarget>;
|
|
399
508
|
/**
|
|
400
509
|
* {@link classifyVectors} reduced to what the repair flow steps on: the counts, the ids to drop
|
|
401
510
|
* (bounded by `limit`) and the decoded vectors that can go back into a lost live index.
|
|
@@ -458,6 +567,7 @@ export declare class MemoryStore {
|
|
|
458
567
|
maintenance(): Promise<MaintenanceResult & {
|
|
459
568
|
entities: EntitySweepReport;
|
|
460
569
|
conflicts: ConflictSweepCounts;
|
|
570
|
+
edges: EdgeReextractReport;
|
|
461
571
|
}>;
|
|
462
572
|
/**
|
|
463
573
|
* Re-extract the entity/triple rows of facts written by OLDER extraction rules (bounded).
|
|
@@ -487,6 +597,56 @@ export declare class MemoryStore {
|
|
|
487
597
|
* selection (`facts.staleEntityRows`, ACTIVE facts only) and the per-fact write below.
|
|
488
598
|
*/
|
|
489
599
|
reindexEntities(budget?: number): Promise<EntitySweepReport>;
|
|
600
|
+
/**
|
|
601
|
+
* Phase 2 (RELATIONAL_RECALL §4.3/R2): re-derive `edges` for facts written by an older edge
|
|
602
|
+
* extractor — the graph's counterpart of {@link reindexEntities}.
|
|
603
|
+
*
|
|
604
|
+
* Why an explicit entry point instead of only the write path: every database upgraded to v15 has
|
|
605
|
+
* `edges.extraction_version = 0` rows (the Phase 1 backfill's), and the alias/canonical rules of
|
|
606
|
+
* this phase are functions of the STORED triples — so the corpus can be brought to the current
|
|
607
|
+
* version without re-tagging a single fact. `all` re-derives every active fact with triples, which
|
|
608
|
+
* is what an operator runs after the curated vocabulary or the confidence floor changed (those do
|
|
609
|
+
* not bump the extractor version by themselves).
|
|
610
|
+
*
|
|
611
|
+
* `maintenance()` runs one bounded pass of it, so the graph becomes current the same way the entity
|
|
612
|
+
* stamp and the conflict queue do; the CLI/UI surface is Phase 5 (R7) work.
|
|
613
|
+
*
|
|
614
|
+
* It is ALSO where STRUCTURED_RELATIONS §四/§七's one-time cleanup happens: `db/graph.ts`'s pass
|
|
615
|
+
* deletes the edges of non-note facts before re-deriving, because a database written by a pre-5c
|
|
616
|
+
* build still carries journal edges. The delete is idempotent (0 rows once clean) and touches
|
|
617
|
+
* `edges` only — the facts and their `triples`, which the contradiction detector reads, stay.
|
|
618
|
+
*/
|
|
619
|
+
reextractEdges(options?: {
|
|
620
|
+
budget?: number;
|
|
621
|
+
all?: boolean;
|
|
622
|
+
}): Promise<EdgeReextractReport>;
|
|
623
|
+
/**
|
|
624
|
+
* Phase 2 (§R2/§R6): what the graph actually covers, and why the rest is missing.
|
|
625
|
+
*
|
|
626
|
+
* Two ratios rather than one, because they answer different questions: `tripleCoverage` is how
|
|
627
|
+
* much of the active corpus yields any relation at all (the string extractor's reach), while
|
|
628
|
+
* `relationCoverage` is how much of THAT survives normalization and the confidence floor (the
|
|
629
|
+
* graph's precision policy). `samples` shows the refused triples with their reason, which is the
|
|
630
|
+
* failure-sample half of §4.3's "记录抽取覆盖率/失败样本".
|
|
631
|
+
*/
|
|
632
|
+
graphCoverage(options?: {
|
|
633
|
+
samples?: number;
|
|
634
|
+
sampleFacts?: number;
|
|
635
|
+
}): Promise<GraphCoverageReport>;
|
|
636
|
+
/**
|
|
637
|
+
* Rebuild this library from an export document — the engine half of `avantf-mem restore <file>`.
|
|
638
|
+
*
|
|
639
|
+
* A THIN adapter on purpose: the empty-table rule, the insert order, the SQL and the report all
|
|
640
|
+
* live in `store/facts_restore.ts`, because this class is a hub and a rebuild of the whole facts
|
|
641
|
+
* table must not become another reason to grow it. What it contributes is the two things only the
|
|
642
|
+
* store has: the ONE write connection (so the restored rows are immediately visible to every
|
|
643
|
+
* reader here) and the derived-state entity sweep that runs after the commit.
|
|
644
|
+
*
|
|
645
|
+
* Reachable from `mem_admin restore_snapshot` (and from the CLI): after a real restore this
|
|
646
|
+
* method refreshes the live vector index and resets the conflict-drain cursor, so a host that
|
|
647
|
+
* rebuilt its library from a tool call answers from the NEW rows without a restart.
|
|
648
|
+
*/
|
|
649
|
+
restoreFacts(doc: FactsExportDocument, options?: RestoreOptions): Promise<RestoreReport>;
|
|
490
650
|
/**
|
|
491
651
|
* A fact left the ACTIVE corpus: retire the open conflicts that name it, so
|
|
492
652
|
* `contradict` never reports a pair whose revision is archived and repeated
|
|
@@ -495,20 +655,204 @@ export declare class MemoryStore {
|
|
|
495
655
|
*/
|
|
496
656
|
private retireConflicts;
|
|
497
657
|
/**
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}[];
|
|
506
|
-
/** Two-hop traversal: subj -pred-> mid, mid -second_pred-> obj. Returns the second-hop facts. */
|
|
507
|
-
chain(subj: string, pred?: string, secondPred?: string, limit?: number): Promise<RecallResult>;
|
|
508
|
-
/** Facts whose entity set contains ALL given entities (AND-join). */
|
|
509
|
-
reason(entities: string[], limit?: number): Promise<RecallResult>;
|
|
510
|
-
/** Hybrid search over the memory corpus (semantic + FTS + entity Jaccard [+ HRR probe] via fusion). */
|
|
658
|
+
* Hybrid search over the memory corpus (semantic + FTS + entity Jaccard [+ HRR probe] via fusion).
|
|
659
|
+
*
|
|
660
|
+
* This is the FUZZY half of `mem_recall.search` (see {@link searchMemory}) — the relevance-ranked
|
|
661
|
+
* retrieval with the floors, the fused-score floor and the output budget. It is also the
|
|
662
|
+
* conditional hint's probe (`AvantfRuntime.wouldFind`) and the cross-store path's memory leg, so it
|
|
663
|
+
* stays a public store method even though the public tool no longer exposes its knobs.
|
|
664
|
+
*/
|
|
511
665
|
search(input: SearchInput): Promise<RecallResult>;
|
|
666
|
+
/**
|
|
667
|
+
* `mem_recall.search` — the ONE content retrieval (docs/RECALL_INTERFACE.md §2.2/§4,
|
|
668
|
+
* docs/RELATIONAL_RECALL.md §6, docs/GRAPH_RECALL_AND_SEMANTICS.md §7.9).
|
|
669
|
+
*
|
|
670
|
+
* Four stages, in this order and with this precedence:
|
|
671
|
+
*
|
|
672
|
+
* 1. EXACT: active facts whose content literally CONTAINS the query. A literal match ends the
|
|
673
|
+
* pass — the answer is a page (`total`/`offset`/`limit`/`truncated`) of raw hits whose
|
|
674
|
+
* `score` is the constant 1 (a literal hit, not a relevance number; RECALL_INTERFACE §5 says
|
|
675
|
+
* the model must not read it as one). The pass runs only when the query is long enough to be
|
|
676
|
+
* a literal answer at all AND the literal set is small enough to be one
|
|
677
|
+
* (`store/recall_policy.ts`: `exactEligible` / `EXACT_MAX_TOTAL`).
|
|
678
|
+
* 2. FACTS, only when stage 1 matched nothing AND the query was a PURE multi-entity connection
|
|
679
|
+
* ("张伟和李娜", no other meaningful token — `planFactQuery`): the ACTIVE facts that mention
|
|
680
|
+
* EVERY named entity, paged at the FACT level. This is the fact-level AND over
|
|
681
|
+
* `fact_entities`, so journal entries and notes without any relation edge are covered. An
|
|
682
|
+
* empty page is NOT an answer — the pass returns `null` and the later stages run, which is
|
|
683
|
+
* the same no-empty-answer rule the relation stage follows.
|
|
684
|
+
* 3. RELATION, only when the earlier stages did not answer AND `store/relation_planner.ts` formed
|
|
685
|
+
* a complete plan: the graph walk whose paths answer the question. A plan that finds no path is
|
|
686
|
+
* NOT an answer — the pass returns `null` and fuzzy runs, which is the documented fallback
|
|
687
|
+
* ("无路径时不能返回空的 relation"). The caller's `kind`/`source`/`time` filters are handed
|
|
688
|
+
* to the graph reader, so the path is walked over exactly the corpus the caller asked about.
|
|
689
|
+
* 4. FUZZY, only when no earlier stage answered: the hybrid retrieval, unchanged, with the
|
|
690
|
+
* engine's fixed shortlist (`topK = 5`, `min_score = 0.5`). The caller's `limit`/`offset` page
|
|
691
|
+
* the exact and facts results (and the relation page is capped by the graph's own limit) and
|
|
692
|
+
* are NOT forwarded here: a five-item ranked shortlist has no page 2.
|
|
693
|
+
*
|
|
694
|
+
* A call with NO `query` skips all four stages and answers the pure time window instead
|
|
695
|
+
* ({@link timePage}, `mode:"time"`): the window is the whole question, so there is nothing to
|
|
696
|
+
* match, plan or rank.
|
|
697
|
+
*
|
|
698
|
+
* The exact, facts and relation passes reinforce the facts they return, exactly like the fuzzy
|
|
699
|
+
* pass does for its hits: all are recalls the caller actually reads. A caller that must stay
|
|
700
|
+
* read-only passes `track: false` (the conditional hint's probe does). Exact, facts, relation and
|
|
701
|
+
* time also honour the caller's output budget (their text is the caller's to bound).
|
|
702
|
+
*
|
|
703
|
+
* It does NOT emit a retrieval-health event. Those counters describe the retrieval PIPELINE
|
|
704
|
+
* (legs, fusion, semantic-live rate); none of an exact, facts, relation or time answer ran a leg,
|
|
705
|
+
* so counting them would report the semantic leg as degraded for a query that never asked it
|
|
706
|
+
* anything.
|
|
707
|
+
*/
|
|
708
|
+
searchMemory(input: MemorySearchInput): Promise<MemorySearchResult>;
|
|
709
|
+
/**
|
|
710
|
+
* The pure time stage of {@link searchMemory} — `mem_recall.search` with no query (RECALL_INTERFACE
|
|
711
|
+
* §2.4). The window IS the question: no literal pass, no relation plan, no legs, nothing to rank.
|
|
712
|
+
*
|
|
713
|
+
* The order is the window's OWN axis, newest first (`db/dao/facts.ts#timeAxisColumn`), so "what
|
|
714
|
+
* happened that day" reads in the order it happened; `score` is the constant 1 exactly like the
|
|
715
|
+
* exact page's, because there is no relevance number here for the model to compare.
|
|
716
|
+
*
|
|
717
|
+
* It honours `kind`/`source`, pages with the same `offset`/`limit` (default 10, cap 50) and the
|
|
718
|
+
* same `max_tokens` budget as every other recall, and reinforces the rows it returns unless
|
|
719
|
+
* `track: false` — a window the caller reads is a recall like any other.
|
|
720
|
+
*/
|
|
721
|
+
private timePage;
|
|
722
|
+
/**
|
|
723
|
+
* The literal stage of {@link searchMemory}, or `null` when it did not apply.
|
|
724
|
+
*
|
|
725
|
+
* `null` has four causes and they are deliberately not distinguished for the caller: the query is
|
|
726
|
+
* too short to be literal (`exactEligible`), it matched no active fact, the literal set is WIDER
|
|
727
|
+
* than a literal answer (`EXACT_MAX_TOTAL` — a common word's dozens of rows are a corpus scan, not
|
|
728
|
+
* a page), or it was blank. All four mean "the rest of the pipeline answers this".
|
|
729
|
+
*/
|
|
730
|
+
private exactContentPage;
|
|
731
|
+
/**
|
|
732
|
+
* The FACT-level entity vocabulary for the fact-level AND stage
|
|
733
|
+
* (docs/GRAPH_RECALL_AND_SEMANTICS.md §7.9): entity surfaces linked to at least one ACTIVE fact
|
|
734
|
+
* through `fact_entities`, so journal entries and notes without a relation edge are visible.
|
|
735
|
+
*
|
|
736
|
+
* Public on the store but not on any tool/action (like {@link graphReader}): the gold evaluator
|
|
737
|
+
* plans the same question the stage answers, without going through `search`.
|
|
738
|
+
*/
|
|
739
|
+
factEntitiesIn(text: string): Promise<SurfaceMatch[]>;
|
|
740
|
+
/**
|
|
741
|
+
* The FACTS stage of {@link searchMemory}, or `null` when it did not answer.
|
|
742
|
+
*
|
|
743
|
+
* `null` has three causes, deliberately not distinguished for the caller: the query is not a PURE
|
|
744
|
+
* multi-entity connection (`planFactQuery` refused it), the caller's `kind`/`source`/`time` filter
|
|
745
|
+
* leaves no fact mentioning every entity, or a named entity does not exist in the live corpus. All
|
|
746
|
+
* three mean "the next stage answers this": a facts result with zero rows is never returned,
|
|
747
|
+
* because `mode:"facts"` is a promise that a FACT page was found.
|
|
748
|
+
*
|
|
749
|
+
* Order inside the stage, and why:
|
|
750
|
+
* - the plan is checked BEFORE the repository read: no conjunction, fewer than two entities or a
|
|
751
|
+
* leftover meaningful token means no fact query at all;
|
|
752
|
+
* - the explicit `time` filter is normalized here (before the read, after the plan), so a
|
|
753
|
+
* malformed window is the caller's error exactly as it is in the exact page;
|
|
754
|
+
* - the page is FACT-level (`total`/`offset`/`limit`/`truncated`), and the caller's output budget
|
|
755
|
+
* only shortens `content` — dropping a row would make `total` disagree with the page;
|
|
756
|
+
* - the returned facts are reinforced unless `track: false`, like every other recall the caller
|
|
757
|
+
* actually reads.
|
|
758
|
+
*/
|
|
759
|
+
private factPage;
|
|
760
|
+
/**
|
|
761
|
+
* The graph view over exactly the facts a search with these filters may read.
|
|
762
|
+
*
|
|
763
|
+
* This is the ONE way the store reaches `@avantf/graph-recall`: the planner's vocabulary
|
|
764
|
+
* (`entitiesIn` / `predicatesIn`), the walks, and — later, for evaluation — a gold-set harness all
|
|
765
|
+
* get the same reader, so none of them can accidentally walk the unfiltered corpus. The filter
|
|
766
|
+
* semantics live in `MemoryGraphReader` (active facts only; kind/source/time in SQL), not here.
|
|
767
|
+
*
|
|
768
|
+
* Public on the store but not on any tool/action: like `graphCoverage()`, it is an internal
|
|
769
|
+
* capability, not a public recall surface.
|
|
770
|
+
*/
|
|
771
|
+
graphReader(filter?: {
|
|
772
|
+
kind?: MemoryKind;
|
|
773
|
+
source?: string;
|
|
774
|
+
time?: TimeFilter;
|
|
775
|
+
}): Promise<GraphReader & RelationVocabulary>;
|
|
776
|
+
/**
|
|
777
|
+
* The relation stage of {@link searchMemory}, or `null` when it did not answer.
|
|
778
|
+
*
|
|
779
|
+
* `null` has exactly two causes, and they are deliberately not distinguished for the caller
|
|
780
|
+
* (RELATIONAL_RECALL §6): the query is not a COMPLETE relation question (`planRelationQuery`
|
|
781
|
+
* refused it), or it is one and the graph has no path for it under the caller's filters. Both mean
|
|
782
|
+
* "fuzzy answers this" — a relation result with zero paths is never returned, because `mode:
|
|
783
|
+
* "relation"` is a promise that structure was found and every edge's fact is openable.
|
|
784
|
+
*
|
|
785
|
+
* ORDER INSIDE THIS STAGE, and why:
|
|
786
|
+
* - the reader is built FIRST (through {@link graphReader}) and carries the caller's
|
|
787
|
+
* `kind`/`source`/`time` filter, so both the vocabulary ("is this a known name") and the walks
|
|
788
|
+
* see one consistent view. An invalid `time` therefore throws here exactly as it would in
|
|
789
|
+
* `search` (the caller's error, not an empty answer);
|
|
790
|
+
* - the plan is checked BEFORE any walk: no plan means no graph read beyond the cheap vocabulary
|
|
791
|
+
* lookup;
|
|
792
|
+
* - evidence is loaded from the paths' own `fact_id`s (`rowsByIds`, one query) and the output
|
|
793
|
+
* budget is applied to it, never to the paths: shortening evidence text is allowed, dropping an
|
|
794
|
+
* evidence row would break the invariant that every path's edges point at a returned fact.
|
|
795
|
+
*/
|
|
796
|
+
private relationPage;
|
|
797
|
+
/**
|
|
798
|
+
* The facts behind a relation answer, in path order and deduplicated.
|
|
799
|
+
*
|
|
800
|
+
* `RelationEvidence` is NOT a `RecallHit`: a relation answer is not a ranked content match, so no
|
|
801
|
+
* `score` is produced here (there is nothing to rank it against) and the handle is `fact_id`, which
|
|
802
|
+
* every path edge also carries. Only ACTIVE rows can reach this point — the graph reader filters
|
|
803
|
+
* on `facts.status` before any edge is walked.
|
|
804
|
+
*/
|
|
805
|
+
private relationEvidence;
|
|
806
|
+
/**
|
|
807
|
+
* Apply the caller's output budget (`max_tokens`) to relation evidence.
|
|
808
|
+
*
|
|
809
|
+
* The shared `applyOutputBudget` cannot be reused directly: its input type carries a `score`, and a
|
|
810
|
+
* relation answer deliberately has none (nothing ranks it). `fitToTokenBudget` itself never reads
|
|
811
|
+
* `score` — it walks the entries in the order it is given and can only SHORTEN text — so the rank
|
|
812
|
+
* below is a placeholder that is dropped before the values leave this function. Order is preserved,
|
|
813
|
+
* `truncated` is set exactly where the text was cut, and no evidence row is ever removed.
|
|
814
|
+
*/
|
|
815
|
+
private budgetRelationEvidence;
|
|
816
|
+
/**
|
|
817
|
+
* Apply the caller's output budget (`max_tokens`) to a FACT page.
|
|
818
|
+
*
|
|
819
|
+
* Same reason as {@link budgetRelationEvidence}: `applyOutputBudget` is typed for hits (`text` +
|
|
820
|
+
* `score`) and a `FactEvidence` carries `content` and no score. `fitToTokenBudget` never reads the
|
|
821
|
+
* score, so the entry rides through as `{ …entry, text: content, score: 0 }` and the budgeted text
|
|
822
|
+
* goes back into `content`; a shortened row keeps its `fact_id` and carries `truncated: true`.
|
|
823
|
+
*/
|
|
824
|
+
private budgetFactEvidence;
|
|
825
|
+
/**
|
|
826
|
+
* `mem_recall.titles` — the ONE title search (JOURNAL_TITLE_SEARCH §4/§11.4, RECALL_INTERFACE §2.3).
|
|
827
|
+
*
|
|
828
|
+
* It answers a different question from `search`: "which journal entries have this title / titles
|
|
829
|
+
* like these keywords", so the caller can read the candidates and decide whether the thing it is
|
|
830
|
+
* about to write belongs to an existing transaction. Consequences, all deliberate:
|
|
831
|
+
*
|
|
832
|
+
* - EXACT first: `normalizeTitle(query)` compared with `=` (case-SENSITIVE: `DSH` and `dsh` are
|
|
833
|
+
* two titles, §11.1) pages like `search`'s exact stage. Any title-equal row ends the pass;
|
|
834
|
+
* - the FTS index it consults is `facts_title_fts`, never `facts_fts` — a title word that does not
|
|
835
|
+
* occur in any content is invisible to {@link searchMemory} by design, and vice versa;
|
|
836
|
+
* - the fuzzy ordering is computed on `facts.title` (exact > prefix > matched keywords > recency),
|
|
837
|
+
* with bm25 only as a tie-break inside the FTS path; no `score` is returned at all, because a
|
|
838
|
+
* title match is not a content-relevance number and the model must not compare the two;
|
|
839
|
+
* - it is READ-ONLY: no `reinforce`, no `touchUsage`, no `retrieval_count` / `last_retrieved_at` /
|
|
840
|
+
* trust change, in EITHER stage. Reading candidates is the common case (the caller may not write
|
|
841
|
+
* at all), so treating it as a recall would let a few searches rewrite the dormancy clock of a
|
|
842
|
+
* whole transaction;
|
|
843
|
+
* - defaults follow the documented ones: `status='active'` (the current revision), `kind='journal'`.
|
|
844
|
+
*/
|
|
845
|
+
titles(input: TitleInput): Promise<TitlesResult>;
|
|
846
|
+
/**
|
|
847
|
+
* Resolve a caller's `title` for a write (JOURNAL_TITLE_SEARCH §2.4/§11.1).
|
|
848
|
+
*
|
|
849
|
+
* Three-way by design: `undefined` stays "not given" so an update can inherit, `null` is the store's
|
|
850
|
+
* "no title" (a blank NOTE title normalizes to it), and a string is normalized. A blank JOURNAL
|
|
851
|
+
* title is refused rather than silently stored as NULL — the caller tried to give one, it just had
|
|
852
|
+
* no content. The title passes the same secret scan as the content because it is rendered into the
|
|
853
|
+
* UI and into the model's context.
|
|
854
|
+
*/
|
|
855
|
+
private resolveWriteTitle;
|
|
512
856
|
/**
|
|
513
857
|
* This store's half of the shared orchestration (`store/hybrid.ts`): the legs, the text load, the
|
|
514
858
|
* fact→hit mapping, and what to do with the hits the caller actually receives.
|
|
@@ -554,11 +898,9 @@ export declare class MemoryStore {
|
|
|
554
898
|
*/
|
|
555
899
|
private timeWindowPath;
|
|
556
900
|
/**
|
|
557
|
-
* The one fact→RecallHit mapper (kind/source_ref/entities shape lives here ONLY;
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
* `domain`/`source` are null — that is what keeps a `domain`-filtered cross query
|
|
561
|
-
* from ever matching a memory hit.
|
|
901
|
+
* The one fact→RecallHit mapper (kind/source_ref/entities shape lives here ONLY; the exact page,
|
|
902
|
+
* the fuzzy search and the single-fact loader all go through it, so a hit cannot describe the same
|
|
903
|
+
* row two ways).
|
|
562
904
|
*/
|
|
563
905
|
private factHits;
|
|
564
906
|
/**
|
|
@@ -566,10 +908,10 @@ export declare class MemoryStore {
|
|
|
566
908
|
* the trust bonus is quota-limited (per fact, calendar 24h), zero-gain recalls
|
|
567
909
|
* cost nothing, and a recall can never push a fact to permanent (D11).
|
|
568
910
|
*
|
|
569
|
-
* Public so
|
|
570
|
-
* returns
|
|
911
|
+
* Public so a caller that post-filters a search result can reinforce ONLY the facts it actually
|
|
912
|
+
* returns (R5/R21).
|
|
571
913
|
*/
|
|
572
|
-
reinforce(ids: number[]): void
|
|
914
|
+
reinforce(ids: number[]): Promise<void>;
|
|
573
915
|
/**
|
|
574
916
|
* Load the persisted retrieval-health counters, and write the current ones back.
|
|
575
917
|
*
|
|
@@ -583,7 +925,7 @@ export declare class MemoryStore {
|
|
|
583
925
|
* Called by `maintenance()` and by the runtime on shutdown — NOT from a `close()`, because the
|
|
584
926
|
* runtime owns the database handle.
|
|
585
927
|
*/
|
|
586
|
-
flushHealth(): void
|
|
928
|
+
flushHealth(): Promise<void>;
|
|
587
929
|
/** Retrieval bookkeeping: `retrieval_count` + the "last used" clock (spec §2.5). */
|
|
588
930
|
private touchUsage;
|
|
589
931
|
/**
|
|
@@ -592,7 +934,7 @@ export declare class MemoryStore {
|
|
|
592
934
|
* `onVector` publishes the vector this leg actually used, so the relaxed retry pass can reuse it
|
|
593
935
|
* instead of re-encoding (performance review §7.7 / P8). The flow is shared
|
|
594
936
|
* (`store/legs.ts#semanticLeg`); the only memory-specific piece is the filter — the vstore has no
|
|
595
|
-
* notion of status/
|
|
937
|
+
* notion of status/kind/source, so candidates go through the DB and archived/purged facts,
|
|
596
938
|
* other categories and other sources never leak into the leg.
|
|
597
939
|
*/
|
|
598
940
|
private semanticPath;
|
|
@@ -602,23 +944,17 @@ export declare class MemoryStore {
|
|
|
602
944
|
* the probe even when surface text shares no FTS trigrams.
|
|
603
945
|
*/
|
|
604
946
|
private hrrPath;
|
|
605
|
-
/**
|
|
606
|
-
* Direction-aware question answering over SPO triples.
|
|
607
|
-
* Parse the query into a TriplePattern, match triples by the filled slots
|
|
608
|
-
* (unfilled = wildcard), and rank the owning facts. Direction is preserved by
|
|
609
|
-
* the triple's (subj, pred, obj) roles.
|
|
610
|
-
*/
|
|
611
|
-
ask(query: string, limit?: number): Promise<RecallResult>;
|
|
612
|
-
/** Direction-aware answer over SPO triples from an explicit pattern. */
|
|
613
|
-
askByPattern(pattern: TriplePattern, limit?: number): Promise<RecallResult>;
|
|
614
947
|
/** One-shot flag for the probe fallback warning (see `hrrPath`). */
|
|
615
948
|
private hrrFallbackWarned;
|
|
616
949
|
/**
|
|
617
|
-
*
|
|
618
|
-
*
|
|
619
|
-
*
|
|
950
|
+
* Diagnostic term-count probe: how many of `text`'s terms this store already holds.
|
|
951
|
+
*
|
|
952
|
+
* NOT the conditional hint's decision any more — that is `AvantfRuntime.wouldFind`, which runs the
|
|
953
|
+
* retrieval pipeline itself (DESIGN §12). Kept as the lexical oracle `test/retrieve.spec.ts` uses
|
|
954
|
+
* to show what the old short-circuit would have answered (a lexical hit can fail `min_score`).
|
|
955
|
+
* `store/lexical.ts` carries how the two-term bar was measured.
|
|
620
956
|
*/
|
|
621
|
-
lexicalProbe(text: string, stopAt?: number): LexicalProbe
|
|
957
|
+
lexicalProbe(text: string, stopAt?: number): Promise<LexicalProbe>;
|
|
622
958
|
private ftsPath;
|
|
623
959
|
/**
|
|
624
960
|
* Entity leg over the precomputed candidate set (see `searchLegs`): the candidates are the facts
|
|
@@ -630,14 +966,18 @@ export declare class MemoryStore {
|
|
|
630
966
|
* the generic-word filter keeps the wider denominator from dragging the corpus in. The fact set is
|
|
631
967
|
* read per candidate and the score is recomputed here even though the DAO ordered by it, so one
|
|
632
968
|
* formula stays in charge (the DAO's `total` tie-break only orders equal scores).
|
|
969
|
+
*
|
|
970
|
+
* The candidate also has to clear the rarity-weighted evidence bar (`entityEvidenceFloor`), which
|
|
971
|
+
* is per candidate because it depends on which anchors the candidate shares; that is why this
|
|
972
|
+
* method returns the drops instead of letting `runner.scored` count them.
|
|
633
973
|
*/
|
|
634
974
|
private jaccardPath;
|
|
635
975
|
/**
|
|
636
976
|
* Insert (or resolve to) a fact row.
|
|
637
977
|
*
|
|
638
|
-
* `
|
|
978
|
+
* `kind` / `ttlDays` are `undefined` when the CALLER specified nothing — the
|
|
639
979
|
* distinction is load-bearing. A duplicate-content hit must not clobber the existing
|
|
640
|
-
* row's values with the insert defaults: re-adding a fact without a
|
|
980
|
+
* row's values with the insert defaults: re-adding a fact without a kind used to
|
|
641
981
|
* reset it to `general`, and reviving an archived one silently cleared an explicit TTL.
|
|
642
982
|
* The rule for all three branches is therefore: an explicitly provided value lands, an
|
|
643
983
|
* omitted one keeps what the row already has, and a brand-new row falls back to the
|
|
@@ -673,6 +1013,70 @@ export declare class MemoryStore {
|
|
|
673
1013
|
*/
|
|
674
1014
|
private normalizeTriples;
|
|
675
1015
|
private insertTriples;
|
|
1016
|
+
/**
|
|
1017
|
+
* Phase 2 (RELATIONAL_RECALL §4.3/R2): the SAME extracted triples, resolved onto IDs and written to
|
|
1018
|
+
* `edges` — the write path's half of the graph. Called from every branch that writes triples (fresh
|
|
1019
|
+
* insert, duplicate/merge, revive, and the entity sweep), always inside the surrounding transaction,
|
|
1020
|
+
* so `triples` and `edges` describe one extraction and never two vintages.
|
|
1021
|
+
*
|
|
1022
|
+
* `kind` is the layer the ROW carries, and it is the STRUCTURED_RELATIONS §四 boundary: a `journal`
|
|
1023
|
+
* entry keeps its `triples` (the contradiction detector reads them) but produces NO edges. The
|
|
1024
|
+
* check uses the row's actual kind rather than the request's, because a duplicate-content write
|
|
1025
|
+
* lands on a DIFFERENT row — and the graph's rule is about that row, not about what was asked.
|
|
1026
|
+
*
|
|
1027
|
+
* Best-effort by contract: §4.3's "关系抽取失败不阻塞写入". A throw here is logged and swallowed —
|
|
1028
|
+
* the fact, its triples and its content index are already written, and the graph can be re-derived
|
|
1029
|
+
* from those triples at any time (`reextractEdges`). That is also why this is a `try` around a
|
|
1030
|
+
* single call rather than a per-triple loop: the policy already refuses bad triples individually.
|
|
1031
|
+
*/
|
|
1032
|
+
private insertEdges;
|
|
1033
|
+
/**
|
|
1034
|
+
* Re-derive one fact's edges under the current policy — the graph half of "the row's layer/status
|
|
1035
|
+
* changed" (STRUCTURED_RELATIONS §五/§七; `db/graph.ts#rebuildEdgesForFact`).
|
|
1036
|
+
*
|
|
1037
|
+
* Called from the states where the incremental write cannot be right: an ARCHIVED row re-entering
|
|
1038
|
+
* the active set (`restore()` and the revive branch of {@link persistFact}) and a note whose
|
|
1039
|
+
* `keepKind` flips to journal on the duplicate path (a journal keeps no edges). Both call sites
|
|
1040
|
+
* are best-effort by the same contract `insertEdges` has (§4.3: a graph write never fails a fact
|
|
1041
|
+
* write), but the rebuild itself is one savepoint: a throw leaves the row's previous edges in
|
|
1042
|
+
* place instead of half-deleted ones.
|
|
1043
|
+
*/
|
|
1044
|
+
private rebuildGraph;
|
|
1045
|
+
/**
|
|
1046
|
+
* STRUCTURED_RELATIONS §2: write the relations the CALLER stated, before the extracted ones.
|
|
1047
|
+
*
|
|
1048
|
+
* Order matters for exactly one reason. Both writers end in `INSERT OR IGNORE` on
|
|
1049
|
+
* `edges(fact_id, subject_id, predicate_id, object_id)`, so when the same relation is both stated
|
|
1050
|
+
* and extracted, whichever runs FIRST owns the row. The caller's statement is the stronger claim
|
|
1051
|
+
* (confidence 1, `source: 'explicit'`), so it goes first and the extractor's guess becomes the
|
|
1052
|
+
* duplicate — the edge keeps the explicit provenance the caller asked for.
|
|
1053
|
+
*
|
|
1054
|
+
* NOT best-effort, unlike {@link insertEdges}: an explicit relation is a request, not a guess. A
|
|
1055
|
+
* throw rolls the whole write back (`db.transaction`), which is also why the caller validated the
|
|
1056
|
+
* list before the transaction — so this path only fails when the DATABASE is broken, and then the
|
|
1057
|
+
* caller must hear about it rather than get a fact whose relations silently do not exist.
|
|
1058
|
+
*
|
|
1059
|
+
* `journal` is refused upstream (`store/explicit_relations.ts`); the guard here is for the merge
|
|
1060
|
+
* branch, where the surviving row's own kind decides and a note request can land on a journal row.
|
|
1061
|
+
*
|
|
1062
|
+
* `explicit === undefined` means "this write said nothing about relations" and leaves the row's
|
|
1063
|
+
* set alone; an EMPTY array means "this write declares none" and drops what is there. That second
|
|
1064
|
+
* case is the only way an unchanged-content update can remove the last relation (the row is not
|
|
1065
|
+
* superseded, so nothing else would delete that edge).
|
|
1066
|
+
*/
|
|
1067
|
+
private insertExplicitEdges;
|
|
1068
|
+
/**
|
|
1069
|
+
* Phase 2 (§4.1): record the EXPLICIT alias statements in this write's text.
|
|
1070
|
+
*
|
|
1071
|
+
* Only cue-bearing forms (`A(又名 B)`, `A 简称 B`, …) and only when the left-hand name is one the
|
|
1072
|
+
* extractor recognised as an entity — see `relations/normalize.ts`. Two similar names, or two
|
|
1073
|
+
* frequent ones, never merge; that is the one rule §4.1 makes non-negotiable.
|
|
1074
|
+
*
|
|
1075
|
+
* Best-effort like the edge write: an alias is a refinement of the graph, never a precondition of
|
|
1076
|
+
* the fact. A learned alias is logged at INFO because an automatic merge is exactly the kind of
|
|
1077
|
+
* decision an operator must be able to see and undo (`DELETE FROM entity_aliases WHERE alias = ?`).
|
|
1078
|
+
*/
|
|
1079
|
+
private recordAliases;
|
|
676
1080
|
private getRow;
|
|
677
1081
|
private entityNames;
|
|
678
1082
|
private entityBags;
|