dsh-mnemon 0.5.12 → 0.5.13

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/lib/index.js CHANGED
@@ -19,6 +19,10 @@ import { findMnemonCommand, mnemonNpmLauncher, nodeLauncherEnvironment } from "d
19
19
  import { spawn } from "node:child_process";
20
20
  import { StringDecoder } from "node:string_decoder";
21
21
  import { z as z$1 } from "zod";
22
+ import { readFile, stat } from "node:fs/promises";
23
+ import { parseDocument } from "yaml";
24
+ import { isVolatile } from "@deepseek-ai/cosmokit";
25
+ import schemastery_live_default from "schemastery-live";
22
26
  import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
23
27
  //#region src/host/view-protocol.ts
24
28
  const MNEMON_VIEW_CHANNEL = "/dsh-mnemon-view";
@@ -85,6 +89,48 @@ const MNEMON_EMBEDDING_PROTOCOLS = [
85
89
  "openai"
86
90
  ];
87
91
  //#endregion
92
+ //#region src/host/view-preferences.ts
93
+ const schema = z.object({
94
+ strategyTypeId: z.string(),
95
+ entries: z.dict(z.object({
96
+ enabled: z.boolean(),
97
+ config: z.dict(z.any()).default({})
98
+ })).default({})
99
+ });
100
+ const legacySchema = z.object({ sources: z.dict(z.object({ enabled: z.boolean() })).default({}) });
101
+ const ENTRY_ID$1 = /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,299}$/u;
102
+ const clone$2 = (value) => JSON.parse(JSON.stringify(value));
103
+ function record$3(value) {
104
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Plugin configuration must be an object");
105
+ const json = JSON.stringify(value);
106
+ if (json.length > 65536) throw new Error("Plugin configuration exceeds 64 KiB");
107
+ const parsed = JSON.parse(json);
108
+ for (const key of Object.keys(parsed)) if ([
109
+ "__proto__",
110
+ "prototype",
111
+ "constructor"
112
+ ].includes(key)) throw new Error("Unsafe plugin configuration key");
113
+ return parsed;
114
+ }
115
+ function preferences(value) {
116
+ if (value.strategyTypeId !== void 0 && !/^[a-z][a-z0-9-]{0,127}$/u.test(value.strategyTypeId)) throw new Error("Invalid Strategy type id");
117
+ const entries = record$3(value.entries ?? {});
118
+ if (Object.keys(entries).length > 64) throw new Error("At most 64 memory plugin Entries can be configured");
119
+ for (const [entryId, item] of Object.entries(entries)) {
120
+ if (!ENTRY_ID$1.test(entryId)) throw new Error("Invalid memory plugin Entry id");
121
+ const candidate = record$3(item);
122
+ if (typeof candidate.enabled !== "boolean") throw new Error("Plugin enabled must be boolean");
123
+ record$3(candidate.config);
124
+ }
125
+ return clone$2(value);
126
+ }
127
+ function legacyPreferences(value) {
128
+ const sources = value.sources ?? {};
129
+ if (Object.keys(sources).length > 64) throw new Error("At most 64 legacy Source Entries can be configured");
130
+ for (const [entryId, item] of Object.entries(sources)) if (!ENTRY_ID$1.test(entryId) || typeof item?.enabled !== "boolean") throw new Error("Invalid legacy Source Entry preference");
131
+ return clone$2({ sources });
132
+ }
133
+ //#endregion
88
134
  //#region src/host/config-values.ts
89
135
  const DEFAULT_IDLE_REVIEW_MS = 3e4;
90
136
  const DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES = 10240;
@@ -221,6 +267,8 @@ const Config = z.object({
221
267
  protocol: DEFAULT_EMBEDDING_PROTOCOL
222
268
  }),
223
269
  memoryTopology: MemoryTopologySchema,
