@avantf/dsh-mem 0.7.0 → 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 +4 -0
- package/lib/client.js +548 -1195
- package/lib/dsh-build.json +1 -0
- 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 +50 -10
- package/lib/engine/mem/index.d.ts +6 -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/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 +61 -88
- 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 +10 -2
- 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 +471 -83
- 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 +145 -132
- package/lib/engine/mem-contract/types.d.ts +364 -288
- package/lib/index.js +12048 -11337
- 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/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 +1 -1
- 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 +14 -7
- package/lib/types/remote.d.ts.map +1 -1
- 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 +15 -14
- 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,16 +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 {
|
|
4
|
+
import type { RestoreOptions, RestoreReport } from './facts_restore.js';
|
|
5
5
|
import type { FactsExportDocument } from '../facts_io.js';
|
|
6
6
|
import { DEFAULT_VECTOR_MIGRATION_BATCH, type VectorRepairTarget } from './vector_repair.js';
|
|
7
|
-
import type { Db } from '../db/conn.js';
|
|
8
7
|
import { type LexicalProbe } from './lexical.js';
|
|
9
8
|
import { type FloorLeg } from './floors.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
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';
|
|
14
16
|
/**
|
|
15
17
|
* Result shapes live in `@avantf/mem-contract` (single source for tool/UI payloads); these
|
|
16
18
|
* aliases keep the long-standing `@avantf/mem` export names working for consumers.
|
|
@@ -78,7 +80,7 @@ export interface ContradictionResolution {
|
|
|
78
80
|
}
|
|
79
81
|
export interface SearchInput {
|
|
80
82
|
query: string;
|
|
81
|
-
|
|
83
|
+
kind?: MemoryKind;
|
|
82
84
|
/**
|
|
83
85
|
* P-08: restrict every leg to facts whose `fact_sources.ref` equals this value. Applied INSIDE
|
|
84
86
|
* each leg's SQL (an `EXISTS` predicate, before its `LIMIT cap`), never as a post-fusion filter:
|
|
@@ -134,6 +136,24 @@ export interface SearchInput {
|
|
|
134
136
|
recordStats?: boolean;
|
|
135
137
|
/** P-01: attach per-leg evidence (`scores` / `final`) to the returned hits. Default false. */
|
|
136
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;
|
|
137
157
|
}
|
|
138
158
|
/**
|
|
139
159
|
* P-08 / P-13 write-side extras on `add` / `update`.
|
|
@@ -149,25 +169,97 @@ export interface FactWriteOptions {
|
|
|
149
169
|
eventDate?: string;
|
|
150
170
|
/** P-13: known end → `valid_to`, WITHOUT archiving the row. */
|
|
151
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;
|
|
152
247
|
}
|
|
153
248
|
export declare class MemoryStore {
|
|
154
|
-
private readonly db;
|
|
155
249
|
private readonly config;
|
|
156
250
|
private readonly semantic;
|
|
157
251
|
private readonly vstore;
|
|
158
252
|
private readonly contradictions;
|
|
159
253
|
private readonly facts;
|
|
254
|
+
private readonly relations;
|
|
160
255
|
private readonly entities;
|
|
161
256
|
private readonly triples;
|
|
162
257
|
private readonly sources;
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
* and building MATCH expressions for the wrong one is silent zero recall (see `db/tokenizer.ts`).
|
|
169
|
-
*/
|
|
170
|
-
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;
|
|
171
263
|
/**
|
|
172
264
|
* Identity object for the shared entity sweep's in-flight guard (`store/entity_sweep.ts`).
|
|
173
265
|
*
|
|
@@ -181,9 +273,18 @@ export declare class MemoryStore {
|
|
|
181
273
|
* derived state: nothing is lost when it resets, and the queue itself is in the database.
|
|
182
274
|
*/
|
|
183
275
|
private conflictDrainCursor;
|
|
184
|
-
constructor(
|
|
276
|
+
constructor(backend: StorageBackend, config: Config, semantic: SemanticBackend, vstore: VectorStore,
|
|
185
277
|
/** The store's database path, used ONLY to name the vector index snapshot beside it. */
|
|
186
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>;
|
|
187
288
|
/**
|
|
188
289
|
* One automatic trust/lifecycle pass (spec §5): advance the active-day clock
|
|
189
290
|
* (when trust is enabled), run the tick, and evict archived/purged vectors from
|
|
@@ -191,7 +292,7 @@ export declare class MemoryStore {
|
|
|
191
292
|
*/
|
|
192
293
|
trustTick(opts?: {
|
|
193
294
|
budget?: number | null;
|
|
194
|
-
}): TickResult
|
|
295
|
+
}): Promise<TickResult>;
|
|
195
296
|
/**
|
|
196
297
|
* Identity of the vector space this store currently writes into — the REPRESENTATION fingerprint
|
|
197
298
|
* (`vectorSpaceId` + the backend's declared pooling/normalize/window/revision), not just the
|
|
@@ -223,14 +324,14 @@ export declare class MemoryStore {
|
|
|
223
324
|
* The counts are the same two `VectorsDiagnostic` already separates (`stale` / `space_stale`); this
|
|
224
325
|
* is the read model those surfaces use instead of the full diagnose pass.
|
|
225
326
|
*/
|
|
226
|
-
vectorSpaceHealth(): VectorSpaceHealth
|
|
327
|
+
vectorSpaceHealth(): Promise<VectorSpaceHealth>;
|
|
227
328
|
/**
|
|
228
329
|
* {@link vectorSpaceHealth} plus the internal `legacy` split (how many `space_stale` rows predate
|
|
229
330
|
* the representation fingerprint). Kept off the contract type so the public detection surface —
|
|
230
331
|
* and every existing assertion on it — is unchanged; only the warning's wording uses it.
|
|
231
332
|
*/
|
|
232
333
|
private vectorSpaceCounts;
|
|
233
|
-
add(content: string,
|
|
334
|
+
add(content: string, kind?: MemoryKind, ttlDays?: number, opts?: FactWriteOptions): Promise<RememberResult>;
|
|
234
335
|
/**
|
|
235
336
|
* Write-path contradiction detection (DESIGN §11): check the fact that was just
|
|
236
337
|
* written against the corpus, then report its FULL open-conflict set.
|
|
@@ -257,18 +358,18 @@ export declare class MemoryStore {
|
|
|
257
358
|
private warnIndexFailure;
|
|
258
359
|
/** Best-effort semantic indexing: encode the fact and add it to the vstore. */
|
|
259
360
|
private maybeIndexSemantic;
|
|
260
|
-
update(fact_id: number, content: string,
|
|
261
|
-
remove(fact_id: number, reason?: string): boolean
|
|
262
|
-
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>;
|
|
263
364
|
/**
|
|
264
365
|
* Bring an archived fact back (spec §2.6): a `forgot` fact is lifted to
|
|
265
366
|
* `recall_floor` (otherwise the very next tick would re-archive it, R4), the
|
|
266
367
|
* idle clock is refreshed (otherwise an `idle`-archived fact dies again, R18),
|
|
267
368
|
* and `settle_clock` moves to now so the downtime is not charged.
|
|
268
369
|
*/
|
|
269
|
-
restore(fact_id: number): boolean
|
|
270
|
-
helpful(fact_id: number): number | null
|
|
271
|
-
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>;
|
|
272
373
|
/**
|
|
273
374
|
* Explicit feedback (spec §2.4). Pinned/archived rows only move `helpful_count`
|
|
274
375
|
* (R7); otherwise the decay is settled first, then `±feedback_delta`, pin at the
|
|
@@ -276,40 +377,46 @@ export declare class MemoryStore {
|
|
|
276
377
|
*/
|
|
277
378
|
private applyFeedback;
|
|
278
379
|
/** Permanent-memory administration (spec §6). */
|
|
279
|
-
pin(fact_id: number): boolean
|
|
280
|
-
unpin(fact_id: number): boolean
|
|
380
|
+
pin(fact_id: number): Promise<boolean>;
|
|
381
|
+
unpin(fact_id: number): Promise<boolean>;
|
|
281
382
|
/**
|
|
282
383
|
* Trust/forgetting diagnostics (spec §6, R17/R24): the quota is PER FACT, so the
|
|
283
384
|
* report exposes "facts that consumed quota today" and "total effective gains"
|
|
284
385
|
* rather than a meaningless global remaining.
|
|
285
386
|
*/
|
|
286
|
-
trustDiagnose(): TrustDiagnostic
|
|
287
|
-
get(fact_id: number): FactDetail | null
|
|
387
|
+
trustDiagnose(): Promise<TrustDiagnostic>;
|
|
388
|
+
get(fact_id: number): Promise<FactDetail | null>;
|
|
288
389
|
private triplesOf;
|
|
289
390
|
/**
|
|
290
391
|
* One page of facts, newest first.
|
|
291
|
-
* @param
|
|
392
|
+
* @param kind - optional kind filter.
|
|
292
393
|
* @param status - `active` (default) or `archived`.
|
|
293
394
|
* @param limit - page size.
|
|
294
395
|
* @param offset - page offset (0-based).
|
|
295
396
|
* @returns the page, the page size (`count`), the matching total, and whether
|
|
296
397
|
* more rows remain after this page.
|
|
297
398
|
*/
|
|
298
|
-
list(
|
|
399
|
+
list(kind?: MemoryKind, status?: string, limit?: number, offset?: number,
|
|
299
400
|
/** P-08: only facts whose `fact_sources.ref` equals this value (reverse provenance lookup). */
|
|
300
|
-
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>;
|
|
301
408
|
/**
|
|
302
409
|
* P-08 coverage over the ACTIVE corpus, for `admin stats`: the share of facts that can answer
|
|
303
410
|
* "why do I remember this". Reported from day one so the field cannot quietly become a second
|
|
304
411
|
* `mirror_source` (filled but meaningless).
|
|
305
412
|
*/
|
|
306
|
-
sourceCoverage(): SourceCoverage
|
|
413
|
+
sourceCoverage(): Promise<SourceCoverage>;
|
|
307
414
|
/** P-13 coverage over the ACTIVE corpus: the share of facts carrying an event time. */
|
|
308
|
-
validityCoverage(): ValidityCoverage
|
|
309
|
-
countByStatus(): {
|
|
415
|
+
validityCoverage(): Promise<ValidityCoverage>;
|
|
416
|
+
countByStatus(): Promise<{
|
|
310
417
|
active: number;
|
|
311
418
|
archived: number;
|
|
312
|
-
}
|
|
419
|
+
}>;
|
|
313
420
|
/** Run one incremental contradiction-detection pass; returns newly logged pairs. */
|
|
314
421
|
/**
|
|
315
422
|
* Drain the DURABLE pending queue, bounded by `budget`.
|
|
@@ -333,9 +440,9 @@ export declare class MemoryStore {
|
|
|
333
440
|
*
|
|
334
441
|
* A throw keeps the batch queued too — `checkMany` never returns, so nothing is stamped.
|
|
335
442
|
*/
|
|
336
|
-
drainConflicts(budget?: number): ConflictDrainReport
|
|
443
|
+
drainConflicts(budget?: number): Promise<ConflictDrainReport>;
|
|
337
444
|
/** {@link drainConflicts}, reduced to the pairs it logged (the `contradict_check` payload). */
|
|
338
|
-
checkContradictions(budget?: number): Sig[]
|
|
445
|
+
checkContradictions(budget?: number): Promise<Sig[]>;
|
|
339
446
|
/**
|
|
340
447
|
* One bounded conflict pass for {@link maintenance}, reduced to COUNTS.
|
|
341
448
|
*
|
|
@@ -351,10 +458,10 @@ export declare class MemoryStore {
|
|
|
351
458
|
/** List contradiction candidates (`resolved` 0 = open, 1 = closed with a recorded verdict). */
|
|
352
459
|
listContradictions(opts?: {
|
|
353
460
|
resolved?: number;
|
|
354
|
-
|
|
461
|
+
kind?: MemoryKind;
|
|
355
462
|
threshold?: number;
|
|
356
463
|
limit?: number;
|
|
357
|
-
}): ContradictionRecord[]
|
|
464
|
+
}): Promise<ContradictionRecord[]>;
|
|
358
465
|
/**
|
|
359
466
|
* Adjudicate one logged conflict — the outlet the detector never had.
|
|
360
467
|
*
|
|
@@ -371,7 +478,7 @@ export declare class MemoryStore {
|
|
|
371
478
|
* The verdict is recorded on this row either way: `archive()` short-circuits for a fact that
|
|
372
479
|
* is already archived, and the row would otherwise stay open on a technicality.
|
|
373
480
|
*/
|
|
374
|
-
resolveContradiction(id: number, resolution: 'true_positive' | 'false_positive', loserFactId?: number): ContradictionResolution
|
|
481
|
+
resolveContradiction(id: number, resolution: 'true_positive' | 'false_positive', loserFactId?: number): Promise<ContradictionResolution>;
|
|
375
482
|
/**
|
|
376
483
|
* Semantic-index health for the ACTIVE corpus (the only thing the index serves):
|
|
377
484
|
* - `missing` active facts with no persisted vector at all;
|
|
@@ -385,7 +492,7 @@ export declare class MemoryStore {
|
|
|
385
492
|
* upgraded past the space id) — usable bytes, incomparable scores.
|
|
386
493
|
* `total`/`with_semantic` still describe the whole table (including archived).
|
|
387
494
|
*/
|
|
388
|
-
vectorsDiagnose(): VectorsDiagnostic
|
|
495
|
+
vectorsDiagnose(): Promise<VectorsDiagnostic>;
|
|
389
496
|
/**
|
|
390
497
|
* ONE read+decode pass over the persisted vectors, shared by diagnose and fix
|
|
391
498
|
* (the two methods used to re-scan and re-decode the same blobs 4-5× per call).
|
|
@@ -397,7 +504,7 @@ export declare class MemoryStore {
|
|
|
397
504
|
* `semantic.auto_migrate`, the dry-run split and the drop-before-encode order — lives there once,
|
|
398
505
|
* for both stores.
|
|
399
506
|
*/
|
|
400
|
-
vectorRepairTarget(): VectorRepairTarget
|
|
507
|
+
vectorRepairTarget(): Promise<VectorRepairTarget>;
|
|
401
508
|
/**
|
|
402
509
|
* {@link classifyVectors} reduced to what the repair flow steps on: the counts, the ids to drop
|
|
403
510
|
* (bounded by `limit`) and the decoded vectors that can go back into a lost live index.
|
|
@@ -460,6 +567,7 @@ export declare class MemoryStore {
|
|
|
460
567
|
maintenance(): Promise<MaintenanceResult & {
|
|
461
568
|
entities: EntitySweepReport;
|
|
462
569
|
conflicts: ConflictSweepCounts;
|
|
570
|
+
edges: EdgeReextractReport;
|
|
463
571
|
}>;
|
|
464
572
|
/**
|
|
465
573
|
* Re-extract the entity/triple rows of facts written by OLDER extraction rules (bounded).
|
|
@@ -489,6 +597,42 @@ export declare class MemoryStore {
|
|
|
489
597
|
* selection (`facts.staleEntityRows`, ACTIVE facts only) and the per-fact write below.
|
|
490
598
|
*/
|
|
491
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>;
|
|
492
636
|
/**
|
|
493
637
|
* Rebuild this library from an export document — the engine half of `avantf-mem restore <file>`.
|
|
494
638
|
*
|
|
@@ -511,20 +655,204 @@ export declare class MemoryStore {
|
|
|
511
655
|
*/
|
|
512
656
|
private retireConflicts;
|
|
513
657
|
/**
|
|
514
|
-
*
|
|
515
|
-
*
|
|
516
|
-
*
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
}[];
|
|
522
|
-
/** Two-hop traversal: subj -pred-> mid, mid -second_pred-> obj. Returns the second-hop facts. */
|
|
523
|
-
chain(subj: string, pred?: string, secondPred?: string, limit?: number): Promise<RecallResult>;
|
|
524
|
-
/** Facts whose entity set contains ALL given entities (AND-join). */
|
|
525
|
-
reason(entities: string[], limit?: number): Promise<RecallResult>;
|
|
526
|
-
/** 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
|
+
*/
|
|
527
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;
|
|
528
856
|
/**
|
|
529
857
|
* This store's half of the shared orchestration (`store/hybrid.ts`): the legs, the text load, the
|
|
530
858
|
* fact→hit mapping, and what to do with the hits the caller actually receives.
|
|
@@ -570,11 +898,9 @@ export declare class MemoryStore {
|
|
|
570
898
|
*/
|
|
571
899
|
private timeWindowPath;
|
|
572
900
|
/**
|
|
573
|
-
* The one fact→RecallHit mapper (kind/source_ref/entities shape lives here ONLY;
|
|
574
|
-
*
|
|
575
|
-
*
|
|
576
|
-
* `domain`/`source` are null — that is what keeps a `domain`-filtered cross query
|
|
577
|
-
* 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).
|
|
578
904
|
*/
|
|
579
905
|
private factHits;
|
|
580
906
|
/**
|
|
@@ -582,10 +908,10 @@ export declare class MemoryStore {
|
|
|
582
908
|
* the trust bonus is quota-limited (per fact, calendar 24h), zero-gain recalls
|
|
583
909
|
* cost nothing, and a recall can never push a fact to permanent (D11).
|
|
584
910
|
*
|
|
585
|
-
* Public so
|
|
586
|
-
* returns
|
|
911
|
+
* Public so a caller that post-filters a search result can reinforce ONLY the facts it actually
|
|
912
|
+
* returns (R5/R21).
|
|
587
913
|
*/
|
|
588
|
-
reinforce(ids: number[]): void
|
|
914
|
+
reinforce(ids: number[]): Promise<void>;
|
|
589
915
|
/**
|
|
590
916
|
* Load the persisted retrieval-health counters, and write the current ones back.
|
|
591
917
|
*
|
|
@@ -599,7 +925,7 @@ export declare class MemoryStore {
|
|
|
599
925
|
* Called by `maintenance()` and by the runtime on shutdown — NOT from a `close()`, because the
|
|
600
926
|
* runtime owns the database handle.
|
|
601
927
|
*/
|
|
602
|
-
flushHealth(): void
|
|
928
|
+
flushHealth(): Promise<void>;
|
|
603
929
|
/** Retrieval bookkeeping: `retrieval_count` + the "last used" clock (spec §2.5). */
|
|
604
930
|
private touchUsage;
|
|
605
931
|
/**
|
|
@@ -608,7 +934,7 @@ export declare class MemoryStore {
|
|
|
608
934
|
* `onVector` publishes the vector this leg actually used, so the relaxed retry pass can reuse it
|
|
609
935
|
* instead of re-encoding (performance review §7.7 / P8). The flow is shared
|
|
610
936
|
* (`store/legs.ts#semanticLeg`); the only memory-specific piece is the filter — the vstore has no
|
|
611
|
-
* notion of status/
|
|
937
|
+
* notion of status/kind/source, so candidates go through the DB and archived/purged facts,
|
|
612
938
|
* other categories and other sources never leak into the leg.
|
|
613
939
|
*/
|
|
614
940
|
private semanticPath;
|
|
@@ -618,23 +944,17 @@ export declare class MemoryStore {
|
|
|
618
944
|
* the probe even when surface text shares no FTS trigrams.
|
|
619
945
|
*/
|
|
620
946
|
private hrrPath;
|
|
621
|
-
/**
|
|
622
|
-
* Direction-aware question answering over SPO triples.
|
|
623
|
-
* Parse the query into a TriplePattern, match triples by the filled slots
|
|
624
|
-
* (unfilled = wildcard), and rank the owning facts. Direction is preserved by
|
|
625
|
-
* the triple's (subj, pred, obj) roles.
|
|
626
|
-
*/
|
|
627
|
-
ask(query: string, limit?: number): Promise<RecallResult>;
|
|
628
|
-
/** Direction-aware answer over SPO triples from an explicit pattern. */
|
|
629
|
-
askByPattern(pattern: TriplePattern, limit?: number): Promise<RecallResult>;
|
|
630
947
|
/** One-shot flag for the probe fallback warning (see `hrrPath`). */
|
|
631
948
|
private hrrFallbackWarned;
|
|
632
949
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
635
|
-
*
|
|
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.
|
|
636
956
|
*/
|
|
637
|
-
lexicalProbe(text: string, stopAt?: number): LexicalProbe
|
|
957
|
+
lexicalProbe(text: string, stopAt?: number): Promise<LexicalProbe>;
|
|
638
958
|
private ftsPath;
|
|
639
959
|
/**
|
|
640
960
|
* Entity leg over the precomputed candidate set (see `searchLegs`): the candidates are the facts
|
|
@@ -646,14 +966,18 @@ export declare class MemoryStore {
|
|
|
646
966
|
* the generic-word filter keeps the wider denominator from dragging the corpus in. The fact set is
|
|
647
967
|
* read per candidate and the score is recomputed here even though the DAO ordered by it, so one
|
|
648
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.
|
|
649
973
|
*/
|
|
650
974
|
private jaccardPath;
|
|
651
975
|
/**
|
|
652
976
|
* Insert (or resolve to) a fact row.
|
|
653
977
|
*
|
|
654
|
-
* `
|
|
978
|
+
* `kind` / `ttlDays` are `undefined` when the CALLER specified nothing — the
|
|
655
979
|
* distinction is load-bearing. A duplicate-content hit must not clobber the existing
|
|
656
|
-
* 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
|
|
657
981
|
* reset it to `general`, and reviving an archived one silently cleared an explicit TTL.
|
|
658
982
|
* The rule for all three branches is therefore: an explicitly provided value lands, an
|
|
659
983
|
* omitted one keeps what the row already has, and a brand-new row falls back to the
|
|
@@ -689,6 +1013,70 @@ export declare class MemoryStore {
|
|
|
689
1013
|
*/
|
|
690
1014
|
private normalizeTriples;
|
|
691
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;
|
|
692
1080
|
private getRow;
|
|
693
1081
|
private entityNames;
|
|
694
1082
|
private entityBags;
|