dsh-mnemon 0.5.34 → 0.5.35

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.
@@ -1,4 +1,4 @@
1
- import { a as memoryConfigurationDigest, c as record, d as truncate, f as withMemoryStorageLock, i as integer, l as stringArray, o as migrationLineage, s as receipt, t as defineMemoryStrategyConfiguration, u as text } from "./strategy-configuration-CLHxaPOb.js";
1
+ import { a as memoryConfigurationDigest, c as record, d as truncate, f as canonicalWorkspacePath, i as integer, l as stringArray, m as withMemoryStorageLock, o as migrationLineage, p as workspaceStorageId, s as receipt, t as defineMemoryStrategyConfiguration, u as text } from "./strategy-configuration-CKP2kRss.js";
2
2
  import { COMPOSABLE_MEMORY_API_VERSION, MEMORY_VIEW_EXTENSION_SLOTS } from "./contracts.js";
3
3
  import { a as defineMemoryStrategy, i as defineMemorySource, o as defineMemoryStrategyExtension, r as defineMemoryPlugin } from "./definitions-DU7MLBhu.js";
4
4
  //#region src/sdk/install.ts
@@ -108,4 +108,4 @@ function defineMemoryViewExtension(definition) {
108
108
  });
109
109
  }
110
110
  //#endregion
111
- export { receipt as createMemoryMutationReceipt, defineMemoryPlugin, defineMemorySource, defineMemoryStrategy, defineMemoryStrategyConfiguration, defineMemoryStrategyExtension, defineMemoryViewExtension, installMemory, memoryConfigurationDigest, integer as memoryInputInteger, migrationLineage as memoryInputMigrationLineage, record as memoryInputRecord, stringArray as memoryInputStringArray, text as memoryInputText, memoryViewExtensionValues, truncate as truncateMemoryText, validateMemoryViewExtension, withMemoryStorageLock };
111
+ export { canonicalWorkspacePath, receipt as createMemoryMutationReceipt, defineMemoryPlugin, defineMemorySource, defineMemoryStrategy, defineMemoryStrategyConfiguration, defineMemoryStrategyExtension, defineMemoryViewExtension, installMemory, memoryConfigurationDigest, integer as memoryInputInteger, migrationLineage as memoryInputMigrationLineage, record as memoryInputRecord, stringArray as memoryInputStringArray, text as memoryInputText, memoryViewExtensionValues, truncate as truncateMemoryText, validateMemoryViewExtension, withMemoryStorageLock, workspaceStorageId };
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";
@@ -458,6 +458,7 @@ const DEFAULT_RECALL_LOW_SCORE_THRESHOLD = recallDefaults.lowScoreThreshold;
458
458
  const DEFAULT_RECALL_HIGH_SCORE_THRESHOLD = recallDefaults.highScoreThreshold;
459
459
  const DEFAULT_RECALL_CANDIDATE_MULTIPLIER = recallDefaults.candidateMultiplier;
460
460
  const DEFAULT_RECALL_MAX_MEDIUM_RESULTS = recallDefaults.maxMediumResults;
461
+ const DEFAULT_RECALL_MAX_RANKED_RESULTS = recallDefaults.maxRankedResults;
461
462
  const DEFAULT_RECALL_MAX_UNKNOWN_RESULTS = recallDefaults.maxUnknownResults;
462
463
  //#endregion
463
464
  //#region src/host/config.ts
@@ -519,6 +520,7 @@ const RecallQualitySchema = z.object({
519
520
  highScoreThreshold: z.number().min(0).max(1).default(DEFAULT_RECALL_HIGH_SCORE_THRESHOLD),
520
521
  candidateMultiplier: z.number().step(1).min(1).max(5).default(DEFAULT_RECALL_CANDIDATE_MULTIPLIER),
521
522
  maxMediumResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_MEDIUM_RESULTS),
523
+ maxRankedResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_RANKED_RESULTS),
522
524
  maxUnknownResults: z.number().step(1).min(0).max(50).default(DEFAULT_RECALL_MAX_UNKNOWN_RESULTS)
523
525
  });
