dsh-mnemon-source-memory-spaces 0.5.20 → 0.5.22

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.
@@ -0,0 +1,365 @@
1
+ import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { createHash, randomUUID } from "node:crypto";
4
+ //#region src/mnemon-store-identity.ts
5
+ /**
6
+ * What a Mnemon store holds, as opposed to how it was used, and the merge of two versions of one.
7
+ * The public `./space-identity` entry exposes it; its limits and test hooks stay here.
8
+ *
9
+ * Mnemon writes to a space whenever it is read: a recall, search or show raises
10
+ * `access_count`, stamps `last_accessed_at` and logs the read in `oplog`. Two machines
11
+ * that only read the same space therefore never hold the same `mnemon.db` bytes, and a
12
+ * merge that compares bytes kept the other machine's space as a new one on every sync,
13
+ * doubling the spaces each round (#370). Spaces are compared by their content instead:
14
+ * every table, column and row, the edges included, except the usage columns, the read log
15
+ * and the derived embedding and effective importance. An edge is the two memories it links
16
+ * and its type, its table's key: a merge of spaces or a new link writes its weight, note and
17
+ * time again, so the same edge with other values is still the same edge (#391). A memory's
18
+ * update time is left out too: filling in its vector or keeping it from pruning moves it, while
19
+ * an edit or a forget changes what is compared.
20
+ *
21
+ * Rows are compared by their columns in name order, so the order a table declares them in
22
+ * does not matter. One version contains another when it holds every row of it. Two versions
23
+ * of one space merge by adding to this machine's database the rows only the other holds:
24
+ * this machine's rows, read counts, read log and schema stay as they are, and the result holds
25
+ * the memories of both; an edge both hold keeps this machine's values. A row both sides hold
26
+ * differently, a memory edited or forgotten on one of them, refuses the merge, and the caller
27
+ * keeps a copy instead.
28
+ *
29
+ * It needs `node:sqlite` (Node 22.13, 23.4 and later). Without it, for bytes that are not a
30
+ * readable database, for a database with a virtual table, or past the row limits, the identity
31
+ * is the bytes' digest, and such a store contains and merges with no other, so a caller keeps
32
+ * both versions rather than losing a memory.
33
+ */
34
+ /** Written by reading a space: the read log. */
35
+ const USAGE_TABLES = /* @__PURE__ */ new Set(["oplog"]);
36
+ /** Written by reading a space, or derived from what it holds. */
37
+ const USAGE_COLUMNS = /* @__PURE__ */ new Set([
38
+ "access_count",
39
+ "last_accessed_at",
40
+ "effective_importance",
41
+ "embedding"
42
+ ]);
43
+ /**
44
+ * Values a row carries beside what it holds, which Mnemon writes again without changing that
45
+ * (#391): an edge is its key, and an import or a new link writes its weight, note and time
46
+ * again; a memory's update time moves when its vector is filled in or it is kept from pruning.
47
+ */
48
+ const ROW_VALUES = /* @__PURE__ */ new Map([["edges", /* @__PURE__ */ new Set([
49
+ "weight",
50
+ "metadata",
51
+ "created_at"
52
+ ])], ["insights", /* @__PURE__ */ new Set(["updated_at"])]]);
53
+ const SCRATCH_PREFIX = ".dsh-space-identity-";
54
+ /**
55
+ * Past this many rows a store is compared by its bytes, and past the second limit, counted over
56
+ * all of one identity's reads, so is every store it reads after that. Its caller may hold a lock
57
+ * while it compares, so a crafted database must not take long: 120,000 real rows read in about
58
+ * 0.4 s.
59
+ */
60
+ const IDENTITY_MAX_ROWS = 5e5;
61
+ const MERGE_MAX_ROWS = 2e6;
62
+ function sqlite() {
63
+ try {
64
+ return process.getBuiltinModule?.("node:sqlite");
65
+ } catch {
66
+ return;
67
+ }
68
+ }
69
+ function quote(name) {
70
+ return "\"" + name.replaceAll("\"", "\"\"") + "\"";
71
+ }
72
+ /** A cell as JSON that keeps SQLite's types apart: 1, 1.0, '1', x'01' and NULL all differ. */
73
+ function cell(value) {
74
+ if (value instanceof Uint8Array) return { blob: Buffer.from(value).toString("base64") };
75
+ if (typeof value === "bigint") return { integer: String(value) };
76
+ if (typeof value === "number") return { real: Object.is(value, -0) ? "-0" : String(value) };
77
+ return value;
78
+ }
79
+ /** 128 bits of a row's digest: enough to tell rows apart, half the memory of the whole digest. */
80
+ function rowDigest(columns, row) {
81
+ return createHash("sha256").update(JSON.stringify(columns.map((column) => cell(row[column])))).digest("base64").slice(0, 22);
82
+ }
83
+ function digest(bytes) {
84
+ return createHash("sha256").update(bytes).digest("hex");
85
+ }
86
+ function sameBytes(left, right) {
87
+ return left === right || Buffer.from(left.buffer, left.byteOffset, left.byteLength).equals(right);
88
+ }
89
+ /** Cleanup never fails a merge: a copy another process holds open is swept by the next one. */
90
+ function remove(path) {
91
+ try {
92
+ rmSync(path, {
93
+ recursive: true,
94
+ force: true,
95
+ maxRetries: 3
96
+ });
97
+ } catch {}
98
+ }
99
+ /**
100
+ * Reads what each database holds once, and merges two of them. Make one per comparison or merge
101
+ * and dispose of it at the end. A process that stopped midway leaves its copies behind; the next
102
+ * identity on the same parent sweeps them before it makes its own.
103
+ */
104
+ var MnemonStoreIdentity = class {
105
+ parent;
106
+ scratch;
107
+ known = /* @__PURE__ */ new WeakMap();
108
+ projections = /* @__PURE__ */ new WeakMap();
109
+ module;
110
+ maxRows;
111
+ remaining;
112
+ /** `parent` holds the short-lived copies a database is read from. */
113
+ constructor(parent, options = {}) {
114
+ this.parent = parent;
115
+ this.module = options.sqlite ?? sqlite;
116
+ this.maxRows = options.maxRows ?? IDENTITY_MAX_ROWS;
117
+ this.remaining = options.maxMergeRows ?? MERGE_MAX_ROWS;
118
+ }
119
+ of(bytes) {
120
+ return this.contents(bytes)?.identity ?? "bytes:" + digest(bytes);
121
+ }
122
+ /** Whether `outer` holds every row `inner` holds; `outer` may have columns `inner` lacks. */
123
+ contains(outer, inner) {
124
+ if (sameBytes(outer, inner)) return true;
125
+ const whole = this.contents(outer);
126
+ const part = this.contents(inner);
127
+ if (whole === void 0 || part === void 0) return false;
128
+ for (const [name, table] of part.tables) {
129
+ const present = this.rowsOn(outer, whole, name, table.columns);
130
+ if (present === void 0) return false;
131
+ for (const row of table.rows) if (!present.has(row)) return false;
132
+ }
133
+ return true;
134
+ }
135
+ /**
136
+ * `local` with the rows only `incoming` holds added to it, or undefined when the two cannot
137
+ * merge: a row both hold differently, a table or column `local` lacks, or a database that
138
+ * cannot be read. `local` itself comes back when it already holds everything.
139
+ */
140
+ merge(local, incoming) {
141
+ if (sameBytes(local, incoming)) return local;
142
+ const mine = this.contents(local);
143
+ const theirs = this.contents(incoming);
144
+ if (mine === void 0 || theirs === void 0) return void 0;
145
+ const missing = /* @__PURE__ */ new Map();
146
+ for (const [name, table] of theirs.tables) {
147
+ const present = this.rowsOn(local, mine, name, table.columns);
148
+ if (present === void 0) return void 0;
149
+ const rows = new Set([...table.rows].filter((row) => !present.has(row)));
150
+ if (rows.size > 0) missing.set(name, rows);
151
+ }
152
+ if (missing.size === 0) return local;
153
+ const module = this.module();
154
+ const directory = this.directory();
155
+ if (module === void 0 || directory === void 0) return void 0;
156
+ const target = join(directory, randomUUID() + ".db");
157
+ const source = join(directory, randomUUID() + ".db");
158
+ let writer;
159
+ let reader;
160
+ try {
161
+ writeFileSync(target, local, { mode: 384 });
162
+ writeFileSync(source, incoming, { mode: 384 });
163
+ writer = this.open(module, target, false);
164
+ reader = this.open(module, source, true);
165
+ if (writer === void 0 || reader === void 0) return void 0;
166
+ writer.exec("BEGIN IMMEDIATE");
167
+ writer.exec("PRAGMA defer_foreign_keys = ON");
168
+ for (const [name, rows] of missing) {
169
+ const table = theirs.tables.get(name);
170
+ const own = mine.tables.get(name);
171
+ const written = table.all.filter((column) => own.all.includes(column));
172
+ const insert = writer.prepare(`INSERT INTO ${quote(name)} (${written.map(quote).join(", ")}) VALUES (${written.map(() => "?").join(", ")})`);
173
+ const statement = reader.prepare(`SELECT ${written.map(quote).join(", ")} FROM ${quote(name)}`);
174
+ statement.setReadBigInts(true);
175
+ for (const row of statement.iterate()) {
176
+ if (!this.spend()) throw new Error("the merge read limit was reached");
177
+ if (!rows.has(rowDigest(table.columns, row))) continue;
178
+ insert.run(...written.map((column) => row[column]));
179
+ }
180
+ }
181
+ writer.exec("COMMIT");
182
+ writer.exec("PRAGMA wal_checkpoint(TRUNCATE)");
183
+ writer.close();
184
+ writer = void 0;
185
+ return readFileSync(target);
186
+ } catch {
187
+ return;
188
+ } finally {
189
+ for (const database of [writer, reader]) try {
190
+ database?.close();
191
+ } catch {}
192
+ for (const path of [target, source]) for (const suffix of [
193
+ "",
194
+ "-wal",
195
+ "-shm",
196
+ "-journal"
197
+ ]) remove(path + suffix);
198
+ }
199
+ }
200
+ dispose() {
201
+ if (this.scratch !== void 0) remove(this.scratch);
202
+ this.scratch = void 0;
203
+ }
204
+ spend() {
205
+ this.remaining -= 1;
206
+ return this.remaining >= 0;
207
+ }
208
+ contents(bytes) {
209
+ const known = this.known.get(bytes);
210
+ if (known !== void 0) return known ?? void 0;
211
+ const contents = this.read(bytes);
212
+ this.known.set(bytes, contents ?? null);
213
+ return contents;
214
+ }
215
+ /** The digests of a table's rows over `columns`, which may leave out columns the table has. */
216
+ rowsOn(bytes, contents, name, columns) {
217
+ const table = contents.tables.get(name);
218
+ if (table === void 0 || !columns.every((column) => table.columns.includes(column))) return void 0;
219
+ if (columns.length === table.columns.length) return table.rows;
220
+ const key = JSON.stringify([name, columns]);
221
+ const cached = this.projections.get(bytes) ?? /* @__PURE__ */ new Map();
222
+ this.projections.set(bytes, cached);
223
+ if (!cached.has(key)) cached.set(key, this.withCopy(bytes, true, (database) => this.digests(database, name, columns) ?? null) ?? null);
224
+ return cached.get(key) ?? void 0;
225
+ }
226
+ digests(database, name, columns) {
227
+ const rows = /* @__PURE__ */ new Set();
228
+ if (columns.length === 0) return rows;
229
+ const statement = database.prepare(`SELECT ${columns.map(quote).join(", ")} FROM ${quote(name)}`);
230
+ statement.setReadBigInts(true);
231
+ let budget = this.maxRows;
232
+ for (const row of statement.iterate()) {
233
+ budget -= 1;
234
+ if (budget < 0 || !this.spend()) return void 0;
235
+ rows.add(rowDigest(columns, row));
236
+ }
237
+ return rows;
238
+ }
239
+ directory() {
240
+ try {
241
+ if (this.scratch === void 0) {
242
+ for (const name of readdirSync(this.parent)) if (name.startsWith(SCRATCH_PREFIX)) remove(join(this.parent, name));
243
+ }
244
+ if (this.scratch === void 0 || !existsSync(this.scratch)) this.scratch = mkdtempSync(join(this.parent, SCRATCH_PREFIX));
245
+ } catch {
246
+ return;
247
+ }
248
+ return this.scratch;
249
+ }
250
+ /** A connection to a private copy, refused when the copy is missing or empty rather than created. */
251
+ open(module, path, readOnly) {
252
+ if (!existsSync(path)) return void 0;
253
+ const database = new module.DatabaseSync(path, { readOnly });
254
+ database.exec("PRAGMA trusted_schema = OFF");
255
+ const pages = database.prepare("PRAGMA page_count").get();
256
+ if (Number(pages?.page_count ?? 0) <= 0) {
257
+ database.close();
258
+ return;
259
+ }
260
+ return database;
261
+ }
262
+ withCopy(bytes, readOnly, use) {
263
+ const module = this.module();
264
+ if (module === void 0) return void 0;
265
+ const directory = this.directory();
266
+ if (directory === void 0) return void 0;
267
+ const path = join(directory, randomUUID() + ".db");
268
+ let database;
269
+ try {
270
+ writeFileSync(path, bytes, { mode: 384 });
271
+ database = this.open(module, path, readOnly);
272
+ return database === void 0 ? void 0 : use(database);
273
+ } catch {
274
+ return;
275
+ } finally {
276
+ try {
277
+ database?.close();
278
+ } catch {}
279
+ for (const suffix of [
280
+ "",
281
+ "-wal",
282
+ "-shm",
283
+ "-journal"
284
+ ]) remove(path + suffix);
285
+ }
286
+ }
287
+ read(bytes) {
288
+ return this.withCopy(bytes, true, (database) => {
289
+ const listed = database.prepare("SELECT name, type FROM pragma_table_list WHERE schema = 'main'").all();
290
+ if (listed.some((entry) => entry.type !== "table" && entry.type !== "view")) return void 0;
291
+ const names = listed.filter((entry) => entry.type === "table" && !entry.name.startsWith("sqlite_") && !USAGE_TABLES.has(entry.name)).map((entry) => entry.name).sort();
292
+ const hash = createHash("sha256");
293
+ const tables = /* @__PURE__ */ new Map();
294
+ let budget = this.maxRows;
295
+ for (const name of names) {
296
+ const all = database.prepare(`PRAGMA table_info(${quote(name)})`).all().map((column) => column.name);
297
+ const columns = all.filter((column) => !USAGE_COLUMNS.has(column) && ROW_VALUES.get(name)?.has(column) !== true).sort();
298
+ const rows = [];
299
+ if (columns.length > 0) {
300
+ const statement = database.prepare(`SELECT ${columns.map(quote).join(", ")} FROM ${quote(name)}`);
301
+ statement.setReadBigInts(true);
302
+ for (const row of statement.iterate()) {
303
+ budget -= 1;
304
+ if (budget < 0 || !this.spend()) return void 0;
305
+ rows.push(rowDigest(columns, row));
306
+ }
307
+ }
308
+ rows.sort();
309
+ hash.update(JSON.stringify([
310
+ "table",
311
+ name,
312
+ columns,
313
+ rows.length
314
+ ]));
315
+ for (const row of rows) hash.update(row);
316
+ tables.set(name, {
317
+ columns,
318
+ all,
319
+ rows: new Set(rows)
320
+ });
321
+ }
322
+ return {
323
+ identity: "content:" + hash.digest("hex"),
324
+ tables
325
+ };
326
+ });
327
+ }
328
+ };
329
+ //#endregion
330
+ //#region src/space-identity.ts
331
+ /**
332
+ * Whether two copies of a Mnemon Native space hold the same memories, and the merge of two
333
+ * versions of one (#370, #391). Reading a space changes its bytes, so copies are compared by what
334
+ * they hold; mnemon-store-identity.ts says exactly what that is. Node-only: it reads stores with
335
+ * `node:sqlite` where Node has it and compares bytes where it does not.
336
+ */
337
+ var MemorySpaceIdentity = class {
338
+ #identity;
339
+ /** `parent` holds the short-lived copies a store is read from; dispose of the identity when done. */
340
+ constructor(parent) {
341
+ this.#identity = new MnemonStoreIdentity(parent);
342
+ }
343
+ /** The store's content identity, or the digest of its bytes when its content cannot be read. */
344
+ of(bytes) {
345
+ return this.#identity.of(bytes);
346
+ }
347
+ /** Whether `outer` holds every row `inner` holds; `outer` may have columns `inner` lacks. */
348
+ contains(outer, inner) {
349
+ return this.#identity.contains(outer, inner);
350
+ }
351
+ /**
352
+ * `local` with the rows only `incoming` holds added to it, or undefined when the two cannot
353
+ * merge: a row both hold differently, a table or column `local` lacks, or a store that cannot
354
+ * be read. `local` itself comes back when it already holds everything.
355
+ */
356
+ merge(local, incoming) {
357
+ return this.#identity.merge(local, incoming);
358
+ }
359
+ /** Remove the short-lived copies the identity made. */
360
+ dispose() {
361
+ this.#identity.dispose();
362
+ }
363
+ };
364
+ //#endregion
365
+ export { MemorySpaceIdentity };
package/lib/testing.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MemoryProviderCatalog, n as PrivateMemorySpaceProviderHost, t as createMemorySpaceProviderPlugin } from "./plugin-DlsNeZbQ.js";
1
+ import { a as MemoryProviderCatalog, n as PrivateMemorySpaceProviderHost, t as createMemorySpaceProviderPlugin } from "./plugin-m1PeU1IE.js";
2
2
  import { n as deepFreeze } from "./definitions-DfmOqJjR.js";
