@avantf/dsh-mem 0.6.3 → 0.7.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.
@@ -6,6 +6,8 @@ import { type Db } from './db/conn.js';
6
6
  import { type RelevanceHit } from './store/lexical.js';
7
7
  import { MemoryStore } from './store/memory.js';
8
8
  import { KnowledgeStore } from './store/knowledge.js';
9
+ import { type FactsExportCodec, type ImportReport, type MergeReport } from './facts_io.js';
10
+ import type { RestoreReport } from './store/facts_restore.js';
9
11
  export interface RuntimeOptions {
10
12
  dataHome?: string;
11
13
  memoryDbPath?: string;
@@ -35,6 +37,22 @@ export interface RuntimeOptions {
35
37
  readonly tools?: string;
36
38
  readonly models?: string;
37
39
  };
40
+ /**
41
+ * Skip the adapters' eager constructor warm even when the caller passes no {@link managedRoots}:
42
+ * the caller owns the warm schedule and will warm explicitly.
43
+ *
44
+ * There are exactly TWO ways to get a deferred warm, and both are requests of the same thing:
45
+ *
46
+ * - {@link managedRoots} — the DSH plugin's path. The framework owns the model root and may still
47
+ * be installing into it, so the backends must not warm themselves (that would race the install
48
+ * and fetch a second copy).
49
+ * - `deferWarm: true` — the CLI's path. The flag exists because the CLI is one-shot and its
50
+ * READ-ONLY commands (`stats`, `list`, `export`, …) must not load or download the model at all;
51
+ * it warms explicitly, and only from `needsModels` (the command table in `cli/src/index.ts`).
52
+ *
53
+ * A first `encode()` still warms lazily, so deferring never costs the commands that do encode.
54
+ */
55
+ deferWarm?: boolean;
38
56
  }
39
57
  /**
40
58
  * The awaited result of a store method — what a dispatch overload promises.
@@ -65,6 +83,28 @@ export type DispatchError = {
65
83
  * admin payload no store method produces, because the runtime composes it.
66
84
  */
67
85
  export type AdminStatsResult = StatsSummary;
86
+ /**
87
+ * `admin export`: the file THIS call wrote and its container. Deliberately carries NO document body
88
+ * — the model gets a receipt, not the library (the file is the artifact; whoever needs the facts
89
+ * reads it with the CLI or imports it back).
90
+ */
91
+ export interface AdminExportReport {
92
+ file: string;
93
+ codec: FactsExportCodec;
94
+ bytes: number;
95
+ plain_bytes: number;
96
+ counts: {
97
+ active: number;
98
+ archived: number;
99
+ };
100
+ /** True when an existing file was replaced (the atomic write is the same either way). */
101
+ overwritten: boolean;
102
+ }
103
+ /** `admin restore_snapshot`: the engine's restore report plus the container it came from. */
104
+ export type AdminRestoreSnapshotReport = RestoreReport & {
105
+ file: string;
106
+ codec: FactsExportCodec;
107
+ };
68
108
  /**
69
109
  * The payload of the four hits-returning recall actions, which one overload groups because a
70
110
  * caller may hold them as a union (`action: 'chain' | 'reason'`).
@@ -119,9 +159,10 @@ export type RecallDispatch = {
119
159
  (req: RecallRequest): Promise<unknown>;
120
160
  };
121
161
  /**
122
- * `admin` is the widest dispatcher: counts, a fact page, a detail, flags, and five diagnostic
123
- * reports. `ReturnType<…>` keeps each member tied to the store method that actually produces it,
124
- * so a change there propagates instead of leaving callers with a cast they invented.
162
+ * `admin` is the widest dispatcher: counts, a fact page, a detail, flags, five diagnostic
163
+ * reports, and the four data-root maintenance actions (export / import / merge / restore_snapshot).
164
+ * `ReturnType<…>` keeps each member tied to the store method that actually produces it, so a change
165
+ * there propagates instead of leaving callers with a cast they invented.
125
166
  */