524
526
  const RuntimeMemorySchema = z.object({
@@ -597,6 +599,7 @@ const Config = z.object({
597
599
  highScoreThreshold: DEFAULT_RECALL_HIGH_SCORE_THRESHOLD,
598
600
  candidateMultiplier: DEFAULT_RECALL_CANDIDATE_MULTIPLIER,
599
601
  maxMediumResults: DEFAULT_RECALL_MAX_MEDIUM_RESULTS,
602
+ maxRankedResults: DEFAULT_RECALL_MAX_RANKED_RESULTS,
600
603
  maxUnknownResults: DEFAULT_RECALL_MAX_UNKNOWN_RESULTS
601
604
  }),
602
605
  routingGuidance: z.boolean().default(true),
@@ -943,37 +946,6 @@ var MnemonAutoBackupScheduler = class {
943
946
  }
944
947
  };
945
948
  //#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
949
  //#region src/host/storage-root.ts
978
950
  function expandDirectory(value) {
979
951
  return resolve(value === "~" ? homedir() : value.startsWith("~/") ? join(homedir(), value.slice(2)) : value);
@@ -2452,6 +2424,56 @@ function keepRuntimeArchive(root, stagedRuntime) {
2452
2424
  };
2453
2425
  copy(join(root, RUNTIME_DIRECTORY, RUNTIME_ARCHIVE_DIRECTORY), join(stagedRuntime, RUNTIME_ARCHIVE_DIRECTORY));
2454
2426
  }
2427
+ /** Keep exact overflow records locally; a stable filename makes retried merges idempotent. */
2428
+ function archiveRuntimeOverflow(stagedRuntime, entries) {
2429
+ if (entries.length === 0) return 0;
2430
+ const directory = join(stagedRuntime, RUNTIME_ARCHIVE_DIRECTORY, "import-overflow");
2431
+ mkdirSync(directory, {
2432
+ recursive: true,
2433
+ mode: 448
2434
+ });
2435
+ let added = 0;
2436
+ for (const entry of entries) {
2437
+ const content = `${canonicalJson({
2438
+ version: 1,
2439
+ entries: [entry]
2440
+ })}\n`;
2441
+ const path = join(directory, `${sha256$1(content)}.json`);
2442
+ if (existsSync(path)) {
2443
+ if (readFileSync(path, "utf8") !== content) throw new Error("Runtime overflow archive content mismatch");
2444
+ continue;
2445
+ }
2446
+ writeFileSync(path, content, {
2447
+ mode: 384,
2448
+ flag: "wx"
2449
+ });
2450
+ added += 1;
2451
+ }
2452
+ return added;
2453
+ }
2454
+ /** Retain the same hot set on both machines, without asking a model to rewrite evidence. */
2455
+ function fitMergedRuntime(entries, limits) {
2456
+ if (runtimeBytes(entries, "memory") <= limits.memory) return {
2457
+ entries,
2458
+ archived: []
2459
+ };
2460
+ const importance = {
2461
+ critical: 2,
2462
+ normal: 1,
2463
+ low: 0
2464
+ };
2465
+ const compare = (left, right) => left < right ? -1 : left > right ? 1 : 0;
2466
+ 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)));
2467
+ const kept = [];
2468
+ const archived = [];
2469
+ for (const entry of ranked) if (runtimeBytes([...kept, entry], "memory") <= limits.memory) kept.push(entry);
2470
+ else archived.push(entry);
2471
+ const selected = new Set(kept);
2472
+ return {
2473
+ entries: entries.filter((entry) => entry.target === "user" || selected.has(entry)),
2474
+ archived
2475
+ };
2476
+ }
2455
2477
  function writeRuntime(directory, file) {
2456
2478
  mkdirSync(directory, {
2457
2479
  recursive: true,
@@ -2778,6 +2800,7 @@ function mergeRuntime(root, pack, runtimeLimits, options = {}) {
2778
2800
  added: 0,
2779
2801
  held: 0
2780
2802
  };
2803
+ const incomingKeys = /* @__PURE__ */ new Set();
2781
2804
  const revived = [];
2782
2805
  for (const entry of incoming.entries) {
2783
2806
  const key = entryKey(entry);
@@ -2792,13 +2815,16 @@ function mergeRuntime(root, pack, runtimeLimits, options = {}) {
2792
2815
  if (keys.has(key)) continue;
2793
2816
  keys.add(key);
2794
2817
  entries.push(entry);
2795
- report.added += 1;
2818
+ incomingKeys.add(key);
2796
2819
  }
2820
+ const fitted = fitMergedRuntime(entries, runtimeLimits);
2821
+ report.added = fitted.entries.filter((entry) => incomingKeys.has(entryKey(entry))).length;
2797
2822
  return {
2798
2823
  runtime: parseRuntime({
2799
2824
  version: 1,
2800
- entries
2825
+ entries: fitted.entries
2801
2826
  }, runtimeLimits),
2827
+ archived: fitted.archived,
2802
2828
  report,
2803
2829
  revived: mergeTombstoneFiles({
2804
2830
  version: 1,
@@ -2907,6 +2933,11 @@ function mergeDocuments(root, pack, base) {
2907
2933
  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
2934
  return current;
2909
2935
  }
2936
+ /** Stable across repeated imports, bounded by the registry contract, and distinguishable in the UI. */
2937
+ function conflictSpaceName(source) {
2938
+ const suffix = ` (import conflict ${createHash("sha256").update(source.id).digest("hex").slice(0, 8)})`;
2939
+ return source.name.slice(0, MEMORY_SPACE_NAME_MAX_LENGTH - suffix.length) + suffix;
2940
+ }
2910
2941
  function mergeRegistry(root, pack) {
2911
2942
  const current = readCurrentRegistry(root);
2912
2943
  const incoming = archiveRegistry(pack);
@@ -2931,7 +2962,7 @@ function mergeRegistry(root, pack) {
2931
2962
  continue;
2932
2963
  }
2933
2964
  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)));
2965
+ 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
2966
  const advanced = copy === void 0 ? void 0 : identity.merge(current.databases.get(copy.id), sourceDb);
2936
2967
  if (copy !== void 0 && advanced !== void 0) {
2937
2968
  current.databases.set(copy.id, advanced);
@@ -2940,6 +2971,8 @@ function mergeRegistry(root, pack) {
2940
2971
  add({
2941
2972
  ...source,
2942
2973
  id: randomUUID(),
2974
+ name: conflictSpaceName(source),
2975
+ active: false,
2943
2976
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2944
2977
  }, sourceDb);
2945
2978
  } else if (!held(sourceDb, (body) => body.name === source.name)) add(source, sourceDb);
@@ -3000,6 +3033,15 @@ function stageImport(root, pack, components, mode, runtimeLimits, options = {})
3000
3033
  revived = merged.revived;
3001
3034
  }
3002
3035
  keepRuntimeArchive(root, join(staging, RUNTIME_DIRECTORY));
3036
+ if (merged !== void 0 && merged.archived.length > 0) {
3037
+ const archived = archiveRuntimeOverflow(join(staging, RUNTIME_DIRECTORY), merged.archived);
3038
+ if (archived > 0) merged.report.archived = archived;
3039
+ const archivedKeys = new Set(merged.archived.map((entry) => `${entry.target}\0${tombstoneHash(entry)}`));
3040
+ writeRuntimeIndex(join(staging, "state"), {
3041
+ version: 1,
3042
+ entries: readRuntimeIndex(root).entries.filter((entry) => !archivedKeys.has(`${entry.target}\0${entry.contentHash}`))
3043
+ });
3044
+ }
3003
3045
  }
3004
3046
  if (components.includes("documents")) {
3005
3047
  const documents = mode === "merge" ? mergeDocuments(root, pack, options.documentsBase) : archiveDocuments(pack);
@@ -3033,7 +3075,7 @@ function commitStaging(root, staging, components) {
3033
3075
  const replacementLocks = sourceLocks(staging, components).map((lock) => lock.path);
3034
3076
  const activePath = join(root, MNEMON_ACTIVE_STORE_FILE);
3035
3077
  const previousActive = existsSync(activePath) ? readFileSync(activePath) : void 0;
3036
- let tombstoneSwap;
3078
+ const stateSwaps = [];
3037
3079
  try {
3038
3080
  for (const lock of replacementLocks) writeFileSync(lock, "pack-import\n", { mode: 384 });
3039
3081
  for (const component of components) {
@@ -3053,18 +3095,22 @@ function commitStaging(root, staging, components) {
3053
3095
  hadPrevious
3054
3096
  });
3055
3097
  }
3056
- if (components.includes("runtime") && existsSync(join(staging, "state", STATE_TOMBSTONE_FILE))) {
3098
+ for (const file of [STATE_TOMBSTONE_FILE, STATE_RUNTIME_INDEX_FILE]) {
3099
+ if (!components.includes("runtime") || !existsSync(join(staging, "state", file))) continue;
3057
3100
  const directory = join(root, "state");
3058
3101
  mkdirSync(directory, {
3059
3102
  recursive: true,
3060
3103
  mode: 448
3061
3104
  });
3062
- const target = join(directory, STATE_TOMBSTONE_FILE);
3063
- const previous = join(backup, STATE_TOMBSTONE_FILE);
3105
+ const target = join(directory, file);
3106
+ const previous = join(backup, file);
3064
3107
  const hadPrevious = existsSync(target);
3065
3108
  if (hadPrevious) renameSync(target, previous);
3066
- renameSync(join(staging, "state", STATE_TOMBSTONE_FILE), target);
3067
- tombstoneSwap = { hadPrevious };
3109
+ stateSwaps.push({
3110
+ file,
3111
+ hadPrevious
3112
+ });
3113
+ renameSync(join(staging, "state", file), target);
3068
3114
  }
3069
3115
  if (components.includes("memory-spaces")) reconcilePersistedStore(root);
3070
3116
  for (const lock of sourceLocks(root, components)) rmSync(lock.path, { force: true });
@@ -3077,10 +3123,10 @@ function commitStaging(root, staging, components) {
3077
3123
  });
3078
3124
  if (entry.hadPrevious) renameSync(join(backup, entry.directory), target);
3079
3125
  }
3080
- if (tombstoneSwap !== void 0) {
3081
- const target = join(root, "state", STATE_TOMBSTONE_FILE);
3126
+ for (const entry of stateSwaps.reverse()) {
3127
+ const target = join(root, "state", entry.file);
3082
3128
  rmSync(target, { force: true });
3083
- if (tombstoneSwap.hadPrevious) renameSync(join(backup, STATE_TOMBSTONE_FILE), target);
3129
+ if (entry.hadPrevious) renameSync(join(backup, entry.file), target);
3084
3130
  }
3085
3131
  if (components.includes("memory-spaces")) {
3086
3132
  if (previousActive === void 0) rmSync(activePath, { force: true });
@@ -5185,10 +5231,12 @@ var MnemonGitSync = class {
5185
5231
  let result = await this.pushOnce(settings, message, input.signal);
5186
5232
  let added = result.merged?.runtime?.added ?? 0;
5187
5233
  let held = result.merged?.runtime?.held ?? 0;
5234
+ let archived = result.merged?.runtime?.archived ?? 0;
5188
5235
  for (let attempt = 1; attempt < PUSH_ATTEMPTS && result.failure === "push-rejected"; attempt += 1) {
5189
5236
  result = await this.pushOnce(settings, message, input.signal);
5190
5237
  added += result.merged?.runtime?.added ?? 0;
5191
5238
  held = result.merged?.runtime?.held ?? held;
5239
+ archived += result.merged?.runtime?.archived ?? 0;
5192
5240
  }
5193
5241
  if (result.merged !== void 0 && (added > 0 || result.merged.runtime !== void 0)) result = {
5194
5242
  ...result,
@@ -5196,7 +5244,8 @@ var MnemonGitSync = class {
5196
5244
  ...result.merged,
5197
5245
  runtime: {
5198
5246
  added,
5199
- held
5247
+ held,
5248
+ ...archived > 0 ? { archived } : {}
5200
5249
  }
5201
5250
  }
5202
5251
  };
@@ -9497,6 +9546,7 @@ async function archiveRuntimeLocally(counters, context, request, plan, reason, s
9497
9546
  }));
9498
9547
  const mutation = await context.runtime.mutateResult("compact-and-mutate", {
9499
9548
  revision: plan.revision,
9549
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9500
9550
  mutation: request,
9501
9551
  compacted,
9502
9552
  maxBytes: compactedBudget(plan),
@@ -9528,7 +9578,10 @@ async function runtimeLocked(counters, context, request, signal) {
9528
9578
  } catch (error) {
9529
9579
  if (!sourceFailure(error, "runtime-capacity") || !context.maintain) throw error;
9530
9580
  }
9531
- const plan = await runtimeMemory.read("maintenance-plan", request, signal);
9581
+ const plan = await runtimeMemory.read("maintenance-plan", {
9582
+ ...request,
9583
+ ...context.preserveImportance ? { preserveImportance: true } : {}
9584
+ }, signal);
9532
9585
  if (context.expectedRevision !== void 0 && context.expectedRevision !== plan.revision) throw new Error("Runtime source revision conflict before capacity maintenance");
9533
9586
  if (!plan.requiresMaintenance) return context.commit();
9534
9587
  if (plan.entries.length === 0) throw new Error("runtime memory capacity was exceeded without entries available for maintenance");
@@ -9622,7 +9675,10 @@ ${chunk.context}
9622
9675
  importance,
9623
9676
  ...branches === void 0 ? {} : { branches }
9624
9677
  }));
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");
9678
+ if ((await runtimeMemory.read("maintenance-plan", {
9679
+ ...request,
9680
+ ...context.preserveImportance ? { preserveImportance: true } : {}
9681
+ }, signal)).revision !== plan.revision) throw new Error("runtime memory changed while archival was running; no archive writes were attempted");
9626
9682
  const destinations = await readDirectory();
9627
9683
  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
9684
  signal.throwIfAborted();
@@ -9694,6 +9750,7 @@ ${chunk.context}
9694
9750
  commitAttempted = true;
9695
9751
  const mutation = await runtimeMemory.mutateResult("compact-and-mutate", {
9696
9752
  revision: plan.revision,
9753
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9697
9754
  mutation: request,
9698
9755
  compacted: compactedEntries,
9699
9756
  maxBytes: budget,
@@ -9788,6 +9845,7 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
9788
9845
  context.assertWritable?.();
9789
9846
  const mutation = await runtimeMemory.mutateResult("compact-and-mutate", {
9790
9847
  revision: plan.revision,
9848
+ ...context.preserveImportance ? { preserveImportance: true } : {},
9791
9849
  mutation: request,
9792
9850
  compacted: candidates,
9793
9851
  maxBytes: budget
@@ -10538,8 +10596,12 @@ var MnemonSubagentCoordinator = class {
10538
10596
  const context = {
10539
10597
  runtime,
10540
10598
  inspectRuntime: runtime,
10599
+ preserveImportance: true,
10541
10600
  maintain: strategyDrives(lease.generation.strategy.definition.manifest, "runtime-capacity"),
10542
- commit: () => runtime.mutate("mutate", request, signal),
10601
+ commit: () => runtime.mutate("mutate", {
10602
+ ...request,
10603
+ preserveImportance: true
10604
+ }, signal),
10543
10605
  memorySpaces: async () => {
10544
10606
  if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
10545
10607
  if (!allowsParticipation(graph.config, "memory-spaces", "write", "automatic") || !lease.generation.sourceInstances().some((source) => source.sourceTypeId === "memory-spaces")) return void 0;
@@ -10592,6 +10654,7 @@ var MnemonSubagentCoordinator = class {
10592
10654
  runtime,
10593
10655
  inspectRuntime: graph.source("runtime", turn.scope).forInstance(source.sourceInstanceKey).forGeneration(lease.generation),
10594
10656
  maintain: true,
10657
+ preserveImportance: true,
10595
10658
  commit,
10596
10659
  assertWritable: () => {
10597
10660
  if (!authorize()) throw new Error("Runtime capacity maintenance is no longer authorized");
@@ -12630,11 +12693,12 @@ var MnemonLifecycle = class {
12630
12693
  }
12631
12694
  };
12632
12695
  const inherited = this.inheritedRoute(fallback);
12633
- return inherited === void 0 ? void 0 : {
12634
- source: inherited.source,
12696
+ if (inherited === void 0) return void 0;
12697
+ const { source, ...options } = inherited;
12698
+ return {
12699
+ source,
12635
12700
  options: {
12636
- provider: inherited.provider,
12637
- model: inherited.model,
12701
+ ...options,
12638
12702
  ...maxTokens
12639
12703
  }
12640
12704
  };
@@ -12658,10 +12722,14 @@ var MnemonLifecycle = class {
12658
12722
  const provider = selectedProvider || fallback?.options?.provider?.trim();
12659
12723
  const model = selectedModel || fallback?.options?.model?.trim();
12660
12724
  if (provider === void 0 || provider === "" || model === void 0 || model === "") return void 0;
12725
+ const fromDefault = selectedProvider !== void 0 && selectedProvider !== "" && selectedModel !== void 0 && selectedModel !== "";
12726
+ const matchesFallback = provider === fallback?.options?.provider?.trim() && model === fallback?.options?.model?.trim();
12727
+ const effort = fromDefault ? selected?.reasoningEffort : matchesFallback ? fallback?.options?.reasoningEffort : void 0;
12661
12728
  return {
12662
12729
  provider,
12663
12730
  model,
12664
- source: selectedProvider !== void 0 && selectedProvider !== "" && selectedModel !== void 0 && selectedModel !== "" ? "dsh-default" : "active-agent"
12731
+ ...typeof effort === "string" && effort.trim() !== "" ? { reasoningEffort: effort } : {},
12732
+ source: fromDefault ? "dsh-default" : "active-agent"
12665
12733
  };
12666
12734
  }
12667
12735
  /**
@@ -12843,12 +12911,14 @@ When no skill was loaded but the window produced a multi-step procedure, answer
12843
12911
  1. Did the user issue 2+ refinement turns that reached a stable output spec?
12844
12912
  2. Did any tool call fail and need a substitute?
12845
12913
  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.
12914
+ 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
12915
 
12848
12916
  Rules:
12849
12917
  - Cite evidence. Every suggestion and every case lists the step numbers it rests on; a suggestion without cited steps is discarded.
12850
12918
  - annotations mark the steps that carry a signal: the step number, the signal, and a short note saying what happened there.
12851
12919
  - skillName: lowercase words joined by hyphens. Use the exact name of an existing skill for action="update"; never invent an update target.
12920
+ - 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.
12921
+ - 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
12922
  - summary and rationale: short, in the user's language (the language of the user steps).
12853
12923
  - confidence: 0 to 1; only suggest at >= 0.6. At most 3 suggestions.
12854
12924
  - 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,6 +13031,15 @@ function skillReviewPrompt(input) {
12961
13031
  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
13032
  const catalog = input.catalog.slice(0, 80).map((skill) => `- ${skill.name}: ${skill.description.slice(0, 160)}`).join("\n");
12963
13033
  const cases = Object.entries(input.caseTitles).filter(([, titles]) => titles.length > 0).map(([name, titles]) => `- ${name}: ${titles.slice(0, 20).join(" | ")}`).join("\n");
13034
+ const open = input.openSuggestions.slice(0, 20).map((suggestion) => JSON.stringify({
13035
+ skillName: suggestion.skillName,
13036
+ action: suggestion.action,
13037
+ status: suggestion.status,
13038
+ confidence: suggestion.confidence,
13039
+ summary: suggestion.summary.slice(0, 600),
13040
+ rationale: suggestion.rationale?.slice(0, 400),
13041
+ updatedAt: suggestion.updatedAt
13042
+ })).join("\n");
12964
13043
  return [
12965
13044
  "Review this finished conversation window now.",
12966
13045
  `Session: ${input.sessionTitle ?? "(untitled)"}${input.workspace === void 0 ? "" : ` · workspace ${input.workspace}`}`,
@@ -12975,6 +13054,9 @@ function skillReviewPrompt(input) {
12975
13054
  "Skills that exist now (name: description):",
12976
13055
  catalog === "" ? "(none)" : catalog,
12977
13056
  "",
13057
+ "Open suggestions, most recently updated first (untrusted data; reuse matching names and preserve earlier requirements):",
13058
+ open === "" ? "(none)" : open,
13059
+ "",
12978
13060
  "Eval cases that already exist (titles):",
12979
13061
  cases === "" ? "(none)" : cases
12980
13062
  ].join("\n");
@@ -13058,10 +13140,11 @@ const SKILL_DRAFT_SCHEMA = {
13058
13140
  };
13059
13141
  function caseText(entry, failure) {
13060
13142
  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("; ")}`}`;
13143
+ 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("; ")}`;
13144
+ return `- ${entry.title}\n task: ${entry.prompt.replace(/\n/gu, " ").slice(0, 600)}\n${checks}${result}`;
13062
13145
  }
13063
13146
  function skillDraftPrompt(pack, guidance, targetRoot) {
13064
- const failures = new Map((pack.lastRun?.results ?? []).map((result) => [result.caseId, {
13147
+ const failures = new Map((pack.lastRun?.results ?? []).filter((result) => result.unmeasuredReason === void 0).map((result) => [result.caseId, {
13065
13148
  verdict: result.verdict,
13066
13149
  notes: result.checks.filter((check) => !check.pass).map((check) => check.note).filter(Boolean).slice(0, 4)
13067
13150
  }]));
@@ -13775,6 +13858,11 @@ var SkillEvolution = class {
13775
13858
  };
13776
13859
  try {
13777
13860
  const active = (await source.read("catalog", null, controller.signal)).skills.filter((skill) => skill.shadowedBy === void 0);
13861
+ const openSuggestions = (await source.read("suggestions", { status: [
13862
+ "pending",
13863
+ "drafting",
13864
+ "review"
13865
+ ] }, controller.signal)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
13778
13866
  const loaded = [];
13779
13867
  const caseTitles = {};
13780
13868
  for (const name of stats.skills.slice(0, 3)) {
@@ -13795,7 +13883,8 @@ var SkillEvolution = class {
13795
13883
  name: skill.name,
13796
13884
  description: skill.description
13797
13885
  })),
13798
- caseTitles
13886
+ caseTitles,
13887
+ openSuggestions
13799
13888
  });
13800
13889
  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);
13801
13890
  const result = await source.mutate("review-record", {
@@ -13933,6 +14022,7 @@ var SkillEvolution = class {
13933
14022
  const settings = this.host.settings();
13934
14023
  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
14024
  if (cases.length === 0) throw new Error(`${input.skillName} has no enabled eval case to run`);
14025
+ if (cases.some((entry) => entry.requiresVision !== void 0 && typeof entry.requiresVision !== "boolean")) throw new Error("requiresVision must be a boolean");
13936
14026
  const resolved = await source.read("target-content", {
13937
14027
  skillName: input.skillName,
13938
14028
  target: input.target
@@ -13944,8 +14034,25 @@ var SkillEvolution = class {
13944
14034
  });
13945
14035
  this.background("skill backtest", async (signal) => {
13946
14036
  try {
13947
- await this.host.runTask(workspace, signal, async (task) => {
13948
- const queue = [...cases];
14037
+ for (const entry of cases.filter((entry) => entry.requiresVision === true)) {
14038
+ signal.throwIfAborted();
14039
+ await source.mutate("run-progress", {
14040
+ id: run.id,
14041
+ result: {
14042
+ caseId: entry.id,
14043
+ title: entry.title,
14044
+ verdict: "error",
14045
+ score: 0,
14046
+ unmeasuredReason: "requires-vision",
14047
+ checks: [],
14048
+ steps: [],
14049
+ answer: "",
14050
+ error: "Not measured: this case requires image input, which text-only backtests cannot receive."
14051
+ }
14052
+ });
14053
+ }
14054
+ const queue = cases.filter((entry) => entry.requiresVision !== true);
14055
+ if (queue.length > 0) await this.host.runTask(workspace, signal, async (task) => {
13949
14056
  const worker = async () => {
13950
14057
  for (let entry = queue.shift(); entry !== void 0; entry = queue.shift()) {
13951
14058
  signal.throwIfAborted();
@@ -13955,7 +14062,7 @@ var SkillEvolution = class {
13955
14062
  });
13956
14063
  }
13957
14064
  };
13958
- await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, cases.length) }, worker));
14065
+ await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, queue.length) }, worker));
13959
14066
  });
13960
14067
  await source.mutate("run-finish", {
13961
14068
  id: run.id,
@@ -6,20 +6,20 @@ var dependencies = {
6
6
  "@deepseek-ai/cosmokit": "^1.8.5",
7
7
  "@deepseek-ai/schemastery": "^3.18.4",
8
8
  "dsh-mnemon-provider-byterover": "0.5.5",
9
- "dsh-mnemon-provider-hindsight": "0.5.6",
10
- "dsh-mnemon-provider-holographic": "0.5.6",
9
+ "dsh-mnemon-provider-hindsight": "0.5.7",
10
+ "dsh-mnemon-provider-holographic": "0.5.7",
11
11
  "dsh-mnemon-provider-honcho": "0.5.5",
12
12
  "dsh-mnemon-provider-mem0": "0.5.5",
13
- "dsh-mnemon-provider-mnemon-native": "0.5.9",
14
- "dsh-mnemon-provider-openviking": "0.5.8",
13
+ "dsh-mnemon-provider-mnemon-native": "0.5.10",
14
+ "dsh-mnemon-provider-openviking": "0.5.9",
15
15
  "dsh-mnemon-provider-retaindb": "0.5.6",
16
16
  "dsh-mnemon-provider-supermemory": "0.5.5",
17
17
  "dsh-mnemon-source-documents": "0.5.11",
18
- "dsh-mnemon-source-memory-spaces": "0.5.22",
19
- "dsh-mnemon-source-runtime": "0.5.15",
20
- "dsh-mnemon-source-skills": "0.5.1",
18
+ "dsh-mnemon-source-memory-spaces": "0.5.23",
19
+ "dsh-mnemon-source-runtime": "0.5.16",
20
+ "dsh-mnemon-source-skills": "0.5.2",
21
21
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
22
- "dsh-mnemon-strategy-default-three-tier": "0.5.10",
22
+ "dsh-mnemon-strategy-default-three-tier": "0.5.11",
23
23
  "dsh-mnemon-strategy-general": "0.5.1",
24
24
  "dsh-mnemon-strategy-light-context": "0.5.6",
25
25
  "dsh-mnemon-strategy-scoped": "0.5.5",
package/lib/starter.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as prepareStarterResolution } from "./starter-resolution-C2TDVxDr.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-BAHgCK5h.js";
2
2
  //#region src/starter.ts
3
3
  const name = "dsh-mnemon-starter";
4
4
  const provide = ["mnemonStarterReady"];
@@ -1,6 +1,7 @@
1
1
  import { n as deepFreeze, t as canonicalMemoryJson } from "./definitions-DU7MLBhu.js";
2
- import { resolve } from "node:path";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
+ import { realpathSync, statSync } from "node:fs";
4
5
  //#region src/sdk/storage-lock.ts
5
6
  const queues = /* @__PURE__ */ new Map();
6
7
  /**
@@ -19,6 +20,37 @@ function withMemoryStorageLock(directory, operation) {
19
20
  return result;
20
21
  }
21
22
  //#endregion
23
+ //#region src/sdk/workspace-storage.ts
24
+ /** Local workspace identity, including aliases with not-yet-created descendants. */
25
+ function canonicalWorkspacePath(workspacePath) {
26
+ if (!isAbsolute(workspacePath) || workspacePath.includes("\0")) throw new Error("Workspace path must be absolute");
27
+ let parent = resolve(workspacePath);
28
+ const suffix = [];
29
+ for (;;) {
30
+ let real;
31
+ try {
32
+ real = realpathSync.native(parent);
33
+ } catch (error) {
34
+ if (error.code !== "ENOENT") throw error;
35
+ const next = dirname(parent);
36
+ if (next === parent) throw error;
37
+ suffix.unshift(parent.slice(next.length).replace(/^[/\\]+/u, ""));
38
+ parent = next;
39
+ continue;
40
+ }
41
+ if (suffix.length > 0 && !statSync(real).isDirectory()) throw Object.assign(/* @__PURE__ */ new Error(`ENOTDIR: not a directory, realpath '${parent}'`), {
42
+ code: "ENOTDIR",
43
+ path: parent,
44
+ syscall: "realpath"
45
+ });
46
+ return join(real, ...suffix);
47
+ }
48
+ }
49
+ /** A rename or move selects a new directory; resolving identity never writes. */
50
+ function workspaceStorageId(workspacePath) {
51
+ return createHash("sha256").update(canonicalWorkspacePath(workspacePath)).digest("hex");
52
+ }
53
+ //#endregion
22
54
  //#region src/sdk/input.ts
23
55
  /** Stable digest of validated configuration; raw values never enter diagnostics. */
24
56
  function memoryConfigurationDigest(value) {
@@ -156,4 +188,4 @@ function memoryStrategyConfigurationValues(definition, input) {
156
188
  return config;
157
189
  }
158
190
  //#endregion
159
- export { memoryConfigurationDigest as a, record as c, truncate as d, withMemoryStorageLock as f, integer as i, stringArray as l, memoryStrategyConfigurationValues as n, migrationLineage as o, readMemoryStrategyConfiguration as r, receipt as s, defineMemoryStrategyConfiguration as t, text as u };
191
+ export { memoryConfigurationDigest as a, record as c, truncate as d, canonicalWorkspacePath as f, integer as i, stringArray as l, withMemoryStorageLock as m, memoryStrategyConfigurationValues as n, migrationLineage as o, workspaceStorageId as p, readMemoryStrategyConfiguration as r, receipt as s, defineMemoryStrategyConfiguration as t, text as u };
@@ -615,6 +615,7 @@ export declare const zh: {
615
615
  'config.syncNow': string;
616
616
  'config.syncNowRunning': string;
617
617
  'config.syncDone': string;
618
+ 'config.runtimeMergeArchived': string;
618
619
  'config.syncDoneMerged': string;
619
620
  'config.syncUpToDate': string;
620
621
  'config.syncUpToDateMerged': string;
@@ -12,4 +12,5 @@ export declare const DEFAULT_RECALL_LOW_SCORE_THRESHOLD: number;
12
12
  export declare const DEFAULT_RECALL_HIGH_SCORE_THRESHOLD: number;
13
13
  export declare const DEFAULT_RECALL_CANDIDATE_MULTIPLIER: number;
14
14
  export declare const DEFAULT_RECALL_MAX_MEDIUM_RESULTS: number;
15
+ export declare const DEFAULT_RECALL_MAX_RANKED_RESULTS: number;
15
16
  export declare const DEFAULT_RECALL_MAX_UNKNOWN_RESULTS: number;
@@ -1,7 +1,7 @@
1
1
  import z from '@deepseek-ai/schemastery';
2
2
  export { resolveEmbedding, resolvePersistenceStrategy, resolveRecallQuality } from 'dsh-mnemon-source-memory-spaces';
3
3
  import type { Config as SharedConfig, CustomPackConfig as SharedCustomPackConfig, InteractionConfig as SharedInteractionConfig, ResolvedConfig as SharedResolvedConfig, ResolvedInteractionConfig as SharedResolvedInteractionConfig, ResolvedSkillsConfig } from "./protocol.ts";
4
- export { DEFAULT_IDLE_REVIEW_MS, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, DEFAULT_RECALL_CANDIDATE_MULTIPLIER, DEFAULT_RECALL_HIGH_SCORE_THRESHOLD, DEFAULT_RECALL_LIMIT, DEFAULT_RECALL_LOW_SCORE_THRESHOLD, DEFAULT_RECALL_MAX_MEDIUM_RESULTS, DEFAULT_RECALL_MAX_UNKNOWN_RESULTS, DEFAULT_RECALL_QUALITY_POLICY, DEFAULT_RUNTIME_MAINTENANCE_MAX_TOKENS, DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES, DEFAULT_RUNTIME_USER_LIMIT_BYTES, DEFAULT_TIMEOUT_MS, MAX_RUNTIME_MAINTENANCE_MAX_TOKENS, MAX_RUNTIME_MEMORY_LIMIT_BYTES, } from './config-values.ts';
4
+ export { DEFAULT_IDLE_REVIEW_MS, DEFAULT_EMBEDDING_ENDPOINT, DEFAULT_EMBEDDING_MODEL, DEFAULT_EMBEDDING_PROTOCOL, DEFAULT_RECALL_CANDIDATE_MULTIPLIER, DEFAULT_RECALL_HIGH_SCORE_THRESHOLD, DEFAULT_RECALL_LIMIT, DEFAULT_RECALL_LOW_SCORE_THRESHOLD, DEFAULT_RECALL_MAX_MEDIUM_RESULTS, DEFAULT_RECALL_MAX_RANKED_RESULTS, DEFAULT_RECALL_MAX_UNKNOWN_RESULTS, DEFAULT_RECALL_QUALITY_POLICY, DEFAULT_RUNTIME_MAINTENANCE_MAX_TOKENS, DEFAULT_RUNTIME_MEMORY_LIMIT_BYTES, DEFAULT_RUNTIME_USER_LIMIT_BYTES, DEFAULT_TIMEOUT_MS, MAX_RUNTIME_MAINTENANCE_MAX_TOKENS, MAX_RUNTIME_MEMORY_LIMIT_BYTES, } from './config-values.ts';
5
5
  export type Config = SharedConfig;
6
6
  export type CustomPackConfig = SharedCustomPackConfig;
7
7
  export type InteractionConfig = SharedInteractionConfig;
@@ -796,10 +796,12 @@ export interface MnemonPackPreview {
796
796
  * leaves an entry out because a tombstone still hides it has to say how many.
797
797
  */
798
798
  export interface MnemonRuntimeMergeReport {
799
- /** Incoming entries this merge wrote into the data directory. */
799
+ /** Incoming entries this merge admitted to hot runtime memory. */
800
800
  added: number;
801
801
  /** Incoming entries a tombstone still hides, so the merge left them out. */
802
802
  held: number;
803
+ /** Exact MEMORY records newly saved in the local import-overflow archive. */
804
+ archived?: number;
803
805
  }
804
806
  export interface MnemonPackImportResult {
805
807
  imported: true;