@avantf/dsh-mem 0.8.1 → 0.10.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 +42 -43
- package/lib/client.js +1181 -327
- package/lib/engine/graph-recall/index.d.ts +2 -2
- package/lib/engine/graph-recall/traverse.d.ts +2 -2
- package/lib/engine/graph-recall/types.d.ts +1 -1
- package/lib/engine/mem/config/config_files.d.ts +1 -1
- package/lib/engine/mem/db/dao/facts.d.ts +80 -82
- package/lib/engine/mem/dbdrivers/sqlite/facts.d.ts +6 -13
- package/lib/engine/mem/defaults.d.ts +4 -1
- package/lib/engine/mem/entities/extract.d.ts +34 -14
- package/lib/engine/mem/facts_io.d.ts +4 -0
- package/lib/engine/mem/index.d.ts +1 -0
- package/lib/engine/mem/interfaces/repositories.d.ts +5 -19
- package/lib/engine/mem/lifecycle/maintenance.d.ts +8 -4
- package/lib/engine/mem/lifecycle/tick.d.ts +11 -10
- package/lib/engine/mem/lifecycle/trust.d.ts +24 -37
- package/lib/engine/mem/modelBootstrap.d.ts +29 -0
- package/lib/engine/mem/runtime.d.ts +4 -8
- package/lib/engine/mem/store/facts_restore.d.ts +8 -6
- package/lib/engine/mem/store/graph_reader.d.ts +2 -2
- package/lib/engine/mem/store/memory.d.ts +98 -37
- package/lib/engine/mem/store/recall_policy.d.ts +10 -0
- package/lib/engine/mem/store/time_filter.d.ts +1 -1
- package/lib/engine/mem/trustHeartbeat.d.ts +1 -1
- package/lib/engine/mem-contract/config.d.ts +144 -9
- package/lib/engine/mem-contract/index.d.ts +2 -0
- package/lib/engine/mem-contract/time.d.ts +75 -0
- package/lib/engine/mem-contract/tools.d.ts +9 -15
- package/lib/engine/mem-contract/trust_curve.d.ts +119 -0
- package/lib/engine/mem-contract/types.d.ts +73 -12
- package/lib/engine/mem-retrieval/adapters/auto_vstore.d.ts +10 -0
- package/lib/engine/mem-retrieval/adapters/hnswlib.d.ts +28 -0
- package/lib/engine/mem-retrieval/adapters/local_bge.d.ts +48 -6
- package/lib/engine/mem-retrieval/interfaces.d.ts +22 -2
- package/lib/engine/mem-retrieval/text_budget.d.ts +31 -0
- package/lib/index.js +2349 -665
- package/lib/interface-version.json +2 -2
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/paging.d.ts +58 -8
- package/lib/types/client/paging.d.ts.map +1 -1
- package/lib/types/client/time_filter.d.ts +54 -0
- package/lib/types/client/time_filter.d.ts.map +1 -0
- package/lib/types/command.d.ts.map +1 -1
- package/lib/types/deps_audit.d.ts +88 -0
- package/lib/types/deps_audit.d.ts.map +1 -0
- package/lib/types/envinit-bootstrap.d.ts +1 -1
- package/lib/types/envinit.d.ts +224 -4
- package/lib/types/envinit.d.ts.map +1 -1
- package/lib/types/hints.d.ts +58 -0
- package/lib/types/hints.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/remote.d.ts.map +1 -1
- package/lib/types/render.d.ts.map +1 -1
- package/lib/types/runtime_deps_resource.d.ts +29 -0
- package/lib/types/runtime_deps_resource.d.ts.map +1 -0
- package/package.json +26 -24
- package/res/runtime-deps.yaml +99 -0
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Surface stability (D10): the original report keys are all preserved
|
|
6
6
|
* (`decayed / archived_ttl / archived_age / purged / purged_ids / archived_ids`)
|
|
7
|
-
* and the new detail is purely additive. `
|
|
8
|
-
*
|
|
7
|
+
* and the new detail is purely additive. `archived_ttl` / `archived_idle` are kept as
|
|
8
|
+
* CONSTANT 0 and marked `@deprecated`: TTL and idle archival are gone (v2), but a report key
|
|
9
|
+
* is a surface scripts read — deleting it would break them for nothing. `archived_age` now
|
|
10
|
+
* means "the automatic archival that still exists" = `forgot`.
|
|
9
11
|
*
|
|
10
12
|
* The `maintenance` action forces a FULL pass (no settle budget); the automatic
|
|
11
13
|
* startup/heartbeat passes use `tick_max_facts`.
|
|
@@ -15,8 +17,9 @@ import type { Db } from '../db/conn.js';
|
|
|
15
17
|
export interface MaintenanceResult {
|
|
16
18
|
/** Rows whose trust was materialized by the settle sweep (see tick.ts). */
|
|
17
19
|
decayed: number;
|
|
20
|
+
/** @deprecated TTL archival is gone (v2); always 0, kept so existing report readers keep working. */
|
|
18
21
|
archived_ttl: number;
|
|
19
|
-
/**
|
|
22
|
+
/** The automatic archival this pass performed = `forgot` (name kept, D10). */
|
|
20
23
|
archived_age: number;
|
|
21
24
|
purged: number;
|
|
22
25
|
/** Fact ids physically deleted by the purge — callers evict their vectors from the live index. */
|
|
@@ -26,10 +29,11 @@ export interface MaintenanceResult {
|
|
|
26
29
|
settled: number;
|
|
27
30
|
clock: number;
|
|
28
31
|
archived_forgot: number;
|
|
32
|
+
/** @deprecated Idle archival is gone (v2); always 0, kept for the same reason as `archived_ttl`. */
|
|
29
33
|
archived_idle: number;
|
|
30
34
|
/** Rows still pending settle because the budget ran out (always 0 for a forced pass). */
|
|
31
35
|
skipped: number;
|
|
32
|
-
/** Rows still due for
|
|
36
|
+
/** Rows still due for forgot because the budget ran out (0 for a forced pass). */
|
|
33
37
|
archived_deferred: number;
|
|
34
38
|
/** Rows still due for purge when the budget ran out (0 for a forced pass). */
|
|
35
39
|
purged_deferred: number;
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One trust/lifecycle tick (spec docs/TRUST_MODEL.md §4) —
|
|
2
|
+
* One trust/lifecycle tick (spec docs/TRUST_MODEL.md §4) — three ordered statements:
|
|
3
3
|
*
|
|
4
4
|
* ① settle — materialize decay for every row whose ACTIVE DAY changed
|
|
5
|
-
* ②
|
|
6
|
-
* ③
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* ② forgot — trust ≤ forget_threshold after settling (pinned exempt)
|
|
6
|
+
* ③ purge — physical delete of archived rows past the window
|
|
7
|
+
*
|
|
8
|
+
* The calendar clocks are GONE (v2): there is no TTL step and no idle fallback, so a fact's
|
|
9
|
+
* lifetime is decided by the active-day curve alone and "expired" is expressed by an explicit
|
|
10
|
+
* `update` / `archive` (docs/TRUST_MODEL.md §3.4). `pinned` is therefore never archived by an
|
|
11
|
+
* automatic pass.
|
|
9
12
|
*
|
|
10
13
|
* EVERY step is budgeted by the same `tick_max_facts`, not just ①.
|
|
11
14
|
*
|
|
12
15
|
* It used to say "only ① is budgeted; ②③④⑤ only ever touch critical rows", and that reading is
|
|
13
|
-
* wrong in the direction that matters:
|
|
16
|
+
* wrong in the direction that matters: every step runs inside the tick's ONE `IMMEDIATE` transaction,
|
|
14
17
|
* which holds the write lock for its whole duration, so an unbounded archive or purge pass over a
|
|
15
18
|
* large corpus blocks every other process (and the startup path, which is where the tick runs) for
|
|
16
19
|
* as long as it takes. Bounding each step is what keeps that lock short; `archived_deferred` and
|
|
@@ -24,17 +27,15 @@ export interface TickResult {
|
|
|
24
27
|
clock: number;
|
|
25
28
|
/** Rows materialized by ① (equals "trust actually changed" — see the note in runTrustTick). */
|
|
26
29
|
settled: number;
|
|
27
|
-
archived_ttl: number;
|
|
28
30
|
archived_forgot: number;
|
|
29
|
-
archived_idle: number;
|
|
30
31
|
purged: number;
|
|
31
32
|
purged_ids: number[];
|
|
32
33
|
archived_ids: number[];
|
|
33
34
|
/** Rows still awaiting ① when the budget ran out (0 for a full pass). */
|
|
34
35
|
skipped: number;
|
|
35
|
-
/** Rows still due for
|
|
36
|
+
/** Rows still due for ② when the budget ran out (0 when the pass finished them). */
|
|
36
37
|
archived_deferred: number;
|
|
37
|
-
/** Rows still due for
|
|
38
|
+
/** Rows still due for ③ when the budget ran out (0 when the pass finished them). */
|
|
38
39
|
purged_deferred: number;
|
|
39
40
|
}
|
|
40
41
|
export declare function runTrustTick(db: Db, cfg: Config, opts: {
|
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
* (`clock` / `nowMs` are always injected, so every rule is unit-testable).
|
|
4
4
|
*
|
|
5
5
|
* Spec: docs/TRUST_MODEL.md §2. Key rules encoded here:
|
|
6
|
-
* - `eff = pinned ? 1 : clamp(
|
|
6
|
+
* - `eff = pinned ? 1 : clamp(T(clock − settle_clock), 0, 1)` (§2.2), where `T(days)` is the decay
|
|
7
|
+
* curve of `contract/src/trust_curve.ts` — the ONE implementation of the formula
|
|
7
8
|
* - recall only ever RAISES trust (`max(eff, …)`, R2) and never pins (D11)
|
|
8
9
|
* - zero-gain recalls neither consume quota nor stamp `last_reinforced_at` (R10)
|
|
9
|
-
* -
|
|
10
|
+
* - permanent is set ONLY by an explicit `pin`; no write path moves a fact across that line (§2.4)
|
|
10
11
|
* - `enabled=false` degrades `eff()` to the stored value (D12)
|
|
11
12
|
*/
|
|
12
13
|
import type { Config } from '../../mem-contract/index.js';
|
|
14
|
+
import { formatUtcTs, parseUtcTs, type TrustCurve } from '../../mem-contract/index.js';
|
|
15
|
+
/**
|
|
16
|
+
* The UTC stamp parse/render pair is `@avantf/mem-contract/time` — ONE implementation, shared with
|
|
17
|
+
* the two display faces (`formatOffsetIso` for the model, `formatLocalHuman` for the UI), so the
|
|
18
|
+
* "a zone-less stamp means UTC" rule cannot drift between the trust math and what a reader sees.
|
|
19
|
+
* Re-exported here because this module is where the trust code (and its specs) have always found it.
|
|
20
|
+
*/
|
|
21
|
+
export { formatUtcTs, parseUtcTs };
|
|
13
22
|
export declare const DAY_MS = 86400000;
|
|
14
23
|
/** Guard for "did the number actually move" comparisons. */
|
|
15
24
|
export declare const EPSILON = 1e-9;
|
|
@@ -24,20 +33,9 @@ export interface TrustRow {
|
|
|
24
33
|
status: string;
|
|
25
34
|
}
|
|
26
35
|
export declare function clamp01(v: number): number;
|
|
36
|
+
/** The curve of one `trust` section — the single mapping from config keys to the math. */
|
|
37
|
+
export declare function trustCurveOf(cfg: TrustConfig): TrustCurve;
|
|
27
38
|
export declare function isPinned(row: Pick<TrustRow, 'pinned'>): boolean;
|
|
28
|
-
/**
|
|
29
|
-
* `'YYYY-MM-DD HH:MM:SS'` (SQLite UTC) or ISO → epoch ms; null/garbage → null.
|
|
30
|
-
*
|
|
31
|
-
* A stamp with NO zone designator is read as UTC, not as local time. `Date.parse` treats a zone-less
|
|
32
|
-
* ISO string as LOCAL, and everything in this store is written in UTC (`CURRENT_TIMESTAMP`, and
|
|
33
|
-
* {@link formatUtcTs}), so handing one over as-is would shift every quota window and every decay
|
|
34
|
-
* measurement by the host's UTC offset — a fact reinforced at 23:00 UTC+8 would look reinforced 8
|
|
35
|
-
* hours earlier, and the 24h cap would open or close at the wrong moment. An explicit `Z` or
|
|
36
|
-
* `±HH:MM` is left alone.
|
|
37
|
-
*/
|
|
38
|
-
export declare function parseUtcTs(ts: string | null | undefined): number | null;
|
|
39
|
-
/** Epoch ms → `'YYYY-MM-DD HH:MM:SS'` (the SQLite `CURRENT_TIMESTAMP` shape, UTC). */
|
|
40
|
-
export declare function formatUtcTs(ms: number): string;
|
|
41
39
|
/** eff(f) — the trust a caller should act on right now (spec §2.2). */
|
|
42
40
|
export declare function effectiveTrust(row: TrustRow, clock: number, cfg: TrustConfig, enabled?: boolean): number;
|
|
43
41
|
/**
|
|
@@ -48,8 +46,9 @@ export declare function effectiveTrust(row: TrustRow, clock: number, cfg: TrustC
|
|
|
48
46
|
*/
|
|
49
47
|
export declare function displayTrust(row: TrustRow, clock: number, cfg: TrustConfig, enabled?: boolean): number;
|
|
50
48
|
/**
|
|
51
|
-
* Remaining active days:
|
|
52
|
-
* when trust is disabled — the contract's
|
|
49
|
+
* Remaining active days: the inverse of the curve at the current eff (`daysToZero`).
|
|
50
|
+
* `null` for pinned rows, non-active rows and when trust is disabled — the contract's
|
|
51
|
+
* `remaining_days` (spec §6 / R11).
|
|
53
52
|
*/
|
|
54
53
|
export declare function remainingDays(row: TrustRow, clock: number, cfg: TrustConfig, enabled?: boolean): number | null;
|
|
55
54
|
/** Everything a recall write needs to persist (spec §2.3). */
|
|
@@ -66,25 +65,13 @@ export interface RecallOutcome {
|
|
|
66
65
|
* Apply one recall to a fact: settle, then grant a bonus inside the daily quota.
|
|
67
66
|
* Returns `next === settled` when the quota is exhausted, the ceiling is reached,
|
|
68
67
|
* the fact is pinned, or the gain would be zero (R2/R10/D11).
|
|
69
|
-
*/
|
|
70
|
-
export declare function grantRecallBonus(row: TrustRow, clock: number, nowMs: number, cfg: TrustConfig, enabled?: boolean): RecallOutcome;
|
|
71
|
-
/** Everything a feedback write needs to persist (spec §2.4). */
|
|
72
|
-
interface FeedbackOutcome {
|
|
73
|
-
settled: number;
|
|
74
|
-
next: number;
|
|
75
|
-
pin: boolean;
|
|
76
|
-
forget: boolean;
|
|
77
|
-
/** true when the row was left untouched (pinned / archived). */
|
|
78
|
-
untouched: boolean;
|
|
79
|
-
bonusCount: number;
|
|
80
|
-
windowAt: string;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Apply one explicit `helpful` (+1) / `unhelpful` (-1) step.
|
|
84
68
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
69
|
+
* The gain is SEGMENTED at the frozen anchor `BASE_TRUST` (D20):
|
|
70
|
+
* - strictly below the base: `min(BASE_TRUST, T + recall_gain_below_base)`, so the shipped
|
|
71
|
+
* `recall_gain_below_base: 0.1` takes 0.05 → 0.5 in five recalls and never past the base;
|
|
72
|
+
* - AT OR ABOVE the base: `recall_gain_above_base × marginal_decay^count`, capped at the ceiling
|
|
73
|
+
* INVERTED from `max_retention_days` (there is no `recall_ceiling` key any more, D16). The
|
|
74
|
+
* boundary is strictly `<` on purpose: a fact sitting exactly on the base (a fresh one, on the
|
|
75
|
+
* first recall after it has finished settling) takes the above-base branch and starts climbing.
|
|
88
76
|
*/
|
|
89
|
-
export declare function
|
|
90
|
-
export {};
|
|
77
|
+
export declare function grantRecallBonus(row: TrustRow, clock: number, nowMs: number, cfg: TrustConfig, enabled?: boolean): RecallOutcome;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type JiebaStatus } from './entities/extract.js';
|
|
1
2
|
import type { AvantfRuntime } from './runtime.js';
|
|
2
3
|
/** The statuses the base's verdict can carry (kept structural — see the note above). */
|
|
3
4
|
export type DshCompatStatus = 'ok' | 'version-mismatch' | 'version-unknown' | 'probe-skipped' | 'probe-failed';
|
|
@@ -72,6 +73,34 @@ export declare function warmModels(rt: AvantfRuntime, options?: WarmOptions, rep
|
|
|
72
73
|
* @returns how long the idle gate waited (ms), or `0` when it was not requested.
|
|
73
74
|
*/
|
|
74
75
|
export declare function awaitStartupGate(options?: WarmOptions): Promise<number>;
|
|
76
|
+
/**
|
|
77
|
+
* The tokenizer states this module reports — one line per observation, in the order they happen.
|
|
78
|
+
*
|
|
79
|
+
* Process-wide on purpose, because the thing being described is: `entities/extract.ts` memoizes the
|
|
80
|
+
* load per process, and a provisioned module root that arrives later (`useTokenizerModuleDir`) can
|
|
81
|
+
* turn the same memo from "unavailable" into "ready". Without the previous verdict a slow repair
|
|
82
|
+
* prints a bare contradiction (`unavailable in 1ms` … `ready in 623ms`); with it the second line
|
|
83
|
+
* says it is a recovery, and the first one knows enough to say it was not final.
|
|
84
|
+
*/
|
|
85
|
+
export type TokenizerVerdict = 'unknown' | 'ready' | 'degraded';
|
|
86
|
+
/** One line to log, and the verdict it leaves behind. */
|
|
87
|
+
export interface TokenizerReport {
|
|
88
|
+
level: 'info' | 'warn';
|
|
89
|
+
message: string;
|
|
90
|
+
next: TokenizerVerdict;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The line one tokenizer observation deserves.
|
|
94
|
+
*
|
|
95
|
+
* `previous` is what makes the pair readable: a `ready` after a `degraded` is a RECOVERY (the
|
|
96
|
+
* earlier probe ran before a loadable copy existed — the provisioned root had not been handed over,
|
|
97
|
+
* or the package was not installed yet), not a change of story. A `degraded` line never claims more
|
|
98
|
+
* than it knows: it names the reason and the two ways to fix it, and says that a loadable copy
|
|
99
|
+
* arriving later is picked up automatically.
|
|
100
|
+
*
|
|
101
|
+
* Exported for the test that pins those transitions; it has no side effects.
|
|
102
|
+
*/
|
|
103
|
+
export declare function tokenizerReport(status: JiebaStatus, elapsedMs: number, previous: TokenizerVerdict): TokenizerReport;
|
|
75
104
|
/**
|
|
76
105
|
* Non-blocking embedding warm, for a caller that knows the model files are already on disk.
|
|
77
106
|
*
|
|
@@ -123,11 +123,10 @@ export type RecallHitsResult = StoreResult<MemoryStore, 'searchMemory'>;
|
|
|
123
123
|
/** The payload of `mem_recall.titles`: an exact page or a fuzzy shortlist of title candidates. */
|
|
124
124
|
export type RecallTitlesResult = StoreResult<MemoryStore, 'titles'>;
|
|
125
125
|
/**
|
|
126
|
-
* `remember` dispatches to
|
|
127
|
-
* written fact, `remove` a flag
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
* narrow first. The callable type says what each ACTION returns.
|
|
126
|
+
* `remember` dispatches to three store operations whose RESULTS differ: `add`/`update` return the
|
|
127
|
+
* written fact, `remove` a flag. One plain signature would either say `unknown` — leaving every
|
|
128
|
+
* caller to invent a cast, which is how a caller ends up reading a field that does not exist — or a
|
|
129
|
+
* union that forces every caller to narrow first. The callable type says what each ACTION returns.
|
|
131
130
|
*/
|
|
132
131
|
export type RememberDispatch = {
|
|
133
132
|
(req: Extract<RememberRequest, {
|
|
@@ -139,9 +138,6 @@ export type RememberDispatch = {
|
|
|
139
138
|
(req: Extract<RememberRequest, {
|
|
140
139
|
action: 'remove';
|
|
141
140
|
}>): Promise<StoreResult<MemoryStore, 'remove'>>;
|
|
142
|
-
(req: Extract<RememberRequest, {
|
|
143
|
-
action: 'helpful' | 'unhelpful';
|
|
144
|
-
}>): Promise<StoreResult<MemoryStore, 'helpful'>>;
|
|
145
141
|
(req: RememberRequest, rules?: RememberWriteRules): Promise<unknown>;
|
|
146
142
|
};
|
|
147
143
|
/**
|
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* RESTORE IS NOT IMPORT. `import` / `merge` (see `facts_io.ts`) are CONTENT-IDENTITY UNIONS: new
|
|
6
6
|
* rows get new ids, only ACTIVE facts travel, and every value the document carries beyond
|
|
7
|
-
* kind/
|
|
8
|
-
* library back exactly as it was" — which is only
|
|
7
|
+
* kind/sources/event time is counted as dropped (the retired `ttl_days` / `valid_to` are ignored).
|
|
8
|
+
* Restore is the opposite promise — "put this library back exactly as it was" — which is only
|
|
9
|
+
* answerable if the target is EMPTY:
|
|
9
10
|
*
|
|
10
11
|
* - `facts` is written DIRECTLY, one transaction, keeping `fact_id`, status, timestamps,
|
|
11
|
-
* `trust_score`, `settle_clock`, counters, pin state, TTL,
|
|
12
|
-
* the mirror bookkeeping. A non-empty target is REFUSED (never merged
|
|
13
|
-
* that silently unions two histories is a different, unreviewable
|
|
12
|
+
* `trust_score`, `settle_clock`, counters, pin state, TTL (format only), event time, the
|
|
13
|
+
* supersedes pointer and the mirror bookkeeping. A non-empty target is REFUSED (never merged
|
|
14
|
+
* into), because "restore" that silently unions two histories is a different, unreviewable
|
|
15
|
+
* operation.
|
|
14
16
|
* - Rows are inserted in `source_id` ASCENDING order, so the row a `supersedes` pointer names
|
|
15
17
|
* already exists when the pointer is written. v1 documents carry the same pointer as
|
|
16
18
|
* `supersedes_source_id`, so both versions resolve identically.
|
|
@@ -64,7 +66,7 @@ export declare const RESTORE_VECTOR_HINT: string;
|
|
|
64
66
|
* rather than trust). A field counts as missing when the document does not carry the key at all —
|
|
65
67
|
* `null` is a value the document states ("unknown"), not an absence.
|
|
66
68
|
*/
|
|
67
|
-
export declare const RESTORE_METADATA_FIELDS: readonly ["kind", "title", "created_at", "updated_at", "trust_score", "pinned", "pinned_at", "assert_count", "helpful_count", "last_reinforced_at", "ttl_days", "valid_from", "
|
|
69
|
+
export declare const RESTORE_METADATA_FIELDS: readonly ["kind", "title", "created_at", "updated_at", "trust_score", "pinned", "pinned_at", "assert_count", "helpful_count", "last_reinforced_at", "ttl_days", "valid_from", "supersedes_source_id", "archive", "sources", "settle_clock", "bonus_count", "bonus_window_at", "retrieval_count", "last_retrieved_at", "mirror_source", "mirror_target", "entities_version", "conflict_checked", "relations"];
|
|
68
70
|
export type RestoreMetadataField = (typeof RESTORE_METADATA_FIELDS)[number];
|
|
69
71
|
/** Per-field count of facts whose document did not carry that field. */
|
|
70
72
|
export type RestoreMetadataMissing = Record<RestoreMetadataField, number>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The memory adapter for the shared `@avantf/graph-recall` port
|
|
3
|
-
*
|
|
2
|
+
* The memory adapter for the shared `@avantf/graph-recall` port (Phase 3b). The port — and the
|
|
3
|
+
* obligations an adapter must honour — is documented in that package's README.
|
|
4
4
|
*
|
|
5
5
|
* WHY THIS FILE EXISTS. The library owns the traversal algorithms (one hop, reverse, two hops, AND,
|
|
6
6
|
* neighbours) and knows nothing about SQLite; memory owns the storage and therefore must supply a
|
|
@@ -104,7 +104,13 @@ export interface SearchInput {
|
|
|
104
104
|
* verify it, so it must come from the same backend.
|
|
105
105
|
*/
|
|
106
106
|
queryVector?: Float32Array;
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Per-call output token budget; `0` = unlimited, omitted = `retriever.max_output_tokens`.
|
|
109
|
+
*
|
|
110
|
+
* A pure time recall (no `query`) is the one exception: omitting it there means NO budget at all.
|
|
111
|
+
* That page is already bounded by its own `limit` (50 by default), so a budget on top would only
|
|
112
|
+
* rewrite the text of rows the caller asked to see (see `timePage`).
|
|
113
|
+
*/
|
|
108
114
|
maxTokens?: number;
|
|
109
115
|
/**
|
|
110
116
|
* Relevance-floor profile for this query. Omitted = the default policy: the configured (strict)
|
|
@@ -158,17 +164,15 @@ export interface SearchInput {
|
|
|
158
164
|
/**
|
|
159
165
|
* P-08 / P-13 write-side extras on `add` / `update`.
|
|
160
166
|
*
|
|
161
|
-
*
|
|
162
|
-
* NULL/1 default, or the existing row's value on a duplicate) — the store never invents a source
|
|
163
|
-
* an event time
|
|
167
|
+
* Both are optional and "omitted" has one meaning everywhere: do not write (a new row's
|
|
168
|
+
* NULL/1 default, or the existing row's value on a duplicate) — the store never invents a source
|
|
169
|
+
* or an event time.
|
|
164
170
|
*/
|
|
165
171
|
export interface FactWriteOptions {
|
|
166
172
|
/** P-08: `fact_sources` provenance. `kind:` prefix selects the kind, else `manual`. */
|
|
167
173
|
sourceRef?: string;
|
|
168
174
|
/** P-13: event time → `valid_from`. */
|
|
169
175
|
eventDate?: string;
|
|
170
|
-
/** P-13: known end → `valid_to`, WITHOUT archiving the row. */
|
|
171
|
-
validUntil?: string;
|
|
172
176
|
/**
|
|
173
177
|
* The journal title (JOURNAL_TITLE_SEARCH §2.2/§11.1). `undefined` = not given, `null` = "no
|
|
174
178
|
* title", a string = the (already normalized) title. On a revision an omitted title INHERITS the
|
|
@@ -242,7 +246,10 @@ export interface TitleInput {
|
|
|
242
246
|
export interface MemorySearchInput extends Omit<SearchInput, 'query'> {
|
|
243
247
|
/** The literal query. Omitted = a pure time window (then `time` is required). */
|
|
244
248
|
query?: string;
|
|
245
|
-
/**
|
|
249
|
+
/**
|
|
250
|
+
* The page's first row (default 0). Read by the exact/facts passes and by the pure time window,
|
|
251
|
+
* where it walks the window one page at a time (see `timePage`).
|
|
252
|
+
*/
|
|
246
253
|
offset?: number;
|
|
247
254
|
}
|
|
248
255
|
export declare class MemoryStore {
|
|
@@ -331,7 +338,7 @@ export declare class MemoryStore {
|
|
|
331
338
|
* and every existing assertion on it — is unchanged; only the warning's wording uses it.
|
|
332
339
|
*/
|
|
333
340
|
private vectorSpaceCounts;
|
|
334
|
-
add(content: string, kind?: MemoryKind,
|
|
341
|
+
add(content: string, kind?: MemoryKind, opts?: FactWriteOptions): Promise<RememberResult>;
|
|
335
342
|
/**
|
|
336
343
|
* Write-path contradiction detection (DESIGN §11): check the fact that was just
|
|
337
344
|
* written against the corpus, then report its FULL open-conflict set.
|
|
@@ -358,24 +365,16 @@ export declare class MemoryStore {
|
|
|
358
365
|
private warnIndexFailure;
|
|
359
366
|
/** Best-effort semantic indexing: encode the fact and add it to the vstore. */
|
|
360
367
|
private maybeIndexSemantic;
|
|
361
|
-
update(fact_id: number, content: string, kind?: MemoryKind,
|
|
368
|
+
update(fact_id: number, content: string, kind?: MemoryKind, opts?: FactWriteOptions): Promise<RememberResult>;
|
|
362
369
|
remove(fact_id: number, reason?: string): Promise<boolean>;
|
|
363
370
|
archive(fact_id: number, reason?: string): Promise<boolean>;
|
|
364
371
|
/**
|
|
365
|
-
* Bring an archived fact back (spec §2.6): a `forgot` fact is lifted to
|
|
366
|
-
* `
|
|
367
|
-
*
|
|
372
|
+
* Bring an archived fact back (spec §2.6): a `forgot` fact is lifted to the frozen base
|
|
373
|
+
* `BASE_TRUST` (otherwise the very next tick would re-archive it, R4),
|
|
374
|
+
* `last_retrieved_at` is refreshed (observation only — no clock reads it any more),
|
|
368
375
|
* and `settle_clock` moves to now so the downtime is not charged.
|
|
369
376
|
*/
|
|
370
377
|
restore(fact_id: number): Promise<boolean>;
|
|
371
|
-
helpful(fact_id: number): Promise<number | null>;
|
|
372
|
-
unhelpful(fact_id: number): Promise<number | null>;
|
|
373
|
-
/**
|
|
374
|
-
* Explicit feedback (spec §2.4). Pinned/archived rows only move `helpful_count`
|
|
375
|
-
* (R7); otherwise the decay is settled first, then `±feedback_delta`, pin at the
|
|
376
|
-
* permanent threshold (snap to 1.0) or archive immediately at the forget line.
|
|
377
|
-
*/
|
|
378
|
-
private applyFeedback;
|
|
379
378
|
/** Permanent-memory administration (spec §6). */
|
|
380
379
|
pin(fact_id: number): Promise<boolean>;
|
|
381
380
|
unpin(fact_id: number): Promise<boolean>;
|
|
@@ -455,7 +454,37 @@ export declare class MemoryStore {
|
|
|
455
454
|
private sweepConflicts;
|
|
456
455
|
/** Async bootstrap the semantic backend (download/config model) without blocking. */
|
|
457
456
|
warmupSemantic(): Promise<boolean>;
|
|
458
|
-
/**
|
|
457
|
+
/**
|
|
458
|
+
* Point the semantic backend at a shared runtime-dependency root (the base provider's
|
|
459
|
+
* `runtime-deps` handle) before it warms. Best-effort by contract: a backend without external
|
|
460
|
+
* module resolution ignores it, and a missing root is reported by the backend itself.
|
|
461
|
+
*/
|
|
462
|
+
useSemanticModuleDir(dir: string): void;
|
|
463
|
+
/**
|
|
464
|
+
* Point the vector store at a shared runtime-dependency root for its NATIVE binding
|
|
465
|
+
* (`hnswlib-node`), after the DSH plugin's native self-heal item is ready.
|
|
466
|
+
*
|
|
467
|
+
* Same best-effort contract as {@link useSemanticModuleDir}: a store with no native binding (the
|
|
468
|
+
* brute-force backend, or an `auto` store still on it) ignores it, and a store that already fell
|
|
469
|
+
* back to brute force rebuilds its tracked vectors under the new root. Nothing here decides whether
|
|
470
|
+
* a root should exist — the caller's dependency audit does.
|
|
471
|
+
*/
|
|
472
|
+
useNativeModuleDir(dir: string): void;
|
|
473
|
+
/**
|
|
474
|
+
* Vectors currently in the LIVE index (sync, one map read).
|
|
475
|
+
*
|
|
476
|
+
* Exposed for the plugin's dependency audit: an `hnswlib-node` notice is conditioned on the corpus
|
|
477
|
+
* actually being large enough for an ANN index to matter (`min_vectors`), and a session line that
|
|
478
|
+
* asks "how big is the store right now" must not run a database query to answer.
|
|
479
|
+
*/
|
|
480
|
+
liveVectorCount(): number;
|
|
481
|
+
/**
|
|
482
|
+
* List contradiction candidates (`resolved` 0 = open, 1 = closed with a recorded verdict).
|
|
483
|
+
*
|
|
484
|
+
* `detected_at` is mapped to the model face's instant shape here (the DAO hands back the raw UTC
|
|
485
|
+
* column): this payload is what the 记忆 tab's 未处理矛盾 table and the CLI both render, and it must
|
|
486
|
+
* not be the one surface still printing a zone-less stamp.
|
|
487
|
+
*/
|
|
459
488
|
listContradictions(opts?: {
|
|
460
489
|
resolved?: number;
|
|
461
490
|
kind?: MemoryKind;
|
|
@@ -695,10 +724,21 @@ export declare class MemoryStore {
|
|
|
695
724
|
* ({@link timePage}, `mode:"time"`): the window is the whole question, so there is nothing to
|
|
696
725
|
* match, plan or rank.
|
|
697
726
|
*
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
*
|
|
701
|
-
*
|
|
727
|
+
* What a pass does to the rows it returns is one decision, and it is deliberately NOT uniform:
|
|
728
|
+
*
|
|
729
|
+
* - the RELATION and FUZZY passes REINFORCE them ({@link reinforce}): each answers a question
|
|
730
|
+
* the caller asked, so the rows are what it read;
|
|
731
|
+
* - the TIME, EXACT and FACTS passes record USAGE ONLY ({@link touchOnly}): they hand back a
|
|
732
|
+
* LIST the caller browses — page 2 of a listing is not a second recall — so "it was looked
|
|
733
|
+
* at" still moves `retrieval_count` / `last_retrieved_at` (the dormancy clock) while
|
|
734
|
+
* `trust_score` / `bonus_count` stay where they are.
|
|
735
|
+
*
|
|
736
|
+
* A caller that must stay read-only passes `track: false` (the conditional hint's probe does);
|
|
737
|
+
* that branch writes nothing at all, usage included. Exact, facts, relation and time also honour
|
|
738
|
+
* the caller's output budget (their text is the caller's to bound). The one difference is the
|
|
739
|
+
* DEFAULT: a time window applies no budget unless the caller names one — its page size (`limit`)
|
|
740
|
+
* is what bounds the list, and the budget would only rewrite rows the caller asked for (see
|
|
741
|
+
* {@link timePage}).
|
|
702
742
|
*
|
|
703
743
|
* It does NOT emit a retrieval-health event. Those counters describe the retrieval PIPELINE
|
|
704
744
|
* (legs, fusion, semantic-live rate); none of an exact, facts, relation or time answer ran a leg,
|
|
@@ -714,9 +754,21 @@ export declare class MemoryStore {
|
|
|
714
754
|
* happened that day" reads in the order it happened; `score` is the constant 1 exactly like the
|
|
715
755
|
* exact page's, because there is no relevance number here for the model to compare.
|
|
716
756
|
*
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
*
|
|
757
|
+
* The PAGE is the contract's, not the caller's guess: `limit` omitted means one page of
|
|
758
|
+
* {@link TIME_DEFAULT_LIMIT} rows (the same 50 the contract caps an explicit page at), and an
|
|
759
|
+
* explicit `limit` (1-50) still wins. A window that holds more than one page is the normal case
|
|
760
|
+
* for a busy day, so the envelope does not leave the caller to infer the shortfall: it reports
|
|
761
|
+
* `remaining = total - (offset + hits.length)`, the count still unseen.
|
|
762
|
+
*
|
|
763
|
+
* The output budget is honoured only when the caller names it (`max_tokens`). Falling back to the
|
|
764
|
+
* configured `retrieval.max_output_tokens` here would rewrite the text of a window's rows behind a
|
|
765
|
+
* page the caller already bounded with `limit` — the page size is the knob for "how much of the
|
|
766
|
+
* list", and the budget stays a knob for a RANKED result (exact/facts/relation/hybrid unchanged).
|
|
767
|
+
* `max_tokens` (including the contract's `0` = unlimited) is passed through unchanged.
|
|
768
|
+
*
|
|
769
|
+
* It honours `kind`/`source`, and records usage ONLY for the rows it returns unless `track: false`
|
|
770
|
+
* ({@link touchOnly}). A window is listed so the caller can look through it, not recalled into an
|
|
771
|
+
* answer: the dormancy clock (`retrieval_count` / `last_retrieved_at`) moves, trust does not.
|
|
720
772
|
*/
|
|
721
773
|
private timePage;
|
|
722
774
|
/**
|
|
@@ -753,8 +805,8 @@ export declare class MemoryStore {
|
|
|
753
805
|
* malformed window is the caller's error exactly as it is in the exact page;
|
|
754
806
|
* - the page is FACT-level (`total`/`offset`/`limit`/`truncated`), and the caller's output budget
|
|
755
807
|
* only shortens `content` — dropping a row would make `total` disagree with the page;
|
|
756
|
-
* - the returned facts
|
|
757
|
-
*
|
|
808
|
+
* - the returned facts get usage recorded but NO reinforcement unless `track: false`: a facts
|
|
809
|
+
* page is a list the caller browses, so only the dormancy clock moves ({@link touchOnly}).
|
|
758
810
|
*/
|
|
759
811
|
private factPage;
|
|
760
812
|
/**
|
|
@@ -928,6 +980,17 @@ export declare class MemoryStore {
|
|
|
928
980
|
flushHealth(): Promise<void>;
|
|
929
981
|
/** Retrieval bookkeeping: `retrieval_count` + the "last used" clock (spec §2.5). */
|
|
930
982
|
private touchUsage;
|
|
983
|
+
/**
|
|
984
|
+
* Record the USAGE of these facts and nothing else — no trust bonus, no quota, no
|
|
985
|
+
* `last_reinforced_at`.
|
|
986
|
+
*
|
|
987
|
+
* This is the paginated passes' half of {@link reinforce}: `timePage` / `exactContentPage` /
|
|
988
|
+
* `factPage` return a list the caller browses rather than a recall it acted on, so "someone
|
|
989
|
+
* looked at this row" still moves `retrieval_count` / `last_retrieved_at` (observation and
|
|
990
|
+
* export only — no lifecycle step reads that clock) while `trust_score` / `bonus_count` stay
|
|
991
|
+
* put.
|
|
992
|
+
*/
|
|
993
|
+
private touchOnly;
|
|
931
994
|
/**
|
|
932
995
|
* Semantic leg. `queryVector` short-circuits the encode when the caller already
|
|
933
996
|
* encoded this exact query (the cross-store router does, for both stores at once);
|
|
@@ -975,13 +1038,11 @@ export declare class MemoryStore {
|
|
|
975
1038
|
/**
|
|
976
1039
|
* Insert (or resolve to) a fact row.
|
|
977
1040
|
*
|
|
978
|
-
* `kind`
|
|
979
|
-
*
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
983
|
-
* omitted one keeps what the row already has, and a brand-new row falls back to the
|
|
984
|
-
* defaults (`general` / `0`).
|
|
1041
|
+
* `kind` is `undefined` when the CALLER specified nothing — the distinction is load-bearing.
|
|
1042
|
+
* A duplicate-content hit must not clobber the existing row's value with the insert default:
|
|
1043
|
+
* re-adding a fact without a kind used to reset it to `general`. The rule for all three
|
|
1044
|
+
* branches is therefore: an explicitly provided value lands, an omitted one keeps what the row
|
|
1045
|
+
* already has, and a brand-new row falls back to the default (`note`).
|
|
985
1046
|
*/
|
|
986
1047
|
private persistFact;
|
|
987
1048
|
/**
|
|
@@ -5,6 +5,16 @@ export declare const EXACT_MIN_LATIN = 3;
|
|
|
5
5
|
/** The default page of an exact result, and its hard cap. */
|
|
6
6
|
export declare const EXACT_DEFAULT_LIMIT = 10;
|
|
7
7
|
export declare const EXACT_MAX_LIMIT = 50;
|
|
8
|
+
/**
|
|
9
|
+
* The default page of a PURE TIME window (`mem_recall.search` with no `query`), and its cap.
|
|
10
|
+
*
|
|
11
|
+
* A window is a list, not a ranked shortlist: the caller asked "what happened then", and one page of
|
|
12
|
+
* ten would silently answer a tenth of the day. It is therefore answered with the widest page the
|
|
13
|
+
* contract allows (`EXACT_MAX_LIMIT`), and the envelope states how much is left
|
|
14
|
+
* (`remaining`) instead of leaving the caller to infer it from `truncated`. Omitting `limit` never
|
|
15
|
+
* means "everything": a window that has outgrown one page is exactly when a cap matters.
|
|
16
|
+
*/
|
|
17
|
+
export declare const TIME_DEFAULT_LIMIT = 50;
|
|
8
18
|
/**
|
|
9
19
|
* The widest literal match set that still counts as an ANSWER (P1-2, REVIEW-2026-10-24).
|
|
10
20
|
*
|
|
@@ -107,7 +107,7 @@ export declare function timeParams(range: TimeRange | undefined): (string | numb
|
|
|
107
107
|
*/
|
|
108
108
|
export declare function dayInZone(ms: number, timeZone?: string): string;
|
|
109
109
|
/**
|
|
110
|
-
* One stored `valid_from
|
|
110
|
+
* One stored event-time (`valid_from`) string → epoch ms, using the SAME grammar as the filters.
|
|
111
111
|
*
|
|
112
112
|
* This is what the write path stamps into `valid_from_ms` and what migration 12's backfill applies to
|
|
113
113
|
* existing rows, so "the event time a caller wrote" and "the event time a window compares" can never
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The trust heartbeat: the interval beat that advances the active-day clock and performs the
|
|
3
|
-
* settle / forget /
|
|
3
|
+
* settle / forget / purge sweeps (TRUST_MODEL.md §5), shared by the DSH plugin and the MCP
|
|
4
4
|
* server.
|
|
5
5
|
*
|
|
6
6
|
* The two surfaces used to install it separately, and the copies drifted in exactly the places a
|