126
167
  export type AdminDispatch = {
127
168
  (req: Extract<AdminRequest, {
@@ -163,6 +204,19 @@ export type AdminDispatch = {
163
204
  (req: Extract<AdminRequest, {
164
205
  action: 'maintenance';
165
206
  }>): StoreResult<MemoryStore, 'maintenance'>;
207
+ (req: Extract<AdminRequest, {
208
+ action: 'export';
209
+ }>): AdminExportReport;
210
+ /** Content-detected: a SQLite source answers with the db report, an export document with the JSON one. */
211
+ (req: Extract<AdminRequest, {
212
+ action: 'import';
213
+ }>): Promise<ImportReport | MergeReport>;
214
+ (req: Extract<AdminRequest, {
215
+ action: 'merge';
216
+ }>): Promise<MergeReport>;
217
+ (req: Extract<AdminRequest, {
218
+ action: 'restore_snapshot';
219
+ }>): Promise<AdminRestoreSnapshotReport>;
166
220
  (req: AdminRequest): unknown;
167
221
  };
168
222
  /**
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Full-fidelity REBUILD of a memory library from an export document — the ONE owner of
3
+ * `avantf-mem restore <file>`.
4
+ *
5
+ * RESTORE IS NOT IMPORT. `import` / `merge` (see `facts_io.ts`) are CONTENT-IDENTITY UNIONS: new
6
+ * rows get new ids, only ACTIVE facts travel, and every value the document carries beyond
7
+ * category/ttl/sources/validity is counted as dropped. Restore is the opposite promise — "put this
8
+ * library back exactly as it was" — which is only answerable if the target is EMPTY:
9
+ *
10
+ * - `facts` is written DIRECTLY, one transaction, keeping `fact_id`, status, timestamps,
11
+ * `trust_score`, `settle_clock`, counters, pin state, TTL, validity, the supersedes pointer and
12
+ * the mirror bookkeeping. A non-empty target is REFUSED (never merged into), because "restore"
13
+ * that silently unions two histories is a different, unreviewable operation.
14
+ * - Rows are inserted in `source_id` ASCENDING order, so the row a `supersedes` pointer names
15
+ * already exists when the pointer is written. v1 documents carry the same pointer as
16
+ * `supersedes_source_id`, so both versions resolve identically.
17
+ * - `fact_sources` is restored IN FULL (a fact may carry several rows); the identity `import`
18
+ * is forced to collapse into one `ref` string is not a limitation restore inherits.
19
+ * - FTS is NOT written by hand. `facts_fts` is an external-content FTS5 table with AFTER INSERT /
20
+ * DELETE / UPDATE triggers (`db/schema.ts`), so inserting into `facts` indexes the row. That is
21
+ * the whole reason this path can bypass `MemoryStore.add` without losing lexical recall.
22
+ * - The DERIVED state is re-derived, not restored. `entities_version = 0` marks every row for the
23
+ * entity sweep, which rebuilds `fact_entities`, `triples` and the HRR bundle in bounded passes
24
+ * after the commit; `conflict_checked = 1` keeps a restored corpus from queueing a whole-library
25
+ * conflict re-scan (the log is not exported — see below).
26
+ * - VECTORS ARE NOT RESTORED (`semantic_vector`, `embedding_model` stay NULL, `hrr_vector` stays
27
+ * NULL until the sweep writes it). They are machine-local encodings of a specific model revision;
28
+ * copying them between hosts would create a vector space the live index cannot serve. The report
29
+ * says so and points at `avantf-mem vectors --fix` / a host restart.
30
+ *
31
+ * WHAT IS DELIBERATELY NOT RESTORED: `contradiction_log`, the retrieval-stat meta table, `tags`
32
+ * (not exported), and `archived_clock`. The last one is a TRUST-CLOCK value ("active days at archive
33
+ * time"); the document only carries `archived_at` (a wall-clock timestamp), and converting one into
34
+ * the other would invent data. Leaving it NULL sends restored archived rows down
35
+ * `purgeArchived`'s CALENDAR branch, which reads the restored `archived_at` — the honest
36
+ * approximation, and the reason a restore does not restart every purge countdown at zero.
37
+ *
38
+ * V1 DOCUMENTS ARE READ. v2 only ADDED optional facts columns, so a v1 row simply lacks them; each
39
+ * absence is counted per field in `metadata_missing` and resolved with the documented fallback. The
40
+ * one fallback with a real decision behind it: `settle_clock` becomes the CURRENT active-day clock.
41
+ * Zero would make every restored fact decay by the host's whole clock in one settlement; the current
42
+ * clock is the "no decay has happened yet" baseline, which is what a fresh restore means.
43
+ *
44
+ * @module store/facts_restore
45
+ */
46
+ import type { FactsExportDocument } from '../facts_io.js';
47
+ import type { Db } from '../db/conn.js';
48
+ import { type EntitySweepReport } from './entity_sweep.js';
49
+ /** The one operator hint about the data restore cannot carry. */
50
+ export declare const RESTORE_VECTOR_HINT: string;
51
+ /**
52
+ * Every metadata field the report accounts for.
53
+ *
54
+ * The list is the DOCUMENT's, not the table's: it includes the v2 additions (so a v1 file shows
55
+ * exactly which columns it could not carry) and the derived-state stamps (which restore re-derives
56
+ * rather than trust). A field counts as missing when the document does not carry the key at all —
57
+ * `null` is a value the document states ("unknown"), not an absence.
58
+ */
59
+ export declare const RESTORE_METADATA_FIELDS: readonly ["category", "created_at", "updated_at", "trust_score", "pinned", "pinned_at", "assert_count", "helpful_count", "last_reinforced_at", "ttl_days", "valid_from", "valid_to", "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"];
60
+ export type RestoreMetadataField = (typeof RESTORE_METADATA_FIELDS)[number];
61
+ /** Per-field count of facts whose document did not carry that field. */
62
+ export type RestoreMetadataMissing = Record<RestoreMetadataField, number>;
63
+ export interface RestoreOptions {
64
+ /** Plan only: no write, no entity rebuild; the report carries `would_restore` + `target_facts`. */
65
+ dryRun?: boolean;
66
+ }
67
+ /**
68
+ * The store's half of the contract, narrow on purpose: the ONE write connection, plus the bounded
69
+ * entity sweep this flow reuses after the commit. Nothing here knows about `MemoryStore`.
70
+ */
71
+ export interface RestoreTarget {
72
+ db: Db;
73
+ reindexEntities(budget?: number): Promise<EntitySweepReport>;
74
+ }
75
+ /** How many facts a document would restore, split by status. */
76
+ export interface RestoreCounts {
77
+ total: number;
78
+ active: number;
79
+ archived: number;
80
+ }
81
+ /** The single JSON object the CLI prints (it adds `file` / `codec`, which are argv facts). */
82
+ export interface RestoreReport {
83
+ /** The DOCUMENT's version, echoed as-is (1 or 2). */
84
+ version: number;
85
+ /** Present on `--dry-run` only. */
86
+ dry_run?: true;
87
+ /** Present on `--dry-run` only: what a real run would write. */
88
+ would_restore?: RestoreCounts;
89
+ /**
90
+ * Present on `--dry-run` only: the target's CURRENT fact count. Non-zero means the real run would
91
+ * be refused (restore only rebuilds into an empty table).
92
+ */
93
+ target_facts?: number;
94
+ /** Facts actually written (0 on a dry run). */
95
+ restored: number;
96
+ active: number;
97
+ archived: number;
98
+ metadata_missing: RestoreMetadataMissing;
99
+ /** FTS5 rows after the commit — the trigger's work, asserted equal to `restored` by the tests. */
100
+ fts_rows: number;
101
+ entities: EntitySweepReport;
102
+ vectors: {
103
+ restored: 0;
104
+ hint: string;
105
+ };
106
+ }
107
+ /**
108
+ * Rebuild the target library from `doc`.
109
+ *
110
+ * Order of guarantees: the document is validated COMPLETELY first (no writes on a bad file), then one
111
+ * `BEGIN IMMEDIATE` transaction re-checks emptiness and inserts facts + sources (a failure anywhere
112
+ * rolls the whole thing back), and only after that commit does the derived state get rebuilt. The
113
+ * entity sweep is deliberately OUTSIDE the transaction: it is bounded, resumable work, and holding
114
+ * the write lock for a whole-corpus re-extraction is exactly what its batch exists to avoid.
115
+ */
116
+ export declare function restoreFactsDocument(target: RestoreTarget, doc: FactsExportDocument, options?: RestoreOptions): Promise<RestoreReport>;
@@ -26,8 +26,6 @@
26
26
  import { type Config, type FloorProfile, type RecallResult, type RetrievalFloorDrops, type RetrievalFloors } from '../../mem-contract/index.js';
27
27
  import { type BudgetInput, type Hit, type SemanticBackend } from '../../mem-retrieval/index.js';
28
28
  import { type FloorLeg } from './floors.js';
29
- /** The `limit` a caller gets when it passes nothing usable. */
30
- export declare const DEFAULT_SEARCH_LIMIT = 10;
31
29
  /**
32
30
  * A retrieval input the CALLER got wrong, as opposed to a backend that failed at runtime.
33
31
  *
@@ -39,8 +37,6 @@ export declare const DEFAULT_SEARCH_LIMIT = 10;
39
37
  */
40
38
  export declare class RetrievalInputError extends Error {
41
39
  }
42
- /** …and the over-fetch factor when the config does not set one (matches the contract default). */
43
- export declare const DEFAULT_OVER_FETCH_FACTOR = 5;
44
40
  /** The three weights a `RecallResult` reports. */
45
41
  export type RetrievalWeights = RecallResult['weights'];
46
42
  /** One retrieval leg's contribution to the fusion. */
@@ -1,6 +1,8 @@
1
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';
2
2
  import type { SemanticBackend, VectorStore } from '../../mem-retrieval/index.js';
3
3
  import { ENTITY_SWEEP_BATCH, type EntitySweepReport } from './entity_sweep.js';
4
+ import { type RestoreOptions, type RestoreReport } from './facts_restore.js';
5
+ import type { FactsExportDocument } from '../facts_io.js';
4
6
  import { DEFAULT_VECTOR_MIGRATION_BATCH, type VectorRepairTarget } from './vector_repair.js';
5
7
  import type { Db } from '../db/conn.js';
6
8
  import { type LexicalProbe } from './lexical.js';
@@ -487,6 +489,20 @@ export declare class MemoryStore {
487
489
  * selection (`facts.staleEntityRows`, ACTIVE facts only) and the per-fact write below.
488
490
  */
489
491
  reindexEntities(budget?: number): Promise<EntitySweepReport>;
492
+ /**
493
+ * Rebuild this library from an export document — the engine half of `avantf-mem restore <file>`.
494
+ *
495
+ * A THIN adapter on purpose: the empty-table rule, the insert order, the SQL and the report all
496
+ * live in `store/facts_restore.ts`, because this class is a hub and a rebuild of the whole facts
497
+ * table must not become another reason to grow it. What it contributes is the two things only the
498
+ * store has: the ONE write connection (so the restored rows are immediately visible to every
499
+ * reader here) and the derived-state entity sweep that runs after the commit.
500
+ *
501
+ * Reachable from `mem_admin restore_snapshot` (and from the CLI): after a real restore this
502
+ * method refreshes the live vector index and resets the conflict-drain cursor, so a host that
503
+ * rebuilt its library from a tool call answers from the NEW rows without a restart.
504
+ */
505
+ restoreFacts(doc: FactsExportDocument, options?: RestoreOptions): Promise<RestoreReport>;
490
506
  /**
491
507
  * A fact left the ACTIVE corpus: retire the open conflicts that name it, so
492
508
  * `contradict` never reports a pair whose revision is archived and repeated
@@ -0,0 +1,60 @@
1
+ import type { StaleVectorCounts } from './common.js';
2
+ /** What one persisted vector IS, in the current space. */
3
+ export type VectorSpaceVerdict = 'current' | 'stale' | 'space_stale';
4
+ /**
5
+ * A persisted vector's width, in whichever shape the CALLER already has it — the discriminant is the
6
+ * shape, never the truth:
7
+ *
8
+ * - `blob` — the raw BLOB (`Uint8Array`), for a caller that has read the bytes;
9
+ * - `byteLength` — a projected `length(semantic_vector)`, for a caller that must not read them;
10
+ * - `decoded` — an already decoded `Float32Array`, for a caller that had to decode anyway.
11
+ *
12
+ * All three answer the same question (`width === dim`), which is the point: the caller with the
13
+ * cheapest possible evidence is not forced to re-read the row in another shape. A `null`/`undefined`
14
+ * width is "no usable bytes" and classifies as `stale`, like a wrong one.
15
+ */
16
+ export type PersistedVectorWidth = {
17
+ readonly source: 'blob';
18
+ readonly vec: Uint8Array | null | undefined;
19
+ } | {
20
+ readonly source: 'byteLength';
21
+ readonly byteLength: number | null | undefined;
22
+ } | {
23
+ readonly source: 'decoded';
24
+ readonly vec: Float32Array | null | undefined;
25
+ };
26
+ /** One width probe plus the space RECORDED beside it (the second element of the rule). */
27
+ export type VectorSpaceProbe = PersistedVectorWidth & {
28
+ /** `facts.embedding_model` / `doc_chunks.embedding_model` of the same row; `null` = unrecorded. */
29
+ readonly embeddingModel: string | null | undefined;
30
+ };
31
+ /** The space the store writes into RIGHT NOW. */
32
+ export interface VectorSpaceIdentity {
33
+ /** The representation fingerprint (`vectorSpaceOf`). */
34
+ readonly space: string;
35
+ /** The width the store writes now, in floats. */
36
+ readonly dim: number;
37
+ }
38
+ /** One row's classification. */
39
+ export interface VectorSpaceClassification {
40
+ readonly verdict: VectorSpaceVerdict;
41
+ /** Of a `space_stale` verdict: the recorded id predates the representation fingerprint. */
42
+ readonly legacy: boolean;
43
+ }
44
+ /**
45
+ * Classify ONE persisted vector. Pure: no store, no database, no logging — a caller that disagrees
46
+ * with the result has to disagree with the rule, not with a copy of it.
47
+ *
48
+ * A caller-supplied vector is trusted to come from this backend, but its DIMENSION is still checked
49
+ * — the same asymmetry `legs.ts` applies to a caller-supplied `queryVector`: provenance is the
50
+ * caller's to assert, width is cheap and decides whether the bytes can be compared at all.
51
+ */
52
+ export declare function classifyVectorSpace(probe: VectorSpaceProbe, identity: VectorSpaceIdentity): VectorSpaceClassification;
53
+ /**
54
+ * {@link classifyVectorSpace} over a corpus: the `{ stale, space_stale, legacy }` triple the warning
55
+ * and the `/mem` status surface report, counted by the same rule that classified each row.
56
+ *
57
+ * `legacy` counts only rows already counted in `space_stale` (`reportStaleVectors` clamps it there
58
+ * too) — it is a REASON split, never a fourth bucket.
59
+ */
60
+ export declare function countVectorSpace(probes: readonly VectorSpaceProbe[], identity: VectorSpaceIdentity): StaleVectorCounts;
@@ -48,6 +48,13 @@ export declare const FACT_VIEW_ADMIN_ACTIONS: readonly ["list", "detail"];
48
48
  * explicitly with `mem_admin`'s diagnostic actions.
49
49
  */
50
50
  export declare function modelFacingToolResult(toolKey: string, action: unknown, value: unknown): unknown;
51
+ /**
52
+ * The statuses an export may narrow to. Declared here (next to the tool schema that exposes it) so
53
+ * the core export path, the CLI flag validation and the admin action read ONE set: a value added
54
+ * here moves the contract, the CLI and the engine together.
55
+ */
56
+ export declare const EXPORT_STATUSES: readonly ["active", "archived", "all"];
57
+ export type ExportStatus = (typeof EXPORT_STATUSES)[number];
51
58
  export declare const REMEMBER_ACTIONS: readonly ["add", "update", "remove", "helpful", "unhelpful"];
52
59
  export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
53
60
  action: z.ZodLiteral<"add">;
@@ -78,6 +85,13 @@ export declare const RememberUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
78
85
  fact_id: z.ZodNumber;
79
86
  }, z.core.$strip>], "action">;
80
87
  export type RememberRequest = z.infer<typeof RememberUnion>;
88
+ /**
89
+ * The writes the model may make. `helpful` / `unhelpful` are operator feedback: the model cannot
90
+ * observe the retention clocks they move, and a model that could would be able to "refresh" a fact by
91
+ * voting on it — the exact behaviour the retention policy exists to prevent.
92
+ */
93
+ export declare const MODEL_REMEMBER_ACTIONS: readonly ["add", "update", "remove"];
94
+ export declare const ModelRememberUnion: z.ZodDiscriminatedUnion<readonly z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], string>;
81
95
  export declare const RECALL_ACTIONS: readonly ["search", "ask", "chain", "probe", "reason", "related", "contradict"];
82
96
  export declare const RecallUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
83
97
  action: z.ZodLiteral<"search">;
@@ -125,7 +139,14 @@ export declare const RecallUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
125
139
  limit: z.ZodOptional<z.ZodNumber>;
126
140
  }, z.core.$strip>], "action">;