3
3
  import { Context } from "@deepseek-ai/cordis";
4
4
  //#region src/testing.ts
@@ -0,0 +1,10 @@
1
+ import type { JSX } from 'react';
2
+ import { type Insight } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ export declare function EntitiesPage(props: {
5
+ client: MemorySpacesPageClient;
6
+ revision: number;
7
+ writeEnabled: boolean;
8
+ onForget: (insight: Insight) => Promise<void>;
9
+ onExplore: (query: string) => void;
10
+ }): JSX.Element;
@@ -0,0 +1,16 @@
1
+ import type { JSX } from 'react';
2
+ import { type Insight } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ export interface MemorySpacesPageStatus {
5
+ defaultRecallLimit?: number;
6
+ }
7
+ export declare function ExplorePage(props: {
8
+ client: MemorySpacesPageClient;
9
+ agentClient: MemorySpacesPageClient;
10
+ agentAvailable: boolean;
11
+ status: MemorySpacesPageStatus | null;
12
+ seed: string;
13
+ writeEnabled: boolean;
14
+ onForget: (insight: Insight) => Promise<void>;
15
+ onRevealElement?(element: HTMLElement): void;
16
+ }): JSX.Element;
@@ -0,0 +1,11 @@
1
+ import type { JSX } from 'react';
2
+ import { type Insight } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ export declare function ListPage(props: {
5
+ client: MemorySpacesPageClient;
6
+ revision: number;
7
+ writeEnabled: boolean;
8
+ onForget: (insight: Insight) => Promise<void>;
9
+ onClone: (insight: Insight) => void;
10
+ onExplore: (query: string) => void;
11
+ }): JSX.Element;
@@ -0,0 +1,13 @@
1
+ import type { JSX } from 'react';
2
+ import { type MemorySpaceView, type MemoryGraphNode, type MemoryGraphSnapshot } from '../contracts.ts';
3
+ import { type MnemonTranslate } from 'dsh-mnemon/client';
4
+ export declare function graphNodeKey(node: MemoryGraphNode): string;
5
+ export declare function graphNodeKind(node: MemoryGraphNode): NonNullable<MemoryGraphNode['kind']>;
6
+ /** Add routing scopes and entity indexes without issuing another recall. */
7
+ export declare function enrichMultiSpaceGraph(graph: MemoryGraphSnapshot, bodies: MemorySpaceView[]): MemoryGraphSnapshot;
8
+ export declare function graphKindLabel(t: MnemonTranslate, node: MemoryGraphNode): string;
9
+ export declare function MemoryGraph(props: {
10
+ graph: MemoryGraphSnapshot;
11
+ selectedId?: string | undefined;
12
+ onSelect: (node: MemoryGraphNode) => void;
13
+ }): JSX.Element;
@@ -0,0 +1,20 @@
1
+ import type { JSX } from 'react';
2
+ import { type MemorySpaceMetadataUpdate, type MemorySpaceView } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ export declare function OverviewPage(props: {
5
+ client: MemorySpacesPageClient;
6
+ metadataClient: MemorySpacesPageClient;
7
+ revision: number;
8
+ reloadKey?: number;
9
+ activationEnabled: boolean;
10
+ writeEnabled: boolean;
11
+ agentAvailable: boolean;
12
+ fallbackBodies: MemorySpaceView[];
13
+ fallbackDirectory: string | undefined;
14
+ catalogKnown: boolean;
15
+ onMutate: () => void;
16
+ onAgentRefresh: () => void;
17
+ onBodyReconnect: (body: MemorySpaceView) => void;
18
+ onBodyMetadata: (updates: readonly MemorySpaceMetadataUpdate[]) => void;
19
+ onExplore: (query: string) => void;
20
+ }): JSX.Element;
@@ -1,76 +1,7 @@
1
- import type { JSX } from 'react';
2
- import { type Insight, type MemorySpaceMetadataUpdate, type MemorySpaceProvider, type MemorySpaceView } from '../contracts.ts';
3
- import type { MemorySpacesPageClient } from './api.ts';
4
- import type { MemoryPersistenceStrategy } from '../contracts.ts';
5
- export interface MemoryPlacementSettings {
6
- setPath(path: string[], value: unknown): Promise<void>;
7
- }
8
- export interface MemoryPlacementPageConfig {
9
- persistenceStrategy?: MemoryPersistenceStrategy;
10
- }
11
- export interface MemorySpacesPageStatus {
12
- defaultRecallLimit?: number;
13
- }
14
- export declare function nativeSpaceProvider(provider: MemorySpaceProvider): boolean;
15
- export declare function OverviewPage(props: {
16
- client: MemorySpacesPageClient;
17
- metadataClient: MemorySpacesPageClient;
18
- revision: number;
19
- reloadKey?: number;
20
- activationEnabled: boolean;
21
- writeEnabled: boolean;
22
- agentAvailable: boolean;
23
- fallbackBodies: MemorySpaceView[];
24
- fallbackDirectory: string | undefined;
25
- catalogKnown: boolean;
26
- onMutate: () => void;
27
- onAgentRefresh: () => void;
28
- onBodyReconnect: (body: MemorySpaceView) => void;
29
- onBodyMetadata: (updates: readonly MemorySpaceMetadataUpdate[]) => void;
30
- onExplore: (query: string) => void;
31
- }): JSX.Element;
32
- export declare function ExplorePage(props: {
33
- client: MemorySpacesPageClient;
34
- agentClient: MemorySpacesPageClient;
35
- agentAvailable: boolean;
36
- status: MemorySpacesPageStatus | null;
37
- seed: string;
38
- writeEnabled: boolean;
39
- onForget: (insight: Insight) => Promise<void>;
40
- onRevealElement?(element: HTMLElement): void;
41
- }): JSX.Element;
42
- export declare function EntitiesPage(props: {
43
- client: MemorySpacesPageClient;
44
- revision: number;
45
- writeEnabled: boolean;
46
- onForget: (insight: Insight) => Promise<void>;
47
- onExplore: (query: string) => void;
48
- }): JSX.Element;
49
- export declare function PersistenceStrategyDialog(props: {
50
- client: MemorySpacesPageClient;
51
- settingsScope: MemoryPlacementSettings;
52
- config: MemoryPlacementPageConfig | undefined;
53
- writable: boolean;
54
- agentAvailable: boolean;
55
- onClose: () => void;
56
- }): JSX.Element;
57
- export declare function RememberPage(props: {
58
- client: MemorySpacesPageClient;
59
- agentAvailable: boolean;
60
- memoryBodies: MemorySpaceView[];
61
- writeEnabled: boolean;
62
- seed: string;
63
- onMutate: () => void;
64
- onClose: () => void;
65
- onView?: () => void;
66
- }): JSX.Element;
67
- export declare function ListPage(props: {
68
- client: MemorySpacesPageClient;
69
- revision: number;
70
- writeEnabled: boolean;
71
- onForget: (insight: Insight) => Promise<void>;
72
- onClone: (insight: Insight) => void;
73
- onExplore: (query: string) => void;
74
- }): JSX.Element;
75
- /** @deprecated Use nativeSpaceProvider. */
76
- export declare const nativeBodyProvider: typeof nativeSpaceProvider;
1
+ export { OverviewPage } from './overview-page.tsx';
2
+ export { ExplorePage, type MemorySpacesPageStatus } from './explore-page.tsx';
3
+ export { EntitiesPage } from './entities-page.tsx';
4
+ export { PersistenceStrategyDialog, type MemoryPlacementPageConfig, type MemoryPlacementSettings } from './persistence-strategy-dialog.tsx';
5
+ export { RememberPage } from './remember-page.tsx';
6
+ export { ListPage } from './list-page.tsx';
7
+ export { nativeBodyProvider, nativeSpaceProvider } from './widgets.tsx';
@@ -0,0 +1,17 @@
1
+ import type { JSX } from 'react';
2
+ import { type MemoryPersistenceStrategy } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ export interface MemoryPlacementSettings {
5
+ setPath(path: string[], value: unknown): Promise<void>;
6
+ }
7
+ export interface MemoryPlacementPageConfig {
8
+ persistenceStrategy?: MemoryPersistenceStrategy;
9
+ }
10
+ export declare function PersistenceStrategyDialog(props: {
11
+ client: MemorySpacesPageClient;
12
+ settingsScope: MemoryPlacementSettings;
13
+ config: MemoryPlacementPageConfig | undefined;
14
+ writable: boolean;
15
+ agentAvailable: boolean;
16
+ onClose: () => void;
17
+ }): JSX.Element;
@@ -3,4 +3,32 @@ import type labels from 'dsh-mnemon-source-memory-spaces/presentation/locales.js
3
3
  export type SourceKey = keyof typeof labels.zh;
