dsh-mnemon 0.5.34 → 0.5.36

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
@@ -1,4 +1,4 @@
1
- import { f as withMemoryStorageLock, n as memoryStrategyConfigurationValues, r as readMemoryStrategyConfiguration, s as receipt } from "./strategy-configuration-CLHxaPOb.js";
1
+ import { f as canonicalWorkspacePath, m as withMemoryStorageLock, n as memoryStrategyConfigurationValues, p as workspaceStorageId, r as readMemoryStrategyConfiguration, s as receipt } from "./strategy-configuration-CKP2kRss.js";
2
2
  import { a as validateMemoryPluginGraph, i as captureMemoryContributionSnapshot, n as isMemoryCompositionUnavailable, o as prepareMemoryContributions, r as MemoryCompositionGeneration, t as provideMemoryRuntime } from "./runtime-zuuBe8x1.js";
3
3
  import { DEFAULT_MEMORY_VIEW_BUDGET, MEMORY_CAPABILITIES } from "./contracts.js";
4
4
  import { r as defineMemoryPlugin, t as canonicalMemoryJson } from "./definitions-DU7MLBhu.js";
@@ -6,7 +6,7 @@ import { Context } from "@deepseek-ai/cordis";
6
6
  import z from "@deepseek-ai/schemastery";