127
141
  export type RecallRequest = z.infer<typeof RecallUnion>;
128
- export declare const ADMIN_ACTIONS: readonly ["stats", "list", "detail", "archive", "restore", "pin", "unpin", "trust_diagnose", "vectors_diagnose", "vectors_fix", "contradict_check", "contradict_resolve", "maintenance"];
142
+ /**
143
+ * The retrieval actions the model may call. `chain` and `related` are operator graph views
144
+ * (the settings page and the CLI use them); the model reaches the same facts through
145
+ * `search`/`probe`/`ask`/`reason`, so the two graph shapes are not part of its tool.
146
+ */
147
+ export declare const MODEL_RECALL_ACTIONS: readonly ["search", "ask", "probe", "reason", "contradict"];
148
+ export declare const ModelRecallUnion: z.ZodDiscriminatedUnion<readonly z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], string>;
149
+ export declare const ADMIN_ACTIONS: readonly ["stats", "list", "detail", "archive", "restore", "pin", "unpin", "trust_diagnose", "vectors_diagnose", "vectors_fix", "contradict_check", "contradict_resolve", "maintenance", "export", "import", "merge", "restore_snapshot"];
129
150
  export declare const AdminUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
130
151
  action: z.ZodLiteral<"stats">;
131
152
  }, z.core.$strip>, z.ZodObject<{
@@ -177,8 +198,41 @@ export declare const AdminUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{
177
198
  loser_fact_id: z.ZodOptional<z.ZodNumber>;
178
199
  }, z.core.$strip>, z.ZodObject<{
179
200
  action: z.ZodLiteral<"maintenance">;
201
+ }, z.core.$strip>, z.ZodObject<{
202
+ action: z.ZodLiteral<"export">;
203
+ status: z.ZodOptional<z.ZodEnum<{
204
+ active: "active";
205
+ archived: "archived";
206
+ all: "all";
207
+ }>>;
208
+ file: z.ZodOptional<z.ZodString>;
209
+ }, z.core.$strip>, z.ZodObject<{
210
+ action: z.ZodLiteral<"import">;
211
+ file: z.ZodString;
212
+ dry_run: z.ZodOptional<z.ZodBoolean>;
213
+ }, z.core.$strip>, z.ZodObject<{
214
+ action: z.ZodLiteral<"merge">;
215
+ file: z.ZodString;
216
+ dry_run: z.ZodOptional<z.ZodBoolean>;
217
+ }, z.core.$strip>, z.ZodObject<{
218
+ action: z.ZodLiteral<"restore_snapshot">;
219
+ file: z.ZodString;
220
+ confirm: z.ZodLiteral<true>;
221
+ dry_run: z.ZodOptional<z.ZodBoolean>;
180
222
  }, z.core.$strip>], "action">;
181
223
  export type AdminRequest = z.infer<typeof AdminUnion>;
224
+ /**
225
+ * The admin actions the model may call: a fact page plus the backup/restore family.
226
+ *
227
+ * Everything else is OPERATOR machinery: diagnostics and repairs (the model cannot see trust values
228
+ * and must not rerun the embedding strategy), curation (pin/archive/contradiction verdicts), and
229
+ * duplicates of what is already model-facing (`detail` is a single-fact view of the same page;
230
+ * `contradict_check` is the write-side half of `mem_recall.contradict`; `merge` is what `import`
231
+ * now does automatically for a SQLite file). The full union stays for the runtime, the Remote
232
+ * gateway (the settings page drives every action) and the CLI.
233
+ */
234
+ export declare const MODEL_ADMIN_ACTIONS: readonly ["list", "export", "import", "restore_snapshot"];
235
+ export declare const ModelAdminUnion: z.ZodDiscriminatedUnion<readonly z.core.$ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>[], string>;
182
236
  /** The `source` a write gets when it omits one; a document's identity is `(domain, source, title)`. */
183
237
  export declare const DEFAULT_KB_SOURCE = "default";
184
238
  export declare const KbUnion: z.ZodDiscriminatedUnion<[z.ZodObject<{