4
4
  export declare const css: Readonly<Record<string, string>>;
5
5
  export declare const sidebarCss: Readonly<Record<string, string>>;
6
- export declare function useT(): (key: SourceKey | MnemonKey, params?: Record<string, unknown>) => string;
6
+ type Translate = (key: SourceKey | MnemonKey, params?: Record<string, unknown>) => string;
7
+ /** DSH's language service, as a client context carries it. */
8
+ export interface DshLocale {
9
+ bind(namespace: 'mnemon'): (key: MnemonKey, params?: Record<string, unknown>) => string;
10
+ getSnapshot?(): DshLocaleSnapshot;
11
+ subscribe?(listener: () => void): () => void;
12
+ }
13
+ /** The part of DSH's locale snapshot a lookup chain needs. */
14
+ export interface DshLocaleSnapshot {
15
+ active: string;
16
+ locales: readonly {
17
+ id: string;
18
+ fallback?: string;
19
+ }[];
20
+ }
21
+ export declare const SourceTranslation: import("react").Provider<Translate | undefined>;
22
+ export declare function useT(): Translate;
23
+ /** The languages DSH asks for a key: the active one, then each one's fallback. */
24
+ export declare function languageChain(snapshot: DshLocaleSnapshot): string[];
25
+ /**
26
+ * Translate as dsh-mnemon drew these settings, through DSH's language chain
27
+ * and the `mnemon` namespace it registers this copy in. This copy answers in
28
+ * the first of zh and en the chain reaches; a language pack ahead of that one
29
+ * is asked first, through the namespace, as dsh-mnemon asked it; a key this
30
+ * copy lacks goes to the namespace too. Without DSH's chain the page's
31
+ * language picks the copy, as the page frame does.
32
+ */
33
+ export declare function settingsTranslate(locale: DshLocale, chain: readonly string[] | undefined, language: string): Translate;
34
+ export {};
@@ -0,0 +1,24 @@
1
+ import { type JSX } from 'react';
2
+ import type { MemoryProviderConfigField } from '../contracts.ts';
3
+ export interface ProviderFieldProps {
4
+ field: MemoryProviderConfigField;
5
+ value: string | number | boolean | undefined;
6
+ /** The Provider keeps a saved credential for this field; it never comes back to the page. */
7
+ saved: boolean;
8
+ /** In a dialog, the saved credential is being cleared, so a required field needs a value again. */
9
+ clearing?: boolean;
10
+ disabled?: boolean;
11
+ /**
12
+ * `settings`, a service field on the configuration page; `dialog`, a space's
13
+ * field in this Source's dialogs. Each draws a field and shows a saved
14
+ * credential as it always has: the configuration page fills the field with a
15
+ * mask that a typed value replaces, and can show what is typed; a dialog
16
+ * leaves the field empty with a note and can clear the credential.
17
+ */
18
+ layout: 'settings' | 'dialog';
19
+ onChange: (value: string | number | boolean) => void;
20
+ /** Offers clearing the saved credential, in a dialog that edits a space. */
21
+ onClearChange?: (clearing: boolean) => void;
22
+ }
23
+ /** One field of a Provider descriptor, wherever it is edited. */
24
+ export declare function ProviderField(props: ProviderFieldProps): JSX.Element;
@@ -0,0 +1,26 @@
1
+ import { type JSX, type ReactNode } from 'react';
2
+ import type { MemorySpacesSettingsClient } from './settings-client.ts';
3
+ interface ProviderSettingsSectionProps {
4
+ /** Absent while the page has no Host connection: nothing is read or written then. */
5
+ client?: MemorySpacesSettingsClient | undefined;
6
+ /** Where the list keeps the Providers it last read or saved: the host's store, which outlives a mount. */
7
+ retained: Map<string, unknown>;
8
+ workspaceLabel?: string;
9
+ activeScope: 'global' | 'workspace';
10
+ refreshKey: number;
11
+ disabled: boolean;
12
+ scopeChanging: boolean;
13
+ /** Rendered first in the list; Settings puts Mnemon Native here as a peer of the others. */
14
+ leading?: ReactNode;
15
+ /** Why the other Providers cannot be listed now; they are not read while it is set. */
16
+ blocked?: ReactNode;
17
+ /** Whether it is not yet known if the Providers can be listed; they are read once it is. */
18
+ pending?: boolean;
19
+ /** Why the listed Providers cannot be switched; rendered above them. */
20
+ notice?: ReactNode;
21
+ /** A Provider was saved or switched, so pages that list Providers re-read them. */
22
+ onChanged?: () => void;
23
+ }
24
+ /** The Providers that store Memory Spaces, one card each, with what their services need. */
25
+ export declare function ProviderSettingsSection(props: ProviderSettingsSectionProps): JSX.Element;
26
+ export {};
@@ -0,0 +1,15 @@
1
+ import type { JSX } from 'react';
2
+ import { type MemorySpaceView } from '../contracts.ts';
3
+ import type { MemorySpacesPageClient } from './api.ts';
4
+ /** `embeddingManaged`, from this Source's status, words a memory saved without a vector; left out, the receipt keeps the caution. */
5
+ export declare function RememberPage(props: {
6
+ client: MemorySpacesPageClient;
7
+ agentAvailable: boolean;
8
+ memoryBodies: MemorySpaceView[];
9
+ writeEnabled: boolean;
10
+ seed: string;
11
+ embeddingManaged?: boolean | undefined;
12
+ onMutate: () => void;
13
+ onClose: () => void;
14
+ onView?: () => void;
15
+ }): JSX.Element;