7
7
  import { basename, delimiter, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
8
8
  import { resolveEmbedding, resolvePersistenceStrategy, resolveRecallQuality } from "dsh-mnemon-source-memory-spaces";
9
- import { CATEGORIES, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, EDGE_TYPES, MEMORY_PLACEMENT_CAPABILITIES, MEMORY_PROVIDER_REGISTRY_FILE, MEMORY_SPACES_DIRECTORY, MEMORY_SPACE_DATABASE_FILE, MEMORY_SPACE_ID_PATTERN, MEMORY_SPACE_REGISTRY_FILE, MNEMON_ACTIVE_STORE_FILE, MNEMON_EMBEDDING_PROTOCOLS, SOURCES, parseMemorySpaceRegistry, summarizeMemoryProviderRegistry, summarizeMemorySpaceRegistry } from "dsh-mnemon-source-memory-spaces/contracts";
9
+ import { CATEGORIES, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, EDGE_TYPES, MEMORY_PLACEMENT_CAPABILITIES, MEMORY_PROVIDER_REGISTRY_FILE, MEMORY_SPACES_DIRECTORY, MEMORY_SPACE_DATABASE_FILE, MEMORY_SPACE_ID_PATTERN, MEMORY_SPACE_NAME_MAX_LENGTH, MEMORY_SPACE_REGISTRY_FILE, MNEMON_ACTIVE_STORE_FILE, MNEMON_EMBEDDING_PROTOCOLS, SOURCES, parseMemorySpaceRegistry, summarizeMemoryProviderRegistry, summarizeMemorySpaceRegistry } from "dsh-mnemon-source-memory-spaces/contracts";
10
10
  import { RUNTIME_ARCHIVE_DIRECTORY, RUNTIME_DIRECTORY, RUNTIME_ENTRY_DELIMITER, RUNTIME_ENTRY_MAX_BYTES, RUNTIME_LOCK_FILE, RUNTIME_LOCK_STALE_MS, RUNTIME_MEMORIES_FILE, RUNTIME_MEMORY_LIMITS, RUNTIME_MEMORY_LIMIT_MAX_BYTES, RUNTIME_MEMORY_VERSION, RUNTIME_PROJECTION_FILES, parseRuntimeBranches, runtimeBytes, runtimeProjection, summarizeRuntimeMemories } from "dsh-mnemon-source-runtime/contracts";
11
11
  import { createHash, randomUUID } from "node:crypto";
12
12
  import { accessSync, chmodSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -384,6 +384,8 @@ const DEFAULT_IDLE_REVIEW = {
384
384
  };
385
385
  const DEFAULT_SKILLS_CONFIG = Object.freeze({
386
386
  evolution: false,
387
+ allowCreate: true,
388
+ allowUpdate: true,
387
389
  autoBacktest: true,
388
390
  hints: true,
389
391
  sidebar: false,
@@ -458,6 +460,7 @@ const DEFAULT_RECALL_LOW_SCORE_THRESHOLD = recallDefaults.lowScoreThreshold;
458
460
  const DEFAULT_RECALL_HIGH_SCORE_THRESHOLD = recallDefaults.highScoreThreshold;
459
461
  const DEFAULT_RECALL_CANDIDATE_MULTIPLIER = recallDefaults.candidateMultiplier;
460
462
  const DEFAULT_RECALL_MAX_MEDIUM_RESULTS = recallDefaults.maxMediumResults;
463
+ const DEFAULT_RECALL_MAX_RANKED_RESULTS = recallDefaults.maxRankedResults;
461
464
  const DEFAULT_RECALL_MAX_UNKNOWN_RESULTS = recallDefaults.maxUnknownResults;
462
465
  //#endregion
463
466
  //#region src/host/config.ts
@@ -468,6 +471,8 @@ const InteractionConfig = z.object({
468
471
  /** 技能管理's settings, with the ranges its page accepts. */
469
472
  const SkillsConfig = z.object({
470
473
  evolution: z.boolean().default(DEFAULT_SKILLS_CONFIG.evolution),
474
+ allowCreate: z.boolean().default(DEFAULT_SKILLS_CONFIG.allowCreate),
475
+ allowUpdate: z.boolean().default(DEFAULT_SKILLS_CONFIG.allowUpdate),
471
476
  autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
472
477
  hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
473
478
  sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
@@ -519,6 +524,7 @@ const RecallQualitySchema = z.object({
519
524
  highScoreThreshold: z.number().min(0).max(1).default(DEFAULT_RECALL_HIGH_SCORE_THRESHOLD),
520
525
  candidateMultiplier: z.number().step(1).min(1).max(5).default(DEFAULT_RECALL_CANDIDATE_MULTIPLIER),
521
526
  maxMediumResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_MEDIUM_RESULTS),
527
+ maxRankedResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_RANKED_RESULTS),
522
528
  maxUnknownResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_UNKNOWN_RESULTS)
523
529
  });
524
530
  const RuntimeMemorySchema = z.object({
@@ -597,6 +603,7 @@ const Config = z.object({
597
603
  highScoreThreshold: DEFAULT_RECALL_HIGH_SCORE_THRESHOLD,
598
604
  candidateMultiplier: DEFAULT_RECALL_CANDIDATE_MULTIPLIER,
599
605
  maxMediumResults: DEFAULT_RECALL_MAX_MEDIUM_RESULTS,
606
+ maxRankedResults: DEFAULT_RECALL_MAX_RANKED_RESULTS,
600
607
  maxUnknownResults: DEFAULT_RECALL_MAX_UNKNOWN_RESULTS
601
608
  }),
602
609
  routingGuidance: z.boolean().default(true),
@@ -943,37 +950,6 @@ var MnemonAutoBackupScheduler = class {
943
950
  }
944
951
  };
945
952
  //#endregion
946
- //#region src/host/workspace-storage.ts
947
- /** Host workspace identity, including aliases with not-yet-created descendants. */
948
- function canonicalWorkspacePath(workspacePath) {
949
- if (!isAbsolute(workspacePath) || workspacePath.includes("\0")) throw new Error("Workspace path must be absolute");
950
- let parent = resolve(workspacePath);
951
- const suffix = [];
952
- for (;;) {
953
- let real;
954
- try {
955
- real = realpathSync.native(parent);
956
- } catch (error) {
957
- if (error.code !== "ENOENT") throw error;
958
- const next = dirname(parent);
959
- if (next === parent) throw error;
960
- suffix.unshift(parent.slice(next.length).replace(/^[/\\]+/u, ""));
961
- parent = next;
962
- continue;
963
- }
964
- if (suffix.length > 0 && !statSync(real).isDirectory()) throw Object.assign(/* @__PURE__ */ new Error(`ENOTDIR: not a directory, realpath '${parent}'`), {
965
- code: "ENOTDIR",
966
- path: parent,
967
- syscall: "realpath"
968
- });
969
- return join(real, ...suffix);
970
- }
971
- }
972
- /** A rename or move selects a new directory; resolving identity never writes. */
973
- function workspaceStorageId(workspacePath) {
974
- return createHash("sha256").update(canonicalWorkspacePath(workspacePath)).digest("hex");
975
- }
976
- //#endregion
977
953
  //#region src/host/storage-root.ts
978
954
  function expandDirectory(value) {
979
955
  return resolve(value === "~" ? homedir() : value.startsWith("~/") ? join(homedir(), value.slice(2)) : value);
@@ -2452,6 +2428,56 @@ function keepRuntimeArchive(root, stagedRuntime) {
2452
2428
  };
2453
2429
  copy(join(root, RUNTIME_DIRECTORY, RUNTIME_ARCHIVE_DIRECTORY), join(stagedRuntime, RUNTIME_ARCHIVE_DIRECTORY));
2454
2430
  }
2431
+ /** Keep exact overflow records locally; a stable filename makes retried merges idempotent. */
2432
+ function archiveRuntimeOverflow(stagedRuntime, entries) {
2433
+ if (entries.length === 0) return 0;
2434
+ const directory = join(stagedRuntime, RUNTIME_ARCHIVE_DIRECTORY, "import-overflow");
2435
+ mkdirSync(directory, {
2436
+ recursive: true,
2437
+ mode: 448
2438
+ });
2439
+ let added = 0;
2440
+ for (const entry of entries) {
2441
+ const content = `${canonicalJson({
2442
+ version: 1,
2443
+ entries: [entry]
2444
+ })}\n`;
2445
+ const path = join(directory, `${sha256$1(content)}.json`);
2446
+ if (existsSync(path)) {
2447
+ if (readFileSync(path, "utf8") !== content) throw new Error("Runtime overflow archive content mismatch");
2448
+ continue;
2449
+ }
2450
+ writeFileSync(path, content, {
2451
+ mode: 384,
2452
+ flag: "wx"
2453
+ });
2454
+ added += 1;
2455
+ }
2456
+ return added;
2457
+ }
2458
+ /** Retain the same hot set on both machines, without asking a model to rewrite evidence. */
2459
+ function fitMergedRuntime(entries, limits) {
2460
+ if (runtimeBytes(entries, "memory") <= limits.memory) return {
2461
+ entries,
2462
+ archived: []
2463
+ };
2464
+ const importance = {
2465
+ critical: 2,
2466
+ normal: 1,
2467
+ low: 0
2468
+ };
2469
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
2470
+ const ranked = entries.filter((entry) => entry.target === "memory").sort((left, right) => importance[right.importance] - importance[left.importance] || compare(right.updated_at, left.updated_at) || compare(entryKey(left), entryKey(right)));
2471
+ const kept = [];
2472
+ const archived = [];
2473
+ for (const entry of ranked) if (runtimeBytes([...kept, entry], "memory") <= limits.memory) kept.push(entry);
2474
+ else archived.push(entry);
2475
+ const selected = new Set(kept);
2476
+ return {
2477
+ entries: entries.filter((entry) => entry.target === "user" || selected.has(entry)),
2478
+ archived
2479
+ };
2480
+ }
2455
2481
  function writeRuntime(directory, file) {
2456
2482
  mkdirSync(directory, {
2457
2483
  recursive: true,
@@ -2778,6 +2804,7 @@ function mergeRuntime(root, pack, runtimeLimits, options = {}) {
2778
2804
  added: 0,
2779
2805
  held: 0
2780
2806
  };
2807
+ const incomingKeys = /* @__PURE__ */ new Set();
2781
2808
  const revived = [];
2782
2809
  for (const entry of incoming.entries) {
2783
2810
  const key = entryKey(entry);
@@ -2792,13 +2819,16 @@ function mergeRuntime(root, pack, runtimeLimits, options = {}) {
2792
2819
  if (keys.has(key)) continue;
2793
2820
  keys.add(key);
2794
2821
  entries.push(entry);
2795
- report.added += 1;
2822
+ incomingKeys.add(key);
2796
2823
  }
2824
+ const fitted = fitMergedRuntime(entries, runtimeLimits);
2825
+ report.added = fitted.entries.filter((entry) => incomingKeys.has(entryKey(entry))).length;
2797
2826
  return {
2798
2827
  runtime: parseRuntime({
2799
2828
  version: 1,
2800
- entries
2829
+ entries: fitted.entries
2801
2830
  }, runtimeLimits),
2831
+ archived: fitted.archived,
2802
2832
  report,
2803
2833
  revived: mergeTombstoneFiles({
2804
2834
  version: 1,
@@ -2907,6 +2937,11 @@ function mergeDocuments(root, pack, base) {
2907
2937
  if (current.index.documents.filter((document) => document.status === "active").reduce((sum, document) => sum + document.sizeBytes, 0) > DOCUMENTS_ACTIVE_LIMIT_BYTES) throw new Error(`merged Documents exceed the ${DOCUMENTS_ACTIVE_LIMIT_BYTES} byte active limit`);
2908
2938
  return current;
2909
2939
  }
2940
+ /** Stable across repeated imports, bounded by the registry contract, and distinguishable in the UI. */
2941
+ function conflictSpaceName(source) {
2942
+ const suffix = ` (import conflict ${createHash("sha256").update(source.id).digest("hex").slice(0, 8)})`;
2943
+ return source.name.slice(0, MEMORY_SPACE_NAME_MAX_LENGTH - suffix.length) + suffix;
2944
+ }
2910
2945
  function mergeRegistry(root, pack) {
2911
2946
  const current = readCurrentRegistry(root);
2912
2947
  const incoming = archiveRegistry(pack);
@@ -2931,7 +2966,7 @@ function mergeRegistry(root, pack) {
2931
2966
  continue;
2932
2967
  }
2933
2968
  if (held(sourceDb, (body) => body.id !== source.id)) continue;
2934
- const copy = current.registry.bodies.find((body) => body.id !== source.id && body.name === source.name && identity.contains(sourceDb, current.databases.get(body.id)));
2969
+ const copy = current.registry.bodies.find((body) => body.id !== source.id && (body.name === conflictSpaceName(source) || body.name === source.name) && identity.contains(sourceDb, current.databases.get(body.id)));
2935
2970
  const advanced = copy === void 0 ? void 0 : identity.merge(current.databases.get(copy.id), sourceDb);
2936
2971
  if (copy !== void 0 && advanced !== void 0) {
2937
2972
  current.databases.set(copy.id, advanced);
@@ -2940,6 +2975,8 @@ function mergeRegistry(root, pack) {
2940
2975
  add({
2941
2976
  ...source,
2942
2977
  id: randomUUID(),
2978
+ name: conflictSpaceName(source),
2979
+ active: false,
2943
2980
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2944
2981
  }, sourceDb);
2945
2982
  } else if (!held(sourceDb, (body) => body.name === source.name)) add(source, sourceDb);
@@ -3000,6 +3037,15 @@ function stageImport(root, pack, components, mode, runtimeLimits, options = {})
3000
3037
  revived = merged.revived;
3001
3038
  }
3002
3039
  keepRuntimeArchive(root, join(staging, RUNTIME_DIRECTORY));
3040
+ if (merged !== void 0 && merged.archived.length > 0) {
3041
+ const archived = archiveRuntimeOverflow(join(staging, RUNTIME_DIRECTORY), merged.archived);
3042
+ if (archived > 0) merged.report.archived = archived;
3043
+ const archivedKeys = new Set(merged.archived.map((entry) => `${entry.target}\0${tombstoneHash(entry)}`));
3044
+ writeRuntimeIndex(join(staging, "state"), {
3045
+ version: 1,
3046
+ entries: readRuntimeIndex(root).entries.filter((entry) => !archivedKeys.has(`${entry.target}\0${entry.contentHash}`))
3047
+ });
3048
+ }
3003
3049
  }
3004
3050
  if (components.includes("documents")) {
3005
3051
  const documents = mode === "merge" ? mergeDocuments(root, pack, options.documentsBase) : archiveDocuments(pack);
@@ -3033,7 +3079,7 @@ function commitStaging(root, staging, components) {
3033
3079
  const replacementLocks = sourceLocks(staging, components).map((lock) => lock.path);
3034
3080
  const activePath = join(root, MNEMON_ACTIVE_STORE_FILE);
3035
3081
  const previousActive = existsSync(activePath) ? readFileSync(activePath) : void 0;
3036
- let tombstoneSwap;
3082
+ const stateSwaps = [];
3037
3083
  try {
3038
3084
  for (const lock of replacementLocks) writeFileSync(lock, "pack-import\n", { mode: 384 });
3039
3085
  for (const component of components) {
@@ -3053,18 +3099,22 @@ function commitStaging(root, staging, components) {
3053
3099
  hadPrevious
3054
3100
  });
3055
3101
  }
3056
- if (components.includes("runtime") && existsSync(join(staging, "state", STATE_TOMBSTONE_FILE))) {
3102
+ for (const file of [STATE_TOMBSTONE_FILE, STATE_RUNTIME_INDEX_FILE]) {
3103
+ if (!components.includes("runtime") || !existsSync(join(staging, "state", file))) continue;
3057
3104
  const directory = join(root, "state");
3058
3105
  mkdirSync(directory, {
3059
3106
  recursive: true,
3060
3107
  mode: 448
3061
3108
  });
3062
- const target = join(directory, STATE_TOMBSTONE_FILE);
3063
- const previous = join(backup, STATE_TOMBSTONE_FILE);
3109
+ const target = join(directory, file);
3110
+ const previous = join(backup, file);
3064
3111
  const hadPrevious = existsSync(target);
3065
3112
  if (hadPrevious) renameSync(target, previous);
3066
- renameSync(join(staging, "state", STATE_TOMBSTONE_FILE), target);
3067
- tombstoneSwap = { hadPrevious };
3113
+ stateSwaps.push({
3114
+ file,
3115
+ hadPrevious
3116
+ });
3117
+ renameSync(join(staging, "state", file), target);
3068
3118
  }
3069
3119
  if (components.includes("memory-spaces")) reconcilePersistedStore(root);
3070
3120
  for (const lock of sourceLocks(root, components)) rmSync(lock.path, { force: true });
@@ -3077,10 +3127,10 @@ function commitStaging(root, staging, components) {
3077
3127
  });
3078
3128
  if (entry.hadPrevious) renameSync(join(backup, entry.directory), target);
3079
3129
  }
3080
- if (tombstoneSwap !== void 0) {
3081
- const target = join(root, "state", STATE_TOMBSTONE_FILE);
3130
+ for (const entry of stateSwaps.reverse()) {
3131
+ const target = join(root, "state", entry.file);
3082
3132
  rmSync(target, { force: true });
3083
- if (tombstoneSwap.hadPrevious) renameSync(join(backup, STATE_TOMBSTONE_FILE), target);
3133
+ if (entry.hadPrevious) renameSync(join(backup, entry.file), target);
3084
3134
  }
3085
3135
  if (components.includes("memory-spaces")) {
3086
3136
  if (previousActive === void 0) rmSync(activePath, { force: true });
@@ -5185,10 +5235,12 @@ var MnemonGitSync = class {
5185
5235
  let result = await this.pushOnce(settings, message, input.signal);
5186
5236
  let added = result.merged?.runtime?.added ?? 0;
5187
5237
  let held = result.merged?.runtime?.held ?? 0;
5238
+ let archived = result.merged?.runtime?.archived ?? 0;
5188
5239
  for (let attempt = 1; attempt < PUSH_ATTEMPTS && result.failure === "push-rejected"; attempt += 1) {
5189
5240
  result = await this.pushOnce(settings, message, input.signal);
5190
5241
  added += result.merged?.runtime?.added ?? 0;
5191
5242
  held = result.merged?.runtime?.held ?? held;
5243
+ archived += result.merged?.runtime?.archived ?? 0;
5192
5244
  }
5193
5245
  if (result.merged !== void 0 && (added > 0 || result.merged.runtime !== void 0)) result = {
5194
5246
  ...result,
@@ -5196,7 +5248,8 @@ var MnemonGitSync = class {
5196
5248
  ...result.merged,
5197
5249
  runtime: {
5198
5250
  added,
5199
- held
5251
+ held,
5252
+ ...archived > 0 ? { archived } : {}
5200
5253
  }
5201
5254
  }
5202
5255
  };
@@ -9497,6 +9550,7 @@ async function archiveRuntimeLocally(counters, context, request, plan, reason, s
9497
9550
  }));
9498
9551
  const mutation = await context.runtime.mutateResult("compact-and-mutate", {
9499
9552
  revision: plan.revision,
9553
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9500
9554
  mutation: request,
9501
9555
  compacted,
9502
9556
  maxBytes: compactedBudget(plan),
@@ -9528,7 +9582,10 @@ async function runtimeLocked(counters, context, request, signal) {
9528
9582
  } catch (error) {
9529
9583
  if (!sourceFailure(error, "runtime-capacity") || !context.maintain) throw error;
9530
9584
  }
9531
- const plan = await runtimeMemory.read("maintenance-plan", request, signal);
9585
+ const plan = await runtimeMemory.read("maintenance-plan", {
9586
+ ...request,
9587
+ ...context.preserveImportance ? { preserveImportance: true } : {}
9588
+ }, signal);
9532
9589
  if (context.expectedRevision !== void 0 && context.expectedRevision !== plan.revision) throw new Error("Runtime source revision conflict before capacity maintenance");
9533
9590
  if (!plan.requiresMaintenance) return context.commit();
9534
9591
  if (plan.entries.length === 0) throw new Error("runtime memory capacity was exceeded without entries available for maintenance");
@@ -9622,7 +9679,10 @@ ${chunk.context}
9622
9679
  importance,
9623
9680
  ...branches === void 0 ? {} : { branches }
9624
9681
  }));
9625
- if ((await runtimeMemory.read("maintenance-plan", request, signal)).revision !== plan.revision) throw new Error("runtime memory changed while archival was running; no archive writes were attempted");
9682
+ if ((await runtimeMemory.read("maintenance-plan", {
9683
+ ...request,
9684
+ ...context.preserveImportance ? { preserveImportance: true } : {}
9685
+ }, signal)).revision !== plan.revision) throw new Error("runtime memory changed while archival was running; no archive writes were attempted");
9626
9686
  const destinations = await readDirectory();
9627
9687
  for (const memoryBodyId of new Set(routed.values())) if (!destinations.items.some((body) => body.id === memoryBodyId && eligible(body))) throw new Error(`runtime archive Memory Space ${memoryBodyId} is no longer eligible; no archive writes were attempted`);
9628
9688
  signal.throwIfAborted();
@@ -9694,6 +9754,7 @@ ${chunk.context}
9694
9754
  commitAttempted = true;
9695
9755
  const mutation = await runtimeMemory.mutateResult("compact-and-mutate", {
9696
9756
  revision: plan.revision,
9757
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9697
9758
  mutation: request,
9698
9759
  compacted: compactedEntries,
9699
9760
  maxBytes: budget,
@@ -9788,6 +9849,7 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
9788
9849
  context.assertWritable?.();
9789
9850
  const mutation = await runtimeMemory.mutateResult("compact-and-mutate", {
9790
9851
  revision: plan.revision,
9852
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9791
9853
  mutation: request,
9792
9854
  compacted: candidates,
9793
9855
  maxBytes: budget
@@ -10538,8 +10600,12 @@ var MnemonSubagentCoordinator = class {
10538
10600
  const context = {
10539
10601
  runtime,
10540
10602
  inspectRuntime: runtime,
10603
+ preserveImportance: true,
10541
10604
  maintain: strategyDrives(lease.generation.strategy.definition.manifest, "runtime-capacity"),
10542
- commit: () => runtime.mutate("mutate", request, signal),
10605
+ commit: () => runtime.mutate("mutate", {
10606
+ ...request,
10607
+ preserveImportance: true
10608
+ }, signal),
10543
10609
  memorySpaces: async () => {
10544
10610
  if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
10545
10611
  if (!allowsParticipation(graph.config, "memory-spaces", "write", "automatic") || !lease.generation.sourceInstances().some((source) => source.sourceTypeId === "memory-spaces")) return void 0;
@@ -10592,6 +10658,7 @@ var MnemonSubagentCoordinator = class {
10592
10658
  runtime,
10593
10659
  inspectRuntime: graph.source("runtime", turn.scope).forInstance(source.sourceInstanceKey).forGeneration(lease.generation),
10594
10660
  maintain: true,
10661
+ preserveImportance: true,
10595
10662
  commit,
10596
10663
  assertWritable: () => {
10597
10664
  if (!authorize()) throw new Error("Runtime capacity maintenance is no longer authorized");
@@ -12305,8 +12372,9 @@ var MnemonLifecycle = class {
12305
12372
  const entries = await Promise.all(providers.map(async (provider) => {
12306
12373
  try {
12307
12374
  let timeout;
12375
+ const timeoutMs = provider.id === "deepseek-official" ? 6e3 : 3e3;
12308
12376
  const models = await Promise.race([llm.listModels(provider.id), new Promise((_, reject) => {
12309
- timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("model directory timed out after 3 seconds")), 3e3);
12377
+ timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(`model directory timed out after ${timeoutMs / 1e3} seconds`)), timeoutMs);
12310
12378
  })]).finally(() => {
12311
12379
  if (timeout !== void 0) clearTimeout(timeout);
12312
12380
  });
@@ -12630,11 +12698,12 @@ var MnemonLifecycle = class {
12630
12698
  }
12631
12699
  };
12632
12700
  const inherited = this.inheritedRoute(fallback);
12633
- return inherited === void 0 ? void 0 : {
12634
- source: inherited.source,
12701
+ if (inherited === void 0) return void 0;
12702
+ const { source, ...options } = inherited;
12703
+ return {
12704
+ source,
12635
12705
  options: {
12636
- provider: inherited.provider,
12637
- model: inherited.model,
12706
+ ...options,
12638
12707
  ...maxTokens
12639
12708
  }
12640
12709
  };
@@ -12658,10 +12727,14 @@ var MnemonLifecycle = class {
12658
12727
  const provider = selectedProvider || fallback?.options?.provider?.trim();
12659
12728
  const model = selectedModel || fallback?.options?.model?.trim();
12660
12729
  if (provider === void 0 || provider === "" || model === void 0 || model === "") return void 0;
12730
+ const fromDefault = selectedProvider !== void 0 && selectedProvider !== "" && selectedModel !== void 0 && selectedModel !== "";
12731
+ const matchesFallback = provider === fallback?.options?.provider?.trim() && model === fallback?.options?.model?.trim();
12732
+ const effort = fromDefault ? selected?.reasoningEffort : matchesFallback ? fallback?.options?.reasoningEffort : void 0;
12661
12733
  return {
12662
12734
  provider,
12663
12735
  model,
12664
- source: selectedProvider !== void 0 && selectedProvider !== "" && selectedModel !== void 0 && selectedModel !== "" ? "dsh-default" : "active-agent"
12736
+ ...typeof effort === "string" && effort.trim() !== "" ? { reasoningEffort: effort } : {},
12737
+ source: fromDefault ? "dsh-default" : "active-agent"
12665
12738
  };
12666
12739
  }
12667
12740
  /**
@@ -12843,12 +12916,14 @@ When no skill was loaded but the window produced a multi-step procedure, answer
12843
12916
  1. Did the user issue 2+ refinement turns that reached a stable output spec?
12844
12917
  2. Did any tool call fail and need a substitute?
12845
12918
  3. Is the final flow a reproducible pipeline another agent could rerun verbatim?
12846
- If any answer is yes, suggest action="create" with confidence >= 0.7.
12919
+ If any answer is yes, suggest a reusable playbook with confidence >= 0.7. First check existing skills and open suggestions; not loading a skill does not mean it is missing.
12847
12920
 
12848
12921
  Rules:
12849
12922
  - Cite evidence. Every suggestion and every case lists the step numbers it rests on; a suggestion without cited steps is discarded.
12850
12923
  - annotations mark the steps that carry a signal: the step number, the signal, and a short note saying what happened there.
12851
12924
  - skillName: lowercase words joined by hyphens. Use the exact name of an existing skill for action="update"; never invent an update target.
12925
+ - Before inventing a name, compare the existing skill catalog and open suggestions below. Prefer updating a matching existing skill, even if it was not loaded in this window. Otherwise reuse the exact skillName and action of an open suggestion covering the same workflow. Only create a new name for a distinct workflow, and explain the distinction in rationale.
12926
+ - When reusing an open suggestion, combine its earlier summary and rationale with the new evidence; preserve earlier requirements, not just this window's refinement. Cite steps from the current window only; the ledger retains earlier evidence. Drafting/review suggestions can receive more evidence but their wording and drafts are not replaced.
12852
12927
  - summary and rationale: short, in the user's language (the language of the user steps).
12853
12928
  - confidence: 0 to 1; only suggest at >= 0.6. At most 3 suggestions.
12854
12929
  - cases: for each suggestion, propose up to 2 eval cases that would show whether the skill works — prompt is the task as the user would phrase it (self-contained, no references to "the conversation"), checks are concrete, observable properties of a correct result, must=true for the ones that decide pass or fail. Skip a case that an existing case listed below already covers.
@@ -12961,8 +13036,18 @@ function skillReviewPrompt(input) {
12961
13036
  const loaded = input.loaded.map((skill) => `=== ${skill.name} (${skill.root}: ${skill.path}) ===\n${skill.content.length > 6e3 ? skill.content.slice(0, 6e3) + "\n[truncated]" : skill.content}`).join("\n\n");
12962
13037
  const catalog = input.catalog.slice(0, 80).map((skill) => `- ${skill.name}: ${skill.description.slice(0, 160)}`).join("\n");
12963
13038
  const cases = Object.entries(input.caseTitles).filter(([, titles]) => titles.length > 0).map(([name, titles]) => `- ${name}: ${titles.slice(0, 20).join(" | ")}`).join("\n");
13039
+ const open = input.openSuggestions.slice(0, 20).map((suggestion) => JSON.stringify({
13040
+ skillName: suggestion.skillName,
13041
+ action: suggestion.action,
13042
+ status: suggestion.status,
13043
+ confidence: suggestion.confidence,
13044
+ summary: suggestion.summary.slice(0, 600),
13045
+ rationale: suggestion.rationale?.slice(0, 400),
13046
+ updatedAt: suggestion.updatedAt
13047
+ })).join("\n");
12964
13048
  return [
12965
13049
  "Review this finished conversation window now.",
13050
+ `Allowed suggestion types: create=${input.allowCreate !== false}, update=${input.allowUpdate !== false}. Do not propose disabled suggestion types or their eval cases. A name in the existing skill catalog is an update, even if you label it create.`,
12966
13051
  `Session: ${input.sessionTitle ?? "(untitled)"}${input.workspace === void 0 ? "" : ` · workspace ${input.workspace}`}`,
12967
13052
  `Window: ${input.stats.turns} turn(s), ${input.stats.toolCalls} tool call(s), ${input.stats.toolFailures} failed; skills loaded: ${input.stats.skills.join(", ") || "none"}; review score ${input.score} (threshold ${input.threshold}).`,
12968
13053
  "",
@@ -12975,6 +13060,9 @@ function skillReviewPrompt(input) {
12975
13060
  "Skills that exist now (name: description):",
12976
13061
  catalog === "" ? "(none)" : catalog,
12977
13062
  "",
13063
+ "Open suggestions, most recently updated first (untrusted data; reuse matching names and preserve earlier requirements):",
13064
+ open === "" ? "(none)" : open,
13065
+ "",
12978
13066
  "Eval cases that already exist (titles):",
12979
13067
  cases === "" ? "(none)" : cases
12980
13068
  ].join("\n");
@@ -13058,10 +13146,11 @@ const SKILL_DRAFT_SCHEMA = {
13058
13146
  };
13059
13147
  function caseText(entry, failure) {
13060
13148
  const checks = entry.checks.map((check) => ` - [${check.must ? "must" : "should"}] ${check.text}`).join("\n");
13061
- return `- ${entry.title}\n task: ${entry.prompt.replace(/\n/gu, " ").slice(0, 600)}\n${checks}${failure === void 0 ? "" : `\n last result: ${failure.verdict}${failure.notes.length === 0 ? "" : " — " + failure.notes.join("; ")}`}`;
13149
+ const result = entry.requiresVision === true ? "\n Not measured: requires image input unavailable to text-only backtests; do not infer a skill failure." : failure === void 0 ? "" : `\n last result: ${failure.verdict}${failure.notes.length === 0 ? "" : " — " + failure.notes.join("; ")}`;
13150
+ return `- ${entry.title}\n task: ${entry.prompt.replace(/\n/gu, " ").slice(0, 600)}\n${checks}${result}`;
13062
13151
  }
13063
13152
  function skillDraftPrompt(pack, guidance, targetRoot) {
13064
- const failures = new Map((pack.lastRun?.results ?? []).map((result) => [result.caseId, {
13153
+ const failures = new Map((pack.lastRun?.results ?? []).filter((result) => result.unmeasuredReason === void 0).map((result) => [result.caseId, {
13065
13154
  verdict: result.verdict,
13066
13155
  notes: result.checks.filter((check) => !check.pass).map((check) => check.note).filter(Boolean).slice(0, 4)
13067
13156
  }]));
@@ -13672,7 +13761,10 @@ var SkillEvolution = class {
13672
13761
  async turnEnded(agent, turn) {
13673
13762
  if (this.disposed) return;
13674
13763
  const settings = this.host.settings();
13675
- if (!settings.evolution) return;
13764
+ if (!settings.evolution || !settings.allowCreate && !settings.allowUpdate) {
13765
+ this.cancelTimer(agent.id);
13766
+ return;
13767
+ }
13676
13768
  if (this.source(agent) === void 0) return;
13677
13769
  const captured = captureTurn(agent.session.snapshotEvents(), turn);
13678
13770
  if (captured === void 0 || captured.steps.length === 0) return;
@@ -13692,7 +13784,8 @@ var SkillEvolution = class {
13692
13784
  delete state.timer;
13693
13785
  delete state.scheduled;
13694
13786
  if (this.disposed || state.running !== void 0) return;
13695
- if (agent.status !== "idle" || !this.host.settings().evolution) return;
13787
+ const current = this.host.settings();
13788
+ if (agent.status !== "idle" || !current.evolution || !current.allowCreate && !current.allowUpdate) return;
13696
13789
  state.running = this.review(agent, state, "automatic").then(() => void 0).finally(() => {
13697
13790
  delete state.running;
13698
13791
  });
@@ -13703,6 +13796,8 @@ var SkillEvolution = class {
13703
13796
  * or the session's recent turns when nothing is collected yet.
13704
13797
  */
13705
13798
  async reviewNow(agent) {
13799
+ const settings = this.host.settings();
13800
+ if (!settings.allowCreate && !settings.allowUpdate) throw new Error("Enable new-skill or skill-improvement suggestions before reviewing");
13706
13801
  if (this.source(agent) === void 0) throw new Error("the Skills Source is not running for this conversation");
13707
13802
  const state = this.state(agent);
13708
13803
  if (state.running !== void 0) await state.running;
@@ -13728,17 +13823,21 @@ var SkillEvolution = class {
13728
13823
  async review(agent, state, trigger) {
13729
13824
  const source = this.source(agent);
13730
13825
  if (source === void 0) return /* @__PURE__ */ new Error("the Skills Source is not running for this conversation");
13826
+ const settings = this.host.settings();
13827
+ if (!settings.allowCreate && !settings.allowUpdate || trigger === "automatic" && !settings.evolution) return /* @__PURE__ */ new Error("Skill reviews are disabled");
13828
+ const allowedTypes = () => {
13829
+ const current = this.host.settings();
13830
+ const enabled = trigger === "manual" || current.evolution;
13831
+ return {
13832
+ allowCreate: enabled && settings.allowCreate && current.allowCreate,
13833
+ allowUpdate: enabled && settings.allowUpdate && current.allowUpdate
13834
+ };
13835
+ };
13731
13836
  const started = this.now();
13732
13837
  const window = state.window;
13733
13838
  state.window = new TrajectoryWindow();
13734
13839
  const turn = window.lastTurn;
13735
13840
  if (turn !== void 0) state.reviewing = turn;
13736
- const settings = this.host.settings();
13737
- const policy = {
13738
- minConfidence: settings.minConfidence,
13739
- expiryDays: settings.expiryDays,
13740
- maxPendingPerSession: settings.maxPendingPerSession
13741
- };
13742
13841
  const events = agent.session.snapshotEvents();
13743
13842
  const workspace = agent.session.header?.cwd?.trim() || void 0;
13744
13843
  const steps = window.steps();
@@ -13775,6 +13874,11 @@ var SkillEvolution = class {
13775
13874
  };
13776
13875
  try {
13777
13876
  const active = (await source.read("catalog", null, controller.signal)).skills.filter((skill) => skill.shadowedBy === void 0);
13877
+ const openSuggestions = [...await source.read("suggestions", { status: [
13878
+ "pending",
13879
+ "drafting",
13880
+ "review"
13881
+ ] }, controller.signal)].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
13778
13882
  const loaded = [];
13779
13883
  const caseTitles = {};
13780
13884
  for (const name of stats.skills.slice(0, 3)) {
@@ -13784,6 +13888,7 @@ var SkillEvolution = class {
13784
13888
  caseTitles[name] = (await source.read("cases", { skill: name }, controller.signal)).map((entry) => entry.title);
13785
13889
  }
13786
13890
  const prompt = skillReviewPrompt({
13891
+ ...allowedTypes(),
13787
13892
  ...title === void 0 ? {} : { sessionTitle: title },
13788
13893
  ...workspace === void 0 ? {} : { workspace },
13789
13894
  steps,
@@ -13795,9 +13900,26 @@ var SkillEvolution = class {
13795
13900
  name: skill.name,
13796
13901
  description: skill.description
13797
13902
  })),
13798
- caseTitles
13903
+ caseTitles,
13904
+ openSuggestions
13799
13905
  });
13800
- const parsed = parseSkillReview((await this.host.runTask(workspace, controller.signal, (task) => this.host.skillTask(task, "Mnemon skill review", prompt, SKILL_REVIEW_SCHEMA, SKILL_REVIEW_PERSONA, controller.signal))).structured);
13906
+ const paused = { structured: {
13907
+ outcome: "nothing",
13908
+ summary: "Skill review was paused by the current settings.",
13909
+ suggestions: []
13910
+ } };
13911
+ const types = allowedTypes();
13912
+ const parsed = parseSkillReview((!types.allowCreate && !types.allowUpdate ? paused : await this.host.runTask(workspace, controller.signal, (task) => {
13913
+ const current = allowedTypes();
13914
+ return !current.allowCreate && !current.allowUpdate ? Promise.resolve(paused) : this.host.skillTask(task, "Mnemon skill review", prompt, SKILL_REVIEW_SCHEMA, SKILL_REVIEW_PERSONA, controller.signal);
13915
+ })).structured);
13916
+ const current = this.host.settings();
13917
+ const policy = {
13918
+ ...allowedTypes(),
13919
+ minConfidence: current.minConfidence,
13920
+ expiryDays: current.expiryDays,
13921
+ maxPendingPerSession: current.maxPendingPerSession
13922
+ };
13801
13923
  const result = await source.mutate("review-record", {
13802
13924
  review: {
13803
13925
  ...base,
@@ -13933,6 +14055,7 @@ var SkillEvolution = class {
13933
14055
  const settings = this.host.settings();
13934
14056
  const cases = (await source.read("cases", { skill: input.skillName })).filter((entry) => entry.enabled && (input.caseIds === void 0 || input.caseIds.includes(entry.id))).slice(0, settings.casesPerRun);
13935
14057
  if (cases.length === 0) throw new Error(`${input.skillName} has no enabled eval case to run`);
14058
+ if (cases.some((entry) => entry.requiresVision !== void 0 && typeof entry.requiresVision !== "boolean")) throw new Error("requiresVision must be a boolean");
13936
14059
  const resolved = await source.read("target-content", {
13937
14060
  skillName: input.skillName,
13938
14061
  target: input.target
@@ -13944,8 +14067,25 @@ var SkillEvolution = class {
13944
14067
  });
13945
14068
  this.background("skill backtest", async (signal) => {
13946
14069
  try {
13947
- await this.host.runTask(workspace, signal, async (task) => {
13948
- const queue = [...cases];
14070
+ for (const entry of cases.filter((entry) => entry.requiresVision === true)) {
14071
+ signal.throwIfAborted();
14072
+ await source.mutate("run-progress", {
14073
+ id: run.id,
14074
+ result: {
14075
+ caseId: entry.id,
14076
+ title: entry.title,
14077
+ verdict: "error",
14078
+ score: 0,
14079
+ unmeasuredReason: "requires-vision",
14080
+ checks: [],
14081
+ steps: [],
14082
+ answer: "",
14083
+ error: "Not measured: this case requires image input, which text-only backtests cannot receive."
14084
+ }
14085
+ });
14086
+ }
14087
+ const queue = cases.filter((entry) => entry.requiresVision !== true);
14088
+ if (queue.length > 0) await this.host.runTask(workspace, signal, async (task) => {
13949
14089
  const worker = async () => {
13950
14090
  for (let entry = queue.shift(); entry !== void 0; entry = queue.shift()) {
13951
14091
  signal.throwIfAborted();
@@ -13955,7 +14095,7 @@ var SkillEvolution = class {
13955
14095
  });
13956
14096
  }
13957
14097
  };
13958
- await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, cases.length) }, worker));
14098
+ await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, queue.length) }, worker));
13959
14099
  });
13960
14100
  await source.mutate("run-finish", {
13961
14101
  id: run.id,
@@ -14726,13 +14866,16 @@ function createWriteHandler(input, lifecycle, versions) {
14726
14866
  };
14727
14867
  }
14728
14868
  /** Pack data stays inside the selected storage root and DSH authentication. */
14729
- function createPackHandler(input) {
14869
+ function createPackHandler(input, nativeDirectoryPicker = () => false) {
14730
14870
  return async (endpoint, rawPayload) => {
14731
14871
  try {
14732
14872
  const payload = object$4(rawPayload);
14733
14873
  const runtime = scoped(input, payload);
14734
14874
  const manager = runtime.graph.packs;
14735
- if (endpoint === "target") return success$1(manager.target());
14875
+ if (endpoint === "target") return success$1({
14876
+ ...manager.target(),
14877
+ nativeDirectoryPicker: nativeDirectoryPicker()
14878
+ });
14736
14879
  if (endpoint === "export") return success$1(await manager.exportPack("full"));
14737
14880
  if (endpoint === "inspect") return success$1(manager.inspectPack(String(payload.base64 ?? ""), payload.fileName === void 0 ? void 0 : String(payload.fileName)));
14738
14881
  if (endpoint === "import") {
@@ -14888,14 +15031,14 @@ function createReviewHandler(input, lifecycle) {
14888
15031
  }
14889
15032
  };
14890
15033
  }
14891
- function registerRpc(connection, input, lifecycle, versions, onSyncConfigured) {
15034
+ function registerRpc(connection, input, lifecycle, versions, onSyncConfigured, nativeDirectoryPicker) {
14892
15035
  const versionManager = versions ?? new VersionUpdateManager({ mnemonCliPath: () => input.config.cliPath });
14893
15036
  const readHandler = createReadHandler(input, lifecycle, versionManager);
14894
15037
  const activationHandler = createActivationHandler(input);
14895
15038
  const writeHandler = createWriteHandler(input, lifecycle, versionManager);
14896
15039
  const syncHandler = createSyncHandler(input, onSyncConfigured);
14897
15040
  const reviewHandler = createReviewHandler(input, lifecycle);
14898
- const packHandler = createPackHandler(input);
15041
+ const packHandler = createPackHandler(input, nativeDirectoryPicker);
14899
15042
  connection.rpc.handle(MNEMON_READ_CHANNEL, readHandler);
14900
15043
  connection.rpc.handle(MNEMON_ACTIVATION_CHANNEL, activationHandler);
14901
15044
  connection.rpc.handle(MNEMON_WRITE_CHANNEL, writeHandler);
@@ -14912,6 +15055,17 @@ function registerRpc(connection, input, lifecycle, versions, onSyncConfigured) {
14912
15055
  };
14913
15056
  }
14914
15057
  //#endregion
15058
+ //#region src/host/directory-picker.ts
15059
+ /** Older hosts and browse-only compositions keep the existing manual path field. */
15060
+ function hasNativeDirectoryPicker(ctx) {
15061
+ try {
15062
+ const capability = ctx.get("directoryPicker")?.capability?.();
15063
+ return capability?.kind === "native" && typeof capability.pick === "function";
15064
+ } catch {
15065
+ return false;
15066
+ }
15067
+ }
15068
+ //#endregion
14915
15069
  //#region src/host/settings.ts
14916
15070
  function success(value) {
14917
15071
  return {
@@ -18098,7 +18252,7 @@ function apply(rawContext, rawConfig = {}) {
18098
18252
  },
18099
18253
  pluginCommand: () => dshPluginCommand(ctx.get("profileContext"))
18100
18254
  });
18101
- const rpc = registerRpc(connection, runtime, lifecycle, versions, () => autoBackup.refresh());
18255
+ const rpc = registerRpc(connection, runtime, lifecycle, versions, () => autoBackup.refresh(), () => hasNativeDirectoryPicker(ctx));
18102
18256
  const settings = registerSettingsRpc(connection, hostSettings);
18103
18257
  const view = registerViewRpc(connection, runtime, extensions, memoryPlugins, lifecycle, pluginInstallation);
18104
18258
  if (Context.is(webContext)) new MnemonRemoteService(webContext, {