270
+ memoryView: schema,
271
+ legacySettingsImported: z.boolean(),
224
272
  recallQuality: RecallQualitySchema.default({
225
273
  policy: DEFAULT_RECALL_QUALITY_POLICY,
226
274
  lowScoreThreshold: DEFAULT_RECALL_LOW_SCORE_THRESHOLD,
@@ -372,6 +420,7 @@ function resolveMemoryTopology(value) {
372
420
  };
373
421
  }
374
422
  function resolveConfig(config = {}) {
423
+ if (config.memoryView !== void 0) preferences(config.memoryView);
375
424
  const cliPath = optionalText(config.cliPath);
376
425
  const legacyDataDir = optionalText(config.dataDir);
377
426
  const legacyPacks = resolveCustomPacks(config.customPacks, legacyDataDir);
@@ -444,7 +493,7 @@ const COMPONENT_ORDER = [
444
493
  ];
445
494
  const BODY_ID = /^[a-zA-Z0-9][a-zA-Z0-9_-]*$/;
446
495
  const SQLITE_HEADER = Buffer.from("SQLite format 3\0", "binary");
447
- function record$3(value) {
496
+ function record$2(value) {
448
497
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
449
498
  }
450
499
  function utf8(bytes, label) {
@@ -487,7 +536,7 @@ function componentsForScope(scope) {
487
536
  return [scope];
488
537
  }
489
538
  function parseManifest(value) {
490
- const manifest = record$3(value);
539
+ const manifest = record$2(value);
491
540
  if (manifest?.format !== "mnemonpack" || manifest.version !== 1) throw new Error("unsupported Mnemon Pack format or version");
492
541
  if (manifest.scope !== "full" && !COMPONENT_ORDER.includes(manifest.scope)) throw new Error("Mnemon Pack scope is invalid");
493
542
  if (typeof manifest.exportedAt !== "string") throw new Error("Mnemon Pack exportedAt is invalid");
@@ -496,11 +545,11 @@ function parseManifest(value) {
496
545
  if (components.length === 0 || new Set(components).size !== components.length || components.some((component) => !COMPONENT_ORDER.includes(component))) throw new Error("Mnemon Pack components are invalid");
497
546
  const expected = componentsForScope(manifest.scope);
498
547
  if (components.length !== expected.length || expected.some((component) => !components.includes(component))) throw new Error("Mnemon Pack scope does not match its components");
499
- const source = record$3(manifest.source);
548
+ const source = record$2(manifest.source);
500
549
  if (source?.plugin !== "dsh-mnemon" || typeof source.pluginVersion !== "string") throw new Error("Mnemon Pack source is invalid");
501
550
  if (!Array.isArray(manifest.summary)) throw new Error("Mnemon Pack summary is invalid");
502
551
  const summary = manifest.summary.map((entry) => {
503
- const item = record$3(entry);
552
+ const item = record$2(entry);
504
553
  const component = item?.component;
505
554
  if (!COMPONENT_ORDER.includes(component) || !Number.isSafeInteger(item?.files) || !Number.isSafeInteger(item?.bytes) || !Number.isSafeInteger(item?.items) || Number(item?.files) < 0 || Number(item?.bytes) < 0 || Number(item?.items) < 0) throw new Error("Mnemon Pack summary entry is invalid");
506
555
  return {
@@ -550,8 +599,8 @@ function parseArchive(base64, runtimeLimits = RUNTIME_MEMORY_LIMITS) {
550
599
  if (path !== "manifest.json" && path !== "checksums.json" && !allowedPayloadPath(path)) throw new Error(`unsupported Mnemon Pack entry: ${path}`);
551
600
  }
552
601
  const manifest = parseManifest(json$1(files["manifest.json"], "manifest.json"));
553
- const checksumValue = record$3(json$1(files["checksums.json"], "checksums.json"));
554
- const checksumFiles = record$3(checksumValue?.files);
602
+ const checksumValue = record$2(json$1(files["checksums.json"], "checksums.json"));
603
+ const checksumFiles = record$2(checksumValue?.files);
555
604
  if (checksumValue?.algorithm !== "sha256" || checksumFiles === void 0) throw new Error("Mnemon Pack checksums are invalid");
556
605
  const payloadNames = names.filter((path) => path.startsWith("payload/")).sort();
557
606
  if (Object.keys(checksumFiles).length !== payloadNames.length) throw new Error("Mnemon Pack checksum inventory does not match the payload");
@@ -574,10 +623,10 @@ function parseArchive(base64, runtimeLimits = RUNTIME_MEMORY_LIMITS) {
574
623
  };
575
624
  }
576
625
  function parseRuntime(value, limits = RUNTIME_MEMORY_LIMITS) {
577
- const source = record$3(value);
626
+ const source = record$2(value);
578
627
  if (source?.version !== RUNTIME_MEMORY_VERSION || !Array.isArray(source.entries)) throw new Error("runtime memories.json is invalid");
579
628
  const entries = source.entries.map((raw) => {
580
- const entry = record$3(raw);
629
+ const entry = record$2(raw);
581
630
  if (typeof entry?.content !== "string" || entry.target !== "memory" && entry.target !== "user" || ![
582
631
  "critical",
583
632
  "normal",
@@ -608,13 +657,13 @@ function runtimeProjection(entries, target) {
608
657
  return content === "" ? "" : `${content}\n`;
609
658
  }
610
659
  function parseDocumentIndex(value) {
611
- const index = record$3(value);
660
+ const index = record$2(value);
612
661
  if (index?.version !== DOCUMENTS_VERSION || !Array.isArray(index.documents)) throw new Error("Documents index.json is invalid");
613
662
  const ids = /* @__PURE__ */ new Set();
614
663
  return {
615
664
  version: 1,
616
665
  documents: index.documents.map((raw) => {
617
- const item = record$3(raw);
666
+ const item = record$2(raw);
618
667
  if (typeof item?.id !== "string" || item.id.trim() === "" || ids.has(item.id)) throw new Error("Documents index contains an invalid or duplicate id");
619
668
  if (typeof item.title !== "string" || typeof item.description !== "string" || item.status !== "active" && item.status !== "archived") throw new Error("Documents index contains an invalid record");
620
669
  if (typeof item.filename !== "string" || basename(item.filename) !== item.filename || !/^[a-zA-Z0-9._-]+\.md$/u.test(item.filename)) throw new Error("Documents index contains an unsafe filename");
@@ -644,13 +693,13 @@ function parseDocumentIndex(value) {
644
693
  };
645
694
  }
646
695
  function parseRegistry(value) {
647
- const registry = record$3(value);
696
+ const registry = record$2(value);
648
697
  if (registry?.version !== 1 || !Array.isArray(registry.bodies)) throw new Error("Memory Space registry is invalid");
649
698
  const ids = /* @__PURE__ */ new Set();
650
699
  return {
651
700
  version: 1,
652
701
  bodies: registry.bodies.map((raw) => {
653
- const body = record$3(raw);
702
+ const body = record$2(raw);
654
703
  if (typeof body?.id !== "string" || !BODY_ID.test(body.id) || ids.has(body.id)) throw new Error("Memory Space registry contains an invalid or duplicate id");
655
704
  if (typeof body.name !== "string" || body.name.trim() === "" || body.name.length > 100 || typeof body.description !== "string" || body.description.length > 1e3) throw new Error("Memory Space registry contains invalid metadata");
656
705
  if (typeof body.createdAt !== "string" || typeof body.updatedAt !== "string") throw new Error("Memory Space registry contains invalid timestamps");
@@ -1220,14 +1269,24 @@ function canonicalWorkspacePath(workspacePath) {
1220
1269
  if (!isAbsolute(workspacePath) || workspacePath.includes("\0")) throw new Error("Workspace path must be absolute");
1221
1270
  let parent = resolve(workspacePath);
1222
1271
  const suffix = [];
1223
- for (;;) try {
1224
- return join(realpathSync.native(parent), ...suffix);
1225
- } catch (error) {
1226
- if (error.code !== "ENOENT") throw error;
1227
- const next = dirname(parent);
1228
- if (next === parent) throw error;
1229
- suffix.unshift(parent.slice(next.length).replace(/^[/\\]+/u, ""));
1230
- parent = next;
1272
+ for (;;) {
1273
+ let real;
1274
+ try {
1275
+ real = realpathSync.native(parent);
1276
+ } catch (error) {
1277
+ if (error.code !== "ENOENT") throw error;
1278
+ const next = dirname(parent);
1279
+ if (next === parent) throw error;
1280
+ suffix.unshift(parent.slice(next.length).replace(/^[/\\]+/u, ""));
1281
+ parent = next;
1282
+ continue;
1283
+ }
1284
+ if (suffix.length > 0 && !statSync(real).isDirectory()) throw Object.assign(/* @__PURE__ */ new Error(`ENOTDIR: not a directory, realpath '${parent}'`), {
1285
+ code: "ENOTDIR",
1286
+ path: parent,
1287
+ syscall: "realpath"
1288
+ });
1289
+ return join(real, ...suffix);
1231
1290
  }
1232
1291
  }
1233
1292
  /** A rename or move selects a new directory; resolving identity never writes. */
@@ -1283,7 +1342,7 @@ function safeBytes(path) {
1283
1342
  function readJson(path) {
1284
1343
  return JSON.parse(readFileSync(path, "utf8"));
1285
1344
  }
1286
- function record$2(value) {
1345
+ function record$1(value) {
1287
1346
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1288
1347
  }
1289
1348
  function missing(kind, path) {
@@ -1301,9 +1360,9 @@ function runtimeArea(root) {
1301
1360
  const source = join(path, "memories.json");
1302
1361
  if (!existsSync(source)) return missing("runtime", path);
1303
1362
  try {
1304
- const file = record$2(readJson(source));
1363
+ const file = record$1(readJson(source));
1305
1364
  if (file === void 0 || !Array.isArray(file.entries)) throw new Error("memories.json is not a valid runtime-memory source");
1306
- const entries = file.entries.map(record$2).filter((entry) => entry !== void 0);
1365
+ const entries = file.entries.map(record$1).filter((entry) => entry !== void 0);
1307
1366
  const userEntries = entries.filter((entry) => entry.target === "user").length;
1308
1367
  const memoryEntries = entries.filter((entry) => entry.target === "memory").length;
1309
1368
  const projectionsHealthy = existsSync(join(path, "USER.md")) && existsSync(join(path, "MEMORY.md"));
@@ -1338,8 +1397,8 @@ function memorySpacesArea(root) {
1338
1397
  if (!existsSync(path)) return missing("memory-bodies", path);
1339
1398
  try {
1340
1399
  const registryPath = join(path, ".dsh-memory-bodies.json");
1341
- const registry = existsSync(registryPath) ? record$2(readJson(registryPath)) : void 0;
1342
- const bodies = Array.isArray(registry?.bodies) ? registry.bodies.map(record$2).filter((body) => body !== void 0) : [];
1400
+ const registry = existsSync(registryPath) ? record$1(readJson(registryPath)) : void 0;
1401
+ const bodies = Array.isArray(registry?.bodies) ? registry.bodies.map(record$1).filter((body) => body !== void 0) : [];
1343
1402
  const databaseCount = readdirSync(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && existsSync(join(path, entry.name, "mnemon.db"))).length;
1344
1403
  const activeCount = bodies.filter((body) => body.active === true).length;
1345
1404
  const invalidRegistry = existsSync(registryPath) && (registry?.version !== 1 || !Array.isArray(registry.bodies));
@@ -1374,9 +1433,9 @@ function documentsArea(root) {
1374
1433
  const indexPath = join(path, "index.json");
1375
1434
  if (!existsSync(indexPath)) return missing("documents", path);
1376
1435
  try {
1377
- const index = record$2(readJson(indexPath));
1436
+ const index = record$1(readJson(indexPath));
1378
1437
  if (index === void 0 || !Array.isArray(index.documents)) throw new Error("index.json is not a valid Documents index");
1379
- const documents = index.documents.map(record$2).filter((document) => document !== void 0);
1438
+ const documents = index.documents.map(record$1).filter((document) => document !== void 0);
1380
1439
  const active = documents.filter((document) => document.status === "active").length;
1381
1440
  const archived = documents.filter((document) => document.status === "archived").length;
1382
1441
  return {
@@ -1412,9 +1471,9 @@ function stateArea(root) {
1412
1471
  let providerConnections = 0;
1413
1472
  let providerServices = 0;
1414
1473
  if (existsSync(providerRegistry)) try {
1415
- const registry = record$2(readJson(providerRegistry));
1474
+ const registry = record$1(readJson(providerRegistry));
1416
1475
  providerConnections = Array.isArray(registry?.bodies) ? registry.bodies.length : 0;
1417
- providerServices = record$2(registry?.services) === void 0 ? 0 : Object.keys(record$2(registry?.services)).length;
1476
+ providerServices = record$1(registry?.services) === void 0 ? 0 : Object.keys(record$1(registry?.services)).length;
1418
1477
  } catch {}
1419
1478
  return {
1420
1479
  kind: "state",
@@ -2723,13 +2782,13 @@ async function startGuardedReview(host, parent, toolNames, start, published) {
2723
2782
  }
2724
2783
  //#endregion
2725
2784
  //#region src/host/review-checkpoint.ts
2726
- function record$1(value) {
2785
+ function record(value) {
2727
2786
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
2728
2787
  }
2729
2788
  function textContent(content, toolResult = false) {
2730
2789
  if (!Array.isArray(content)) return "";
2731
2790
  return content.map((block) => {
2732
- const value = record$1(block);
2791
+ const value = record(block);
2733
2792
  if (value.type === "text" && typeof value.text === "string") return value.text;
2734
2793
  if (toolResult && value.type === "tool-result" && value.isError !== true) return textContent(value.content);
2735
2794
  return "";
@@ -2754,8 +2813,8 @@ function reviewCheckpoint(session, maxChars) {
2754
2813
  "assistant/message",
2755
2814
  "tool/result"
2756
2815
  ].includes(event.type)) continue;
2757
- const message = record$1(event.data.message);
2758
- const source = record$1(message.source);
2816
+ const message = record(event.data.message);
2817
+ const source = record(message.source);
2759
2818
  if (event.type === "user/message" && source.kind !== "user") continue;
2760
2819
  const text = textContent(message.content, event.type === "tool/result");
2761
2820
  if (!text) continue;
@@ -2871,7 +2930,7 @@ var IdleReviewError = class extends Error {
2871
2930
  };
2872
2931
  function reviewReceipt(receipt) {
2873
2932
  if (!mutationResultCommitted(receipt.value)) return void 0;
2874
- const value = object$4(receipt.value);
2933
+ const value = object$6(receipt.value);
2875
2934
  const args = optionalObject(receipt.arguments);
2876
2935
  const document = optionalObject(value.document);
2877
2936
  return {
@@ -3131,7 +3190,7 @@ function assertDshOutputValue(schema, candidate, path = "result") {
3131
3190
  default: throw new Error(`${path} uses unsupported schema type ${JSON.stringify(value.type)}`);
3132
3191
  }
3133
3192
  }
3134
- function object$4(value) {
3193
+ function object$6(value) {
3135
3194
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("memory subagent returned an invalid structured result");
3136
3195
  return value;
3137
3196
  }
@@ -3484,7 +3543,7 @@ var MnemonSubagentCoordinator = class {
3484
3543
  if (!isSubagent(execution.agent)) throw new Error("Mnemon subagent result tools are restricted to delegated children");
3485
3544
  execution.signal.throwIfAborted();
3486
3545
  assertDshOutputValue(RESULT_TOOL_INPUT_SCHEMA, args);
3487
- const request = object$4(args);
3546
+ const request = object$6(args);
3488
3547
  const submit = this.resultRequests.get(String(request.requestId));
3489
3548
  if (this.disposed || submit === void 0) throw new Error("Mnemon subagent result request is unknown or no longer active");
3490
3549
  return submit(request.result, execution);
@@ -3615,7 +3674,7 @@ var MnemonSubagentCoordinator = class {
3615
3674
  ].join("\n\n");
3616
3675
  const schema = providerPlacementSchema(prepared.candidates.map((candidate) => candidate.id));
3617
3676
  const { provider, runId, result } = await this.delegate(parent, "placement", "Choose Memory Space provider", prompt, [], schema, signal, "spawn", PROVIDER_PLACEMENT_PERSONA);
3618
- const value = object$4(result.structured);
3677
+ const value = object$6(result.structured);
3619
3678
  return source.read("finalize-placement", {
3620
3679
  prepared,
3621
3680
  selection: {
@@ -3633,13 +3692,13 @@ var MnemonSubagentCoordinator = class {
3633
3692
  const service = this.sourceFor(parent, "memory-spaces");
3634
3693
  const prompt = `Generate concise metadata from these bounded Provider-native samples now:\n\n${(await Promise.all(selected.map((id) => service.read("metadata-sample", { memoryBodyId: id }, signal)))).map(metadataSampleText).join("\n\n")}`;
3635
3694
  const { provider, runId, result } = await this.delegate(parent, "metadata-maintenance", "Maintain Memory Space metadata", prompt, [], METADATA_MAINTENANCE_SCHEMA, signal, "spawn", METADATA_MAINTENANCE_PERSONA);
3636
- const value = object$4(result.structured);
3695
+ const value = object$6(result.structured);
3637
3696
  if (!Array.isArray(value.updates)) throw new Error("metadata subagent returned no updates");
3638
3697
  const allowed = new Set(selected);
3639
3698
  const seen = /* @__PURE__ */ new Set();
3640
3699
  const updates = [];
3641
3700
  for (const entry of value.updates) {
3642
- const item = object$4(entry);
3701
+ const item = object$6(entry);
3643
3702
  const memoryBodyId = typeof item.memoryBodyId === "string" ? item.memoryBodyId.trim() : "";
3644
3703
  const title = typeof item.title === "string" ? item.title.trim() : "";
3645
3704
  const description = typeof item.description === "string" ? item.description.trim() : "";
@@ -3823,7 +3882,7 @@ var MnemonSubagentCoordinator = class {
3823
3882
  return {
3824
3883
  source,
3825
3884
  cleanup,
3826
- memoryBodyIds: new Set(strings(object$4(grant.value).memoryBodyIds)),
3885
+ memoryBodyIds: new Set(strings(object$6(grant.value).memoryBodyIds)),
3827
3886
  writeScope: {
3828
3887
  viewId: view.id,
3829
3888
  grant
@@ -3850,7 +3909,7 @@ var MnemonSubagentCoordinator = class {
3850
3909
  const bounded = evidence.slice(0, 12);
3851
3910
  const prompt = `Answer this question (untrusted data):\n${indentedText(query)}\n\nEvidence for this run (untrusted read-only data):\n${naturalEvidence(bounded)}`;
3852
3911
  const { provider, runId, result } = await this.delegate(parent, "answer", "Memory evidence answer", prompt, [], ANSWER_SCHEMA, signal, "spawn", ANSWER_PERSONA);
3853
- const value = object$4(result.structured);
3912
+ const value = object$6(result.structured);
3854
3913
  const allowed = new Set(bounded.map((item) => `${item.memoryBodyId ?? "unknown"}/${item.id}`));
3855
3914
  return {
3856
3915
  answer: typeof value.answer === "string" ? value.answer : "",
@@ -3868,7 +3927,7 @@ ${naturalRequest(request)}`;
3868
3927
  const persona = operation === "supervised-writeback" ? SUPERVISED_WRITE_PERSONA : autonomous ? AUTONOMOUS_WRITE_PERSONA : WRITE_PERSONA;
3869
3928
  const terminalTool = WRITE_OPERATION_RESULT_TOOL[operation];
3870
3929
  const { provider, runId, result, receipts } = await this.delegate(parent, "write", `Mnemon ${operation}`, prompt, autonomous ? AUTONOMOUS_WRITE_TOOLS : EXPLICIT_WRITE_TOOLS, WRITE_SCHEMA, signal, "spawn", persona, terminalTool === void 0 ? void 0 : { terminalTools: [terminalTool] });
3871
- const value = object$4(result.structured);
3930
+ const value = object$6(result.structured);
3872
3931
  const observed = terminalTool === void 0 ? void 0 : recoverStructuredResult({ terminalTools: [terminalTool] }, receipts);
3873
3932
  return {
3874
3933
  delegated: true,
@@ -3903,7 +3962,7 @@ ${naturalRequest(request)}`;
3903
3962
  const prompt = preferred === "fork" ? "Review the inherited completed checkpoint now." : reviewCheckpoint(parent.session, config.maxContextChars);
3904
3963
  const persona = preferred === "fork" ? REVIEW_PERSONA : REVIEW_PERSONA.replaceAll("inherited", "supplied bounded");
3905
3964
  const { provider, runId, result } = await this.delegate(parent, "review", "Mnemon idle checkpoint review", prompt, REVIEW_TOOLS, WRITE_SCHEMA, signal, preferred, persona, { terminalTools: ["mnemon_runtime_memory", "mnemon_document_create"] });
3906
- const value = object$4(result.structured);
3965
+ const value = object$6(result.structured);
3907
3966
  return {
3908
3967
  delegated: true,
3909
3968
  runId,
@@ -3965,12 +4024,12 @@ ${naturalRequest(request)}`;
3965
4024
  const cleanup = graph.source("memory-spaces", agentScope(parent, graph.config)).forInstance(identity.sourceInstanceKey).forGeneration(lease.generation);
3966
4025
  const grant = authority?.context.view.readGrants.find((grant) => grant.sourceInstanceKey === identity.sourceInstanceKey && grant.schema === "dsh-mnemon.memory-spaces/v1");
3967
4026
  if (authority !== void 0 && grant === void 0) throw new Error("document archive requires a Memory Space namespace grant");
3968
- const allowed = grant === void 0 ? void 0 : new Set(strings(object$4(grant.value).memoryBodyIds));
4027
+ const allowed = grant === void 0 ? void 0 : new Set(strings(object$6(grant.value).memoryBodyIds));
3969
4028
  const eligible = (body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember && body.provider.capabilities.forget && body.provider.capabilities.writeMode === "exact" && (allowed === void 0 || allowed.has(body.id));
3970
4029
  const bodies = (await memoryService.read("body-directory", null, signal)).items.filter(eligible);
3971
4030
  if (bodies.length === 0) throw new Error("document archive requires an existing active Memory Space with exact writes and safe forget");
3972
4031
  const { provider, runId, result } = await this.delegate(parent, "document-archive", "Plan managed document archive", documentArchivePrompt(document, source, bodies), [], DOCUMENT_ARCHIVE_SCHEMA, signal, "spawn", DOCUMENT_ARCHIVE_PERSONA);
3973
- const value = object$4(result.structured);
4032
+ const value = object$6(result.structured);
3974
4033
  const summary = typeof value.summary === "string" ? value.summary.trim() : "";
3975
4034
  if (value.action !== "planned") throw new Error(summary || "document archive planning failed");
3976
4035
  if (summary === "" || summary.length > 1e3) throw new Error("document archive summary must contain 1–1000 characters");
@@ -4145,13 +4204,13 @@ ${chunk.context}
4145
4204
  provider = delegated.provider;
4146
4205
  runId = delegated.runId;
4147
4206
  }
4148
- const value = object$4(delegated.result.structured);
4207
+ const value = object$6(delegated.result.structured);
4149
4208
  if (value.action !== "planned") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "runtime memory archival routing failed");
4150
4209
  if (!Array.isArray(value.routes) || value.routes.length === 0) throw new Error("runtime memory migration returned no routes");
4151
4210
  const allowedIndexes = new Set(chunk.indexes);
4152
4211
  const proposed = /* @__PURE__ */ new Map();
4153
4212
  for (const candidate of value.routes) {
4154
- const route = object$4(candidate);
4213
+ const route = object$6(candidate);
4155
4214
  const memoryBodyId = typeof route.memoryBodyId === "string" ? route.memoryBodyId.trim() : "";
4156
4215
  if (memoryBodyId === "" || !eligibleById.has(memoryBodyId)) throw new Error(`runtime memory migration selected an invalid Memory Space: ${memoryBodyId || "(empty)"}`);
4157
4216
  if (!Array.isArray(route.sourceIndexes) || route.sourceIndexes.length === 0) throw new Error("runtime memory migration route must contain source indexes");
@@ -4299,10 +4358,10 @@ ${pendingMutationContext(plan)}
4299
4358
 
4300
4359
  ${runtimeSnapshotContext("user", plan.entries)}`;
4301
4360
  const { provider, runId, result } = await context.model("compaction", "Consolidate local user profile", prompt, USER_COMPACTION_SCHEMA, USER_COMPACTION_PERSONA);
4302
- const value = object$4(result.structured);
4361
+ const value = object$6(result.structured);
4303
4362
  if (value.action !== "compacted") throw new Error(typeof value.summary === "string" && value.summary !== "" ? value.summary : "USER.md compaction failed");
4304
4363
  const compactedEntries = Array.isArray(value.compactedEntries) ? value.compactedEntries.map((entry) => {
4305
- const item = object$4(entry);
4364
+ const item = object$6(entry);
4306
4365
  if (typeof item.content !== "string" || ![
4307
4366
  "critical",
4308
4367
  "normal",
@@ -4515,7 +4574,7 @@ This is the only completion channel for this run. Do not finish with a plain-tex
4515
4574
  "failed",
4516
4575
  "partial",
4517
4576
  "unknown"
4518
- ].includes(String(object$4(structured).action))) throw new Error(`memory review reported ${object$4(structured).action}`);
4577
+ ].includes(String(object$6(structured).action))) throw new Error(`memory review reported ${object$6(structured).action}`);
4519
4578
  completed = {
4520
4579
  provider,
4521
4580
  runId: activeRun.id,
@@ -4680,7 +4739,7 @@ const MNEMON_ACTIVITY_SCHEMA = "dsh-mnemon.activity/v1";
4680
4739
  const MAX_ITEMS = 8;
4681
4740
  const MAX_TITLE = 160;
4682
4741
  const MAX_EXCERPT = 500;
4683
- function object$3(value) {
4742
+ function object$5(value) {
4684
4743
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
4685
4744
  }
4686
4745
  function bounded(value, maximum) {
@@ -4690,7 +4749,7 @@ function bounded(value, maximum) {
4690
4749
  return normalized.length <= maximum ? normalized : normalized.slice(0, maximum - 1) + "…";
4691
4750
  }
4692
4751
  function item(value, index) {
4693
- const entry = object$3(value);
4752
+ const entry = object$5(value);
4694
4753
  if (entry === void 0) return void 0;
4695
4754
  const content = bounded(entry.content ?? entry.text ?? entry.excerpt, MAX_EXCERPT);
4696
4755
  const explicitTitle = bounded(entry.title ?? entry.label ?? entry.name ?? entry.memoryBodyName, MAX_TITLE);
@@ -4704,7 +4763,7 @@ function item(value, index) {
4704
4763
  };
4705
4764
  }
4706
4765
  function resultItems(value) {
4707
- const root = object$3(value);
4766
+ const root = object$5(value);
4708
4767
  if (root === void 0) return [];
4709
4768
  const candidates = [
4710
4769
  root.results,
@@ -4723,8 +4782,8 @@ function resultItems(value) {
4723
4782
  return items;
4724
4783
  }
4725
4784
  function writeItem(argsValue, operationId) {
4726
- const outer = object$3(argsValue) ?? {};
4727
- const input = object$3(outer.input) ?? outer;
4785
+ const outer = object$5(argsValue) ?? {};
4786
+ const input = object$5(outer.input) ?? outer;
4728
4787
  const content = bounded(input.content ?? input.description ?? input.reason ?? input.summary, MAX_EXCERPT);
4729
4788
  const explicitTitle = bounded(input.title ?? input.name, MAX_TITLE);
4730
4789
  const identity = bounded(input.id ?? input.memoryBodyId ?? input.targetMemoryBodyId ?? input.sourceId, 300);
@@ -4737,7 +4796,7 @@ function writeItem(argsValue, operationId) {
4737
4796
  };
4738
4797
  }
4739
4798
  function reference(argsValue, key) {
4740
- return bounded(object$3(argsValue)?.[key], 600);
4799
+ return bounded(object$5(argsValue)?.[key], 600);
4741
4800
  }
4742
4801
  function memoryReadPresentation(sourceTypeId, operationId, referenceKey) {
4743
4802
  return (args, value) => ({
@@ -4756,12 +4815,12 @@ function memoryWritePresentation(sourceTypeId, operationId, referenceKey) {
4756
4815
  operationId,
4757
4816
  ...sourceTypeId === void 0 ? {} : { sourceTypeId },
4758
4817
  ...referenceKey === void 0 || reference(args, referenceKey) === void 0 ? {} : { reference: reference(args, referenceKey) },
4759
- committed: mutationResultCommitted(value) || operationId === "mutate" && object$3(value)?.success === true,
4818
+ committed: mutationResultCommitted(value) || operationId === "mutate" && object$5(value)?.success === true,
4760
4819
  item: writeItem(args, operationId)
4761
4820
  });
4762
4821
  }
4763
4822
  function validItem(value) {
4764
- const entry = object$3(value);
4823
+ const entry = object$5(value);
4765
4824
  return entry !== void 0 && bounded(entry.id, 300) === entry.id && bounded(entry.title, MAX_TITLE) === entry.title && (entry.excerpt === void 0 || bounded(entry.excerpt, MAX_EXCERPT) === entry.excerpt);
4766
4825
  }
4767
4826
  function cleanItem(value) {
@@ -4774,7 +4833,7 @@ function cleanItem(value) {
4774
4833
  }
4775
4834
  /** Fail closed on arbitrary third-party/session metadata. */
4776
4835
  function parseMnemonActivityMeta(value, callId, toolName) {
4777
- const meta = object$3(value);
4836
+ const meta = object$5(value);
4778
4837
  if (meta?.schema !== "dsh-mnemon.activity/v1" || meta.kind !== "read" && meta.kind !== "write") return {};
4779
4838
  const operationId = bounded(meta.operationId, 300);
4780
4839
  const sourceTypeId = bounded(meta.sourceTypeId, 128);
@@ -5156,24 +5215,22 @@ function createPluginMessage(text, form) {
5156
5215
  text
5157
5216
  }],
5158
5217
  source: {
5159
- kind: "plugin",
5160
- plugin: MNEMON_PLUGIN_SOURCE,
5218
+ kind: MNEMON_PLUGIN_SOURCE,
5161
5219
  form
5162
5220
  }
5163
5221
  });
5164
5222
  }
5165
- function sourceOf(message) {
5166
- return message.source;
5223
+ function isMnemonMessageSource(source) {
5224
+ if (typeof source !== "object" || source === null) return false;
5225
+ const { kind, plugin } = source;
5226
+ return kind === "dsh-mnemon" || kind === `plugin:dsh-mnemon` || kind === "plugin" && plugin === "dsh-mnemon";
5167
5227
  }
5168
5228
  /**
5169
5229
  * Whether an event is a durable user message this plugin produced.
5170
5230
  */
5171
5231
  function isOwnUserMessageEvent(event) {
5172
5232
  if (event?.type !== "user/message") return false;
5173
- const source = event.data.source;
5174
- if (typeof source !== "object" || source === null) return false;
5175
- const { kind, plugin } = source;
5176
- return kind === "plugin" && plugin === "dsh-mnemon";
5233
+ return isMnemonMessageSource(event.data.source);
5177
5234
  }
5178
5235
  function eventTurn(event) {
5179
5236
  return typeof event.data.turn === "number" ? event.data.turn : void 0;
@@ -5396,10 +5453,7 @@ var MnemonAgentLifecycle = class {
5396
5453
  if (!this.config.lifecycleEnabled) return decision;
5397
5454
  if (this.config.writeEnabled && this.config.writebackMode === "guided") this.recordTurnMessages(payload.turn, decision.messages);
5398
5455
  if (payload.step !== 1) return decision;
5399
- if (decision.messages.some((message) => {
5400
- const source = sourceOf(message);
5401
- return source.kind === "plugin" && source.plugin === "dsh-mnemon";
5402
- })) return decision;
5456
+ if (decision.messages.some((message) => isMnemonMessageSource(message.source))) return decision;
5403
5457
  if (decision.messages.length === 0) return decision;
5404
5458
  const snapshot = this.memorySnapshotMessage();
5405
5459
  const withSnapshot = (messages) => snapshot === void 0 ? messages : [...messages, snapshot];
@@ -6706,7 +6760,7 @@ var VersionUpdateManager = class {
6706
6760
  };
6707
6761
  //#endregion
6708
6762
  //#region src/host/rpc.ts
6709
- function object$2(value) {
6763
+ function object$4(value) {
6710
6764
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("payload must be an object");
6711
6765
  return value;
6712
6766
  }
@@ -6855,7 +6909,7 @@ async function assisted(runtime, lifecycle, typeId, operation, input, signal, ta
6855
6909
  ...signal === void 0 ? {} : { signal }
6856
6910
  });
6857
6911
  return {
6858
- ...object$2(result.value),
6912
+ ...object$4(result.value),
6859
6913
  revision: result.revision
6860
6914
  };
6861
6915
  } finally {
@@ -6924,7 +6978,7 @@ async function assisted(runtime, lifecycle, typeId, operation, input, signal, ta
6924
6978
  function createReadHandler(input, lifecycle, versions) {
6925
6979
  return async (endpoint, rawPayload, signal) => {
6926
6980
  try {
6927
- const payload = object$2(rawPayload);
6981
+ const payload = object$4(rawPayload);
6928
6982
  if (endpoint === "versions") {
6929
6983
  if (versions === void 0) throw new Error("version checks are unavailable");
6930
6984
  return success$1(await versions.check());
@@ -6944,7 +6998,7 @@ function createReadHandler(input, lifecycle, versions) {
6944
6998
  if (lifecycle === void 0 || payload.operation !== "agent-search") throw new Error("Read-only Host assistance is unavailable");
6945
6999
  const source = (await catalog(runtime, lifecycle)).sources.find((item) => item.sourceInstanceKey === payload.sourceInstanceKey);
6946
7000
  if (source === void 0 || !source.assistance.includes("agent-search")) throw new Error("Host assistance is not available for this Source instance");
6947
- const value = await assisted(runtime, lifecycle, source.sourceTypeId, "agent-search", object$2(payload.input), signal);
7001
+ const value = await assisted(runtime, lifecycle, source.sourceTypeId, "agent-search", object$4(payload.input), signal);
6948
7002
  return success$1({
6949
7003
  revision: (await catalog(runtime)).sources.find((item) => item.sourceInstanceKey === source.sourceInstanceKey)?.revision ?? source.revision,
6950
7004
  value
@@ -7037,9 +7091,9 @@ function createActivationHandler(input) {
7037
7091
  return async (endpoint, rawPayload, signal) => {
7038
7092
  try {
7039
7093
  if (endpoint === "source-assistance") {
7040
- const payload = object$2(rawPayload);
7094
+ const payload = object$4(rawPayload);
7041
7095
  if (payload.operation !== "activation" || payload.confirmed !== true) throw new Error("Unsupported activation operation");
7042
- const fields = object$2(payload.input);
7096
+ const fields = object$4(payload.input);
7043
7097
  if (Object.keys(fields).some((key) => key !== "memoryBodyId" && key !== "active") || typeof fields.active !== "boolean" || typeof fields.memoryBodyId !== "string") throw new Error("Activation accepts only a Memory Space id and boolean state");
7044
7098
  const runtime = scoped(input, payload);
7045
7099
  requireWritable(runtime);
@@ -7062,7 +7116,7 @@ function createActivationHandler(input) {
7062
7116
  }
7063
7117
  }
7064
7118
  if (endpoint !== "body") return badRequest$1("unknown activation endpoint: " + endpoint);
7065
- const payload = object$2(rawPayload);
7119
+ const payload = object$4(rawPayload);
7066
7120
  const unexpected = Object.keys(payload).filter((field) => !ACTIVATION_FIELDS.has(field));
7067
7121
  if (unexpected.length > 0) return badRequest$1("unsupported activation fields: " + unexpected.join(", "));
7068
7122
  if (typeof payload.memoryBodyId !== "string" || payload.memoryBodyId.trim() === "") return badRequest$1("memoryBodyId must be a non-empty string");
@@ -7081,7 +7135,7 @@ function createActivationHandler(input) {
7081
7135
  function createWriteHandler(input, lifecycle, versions) {
7082
7136
  return async (endpoint, rawPayload, signal) => {
7083
7137
  try {
7084
- const payload = object$2(rawPayload);
7138
+ const payload = object$4(rawPayload);
7085
7139
  if (endpoint === "version-update") {
7086
7140
  if (versions === void 0) throw new Error("version updates are unavailable");
7087
7141
  if (!input.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only (writeEnabled: false)");
@@ -7115,12 +7169,12 @@ function createWriteHandler(input, lifecycle, versions) {
7115
7169
  const operation = String(payload.operation ?? "");
7116
7170
  if (source === void 0 || !source.assistance.includes(operation)) throw new Error("Host assistance is not available for this Source instance");
7117
7171
  if (operation !== "agent-search" && (payload.confirmed !== true || payload.expectedRevision !== source.revision)) throw new Error("Host assistance requires confirmation of the current Source revision");
7118
- const value = await assisted(runtime, lifecycle, source.sourceTypeId, operation, object$2(payload.input), signal, {
7172
+ const value = await assisted(runtime, lifecycle, source.sourceTypeId, operation, object$4(payload.input), signal, {
7119
7173
  sourceInstanceKey: source.sourceInstanceKey,
7120
7174
  expectedRevision: source.revision
7121
7175
  });
7122
7176
  if (source.sourceTypeId === "runtime") return success$1({
7123
- revision: object$2(value).revision,
7177
+ revision: object$4(value).revision,
7124
7178
  value
7125
7179
  });
7126
7180
  return success$1({
@@ -7164,7 +7218,7 @@ function createWriteHandler(input, lifecycle, versions) {
7164
7218
  function createPackHandler(input) {
7165
7219
  return async (endpoint, rawPayload) => {
7166
7220
  try {
7167
- const payload = object$2(rawPayload);
7221
+ const payload = object$4(rawPayload);
7168
7222
  const runtime = scoped(input, payload);
7169
7223
  const manager = runtime.graph.packs;
7170
7224
  if (endpoint === "target") return success$1(manager.target());
@@ -7233,7 +7287,7 @@ function descriptor(settings, namespace) {
7233
7287
  return {
7234
7288
  status: "ready",
7235
7289
  value: namespace === "mnemon" && displayModeOf(view.value) === "buildin" ? {
7236
- ...object$1(view.value),
7290
+ ...object$3(view.value),
7237
7291
  displayMode: "builtin"
7238
7292
  } : view.value,
7239
7293
  base: view.base,
@@ -7244,7 +7298,7 @@ function descriptor(settings, namespace) {
7244
7298
  applies: view.applies
7245
7299
  };
7246
7300
  }
7247
- function object$1(value) {
7301
+ function object$3(value) {
7248
7302
  if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("payload must be an object");
7249
7303
  return value;
7250
7304
  }
@@ -7315,14 +7369,14 @@ function createSettingsHandler(settings) {
7315
7369
  return async (endpoint, rawPayload) => {
7316
7370
  let namespace = MNEMON_SETTINGS_NAMESPACE;
7317
7371
  try {
7318
- const payload = object$1(rawPayload);
7372
+ const payload = object$3(rawPayload);
7319
7373
  namespace = namespaceOf(payload);
7320
7374
  if (endpoint === "get") return success(descriptor(settings, namespace));
7321
7375
  if (endpoint !== "mutate") return badRequest(`unknown settings endpoint: ${endpoint}`);
7322
7376
  if (!settings.writable) throw new Error("DSH settings are read-only");
7323
7377
  if (!Array.isArray(payload.ops) || payload.ops.length === 0 || payload.ops.length > 16) throw new Error("ops must contain 1..16 settings edits");
7324
7378
  const ops = payload.ops.map((raw) => {
7325
- const op = object$1(raw);
7379
+ const op = object$3(raw);
7326
7380
  const path = Array.isArray(op.path) && op.path.length > 0 ? op.path.map((segment) => String(segment)) : [];
7327
7381
  if (!mutablePath(namespace, path)) throw new Error(`unsupported ${namespace} settings field: ${path.join(".")}`);
7328
7382
  if (op.op === "unset") return {
@@ -8304,49 +8358,527 @@ function registerMnemonSubagentTokenUsageProjection(ctx) {
8304
8358
  });
8305
8359
  }
8306
8360
  //#endregion
8307
- //#region src/host/plugin-management.ts
8308
- const schema = z.object({
8309
- strategyTypeId: z.string(),
8310
- entries: z.dict(z.object({
8311
- enabled: z.boolean(),
8312
- config: z.dict(z.any()).default({})
8313
- })).default({})
8314
- });
8315
- const legacySchema = z.object({ sources: z.dict(z.object({ enabled: z.boolean() })).default({}) });
8316
- const PACKAGE$1 = /^(?:@[a-z0-9._-]+\/)?dsh-mnemon-(source|strategy)-[a-z0-9._-]+$/u;
8361
+ //#region src/host/live-config.ts
8362
+ function plain(value) {
8363
+ if (isVolatile(value)) return plain(value.get());
8364
+ if (Array.isArray(value)) return value.map(plain);
8365
+ if (value === null || typeof value !== "object") return value;
8366
+ const prototype = Object.getPrototypeOf(value);
8367
+ if (prototype !== Object.prototype && prototype !== null) return value;
8368
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [key, plain(child)]));
8369
+ }
8370
+ /** Read a detached configuration snapshot from either DSH generation. */
8371
+ function plainHostConfig(value = {}) {
8372
+ return plain(value);
8373
+ }
8374
+ function liveConfig() {
8375
+ const schema = new schemastery_live_default(Config.toJSON());
8376
+ schema.dict = Object.fromEntries(Object.entries(schema.dict ?? {}).map(([key, field]) => [key, key === "remoteAccess" ? field : field.volatile()]));
8377
+ const standard = schema["~standard"];
8378
+ Object.defineProperty(schema, "~standard", { value: {
8379
+ ...standard,
8380
+ validate(value) {
8381
+ const parsed = standard.validate(value);
8382
+ if ("then" in parsed) throw new TypeError("Mnemon configuration validation must be synchronous");
8383
+ if (parsed.issues) return parsed;
8384
+ try {
8385
+ resolveConfig(plainHostConfig(parsed.value));
8386
+ return parsed;
8387
+ } catch (error) {
8388
+ return { issues: [{ message: error instanceof Error ? error.message : String(error) }] };
8389
+ }
8390
+ }
8391
+ } });
8392
+ return schema;
8393
+ }
8394
+ /** Profile Config for live forms; transport authority retains normal remounts. */
8395
+ const LiveConfig = liveConfig();
8396
+ //#endregion
8397
+ //#region src/host/legacy-settings-import.ts
8398
+ const UNSAFE_KEYS = /* @__PURE__ */ new Set([
8399
+ "__proto__",
8400
+ "prototype",
8401
+ "constructor"
8402
+ ]);
8317
8403
  const ENTRY_ID = /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,299}$/u;
8318
- const hash = (value) => createHash("sha256").update(canonicalMemoryJson(value)).digest("hex");
8319
- const clone = (value) => JSON.parse(JSON.stringify(value));
8320
- function record(value) {
8321
- if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Plugin configuration must be an object");
8322
- const json = JSON.stringify(value);
8323
- if (json.length > 65536) throw new Error("Plugin configuration exceeds 64 KiB");
8324
- const parsed = JSON.parse(json);
8325
- for (const key of Object.keys(parsed)) if ([
8326
- "__proto__",
8327
- "prototype",
8328
- "constructor"
8329
- ].includes(key)) throw new Error("Unsafe plugin configuration key");
8330
- return parsed;
8404
+ const STRATEGY_ID = /^[a-z][a-z0-9-]{0,127}$/u;
8405
+ const VIEW_NAMESPACE = /^mnemon-view(?:-[a-f0-9]{16})?$/u;
8406
+ const LEGACY_NAMESPACE = /^mnemon-plugins(?:-[a-f0-9]{16})?$/u;
8407
+ function object$2(value, label) {
8408
+ if (value === null || typeof value !== "object" || Array.isArray(value) || ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new Error(`${label} must be a plain object`);
8409
+ for (const key of Reflect.ownKeys(value)) {
8410
+ if (typeof key !== "string" || UNSAFE_KEYS.has(key)) throw new Error(`${label} contains an unsafe key`);
8411
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
8412
+ if (!descriptor.enumerable || !Object.hasOwn(descriptor, "value")) throw new Error(`${label} must contain only data properties`);
8413
+ }
8414
+ return value;
8331
8415
  }
8332
- function preferences(value) {
8333
- if (value.strategyTypeId !== void 0 && !/^[a-z][a-z0-9-]{0,127}$/u.test(value.strategyTypeId)) throw new Error("Invalid Strategy type id");
8334
- const entries = record(value.entries ?? {});
8335
- if (Object.keys(entries).length > 64) throw new Error("At most 64 memory plugin Entries can be configured");
8336
- for (const [entryId, item] of Object.entries(entries)) {
8337
- if (!ENTRY_ID.test(entryId)) throw new Error("Invalid memory plugin Entry id");
8338
- const candidate = record(item);
8339
- if (typeof candidate.enabled !== "boolean") throw new Error("Plugin enabled must be boolean");
8340
- record(candidate.config);
8416
+ /** Copy JSON data without invoking getters, serializers or prototype methods. */
8417
+ function copy(value, label, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
8418
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
8419
+ if (typeof value === "number" && Number.isFinite(value)) return value;
8420
+ if (typeof value !== "object" || value === null) throw new Error(`${label} must contain only JSON data`);
8421
+ if (depth > 64 || ancestors.has(value)) throw new Error(`${label} contains cyclic or excessively nested data`);
8422
+ ancestors.add(value);
8423
+ try {
8424
+ if (Array.isArray(value)) {
8425
+ if (Object.getPrototypeOf(value) !== Array.prototype) throw new Error(`${label} must contain plain arrays`);
8426
+ if (Reflect.ownKeys(value).length !== value.length + 1) throw new Error(`${label} contains a sparse or extended array`);
8427
+ return Array.from({ length: value.length }, (_, index) => {
8428
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
8429
+ if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) throw new Error(`${label} must contain only data properties`);
8430
+ return copy(descriptor.value, label, ancestors, depth + 1);
8431
+ });
8432
+ }
8433
+ return Object.fromEntries(Object.entries(object$2(value, label)).map(([key, item]) => [key, copy(item, label, ancestors, depth + 1)]));
8434
+ } finally {
8435
+ ancestors.delete(value);
8341
8436
  }
8342
- return clone(value);
8343
8437
  }
8344
- function legacyPreferences(value) {
8345
- const sources = value.sources ?? {};
8346
- if (Object.keys(sources).length > 64) throw new Error("At most 64 legacy Source Entries can be configured");
8347
- for (const [entryId, item] of Object.entries(sources)) if (!ENTRY_ID.test(entryId) || typeof item?.enabled !== "boolean") throw new Error("Invalid legacy Source Entry preference");
8348
- return clone({ sources });
8438
+ function copiedObject(value, label) {
8439
+ object$2(value, label);
8440
+ return copy(value, label);
8441
+ }
8442
+ /** Inspect validated copies only; never evaluate expressions or invoke getters. */
8443
+ function containsExpression(value) {
8444
+ return value !== null && typeof value === "object" && (Object.hasOwn(value, "__jsExpr") || Object.values(value).some(containsExpression));
8445
+ }
8446
+ function fields(value, allowed, label) {
8447
+ if (Object.keys(value).some((key) => !allowed.includes(key))) throw new Error(`${label} contains unsupported fields`);
8448
+ }
8449
+ function interaction(value, label, allowed) {
8450
+ const result = copiedObject(value, label);
8451
+ fields(result, allowed, label);
8452
+ if (Object.values(result).some((item) => typeof item !== "boolean")) throw new Error(`${label} preferences must be boolean`);
8453
+ return result;
8454
+ }
8455
+ function entryId(value, label) {
8456
+ if (typeof value !== "string" || !ENTRY_ID.test(value) || UNSAFE_KEYS.has(value)) throw new Error(`${label} contains an invalid Entry id`);
8457
+ }
8458
+ function view(value, label) {
8459
+ const result = copiedObject(value, label);
8460
+ fields(result, ["strategyTypeId", "entries"], label);
8461
+ if (Object.hasOwn(result, "strategyTypeId") && (typeof result.strategyTypeId !== "string" || !STRATEGY_ID.test(result.strategyTypeId))) throw new Error(`${label} contains an invalid Strategy type id`);
8462
+ const entries = {};
8463
+ if (Object.hasOwn(result, "entries")) {
8464
+ const values = object$2(result.entries, `${label}.entries`);
8465
+ if (Object.keys(values).length > 64) throw new Error(`${label} contains too many Entries`);
8466
+ for (const [id, raw] of Object.entries(values)) {
8467
+ entryId(id, label);
8468
+ const item = copiedObject(raw, `${label}.entries`);
8469
+ fields(item, ["enabled", "config"], `${label}.entries`);
8470
+ if (typeof item.enabled !== "boolean") throw new Error(`${label} Entry enabled must be boolean`);
8471
+ const config = Object.hasOwn(item, "config") ? copiedObject(item.config, `${label} Entry config`) : {};
8472
+ if (JSON.stringify(config).length > 65536) throw new Error(`${label} Entry config exceeds 64 KiB`);
8473
+ entries[id] = {
8474
+ enabled: item.enabled,
8475
+ config
8476
+ };
8477
+ }
8478
+ }
8479
+ return {
8480
+ ...typeof result.strategyTypeId === "string" ? { strategyTypeId: result.strategyTypeId } : {},
8481
+ ...Object.hasOwn(result, "entries") ? { entries } : {}
8482
+ };
8483
+ }
8484
+ function sources(value, label, sourceEntryIds, diagnostics) {
8485
+ const result = copiedObject(value, label);
8486
+ fields(result, ["sources"], label);
8487
+ const values = Object.hasOwn(result, "sources") ? object$2(result.sources, `${label}.sources`) : {};
8488
+ if (Object.keys(values).length > 64) throw new Error(`${label} contains too many Source Entries`);
8489
+ const entries = {};
8490
+ for (const [id, raw] of Object.entries(values)) {
8491
+ entryId(id, label);
8492
+ const item = copiedObject(raw, `${label}.sources`);
8493
+ fields(item, ["enabled"], `${label}.sources`);
8494
+ if (typeof item.enabled !== "boolean") throw new Error(`${label} Source enabled must be boolean`);
8495
+ if (!sourceEntryIds.has(id)) {
8496
+ diagnostics.push(`Skipped legacy Source preference for ${id}: the current profile does not declare it as a Source.`);
8497
+ continue;
8498
+ }
8499
+ entries[id] = {
8500
+ enabled: item.enabled,
8501
+ config: {}
8502
+ };
8503
+ }
8504
+ return entries;
8505
+ }
8506
+ function applyEntryOverrides(entries, overrides, sourceEntryIds, diagnostics) {
8507
+ if (!Array.isArray(overrides)) throw new Error("Explicit Entry overrides must be an array");
8508
+ const matching = /* @__PURE__ */ new Map();
8509
+ for (const raw of overrides) {
8510
+ const row = object$2(raw, "Explicit Entry override");
8511
+ entryId(row.id, "Explicit Entry override");
8512
+ if (!Object.hasOwn(entries, row.id)) continue;
8513
+ const previous = matching.get(row.id) ?? {};
8514
+ matching.set(row.id, {
8515
+ ...previous,
8516
+ ...row.disabled === void 0 ? {} : { disabled: row.disabled },
8517
+ ...row.config === void 0 ? {} : { config: row.config }
8518
+ });
8519
+ }
8520
+ for (const [id, row] of matching) {
8521
+ if (row.disabled !== void 0 && typeof row.disabled !== "boolean") {
8522
+ delete entries[id];
8523
+ diagnostics.push(`Skipped legacy View preference for ${id}: its explicit disabled value is not boolean.`);
8524
+ continue;
8525
+ }
8526
+ if (row.config !== void 0 && !sourceEntryIds.has(id)) {
8527
+ const config = copiedObject(row.config, "Explicit Entry config");
8528
+ if (containsExpression(config)) {
8529
+ delete entries[id];
8530
+ diagnostics.push(`Skipped legacy View preference for ${id}: its explicit config contains an expression.`);
8531
+ continue;
8532
+ }
8533
+ entries[id].config = config;
8534
+ }
8535
+ if (typeof row.disabled === "boolean") entries[id].enabled = !row.disabled;
8536
+ }
8349
8537
  }
8538
+ /**
8539
+ * Recover only the canonical root and the exact owning profile's old namespaces.
8540
+ * This does not read files, edit profile rows, validate plugin domains or commit.
8541
+ * Malformed relevant input rejects the complete plan without an import marker.
8542
+ */
8543
+ function planLegacySettingsImport(options) {
8544
+ const diagnostics = [];
8545
+ try {
8546
+ const rawCurrent = object$2(options.currentOverride, "Current config override");
8547
+ if (rawCurrent.legacySettingsImported === true) return {
8548
+ patch: {},
8549
+ diagnostics
8550
+ };
8551
+ if (options.entryId !== "mnemon") throw new Error("Legacy settings belong to the canonical mnemon Entry; another root Entry is ambiguous.");
8552
+ if (!VIEW_NAMESPACE.test(options.viewNamespace) || !LEGACY_NAMESPACE.test(options.legacyNamespace) || options.legacyNamespace !== options.viewNamespace.replace(/^mnemon-view/u, "mnemon-plugins")) throw new Error("Legacy settings namespaces must identify the same exact profile.");
8553
+ const sections = object$2(options.sections, "Legacy settings document");
8554
+ for (const key of Object.keys(sections)) if (key.startsWith("mnemon-view-") && !VIEW_NAMESPACE.test(key) || key.startsWith("mnemon-plugins-") && !LEGACY_NAMESPACE.test(key)) throw new Error("Legacy settings document contains an invalid Mnemon namespace.");
8555
+ const relevant = [
8556
+ "mnemon",
8557
+ "mnemon-ui",
8558
+ options.viewNamespace,
8559
+ options.legacyNamespace
8560
+ ];
8561
+ if (!relevant.some((key) => Object.hasOwn(sections, key))) return {
8562
+ patch: {},
8563
+ diagnostics
8564
+ };
8565
+ for (const key of relevant) if (Object.hasOwn(sections, key) && containsExpression(copiedObject(sections[key], key))) throw new Error(`${key} contains an expression; legacy settings must contain only literal data.`);
8566
+ const current = copiedObject(rawCurrent, "Current config override");
8567
+ if (Object.hasOwn(current, "legacySettingsImported") && typeof current.legacySettingsImported !== "boolean") throw new Error("Current import marker must be boolean");
8568
+ const root = Object.hasOwn(sections, "mnemon") ? copiedObject(sections.mnemon, "mnemon") : {};
8569
+ if (Object.hasOwn(root, "memoryView") || Object.hasOwn(root, "legacySettingsImported")) throw new Error("Legacy mnemon contains reserved migration fields");
8570
+ const oldInteraction = Object.hasOwn(root, "conversationInteraction") ? interaction(root.conversationInteraction, "mnemon.conversationInteraction", [
8571
+ "toolviews",
8572
+ "turnBar",
8573
+ "saveAction"
8574
+ ]) : {};
8575
+ const oldUi = Object.hasOwn(sections, "mnemon-ui") ? interaction(sections["mnemon-ui"], "mnemon-ui", ["turnBar", "saveAction"]) : {};
8576
+ const currentInteraction = Object.hasOwn(current, "conversationInteraction") ? interaction(current.conversationInteraction, "Current conversationInteraction", [
8577
+ "toolviews",
8578
+ "turnBar",
8579
+ "saveAction"
8580
+ ]) : {};
8581
+ const patch = Object.fromEntries(Object.entries(root).filter(([key]) => key !== "conversationInteraction" && !Object.hasOwn(current, key)));
8582
+ const inheritedInteraction = {
8583
+ ...oldInteraction,
8584
+ ...oldUi
8585
+ };
8586
+ if (Object.keys(inheritedInteraction).some((key) => !Object.hasOwn(currentInteraction, key))) patch.conversationInteraction = {
8587
+ ...inheritedInteraction,
8588
+ ...currentInteraction
8589
+ };
8590
+ const currentView = Object.hasOwn(current, "memoryView") ? view(current.memoryView, "Current memoryView") : {};
8591
+ if (Object.hasOwn(sections, options.viewNamespace) || Object.hasOwn(sections, options.legacyNamespace)) {
8592
+ if (!Array.isArray(options.sourceEntryIds)) throw new Error("Source Entry ids must be an array");
8593
+ for (const id of options.sourceEntryIds) entryId(id, "Source Entry ids");
8594
+ const sourceEntryIds = new Set(options.sourceEntryIds);
8595
+ const oldSources = Object.hasOwn(sections, options.legacyNamespace) ? sources(sections[options.legacyNamespace], options.legacyNamespace, sourceEntryIds, diagnostics) : {};
8596
+ const oldView = Object.hasOwn(sections, options.viewNamespace) ? view(sections[options.viewNamespace], options.viewNamespace) : {};
8597
+ const entries = {
8598
+ ...oldSources,
8599
+ ...oldView.entries
8600
+ };
8601
+ applyEntryOverrides(entries, options.entryOverrides, sourceEntryIds, diagnostics);
8602
+ const migratedView = {
8603
+ ...oldView,
8604
+ entries,
8605
+ ...currentView
8606
+ };
8607
+ if (!Object.hasOwn(currentView, "entries") || !Object.hasOwn(currentView, "strategyTypeId") && oldView.strategyTypeId !== void 0) patch.memoryView = migratedView;
8608
+ }
8609
+ patch.legacySettingsImported = true;
8610
+ return {
8611
+ patch,
8612
+ diagnostics
8613
+ };
8614
+ } catch (error) {
8615
+ diagnostics.push(error instanceof Error ? error.message : "Legacy settings import could not be planned");
8616
+ return {
8617
+ patch: {},
8618
+ diagnostics
8619
+ };
8620
+ }
8621
+ }
8622
+ //#endregion
8623
+ //#region src/host/settings-service.ts
8624
+ const pendingNativeImports = /* @__PURE__ */ new WeakSet();
8625
+ const clone$1 = (value) => structuredClone(value);
8626
+ const object$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
8627
+ function profileYaml(source) {
8628
+ const document = parseDocument(source, { customTags: [{
8629
+ tag: "tag:yaml.org,2002:js",
8630
+ resolve: (value) => ({ __jsExpr: value })
8631
+ }] });
8632
+ if (document.errors.length > 0 || document.warnings.length > 0) throw new Error("Invalid or unsupported legacy/profile YAML; retained backup was not changed");
8633
+ return document.toJS({ maxAliasCount: 100 });
8634
+ }
8635
+ /** A local facade preserves Mnemon's wire namespaces; DSH owns all persistence. */
8636
+ var ProfileMnemonSettings = class {
8637
+ ctx;
8638
+ input;
8639
+ registrations = /* @__PURE__ */ new Map();
8640
+ listeners = /* @__PURE__ */ new Set();
8641
+ revisions = /* @__PURE__ */ new Map();
8642
+ owner;
8643
+ forms;
8644
+ editor;
8645
+ closed = false;
8646
+ importing;
8647
+ nativeImportPending;
8648
+ constructor(ctx, input) {
8649
+ this.ctx = ctx;
8650
+ this.input = input;
8651
+ this.forms = ctx.settings;
8652
+ this.owner = ctx.fiber;
8653
+ this.editor = ctx.get("configEditor");
8654
+ const profile = ctx.get("profileContext");
8655
+ const root = ctx.root ?? ctx;
8656
+ if (profile?.home !== void 0 && existsSync(join(profile.home, "settings.yaml"))) pendingNativeImports.add(root);
8657
+ this.nativeImportPending = pendingNativeImports.has(root);
8658
+ const owner = this.owner;
8659
+ const validate = (candidate) => {
8660
+ const config = plainHostConfig(candidate);
8661
+ for (const registration of this.registrations.values()) {
8662
+ if (registration.legacy) continue;
8663
+ const value = this.resolve(registration, config);
8664
+ registration.validate?.(value);
8665
+ }
8666
+ };
8667
+ ctx.on("internal/config", function(_raw, next) {
8668
+ const candidate = next();
8669
+ if (this === owner) validate(candidate);
8670
+ return candidate;
8671
+ }, { prepend: true });
8672
+ ctx.on("loader/volatile-update", ((paths) => {
8673
+ if (this.closed) return;
8674
+ for (const registration of this.registrations.values()) {
8675
+ if (registration.legacy) continue;
8676
+ if (!paths.some((path) => registration.field === void 0 ? path.length === 0 || !["memoryView", "legacySettingsImported"].includes(path[0]) : path.length === 0 || path[0] === registration.field)) continue;
8677
+ const value = this.resolve(registration);
8678
+ for (const listener of this.listeners) try {
8679
+ listener(registration.namespace, value);
8680
+ } catch (error) {
8681
+ console.warn("dsh-mnemon: committed settings listener failed", error);
8682
+ }
8683
+ }
8684
+ }));
8685
+ ctx.effect(() => {
8686
+ const disposePresentation = owner === void 0 ? void 0 : this.forms.configure({ auto: false }, owner);
8687
+ return () => {
8688
+ this.closed = true;
8689
+ this.listeners.clear();
8690
+ this.revisions.clear();
8691
+ disposePresentation?.();
8692
+ };
8693
+ }, "dsh-mnemon: profile settings");
8694
+ }
8695
+ get writable() {
8696
+ const entry = this.owner?.entry;
8697
+ return !this.closed && this.forms.writable && entry !== void 0 && this.editor?.entries().includes(entry) === true;
8698
+ }
8699
+ onUpdated(listener) {
8700
+ this.listeners.add(listener);
8701
+ return () => {
8702
+ this.listeners.delete(listener);
8703
+ };
8704
+ }
8705
+ resolve(registration, config = plainHostConfig(this.owner?.config ?? this.input)) {
8706
+ const value = registration.legacy ? registration.base : registration.field === void 0 ? config : config[registration.field];
8707
+ return registration.schema(object$1(value));
8708
+ }
8709
+ register(namespace, schema, options) {
8710
+ if (this.registrations.has(namespace)) throw new Error(`Mnemon settings already registered: ${namespace}`);
8711
+ if (typeof schema !== "function") throw new Error("Mnemon settings require a schema");
8712
+ const field = namespace === "mnemon-ui" ? "conversationInteraction" : /^mnemon-view(?:-[a-f0-9]{16})?$/u.test(namespace) ? "memoryView" : void 0;
8713
+ const legacy = /^mnemon-plugins(?:-[a-f0-9]{16})?$/u.test(namespace);
8714
+ if (namespace !== "mnemon" && field === void 0 && !legacy) throw new Error("Unsupported Mnemon settings namespace");
8715
+ const registration = {
8716
+ namespace,
8717
+ ...field === void 0 ? {} : { field },
8718
+ legacy,
8719
+ schema,
8720
+ base: clone$1(options.base ?? {}),
8721
+ ...options.validate === void 0 ? {} : { validate: options.validate }
8722
+ };
8723
+ const value = this.resolve(registration);
8724
+ options.validate?.(value);
8725
+ this.registrations.set(namespace, registration);
8726
+ return { get: () => this.resolve(registration) };
8727
+ }
8728
+ nativeDescriptor() {
8729
+ if (this.closed || this.owner?.entry === void 0 || !this.editor?.entries().includes(this.owner.entry)) return void 0;
8730
+ const descriptor = this.forms.describe().find((item) => item.ns === this.owner?.entry?.options.id);
8731
+ if (descriptor !== void 0) {
8732
+ this.revisions.set(descriptor.revision, clone$1({
8733
+ ns: descriptor.ns,
8734
+ revision: descriptor.revision,
8735
+ applies: descriptor.applies,
8736
+ value: descriptor.value,
8737
+ base: descriptor.base,
8738
+ user: descriptor.user
8739
+ }));
8740
+ if (this.revisions.size > 64) this.revisions.delete(this.revisions.keys().next().value);
8741
+ }
8742
+ return descriptor;
8743
+ }
8744
+ unchanged(previous, current, registration, operations) {
8745
+ const project = (value) => {
8746
+ if (registration.field !== void 0) return object$1(value)[registration.field];
8747
+ const { memoryView: _view, legacySettingsImported: _imported, conversationInteraction, ...core } = object$1(value);
8748
+ return operations.some((operation) => operation.path[0] === "conversationInteraction") ? {
8749
+ ...core,
8750
+ conversationInteraction
8751
+ } : core;
8752
+ };
8753
+ const signature = (descriptor) => canonicalMemoryJson(JSON.parse(JSON.stringify([
8754
+ project(descriptor.value) ?? null,
8755
+ project(descriptor.base) ?? null,
8756
+ project(descriptor.user) ?? null
8757
+ ])));
8758
+ return signature(previous) === signature(current);
8759
+ }
8760
+ describe(options) {
8761
+ const raw = this.nativeDescriptor();
8762
+ if (raw === void 0) return [];
8763
+ const descriptor = options?.redactSecrets ? this.forms.describe(options).find((item) => item.ns === this.owner?.entry?.options.id) : raw;
8764
+ if (descriptor === void 0) return [];
8765
+ return [...this.registrations.values()].map((registration) => {
8766
+ const project = (value) => registration.legacy ? {} : registration.field === void 0 ? value : object$1(value)[registration.field];
8767
+ return {
8768
+ ns: registration.namespace,
8769
+ value: project(descriptor.value) ?? {},
8770
+ base: project(descriptor.base) ?? {},
8771
+ user: project(descriptor.user) ?? {},
8772
+ revision: descriptor.revision,
8773
+ applies: "live"
8774
+ };
8775
+ });
8776
+ }
8777
+ async mutate(namespace, operations, expectedRevision) {
8778
+ const registration = this.registrations.get(namespace);
8779
+ if (registration === void 0 || registration.legacy) throw new Error("Unsupported Mnemon settings namespace");
8780
+ if (!this.writable) throw new Error("DSH settings are read-only");
8781
+ const translated = operations.map((operation) => ({
8782
+ ...operation,
8783
+ path: [...registration.field === void 0 ? [] : [registration.field], ...operation.path]
8784
+ }));
8785
+ let revision = expectedRevision;
8786
+ const previous = revision === void 0 ? void 0 : this.revisions.get(revision);
8787
+ for (let attempt = 0;; attempt += 1) {
8788
+ if (!this.writable) throw new Error("DSH settings are read-only");
8789
+ try {
8790
+ await this.forms.mutate(this.owner.entry.options.id, translated, revision);
8791
+ return;
8792
+ } catch (error) {
8793
+ if (attempt >= 2 || previous === void 0 || typeof error !== "object" || error === null || !("code" in error) || error.code !== "SETTINGS_CONFLICT") throw error;
8794
+ if (!this.writable) throw new Error("DSH settings are read-only");
8795
+ const current = this.nativeDescriptor();
8796
+ if (current === void 0 || !this.unchanged(previous, current, registration, operations)) throw error;
8797
+ revision = current.revision;
8798
+ }
8799
+ }
8800
+ }
8801
+ /** Read only the retained backup; ConfigEditor preserves all other patch rows. */
8802
+ async importLegacy(sourceEntryIds) {
8803
+ if (this.importing !== void 0) return this.importing;
8804
+ this.importing = this.performImport(sourceEntryIds).finally(() => {
8805
+ this.importing = void 0;
8806
+ });
8807
+ return this.importing;
8808
+ }
8809
+ async performImport(sourceEntryIds) {
8810
+ const profile = this.ctx.get("profileContext");
8811
+ const entry = this.owner?.entry;
8812
+ if (!this.writable || profile?.home === void 0 || entry === void 0) return;
8813
+ if (this.nativeImportPending) {
8814
+ console.warn("dsh-mnemon: DSH is importing settings.yaml; restart once to recover retained Mnemon UI and plugin preferences");
8815
+ return;
8816
+ }
8817
+ if (plainHostConfig(this.owner?.config ?? this.input).legacySettingsImported) return;
8818
+ const backup = join(profile.home, "settings.yaml.imported");
8819
+ let text;
8820
+ try {
8821
+ if ((await stat(backup)).size > 1048576) throw new Error("Legacy settings backup exceeds the import limit");
8822
+ text = await readFile(backup, "utf8");
8823
+ } catch (error) {
8824
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return;
8825
+ throw error;
8826
+ }
8827
+ const sections = profileYaml(text);
8828
+ const viewNamespace = [...this.registrations.keys()].find((namespace) => namespace.startsWith("mnemon-view"));
8829
+ const legacyNamespace = [...this.registrations.keys()].find((namespace) => namespace.startsWith("mnemon-plugins"));
8830
+ const plan = () => {
8831
+ const configuration = this.editor.configuration().find((item) => item.entry === entry);
8832
+ if (configuration === void 0) throw new Error("Mnemon profile entry is no longer editable");
8833
+ const document = profileYaml(readFileSync(this.editor.documentPath, "utf8"));
8834
+ const entryOverrides = Array.isArray(document) ? document.flatMap((value) => {
8835
+ const row = object$1(value);
8836
+ if (typeof row.id !== "string") return [];
8837
+ const entries = this.editor.entries().filter((candidate) => candidate.options.id === row.id);
8838
+ if (entries.length > 1) throw new Error("Legacy settings import requires unambiguous profile Entry ids");
8839
+ return [{
8840
+ id: entries[0]?.id ?? row.id,
8841
+ ..."disabled" in row ? { disabled: row.disabled } : {},
8842
+ ..."config" in row ? { config: row.config } : {}
8843
+ }];
8844
+ }) : [];
8845
+ return planLegacySettingsImport({
8846
+ entryId: entry.options.id,
8847
+ viewNamespace,
8848
+ legacyNamespace,
8849
+ sections,
8850
+ currentOverride: configuration.override,
8851
+ entryOverrides,
8852
+ sourceEntryIds
8853
+ });
8854
+ };
8855
+ const preview = plan();
8856
+ if (Object.keys(preview.patch).length === 0) {
8857
+ if (preview.diagnostics.length > 0) console.warn("dsh-mnemon: legacy settings were not imported; retained backup requires review");
8858
+ return;
8859
+ }
8860
+ if (this.closed) return;
8861
+ await this.editor.edit(entry, (current) => {
8862
+ if (this.closed) throw new Error("Mnemon settings are disposed");
8863
+ const next = plan();
8864
+ return {
8865
+ ...current,
8866
+ ...next.patch
8867
+ };
8868
+ });
8869
+ }
8870
+ };
8871
+ function createHostSettings(ctx, config) {
8872
+ return typeof ctx.settings.register === "function" ? ctx.settings : new ProfileMnemonSettings(ctx, config);
8873
+ }
8874
+ function subscribeSettings(ctx, settings, listener) {
8875
+ return settings instanceof ProfileMnemonSettings ? settings.onUpdated(listener) : ctx.on("settings/updated", listener);
8876
+ }
8877
+ //#endregion
8878
+ //#region src/host/plugin-management.ts
8879
+ const PACKAGE$1 = /^(?:@[a-z0-9._-]+\/)?dsh-mnemon-(source|strategy)-[a-z0-9._-]+$/u;
8880
+ const hash = (value) => createHash("sha256").update(canonicalMemoryJson(value)).digest("hex");
8881
+ const clone = (value) => JSON.parse(JSON.stringify(value));
8350
8882
  function contributionRoles(snapshot, entryId) {
8351
8883
  const roles = /* @__PURE__ */ new Set();
8352
8884
  if (snapshot.sources.some((value) => value.provenance.entryId === entryId)) roles.add("source");
@@ -8436,6 +8968,7 @@ function orderPlugins(values) {
8436
8968
  var MemoryPluginManagement = class {
8437
8969
  ctx;
8438
8970
  engine;
8971
+ settingsService;
8439
8972
  settingsNamespace;
8440
8973
  settings;
8441
8974
  legacySettings;
@@ -8445,21 +8978,23 @@ var MemoryPluginManagement = class {
8445
8978
  restoreError;
8446
8979
  discoveryWarnings = [];
8447
8980
  changingEntries = false;
8448
- constructor(ctx, engine) {
8981
+ restorePending = false;
8982
+ constructor(ctx, engine, settingsService = ctx.settings) {
8449
8983
  this.ctx = ctx;
8450
8984
  this.engine = engine;
8985
+ this.settingsService = settingsService;
8451
8986
  const loader = this.loader();
8452
- const anchor = loader?.config?.baseUrl ?? loader?.context?.baseUrl;
8987
+ const anchor = loader?.config?.baseUrl ?? loader?.ctx?.baseUrl ?? loader?.context?.baseUrl;
8453
8988
  const suffix = anchor ? `-${hash(anchor).slice(0, 16)}` : "";
8454
8989
  this.settingsNamespace = `${MNEMON_VIEW_SETTINGS_NAMESPACE}${suffix}`;
8455
- this.settings = ctx.settings.register(this.settingsNamespace, schema, {
8990
+ this.settings = settingsService.register(this.settingsNamespace, schema, {
8456
8991
  base: { entries: {} },
8457
8992
  applies: "live",
8458
8993
  validate: (value) => {
8459
8994
  preferences(value);
8460
8995
  }
8461
8996
  });
8462
- this.legacySettings = ctx.settings.register(`mnemon-plugins${suffix}`, legacySchema, {
8997
+ this.legacySettings = settingsService.register(`mnemon-plugins${suffix}`, legacySchema, {
8463
8998
  base: { sources: {} },
8464
8999
  applies: "live",
8465
9000
  validate: (value) => {
@@ -8467,8 +9002,8 @@ var MemoryPluginManagement = class {
8467
9002
  }
8468
9003
  });
8469
9004
  }
8470
- resolveConfig(config) {
8471
- const selected = this.settings.get().strategyTypeId;
9005
+ resolveConfig(config, view = this.settings.get()) {
9006
+ const selected = view.strategyTypeId;
8472
9007
  return selected === void 0 ? config : {
8473
9008
  ...config,
8474
9009
  memoryTopology: {
@@ -8478,36 +9013,61 @@ var MemoryPluginManagement = class {
8478
9013
  };
8479
9014
  }
8480
9015
  start() {
8481
- const schedule = () => {
8482
- const hasSavedState = Object.keys(this.settings.get().entries ?? {}).length > 0 || Object.keys(this.legacySettings.get().sources ?? {}).length > 0;
8483
- if (this.closed || this.changingEntries || this.timer || !hasSavedState) return;
8484
- this.timer = setTimeout(() => {
8485
- this.timer = void 0;
8486
- this.exclusive(() => this.restore()).catch((error) => {
8487
- this.restoreError = error instanceof Error ? error.message : String(error);
8488
- });
8489
- }, 0);
8490
- };
9016
+ const schedule = () => this.scheduleRestore();
8491
9017
  const stops = [
8492
9018
  this.ctx.on("loader/entry-init", schedule),
8493
9019
  this.ctx.on("loader/partial-dispose", schedule),
8494
- this.ctx.on("settings/updated", ((namespace) => {
9020
+ this.ctx.on("app-boot/config-reload", () => this.scheduleRestore(true)),
9021
+ subscribeSettings(this.ctx, this.settingsService, (namespace) => {
8495
9022
  if (namespace === this.settingsNamespace || namespace === `mnemon-plugins${this.settingsNamespace.slice(11)}`) schedule();
8496
- }))
9023
+ })
8497
9024
  ];
8498
9025
  schedule();
8499
9026
  return () => {
8500
9027
  this.closed = true;
9028
+ this.restorePending = false;
8501
9029
  clearTimeout(this.timer);
8502
9030
  for (const stop of stops.reverse()) stop();
8503
9031
  };
8504
9032
  }
9033
+ scheduleRestore(profileReload = false) {
9034
+ const hasSavedState = Object.keys(this.settings.get().entries ?? {}).length > 0 || Object.keys(this.legacySettings.get().sources ?? {}).length > 0;
9035
+ if (this.closed || !profileReload && this.changingEntries || !hasSavedState) return;
9036
+ this.restorePending = true;
9037
+ if (this.changingEntries || this.timer) return;
9038
+ this.timer = setTimeout(() => {
9039
+ this.timer = void 0;
9040
+ this.enqueueRestore();
9041
+ }, 0);
9042
+ }
9043
+ enqueueRestore() {
9044
+ if (this.closed || this.changingEntries || !this.restorePending) return;
9045
+ clearTimeout(this.timer);
9046
+ this.timer = void 0;
9047
+ this.restorePending = false;
9048
+ this.exclusive(async () => {
9049
+ try {
9050
+ await this.restore();
9051
+ } catch (error) {
9052
+ this.restoreError = error instanceof Error ? error.message : String(error);
9053
+ }
9054
+ }).catch(() => {});
9055
+ }
9056
+ /** A response must not race the deferred restore after profile reconciliation. */
9057
+ async settled() {
9058
+ do {
9059
+ this.enqueueRestore();
9060
+ const pending = this.queue;
9061
+ await pending;
9062
+ if (pending === this.queue && !this.restorePending) return;
9063
+ } while (!this.closed);
9064
+ }
8505
9065
  loader() {
8506
9066
  const candidate = this.ctx.get("loader");
8507
9067
  return candidate && typeof candidate.entries === "function" ? candidate : void 0;
8508
9068
  }
8509
9069
  settingsRevision() {
8510
- return this.ctx.settings.describe({ redactSecrets: true }).find((value) => value.ns === this.settingsNamespace)?.revision ?? 0;
9070
+ return this.settingsService.describe({ redactSecrets: true }).find((value) => value.ns === this.settingsNamespace)?.revision ?? 0;
8511
9071
  }
8512
9072
  async managed() {
8513
9073
  const loader = this.loader();
@@ -8552,7 +9112,7 @@ var MemoryPluginManagement = class {
8552
9112
  config: editor === void 0 ? {} : memoryStrategyConfigurationValues(editor, entry.options.config),
8553
9113
  enabled: !entry.disabled,
8554
9114
  active,
8555
- writable: this.ctx.settings.writable && !ancestorDisabled && (entry.options.disabled === void 0 || typeof entry.options.disabled === "boolean"),
9115
+ writable: this.settingsService.writable && !ancestorDisabled && (entry.options.disabled === void 0 || typeof entry.options.disabled === "boolean"),
8556
9116
  ...ancestorDisabled ? { diagnostic: "This Entry is disabled by its parent." } : !entry.disabled && !active ? { diagnostic: "This Entry is enabled but has not registered a memory contribution." } : {}
8557
9117
  };
8558
9118
  const item = {
@@ -8583,7 +9143,7 @@ var MemoryPluginManagement = class {
8583
9143
  `mnemon-plugins${this.settingsNamespace.slice(11)}`
8584
9144
  ]);
8585
9145
  return hash({
8586
- settings: this.ctx.settings.describe({ redactSecrets: true }).filter((value) => namespaces.has(value.ns)).map((value) => [value.ns, value.revision]),
9146
+ settings: this.settingsService.describe({ redactSecrets: true }).filter((value) => namespaces.has(value.ns)).map((value) => [value.ns, value.revision]),
8587
9147
  contribution: this.engine.contributionSnapshot().revision,
8588
9148
  entries: items.map(({ entry, value, descriptor }) => ({
8589
9149
  id: entry.id,
@@ -8595,8 +9155,10 @@ var MemoryPluginManagement = class {
8595
9155
  });
8596
9156
  }
8597
9157
  async catalog() {
8598
- await this.queue;
9158
+ await this.settled();
9159
+ const pending = this.queue;
8599
9160
  const items = await this.managed();
9161
+ if (pending !== this.queue || this.restorePending) return this.catalog();
8600
9162
  const values = items.map((item) => clone(item.value));
8601
9163
  const snapshot = this.engine.contributionSnapshot();
8602
9164
  const managedIds = new Set(items.map((item) => item.entry.id));
@@ -8655,7 +9217,7 @@ var MemoryPluginManagement = class {
8655
9217
  }
8656
9218
  return {
8657
9219
  revision: this.revision(items),
8658
- writable: this.ctx.settings.writable && this.loader() !== void 0,
9220
+ writable: this.settingsService.writable && this.loader() !== void 0,
8659
9221
  entries: orderPlugins(values),
8660
9222
  diagnostics: [...this.discoveryWarnings, ...this.restoreError === void 0 ? [] : [this.restoreError]]
8661
9223
  };
@@ -8737,7 +9299,7 @@ var MemoryPluginManagement = class {
8737
9299
  return captureMemoryContributionSnapshot(candidate);
8738
9300
  }
8739
9301
  async preview(config, scope, request, signal) {
8740
- await this.queue;
9302
+ await this.settled();
8741
9303
  const items = await this.managed();
8742
9304
  const value = await this.evaluateSnapshot(this.proposal(items, request), config, scope, request.strategyTypeId, signal);
8743
9305
  if (request.expectedRevision !== this.revision(items)) throw new Error("Memory plugin configuration changed during preview; refresh and retry.");
@@ -8762,8 +9324,9 @@ var MemoryPluginManagement = class {
8762
9324
  }
8763
9325
  }
8764
9326
  async apply(config, scope, request, signal) {
9327
+ await this.settled();
8765
9328
  return this.exclusive(async () => {
8766
- if (!this.ctx.settings.writable || !this.loader()) throw new Error("Memory plugin configuration is read-only");
9329
+ if (!this.settingsService.writable || !this.loader()) throw new Error("Memory plugin configuration is read-only");
8767
9330
  const items = await this.managed();
8768
9331
  const choices = this.validateRequest(items, request);
8769
9332
  const expectedSettingsRevision = this.settingsRevision();
@@ -8775,19 +9338,38 @@ var MemoryPluginManagement = class {
8775
9338
  ...request.entries
8776
9339
  }
8777
9340
  });
8778
- await this.updateEntries(items, choices, async () => {
8779
- await this.evaluateSnapshot(this.engine.contributionSnapshot(), config, scope, request.strategyTypeId, signal);
8780
- await this.ctx.settings.mutate(this.settingsNamespace, [{
8781
- op: "set",
8782
- path: ["strategyTypeId"],
8783
- value: next.strategyTypeId
8784
- }, {
8785
- op: "set",
8786
- path: ["entries"],
8787
- value: next.entries
8788
- }], expectedSettingsRevision);
8789
- this.restoreError = void 0;
9341
+ let committedRevision;
9342
+ await this.updateEntries(items, choices, {
9343
+ validate: () => this.evaluateSnapshot(this.engine.contributionSnapshot(), config, scope, request.strategyTypeId, signal),
9344
+ persist: async () => {
9345
+ await this.settingsService.mutate(this.settingsNamespace, [{
9346
+ op: "set",
9347
+ path: ["strategyTypeId"],
9348
+ value: next.strategyTypeId
9349
+ }, {
9350
+ op: "set",
9351
+ path: ["entries"],
9352
+ value: next.entries
9353
+ }], expectedSettingsRevision);
9354
+ committedRevision = this.settingsRevision();
9355
+ },
9356
+ compensate: async () => {
9357
+ if (hash(preferences(this.settings.get())) !== hash(next)) throw new Error("Memory plugin settings changed before rollback; refresh and inspect the saved configuration.");
9358
+ await this.settingsService.mutate(this.settingsNamespace, [previous.strategyTypeId === void 0 ? {
9359
+ op: "unset",
9360
+ path: ["strategyTypeId"]
9361
+ } : {
9362
+ op: "set",
9363
+ path: ["strategyTypeId"],
9364
+ value: previous.strategyTypeId
9365
+ }, {
9366
+ op: "set",
9367
+ path: ["entries"],
9368
+ value: previous.entries
9369
+ }], committedRevision);
9370
+ }
8790
9371
  }, signal);
9372
+ this.restoreError = void 0;
8791
9373
  });
8792
9374
  }
8793
9375
  async restore() {
@@ -8814,55 +9396,99 @@ var MemoryPluginManagement = class {
8814
9396
  this.restoreError = void 0;
8815
9397
  }
8816
9398
  async updateEntries(items, choices, commit, signal) {
8817
- const changed = items.filter((item) => {
8818
- const desired = choices.get(item.entry.id);
8819
- return hash(desired) !== hash({
8820
- enabled: item.value.enabled,
8821
- config: item.value.config
8822
- });
8823
- }).sort((left, right) => Number(choices.get(left.entry.id).enabled) - Number(choices.get(right.entry.id).enabled));
8824
- const restored = [];
9399
+ const previous = items.map((item) => ({
9400
+ item,
9401
+ disabled: item.entry.options.disabled ?? null,
9402
+ config: clone(item.entry.options.config ?? {}),
9403
+ enabled: item.value.enabled,
9404
+ active: item.value.active
9405
+ }));
9406
+ const touched = /* @__PURE__ */ new Set();
9407
+ const replay = async (current) => {
9408
+ for (const item of items) if (!current.some((value) => value.entry.id === item.entry.id)) throw new Error("Memory plugin Entry disappeared during configuration: " + item.entry.id);
9409
+ const ordered = current.filter((item) => choices.has(item.entry.id)).sort((left, right) => Number(choices.get(left.entry.id).enabled) - Number(choices.get(right.entry.id).enabled));
9410
+ for (const item of ordered) {
9411
+ signal?.throwIfAborted();
9412
+ if (this.closed) throw new Error("Memory plugin management service is disposed");
9413
+ const desired = choices.get(item.entry.id);
9414
+ const actual = {
9415
+ enabled: !item.entry.disabled,
9416
+ config: item.editor === void 0 ? {} : memoryStrategyConfigurationValues(item.editor, item.entry.options.config)
9417
+ };
9418
+ const active = contributionRoles(this.engine.contributionSnapshot(), item.entry.id).length > 0;
9419
+ if (hash(desired) === hash(actual) && active === desired.enabled) continue;
9420
+ touched.add(item.entry.id);
9421
+ await item.entry.update(item.editor === void 0 ? { disabled: !desired.enabled } : {
9422
+ disabled: !desired.enabled,
9423
+ config: clone(desired.config)
9424
+ });
9425
+ await item.entry.fiber?.await?.();
9426
+ const registered = contributionRoles(this.engine.contributionSnapshot(), item.entry.id).length > 0;
9427
+ if (item.entry.disabled === desired.enabled || registered !== desired.enabled) throw new Error("Cordis did not register the requested memory plugin state: " + item.entry.id);
9428
+ }
9429
+ };
9430
+ let persisted = false;
8825
9431
  this.changingEntries = true;
8826
9432
  try {
8827
9433
  await this.engine.batch(async () => {
8828
9434
  try {
8829
- for (const item of changed) {
8830
- signal?.throwIfAborted();
8831
- if (this.closed) throw new Error("Memory plugin management service is disposed");
8832
- const desired = choices.get(item.entry.id);
8833
- restored.push({
8834
- item,
8835
- disabled: item.entry.options.disabled ?? null,
8836
- config: clone(item.entry.options.config ?? {})
8837
- });
8838
- await item.entry.update(item.editor === void 0 ? { disabled: !desired.enabled } : {
8839
- disabled: !desired.enabled,
8840
- config: clone(desired.config)
8841
- });
8842
- await item.entry.fiber?.await?.();
8843
- const active = contributionRoles(this.engine.contributionSnapshot(), item.entry.id).length > 0;
8844
- if (item.entry.disabled === desired.enabled || active !== desired.enabled) throw new Error("Cordis did not register the requested memory plugin state: " + item.entry.id);
8845
- }
9435
+ await replay(items);
8846
9436
  signal?.throwIfAborted();
8847
9437
  if (this.closed) throw new Error("Memory plugin management service is disposed");
8848
- await commit?.();
9438
+ if (commit !== void 0) {
9439
+ await commit.validate();
9440
+ await commit.persist();
9441
+ persisted = true;
9442
+ const entries = new Map(Array.from(this.loader()?.entries() ?? []).map((entry) => [entry.id, entry]));
9443
+ await replay(items.flatMap((item) => {
9444
+ const entry = entries.get(item.entry.id);
9445
+ return entry === void 0 ? [] : [{
9446
+ ...item,
9447
+ entry
9448
+ }];
9449
+ }));
9450
+ await commit.validate();
9451
+ }
8849
9452
  } catch (error) {
8850
9453
  const failures = [];
8851
- for (const previous of restored.reverse()) try {
8852
- await previous.item.entry.update(previous.item.editor === void 0 ? { disabled: previous.disabled } : {
8853
- disabled: previous.disabled,
8854
- config: previous.config
8855
- });
8856
- await previous.item.entry.fiber?.await?.();
9454
+ if (persisted) try {
9455
+ await commit.compensate();
8857
9456
  } catch (rollback) {
8858
9457
  failures.push(rollback);
8859
9458
  }
8860
- if (failures.length) throw new AggregateError([error, ...failures], "Plugin change failed and rollback was incomplete; inspect DSH plugin state.");
9459
+ const current = new Map(Array.from(this.loader()?.entries() ?? []).map((entry) => [entry.id, entry]));
9460
+ for (const before of previous.reverse()) {
9461
+ const entry = current.get(before.item.entry.id);
9462
+ if (!(entry === void 0 || hash({
9463
+ disabled: entry.options.disabled ?? null,
9464
+ config: entry.options.config ?? {}
9465
+ }) !== hash({
9466
+ disabled: before.disabled,
9467
+ config: before.config
9468
+ }) || !entry.disabled !== before.enabled || contributionRoles(this.engine.contributionSnapshot(), before.item.entry.id).length > 0 !== before.active) && !touched.has(before.item.entry.id)) continue;
9469
+ try {
9470
+ if (entry === void 0) throw new Error("Memory plugin Entry is unavailable for rollback: " + before.item.entry.id);
9471
+ await entry.update(before.item.editor === void 0 ? { disabled: before.disabled } : {
9472
+ disabled: before.disabled,
9473
+ config: before.config
9474
+ });
9475
+ await entry.fiber?.await?.();
9476
+ if (!entry.disabled !== before.enabled || contributionRoles(this.engine.contributionSnapshot(), entry.id).length > 0 !== before.active) throw new Error("Cordis did not restore the previous memory plugin state: " + entry.id);
9477
+ } catch (rollback) {
9478
+ failures.push(rollback);
9479
+ }
9480
+ }
9481
+ if (failures.length) {
9482
+ this.restorePending = false;
9483
+ this.restoreError = "Plugin change failed and rollback was incomplete; inspect DSH plugin state.";
9484
+ throw new AggregateError([error, ...failures], this.restoreError);
9485
+ }
8861
9486
  throw error;
8862
9487
  }
8863
9488
  });
8864
9489
  } finally {
8865
9490
  this.changingEntries = false;
9491
+ if (this.restorePending) this.scheduleRestore(true);
8866
9492
  }
8867
9493
  }
8868
9494
  exclusive(operation) {
@@ -9437,19 +10063,21 @@ function optionalWorkspaceRegistry(ctx) {
9437
10063
  };
9438
10064
  }
9439
10065
  /** DSH owns assembly; this Host only wires scope, phases and user preferences. */
9440
- function apply(rawContext, config = {}) {
10066
+ function apply(rawContext, rawConfig = {}) {
9441
10067
  const ctx = rawContext;
10068
+ const config = plainHostConfig(rawConfig);
10069
+ const hostSettings = createHostSettings(ctx, rawConfig);
9442
10070
  registerMnemonSubagentTokenUsageProjection(ctx);
9443
10071
  const extensions = provideMemoryRuntime(ctx);
9444
- const memoryPlugins = new MemoryPluginManagement(ctx, extensions);
10072
+ const memoryPlugins = new MemoryPluginManagement(ctx, extensions, hostSettings);
9445
10073
  const pluginInstallation = new MemoryPluginInstallation(ctx);
9446
- const effectiveConfig = (value) => memoryPlugins.resolveConfig(resolveConfig(value));
10074
+ const effectiveConfig = (value) => memoryPlugins.resolveConfig(resolveConfig(value), hostSettings instanceof ProfileMnemonSettings ? value.memoryView ?? { entries: {} } : void 0);
9447
10075
  const prepared = /* @__PURE__ */ new Map();
9448
10076
  const disposePrepared = () => {
9449
10077
  for (const candidate of prepared.values()) candidate.graph.dispose();
9450
10078
  prepared.clear();
9451
10079
  };
9452
- const settings = ctx.settings.register("mnemon", Config, {
10080
+ const settings = hostSettings.register("mnemon", Config, {
9453
10081
  base: config,
9454
10082
  applies: "live",
9455
10083
  validate: (value) => {
@@ -9458,6 +10086,10 @@ function apply(rawContext, config = {}) {
9458
10086
  graph: createRuntimeGraph(effectiveConfig(value), void 0, extensions),
9459
10087
  token: Symbol("prepared-runtime")
9460
10088
  };
10089
+ if (hostSettings instanceof ProfileMnemonSettings) {
10090
+ candidate.graph.dispose();
10091
+ return;
10092
+ }
9461
10093
  prepared.set(value, candidate);
9462
10094
  queueMicrotask(() => {
9463
10095
  if (prepared.get(value)?.token !== candidate.token) return;
@@ -9471,7 +10103,8 @@ function apply(rawContext, config = {}) {
9471
10103
  if (initialCandidate !== void 0) prepared.delete(initialSettings);
9472
10104
  const runtime = new LiveMnemonRuntime(initialCandidate?.graph ?? createRuntimeGraph(effectiveConfig(initialSettings), void 0, extensions), optionalWorkspaceRegistry(ctx), ctx.agents, extensions);
9473
10105
  const resolved = runtime.config;
9474
- ctx.on("settings/updated", ((namespace, next) => {
10106
+ ctx.effect(() => subscribeSettings(ctx, hostSettings, (namespace, value) => {
10107
+ const next = value;
9475
10108
  if (namespace === memoryPlugins.settingsNamespace) {
9476
10109
  runtime.swap(createRuntimeGraph(effectiveConfig(settings.get()), void 0, extensions));
9477
10110
  return;
@@ -9481,9 +10114,9 @@ function apply(rawContext, config = {}) {
9481
10114
  if (candidate !== void 0) prepared.delete(next);
9482
10115
  disposePrepared();
9483
10116
  runtime.swap(candidate?.graph ?? createRuntimeGraph(effectiveConfig(next), void 0, extensions));
9484
- }));
10117
+ }), "dsh-mnemon: live runtime settings");
9485
10118
  ctx.effect(() => memoryPlugins.start(), "dsh-mnemon: plugin graph settings");
9486
- ctx.settings.register("mnemon-ui", InteractionConfig, {
10119
+ hostSettings.register("mnemon-ui", InteractionConfig, {
9487
10120
  base: resolveInteractionConfig(resolved.conversationInteraction),
9488
10121
  applies: "live"
9489
10122
  });
@@ -9491,7 +10124,7 @@ function apply(rawContext, config = {}) {
9491
10124
  let disposed = false;
9492
10125
  const migrate = () => {
9493
10126
  if (disposed) return;
9494
- migrateLegacyDisplayMode(ctx.settings).catch((error) => {
10127
+ migrateLegacyDisplayMode(hostSettings).catch((error) => {
9495
10128
  console.warn("dsh-mnemon: could not persist the builtin displayMode migration", error);
9496
10129
  });
9497
10130
  };
@@ -9504,6 +10137,21 @@ function apply(rawContext, config = {}) {
9504
10137
  unsubscribe();
9505
10138
  };
9506
10139
  }, "dsh-mnemon: canonical displayMode migration");
10140
+ if (hostSettings instanceof ProfileMnemonSettings) ctx.effect(() => {
10141
+ let disposed = false;
10142
+ const loader = ctx.get("loader");
10143
+ Promise.resolve(loader?.await?.()).then(async () => {
10144
+ if (disposed) return;
10145
+ const catalog = await memoryPlugins.catalog();
10146
+ if (disposed) return;
10147
+ await hostSettings.importLegacy(catalog.entries.filter((entry) => entry.roles.includes("source")).map((entry) => entry.entryId));
10148
+ }).catch((error) => {
10149
+ console.warn("dsh-mnemon: could not recover retained legacy settings", error);
10150
+ });
10151
+ return () => {
10152
+ disposed = true;
10153
+ };
10154
+ }, "dsh-mnemon: retained settings migration");
9507
10155
  const coordinator = new MnemonSubagentCoordinator(ctx.subagents, runtime, ctx, () => {
9508
10156
  const taskAgentModel = runtime.config.taskAgentModel;
9509
10157
  if (taskAgentModel.mode !== "fixed") return void 0;
@@ -9532,7 +10180,7 @@ function apply(rawContext, config = {}) {
9532
10180
  if (webContext.connection === void 0) return;
9533
10181
  const managementAuthority = resolved.remoteAccess === "trusted-host" ? "trusted-host" : "loopback";
9534
10182
  const rpc = registerRpc(webContext.connection, runtime, lifecycle, void 0, managementAuthority);
9535
- const settings = registerSettingsRpc(webContext.connection, ctx.settings, managementAuthority);
10183
+ const settings = registerSettingsRpc(webContext.connection, hostSettings, managementAuthority);
9536
10184
  const view = registerViewRpc(webContext.connection, runtime, extensions, memoryPlugins, lifecycle, managementAuthority, pluginInstallation);
9537
10185
  if (Context.is(webContext)) new MnemonRemoteService(webContext, {
9538
10186
  ...rpc,
@@ -9544,4 +10192,4 @@ function apply(rawContext, config = {}) {
9544
10192
  });
9545
10193
  }
9546
10194
  //#endregion
9547
- export { Config, apply, inject, name, provide };
10195
+ export { LiveConfig as Config, apply, inject, name, provide };