dsh-mnemon 0.5.35 → 0.5.37

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
@@ -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,
@@ -469,6 +471,8 @@ const InteractionConfig = z.object({
469
471
  /** 技能管理's settings, with the ranges its page accepts. */
470
472
  const SkillsConfig = z.object({
471
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),
472
476
  autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
473
477
  hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
474
478
  sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
@@ -10837,8 +10841,8 @@ var MnemonSubagentCoordinator = class {
10837
10841
  * One tool-less structured run for skill evolution: a review, a draft, a
10838
10842
  * backtest step or a case derivation. The child sees only the prompt.
10839
10843
  */
10840
- async skillTask(parent, label, prompt, schema, persona, signal) {
10841
- const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona);
10844
+ async skillTask(parent, label, prompt, schema, persona, signal, route) {
10845
+ const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona, void 0, void 0, route);
10842
10846
  return {
10843
10847
  provider,
10844
10848
  runId,
@@ -10955,7 +10959,7 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
10955
10959
  documentIds: strings(value.documentIds)
10956
10960
  };
10957
10961
  }
10958
- async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard) {
10962
+ async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route) {
10959
10963
  if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
10960
10964
  const provider = this.provider(preferredProvider);
10961
10965
  assertDshOutputSchema(outputSchema);
@@ -11064,7 +11068,7 @@ Completion protocol: call \`${resultToolName}\` exactly once with requestId \`${
11064
11068
  ${JSON.stringify(outputSchema)}
11065
11069
  This is the only completion channel for this run. Do not finish with a plain-text answer. The requestId expires when this run finishes or is cancelled.`;
11066
11070
  const perOpMaxTokens = operation === "migration" || operation === "compaction" || operation === "reconcile" ? this.runtimeMaintenanceMaxTokensResolver?.() ?? 8192 : operation === "document-archive" ? 8192 : operation === "metadata-maintenance" ? 4096 : operation === "review" ? this.runtimeSource.config.idleReview.maxTokens : operation === "skill" ? 16384 : void 0;
11067
- const fixed = this.taskAgentModelResolver?.();
11071
+ const fixed = route ?? this.taskAgentModelResolver?.();
11068
11072
  const baseAgentOptions = perOpMaxTokens === void 0 ? void 0 : { maxTokens: perOpMaxTokens };
11069
11073
  const resolvedAgentOptions = fixed === void 0 ? baseAgentOptions : {
11070
11074
  ...baseAgentOptions ?? {},
@@ -12195,6 +12199,16 @@ var MnemonAgentLifecycle = class {
12195
12199
  function isDirectory(path) {
12196
12200
  return stat(path).then((info) => info.isDirectory(), () => false);
12197
12201
  }
12202
+ /** Explicit model route carried by the task Agent a skill run created. */
12203
+ const TASK_ROUTE = Symbol.for("dsh-mnemon.taskRoute");
12204
+ /**
12205
+ * The route a task Agent was created with, when its caller picked one. Skill
12206
+ * work started under that Agent hands the same pick to its own children, so a
12207
+ * run never falls back to the profile default halfway through.
12208
+ */
12209
+ function taskRoute(agent) {
12210
+ return agent[TASK_ROUTE];
12211
+ }
12198
12212
  /** DSH-native owner for per-agent Mnemon lifecycle hooks and UI-triggered LLM work. */
12199
12213
  var MnemonLifecycle = class {
12200
12214
  ctx;
@@ -12230,8 +12244,8 @@ var MnemonLifecycle = class {
12230
12244
  return this.skillEvolution;
12231
12245
  }
12232
12246
  /** Model work for skill evolution under a fresh task Agent, outside any conversation. */
12233
- runSkillTask(workspaceRoot, signal, operation) {
12234
- return this.runTaskAgent("", workspaceRoot, signal, operation);
12247
+ runSkillTask(workspaceRoot, signal, operation, route) {
12248
+ return this.runTaskAgent("", workspaceRoot, signal, operation, route);
12235
12249
  }
12236
12250
  /**
12237
12251
  * What one conversation did with skills, for the bars under its turns. A
@@ -12368,8 +12382,9 @@ var MnemonLifecycle = class {
12368
12382
  const entries = await Promise.all(providers.map(async (provider) => {
12369
12383
  try {
12370
12384
  let timeout;
12385
+ const timeoutMs = provider.id === "deepseek-official" ? 6e3 : 3e3;
12371
12386
  const models = await Promise.race([llm.listModels(provider.id), new Promise((_, reject) => {
12372
- timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("model directory timed out after 3 seconds")), 3e3);
12387
+ timeout = setTimeout(() => reject(/* @__PURE__ */ new Error(`model directory timed out after ${timeoutMs / 1e3} seconds`)), timeoutMs);
12373
12388
  })]).finally(() => {
12374
12389
  if (timeout !== void 0) clearTimeout(timeout);
12375
12390
  });
@@ -12577,18 +12592,21 @@ var MnemonLifecycle = class {
12577
12592
  * is the explicit Web workbench scope, so LiveMnemonRuntime resolves the same
12578
12593
  * workspace graph without borrowing conversation history or ownership.
12579
12594
  */
12580
- async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation) {
12595
+ async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation, route) {
12581
12596
  const sessionId = randomUUID();
12582
12597
  this.taskAgentIds.add(sessionId);
12583
12598
  let handle;
12584
12599
  let failure;
12585
12600
  try {
12586
- const creation = await this.taskAgentCreation(fallbackSessionId, workspaceRoot, signal);
12601
+ const creation = await this.taskAgentCreation(fallbackSessionId, workspaceRoot, signal, route);
12587
12602
  handle = await this.ctx.agents.create({
12588
12603
  sessionId,
12589
12604
  ...creation,
12590
12605
  signal
12591
12606
  });
12607
+ if (route !== void 0) try {
12608
+ handle.agent[TASK_ROUTE] = route;
12609
+ } catch {}
12592
12610
  return await operation(handle.agent);
12593
12611
  } catch (error) {
12594
12612
  failure = error;
@@ -12613,8 +12631,15 @@ var MnemonLifecycle = class {
12613
12631
  * or `delegationDepth`: the task belongs to no conversation, and its children stay
12614
12632
  * one level deep.
12615
12633
  */
12616
- async taskAgentCreation(fallbackSessionId, workspaceRoot, signal) {
12617
- const agentOptions = this.taskAgentModelOptions(fallbackSessionId, workspaceRoot);
12634
+ async taskAgentCreation(fallbackSessionId, workspaceRoot, signal, route) {
12635
+ const baseOptions = this.taskAgentModelOptions(fallbackSessionId, workspaceRoot);
12636
+ const { reasoningEffort: _inheritedEffort, ...baseRest } = baseOptions ?? {};
12637
+ const agentOptions = route === void 0 ? baseOptions : {
12638
+ ...baseRest,
12639
+ provider: route.provider,
12640
+ model: route.model,
12641
+ ...route.reasoningEffort === void 0 ? {} : { reasoningEffort: route.reasoningEffort }
12642
+ };
12618
12643
  if (agentOptions === void 0) throw new Error("no default provider/model is available for a clean task Agent");
12619
12644
  const { cwd, workFrom } = await this.taskAgentDirectories(workspaceRoot);
12620
12645
  const presets = presetService(this.ctx.get("agentPresets"));
@@ -13042,6 +13067,7 @@ function skillReviewPrompt(input) {
13042
13067
  })).join("\n");
13043
13068
  return [
13044
13069
  "Review this finished conversation window now.",
13070
+ `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.`,
13045
13071
  `Session: ${input.sessionTitle ?? "(untitled)"}${input.workspace === void 0 ? "" : ` · workspace ${input.workspace}`}`,
13046
13072
  `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}).`,
13047
13073
  "",
@@ -13755,7 +13781,10 @@ var SkillEvolution = class {
13755
13781
  async turnEnded(agent, turn) {
13756
13782
  if (this.disposed) return;
13757
13783
  const settings = this.host.settings();
13758
- if (!settings.evolution) return;
13784
+ if (!settings.evolution || !settings.allowCreate && !settings.allowUpdate) {
13785
+ this.cancelTimer(agent.id);
13786
+ return;
13787
+ }
13759
13788
  if (this.source(agent) === void 0) return;
13760
13789
  const captured = captureTurn(agent.session.snapshotEvents(), turn);
13761
13790
  if (captured === void 0 || captured.steps.length === 0) return;
@@ -13775,7 +13804,8 @@ var SkillEvolution = class {
13775
13804
  delete state.timer;
13776
13805
  delete state.scheduled;
13777
13806
  if (this.disposed || state.running !== void 0) return;
13778
- if (agent.status !== "idle" || !this.host.settings().evolution) return;
13807
+ const current = this.host.settings();
13808
+ if (agent.status !== "idle" || !current.evolution || !current.allowCreate && !current.allowUpdate) return;
13779
13809
  state.running = this.review(agent, state, "automatic").then(() => void 0).finally(() => {
13780
13810
  delete state.running;
13781
13811
  });
@@ -13786,6 +13816,8 @@ var SkillEvolution = class {
13786
13816
  * or the session's recent turns when nothing is collected yet.
13787
13817
  */
13788
13818
  async reviewNow(agent) {
13819
+ const settings = this.host.settings();
13820
+ if (!settings.allowCreate && !settings.allowUpdate) throw new Error("Enable new-skill or skill-improvement suggestions before reviewing");
13789
13821
  if (this.source(agent) === void 0) throw new Error("the Skills Source is not running for this conversation");
13790
13822
  const state = this.state(agent);
13791
13823
  if (state.running !== void 0) await state.running;
@@ -13811,17 +13843,21 @@ var SkillEvolution = class {
13811
13843
  async review(agent, state, trigger) {
13812
13844
  const source = this.source(agent);
13813
13845
  if (source === void 0) return /* @__PURE__ */ new Error("the Skills Source is not running for this conversation");
13846
+ const settings = this.host.settings();
13847
+ if (!settings.allowCreate && !settings.allowUpdate || trigger === "automatic" && !settings.evolution) return /* @__PURE__ */ new Error("Skill reviews are disabled");
13848
+ const allowedTypes = () => {
13849
+ const current = this.host.settings();
13850
+ const enabled = trigger === "manual" || current.evolution;
13851
+ return {
13852
+ allowCreate: enabled && settings.allowCreate && current.allowCreate,
13853
+ allowUpdate: enabled && settings.allowUpdate && current.allowUpdate
13854
+ };
13855
+ };
13814
13856
  const started = this.now();
13815
13857
  const window = state.window;
13816
13858
  state.window = new TrajectoryWindow();
13817
13859
  const turn = window.lastTurn;
13818
13860
  if (turn !== void 0) state.reviewing = turn;
13819
- const settings = this.host.settings();
13820
- const policy = {
13821
- minConfidence: settings.minConfidence,
13822
- expiryDays: settings.expiryDays,
13823
- maxPendingPerSession: settings.maxPendingPerSession
13824
- };
13825
13861
  const events = agent.session.snapshotEvents();
13826
13862
  const workspace = agent.session.header?.cwd?.trim() || void 0;
13827
13863
  const steps = window.steps();
@@ -13858,11 +13894,11 @@ var SkillEvolution = class {
13858
13894
  };
13859
13895
  try {
13860
13896
  const active = (await source.read("catalog", null, controller.signal)).skills.filter((skill) => skill.shadowedBy === void 0);
13861
- const openSuggestions = (await source.read("suggestions", { status: [
13897
+ const openSuggestions = [...await source.read("suggestions", { status: [
13862
13898
  "pending",
13863
13899
  "drafting",
13864
13900
  "review"
13865
- ] }, controller.signal)).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
13901
+ ] }, controller.signal)].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
13866
13902
  const loaded = [];
13867
13903
  const caseTitles = {};
13868
13904
  for (const name of stats.skills.slice(0, 3)) {
@@ -13872,6 +13908,7 @@ var SkillEvolution = class {
13872
13908
  caseTitles[name] = (await source.read("cases", { skill: name }, controller.signal)).map((entry) => entry.title);
13873
13909
  }
13874
13910
  const prompt = skillReviewPrompt({
13911
+ ...allowedTypes(),
13875
13912
  ...title === void 0 ? {} : { sessionTitle: title },
13876
13913
  ...workspace === void 0 ? {} : { workspace },
13877
13914
  steps,
@@ -13886,7 +13923,23 @@ var SkillEvolution = class {
13886
13923
  caseTitles,
13887
13924
  openSuggestions
13888
13925
  });
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);
13926
+ const paused = { structured: {
13927
+ outcome: "nothing",
13928
+ summary: "Skill review was paused by the current settings.",
13929
+ suggestions: []
13930
+ } };
13931
+ const types = allowedTypes();
13932
+ const parsed = parseSkillReview((!types.allowCreate && !types.allowUpdate ? paused : await this.host.runTask(workspace, controller.signal, (task) => {
13933
+ const current = allowedTypes();
13934
+ return !current.allowCreate && !current.allowUpdate ? Promise.resolve(paused) : this.host.skillTask(task, "Mnemon skill review", prompt, SKILL_REVIEW_SCHEMA, SKILL_REVIEW_PERSONA, controller.signal);
13935
+ })).structured);
13936
+ const current = this.host.settings();
13937
+ const policy = {
13938
+ ...allowedTypes(),
13939
+ minConfidence: current.minConfidence,
13940
+ expiryDays: current.expiryDays,
13941
+ maxPendingPerSession: current.maxPendingPerSession
13942
+ };
13890
13943
  const result = await source.mutate("review-record", {
13891
13944
  review: {
13892
13945
  ...base,
@@ -13975,7 +14028,7 @@ var SkillEvolution = class {
13975
14028
  let prompt = skillDraftPrompt(pack, guidance, targetRoot);
13976
14029
  let draft;
13977
14030
  for (let attempt = 0; draft === void 0; attempt++) {
13978
- const parsed = parseSkillDraft((await this.host.runTask(workspace, signal, (task) => this.host.skillTask(task, "Mnemon skill draft", prompt, SKILL_DRAFT_SCHEMA, SKILL_DRAFT_PERSONA, signal))).structured);
14031
+ const parsed = parseSkillDraft((await this.host.runTask(workspace, signal, (task) => this.host.skillTask(task, "Mnemon skill draft", prompt, SKILL_DRAFT_SCHEMA, SKILL_DRAFT_PERSONA, signal, input.route), input.route)).structured);
13979
14032
  try {
13980
14033
  draft = await source.mutate("draft-record", {
13981
14034
  ...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
@@ -13997,16 +14050,18 @@ var SkillEvolution = class {
13997
14050
  target: {
13998
14051
  kind: "draft",
13999
14052
  draftId: draft.id
14000
- }
14053
+ },
14054
+ ...input.route === void 0 ? {} : { route: input.route }
14001
14055
  });
14002
- if (pack.action === "update" && pack.lastRun !== void 0 && pack.lastRun.target.hash === pack.skill?.hash) await source.mutate("run-link", {
14056
+ if (input.route === void 0 && pack.action === "update" && pack.lastRun !== void 0 && pack.lastRun.target.hash === pack.skill?.hash) await source.mutate("run-link", {
14003
14057
  draftId: draft.id,
14004
14058
  runId: pack.lastRun.id
14005
14059
  });
14006
14060
  else {
14007
14061
  const baseline = await this.backtest(source, workspace, {
14008
14062
  skillName: pack.skillName,
14009
- target: { kind: pack.action === "update" ? "current" : "none" }
14063
+ target: { kind: pack.action === "update" ? "current" : "none" },
14064
+ ...input.route === void 0 ? {} : { route: input.route }
14010
14065
  });
14011
14066
  await source.mutate("run-link", {
14012
14067
  draftId: draft.id,
@@ -14063,7 +14118,7 @@ var SkillEvolution = class {
14063
14118
  }
14064
14119
  };
14065
14120
  await Promise.all(Array.from({ length: Math.min(BACKTEST_CONCURRENCY, queue.length) }, worker));
14066
- });
14121
+ }, input.route);
14067
14122
  await source.mutate("run-finish", {
14068
14123
  id: run.id,
14069
14124
  status: "completed"
@@ -14505,6 +14560,20 @@ function optionalString(value, label) {
14505
14560
  if (typeof value !== "string") throw new Error(label + " must be a string");
14506
14561
  return value.trim() === "" ? void 0 : value.trim();
14507
14562
  }
14563
+ /**
14564
+ * The `provider` + `model` pair a page may send with model work. Both or
14565
+ * neither: a half route cannot run, and an absent one keeps the profile's own.
14566
+ */
14567
+ function modelRoute(input, missing = "a draft model needs both provider and model") {
14568
+ const provider = optionalString(input.provider, "provider");
14569
+ const model = optionalString(input.model, "model");
14570
+ if (provider === void 0 && model === void 0) return void 0;
14571
+ if (provider === void 0 || model === void 0) throw new Error(missing);
14572
+ return {
14573
+ provider,
14574
+ model
14575
+ };
14576
+ }
14508
14577
  /** Skill evolution's model work: drafts and backtests run in the background, cases and reviews answer here. */
14509
14578
  async function skillAssistance(runtime, lifecycle, operation, input, signal) {
14510
14579
  const skills = lifecycle.skills;
@@ -14525,11 +14594,13 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
14525
14594
  if (suggestionId === void 0 && skill === void 0) throw new Error("a draft needs a suggestion or a skill");
14526
14595
  const guidance = optionalString(input.guidance, "guidance");
14527
14596
  if (guidance !== void 0 && guidance.length > 4e3) throw new Error("guidance is too long (max 4000 characters)");
14597
+ const draftRoute = modelRoute(input);
14528
14598
  return skills.draft(source, workspaceRoot, {
14529
14599
  ...suggestionId === void 0 ? {} : { suggestionId },
14530
14600
  ...skill === void 0 ? {} : { skill },
14531
14601
  ...guidance === void 0 ? {} : { guidance },
14532
- ...root === void 0 ? {} : { targetRoot: root }
14602
+ ...root === void 0 ? {} : { targetRoot: root },
14603
+ ...draftRoute === void 0 ? {} : { route: draftRoute }
14533
14604
  });
14534
14605
  }
14535
14606
  case "skill-backtest": {
@@ -14544,10 +14615,12 @@ async function skillAssistance(runtime, lifecycle, operation, input, signal) {
14544
14615
  ].includes(String(target.kind))) throw new Error("target kind must be current, draft, version or none");
14545
14616
  const caseIds = input.caseIds === void 0 ? void 0 : input.caseIds;
14546
14617
  if (caseIds !== void 0 && (!Array.isArray(caseIds) || caseIds.some((id) => typeof id !== "string"))) throw new Error("caseIds must be a string array");
14618
+ const runRoute = modelRoute(input, "a backtest model needs both provider and model");
14547
14619
  return skills.backtest(source, workspaceRoot, {
14548
14620
  skillName,
14549
14621
  target,
14550
- ...caseIds === void 0 ? {} : { caseIds }
14622
+ ...caseIds === void 0 ? {} : { caseIds },
14623
+ ...runRoute === void 0 ? {} : { route: runRoute }
14551
14624
  });
14552
14625
  }
14553
14626
  case "skill-derive-cases": {
@@ -14833,13 +14906,16 @@ function createWriteHandler(input, lifecycle, versions) {
14833
14906
  };
14834
14907
  }
14835
14908
  /** Pack data stays inside the selected storage root and DSH authentication. */
14836
- function createPackHandler(input) {
14909
+ function createPackHandler(input, nativeDirectoryPicker = () => false) {
14837
14910
  return async (endpoint, rawPayload) => {
14838
14911
  try {
14839
14912
  const payload = object$4(rawPayload);
14840
14913
  const runtime = scoped(input, payload);
14841
14914
  const manager = runtime.graph.packs;
14842
- if (endpoint === "target") return success$1(manager.target());
14915
+ if (endpoint === "target") return success$1({
14916
+ ...manager.target(),
14917
+ nativeDirectoryPicker: nativeDirectoryPicker()
14918
+ });
14843
14919
  if (endpoint === "export") return success$1(await manager.exportPack("full"));
14844
14920
  if (endpoint === "inspect") return success$1(manager.inspectPack(String(payload.base64 ?? ""), payload.fileName === void 0 ? void 0 : String(payload.fileName)));
14845
14921
  if (endpoint === "import") {
@@ -14995,14 +15071,14 @@ function createReviewHandler(input, lifecycle) {
14995
15071
  }
14996
15072
  };
14997
15073
  }
14998
- function registerRpc(connection, input, lifecycle, versions, onSyncConfigured) {
15074
+ function registerRpc(connection, input, lifecycle, versions, onSyncConfigured, nativeDirectoryPicker) {
14999
15075
  const versionManager = versions ?? new VersionUpdateManager({ mnemonCliPath: () => input.config.cliPath });
15000
15076
  const readHandler = createReadHandler(input, lifecycle, versionManager);
15001
15077
  const activationHandler = createActivationHandler(input);
15002
15078
  const writeHandler = createWriteHandler(input, lifecycle, versionManager);
15003
15079
  const syncHandler = createSyncHandler(input, onSyncConfigured);
15004
15080
  const reviewHandler = createReviewHandler(input, lifecycle);
15005
- const packHandler = createPackHandler(input);
15081
+ const packHandler = createPackHandler(input, nativeDirectoryPicker);
15006
15082
  connection.rpc.handle(MNEMON_READ_CHANNEL, readHandler);
15007
15083
  connection.rpc.handle(MNEMON_ACTIVATION_CHANNEL, activationHandler);
15008
15084
  connection.rpc.handle(MNEMON_WRITE_CHANNEL, writeHandler);
@@ -15019,6 +15095,17 @@ function registerRpc(connection, input, lifecycle, versions, onSyncConfigured) {
15019
15095
  };
15020
15096
  }
15021
15097
  //#endregion
15098
+ //#region src/host/directory-picker.ts
15099
+ /** Older hosts and browse-only compositions keep the existing manual path field. */
15100
+ function hasNativeDirectoryPicker(ctx) {
15101
+ try {
15102
+ const capability = ctx.get("directoryPicker")?.capability?.();
15103
+ return capability?.kind === "native" && typeof capability.pick === "function";
15104
+ } catch {
15105
+ return false;
15106
+ }
15107
+ }
15108
+ //#endregion
15022
15109
  //#region src/host/settings.ts
15023
15110
  function success(value) {
15024
15111
  return {
@@ -18168,8 +18255,8 @@ function apply(rawContext, rawConfig = {}) {
18168
18255
  const skills = new SkillEvolution({
18169
18256
  settings: () => skillSettings.get(),
18170
18257
  source: (agent) => skillsSourceFor(runtime, agent),
18171
- runTask: (workspaceRoot, signal, operation) => lifecycle.runSkillTask(workspaceRoot, signal, operation),
18172
- skillTask: (parent, label, prompt, schema, persona, signal) => coordinator.skillTask(parent, label, prompt, schema, persona, signal)
18258
+ runTask: (workspaceRoot, signal, operation, route) => lifecycle.runSkillTask(workspaceRoot, signal, operation, route),
18259
+ skillTask: (parent, label, prompt, schema, persona, signal) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent))
18173
18260
  });
18174
18261
  lifecycle.useSkillEvolution(skills);
18175
18262
  ctx.effect(() => {
@@ -18205,7 +18292,7 @@ function apply(rawContext, rawConfig = {}) {
18205
18292
  },
18206
18293
  pluginCommand: () => dshPluginCommand(ctx.get("profileContext"))
18207
18294
  });
18208
- const rpc = registerRpc(connection, runtime, lifecycle, versions, () => autoBackup.refresh());
18295
+ const rpc = registerRpc(connection, runtime, lifecycle, versions, () => autoBackup.refresh(), () => hasNativeDirectoryPicker(ctx));
18209
18296
  const settings = registerSettingsRpc(connection, hostSettings);
18210
18297
  const view = registerViewRpc(connection, runtime, extensions, memoryPlugins, lifecycle, pluginInstallation);
18211
18298
  if (Context.is(webContext)) new MnemonRemoteService(webContext, {
@@ -11,13 +11,13 @@ var dependencies = {
11
11
  "dsh-mnemon-provider-honcho": "0.5.5",
12
12
  "dsh-mnemon-provider-mem0": "0.5.5",
13
13
  "dsh-mnemon-provider-mnemon-native": "0.5.10",
14
- "dsh-mnemon-provider-openviking": "0.5.9",
14
+ "dsh-mnemon-provider-openviking": "0.5.10",
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.23",
18
+ "dsh-mnemon-source-memory-spaces": "0.5.24",
19
19
  "dsh-mnemon-source-runtime": "0.5.16",
20
- "dsh-mnemon-source-skills": "0.5.2",
20
+ "dsh-mnemon-source-skills": "0.5.4",
21
21
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
22
22
  "dsh-mnemon-strategy-default-three-tier": "0.5.11",
23
23
  "dsh-mnemon-strategy-general": "0.5.1",
package/lib/starter.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as prepareStarterResolution } from "./starter-resolution-BAHgCK5h.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-DYeYvfwr.js";
2
2
  //#region src/starter.ts
3
3
  const name = "dsh-mnemon-starter";
4
4
  const provide = ["mnemonStarterReady"];
@@ -1,5 +1,6 @@
1
1
  export { installMemorySourceUI, memorySourcePageEntryId, MNEMON_SOURCE_PAGE_SLOT, MNEMON_SOURCE_CONFIGURATION_MUTATE, MNEMON_SOURCE_CONFIGURATION_READ, type MemorySourcePageComponent, type MemorySourcePageDefinition, type MemorySourcePageProps, type MemorySourceUIContribution, type MemorySourceUIContext, type MemorySourcePageNavigation, } from './source-pages.tsx';
2
2
  export type { MnemonSourceManagementClient, MemorySourcePageInstance } from './source-contracts.ts';
3
+ export type { TaskAgentModelCatalog, TaskAgentModelCatalogGroup, TaskAgentModelCatalogModel } from "../core/contracts/index.js";
3
4
  export { installMemoryComponentUI, MNEMON_COMPONENT_FALLBACK_PRIORITY, MNEMON_COMPONENT_SETTINGS_SLOT, MNEMON_COMPONENT_STATUS_SLOT, type MemoryComponentConfigurationSnapshot, type MemoryComponentHost, type MemoryComponentSettingsComponent, type MemoryComponentSettingsProps, type MemoryComponentStatusComponent, type MemoryComponentStatusProps, type MemoryComponentUIContext, type MemoryComponentUIContribution, } from './component-ui.tsx';
4
5
  export { memorySettingsStyles, ToggleRow } from './settings-controls.tsx';
5
6
  export { PanelActions, useLive, useStaged, WriteFailure } from './settings-panel.tsx';
@@ -328,6 +328,8 @@ export declare const zh: {
328
328
  'storage.directoryExample': string;
329
329
  'storage.thisWorkspace': string;
330
330
  'storage.moveNote': string;
331
+ 'storage.chooseDirectory': string;
332
+ 'storage.pickFailed': string;
331
333
  'config.scopeTitle': string;
332
334
  'config.global': string;
333
335
  'config.workspace': string;
@@ -342,6 +344,12 @@ export declare const zh: {
342
344
  'config.runtimeUserScopeStorageHint': string;
343
345
  'config.runtimeUserScopeGlobal': string;
344
346
  'config.runtimeUserScopeGlobalHint': string;
347
+ 'config.runtimeLimitsTitle': string;
348
+ 'config.runtimeLimitsHint': string;
349
+ 'config.runtime.userLimitBytes': string;
350
+ 'config.runtime.memoryLimitBytes': string;
351
+ 'config.runtimeLimitsApply': string;
352
+ 'config.runtimeLimitInvalid': string;
345
353
  'config.enhancementsRefreshFailed': string;
346
354
  'config.strategyTitle': string;
347
355
  'config.strategyFailed': string;
@@ -757,6 +765,10 @@ export declare const zh: {
757
765
  'review.rejectedHint': string;
758
766
  'skills.label': string;
759
767
  'skills.evolution': string;
768
+ 'skills.allowCreate': string;
769
+ 'skills.allowCreateHint': string;
770
+ 'skills.allowUpdate': string;
771
+ 'skills.allowUpdateHint': string;
760
772
  'skills.evolutionHint': string;
761
773
  'skills.autoBacktest': string;
762
774
  'skills.autoBacktestHint': string;
@@ -32,6 +32,11 @@ export declare function SelectField<T extends string>(props: {
32
32
  /** A fuller name for assistive technology than the visible label. */
33
33
  ariaLabel?: string | undefined;
34
34
  className?: string | undefined;
35
+ /**
36
+ * Repeat the chosen option's detail under its name in the selector itself, as
37
+ * the menu shows it. Off by default: most fields have no room for a second line.
38
+ */
39
+ triggerDetail?: boolean | undefined;
35
40
  onChange: (value: T) => void;
36
41
  }): JSX.Element;
37
42
  /** How a task Agent's write ended, as the receipt names it. */
@@ -1,4 +1,4 @@
1
- import type { MemoryJsonValue, MemorySourceManagementInstance, MemorySourceManagementResult } from "../core/contracts/index.js";
1
+ import type { MemoryJsonValue, MemorySourceManagementInstance, MemorySourceManagementResult, TaskAgentModelCatalog } from "../core/contracts/index.js";
2
2
  /** Instance- and scope-bound capability; never the Host's raw transport. */
3
3
  export interface MnemonSourceManagementClient {
4
4
  readonly sourceInstanceKey: string;
@@ -16,6 +16,11 @@ export interface MnemonSourceManagementClient {
16
16
  expectedRevision?: string;
17
17
  confirmed: true;
18
18
  }): Promise<MemorySourceManagementResult>;
19
+ /**
20
+ * The routes and model directory a page may offer as a per-run choice. Absent
21
+ * on a Host that offers none, and the page then hides its pickers.
22
+ */
23
+ readonly models?: () => Promise<TaskAgentModelCatalog>;
19
24
  }
20
25
  /** Browser metadata is display-only; the Host validates capability identifiers. */
21
26
  export type MemorySourcePageInstance = Omit<MemorySourceManagementInstance, 'capabilities'> & {
@@ -38,3 +38,4 @@ export interface MemoryWake {
38
38
  }
39
39
  export * from './plugin.ts';
40
40
  export * from './view.ts';
41
+ export * from './task-agent-model.ts';
@@ -0,0 +1,49 @@
1
+ export interface TaskAgentModelCatalogModel {
2
+ id: string;
3
+ name: string;
4
+ description?: string;
5
+ /** DSH's merge-extensible model modality ids; absent means capability is unknown. */
6
+ inputModalities?: string[];
7
+ }
8
+ export interface TaskAgentModelCatalogGroup {
9
+ id: string;
10
+ name: string;
11
+ models: TaskAgentModelCatalogModel[];
12
+ }
13
+ export interface TaskAgentModelCatalogFailure {
14
+ id: string;
15
+ name: string;
16
+ message: string;
17
+ }
18
+ export interface TaskAgentModelCatalog {
19
+ effective?: {
20
+ provider: string;
21
+ model: string;
22
+ source: 'fixed' | 'session' | 'dsh-default' | 'active-agent';
23
+ };
24
+ defaultSelection?: {
25
+ provider: string;
26
+ model: string;
27
+ };
28
+ /**
29
+ * The route the asking conversation's own delegated work takes while it is
30
+ * open: the model of its last request, or the one it was created with before
31
+ * its first. An inherited setting that follows its conversation takes it too;
32
+ * it is reported whether or not that switch is on.
33
+ */
34
+ session?: {
35
+ provider: string;
36
+ model: string;
37
+ };
38
+ /**
39
+ * The route an inherited setting takes without following a conversation: the
40
+ * DSH new-session default, else an open Agent's creation route.
41
+ */
42
+ inherited?: {
43
+ provider: string;
44
+ model: string;
45
+ source: 'dsh-default' | 'active-agent';
46
+ };
47
+ groups: TaskAgentModelCatalogGroup[];
48
+ failures: TaskAgentModelCatalogFailure[];
49
+ }
@@ -325,55 +325,7 @@ export interface ResolvedTaskAgentModelConfig {
325
325
  /** Present, and true, only on an inherited route that follows its conversation. */
326
326
  followConversation?: true;
327
327
  }
328
- export interface TaskAgentModelCatalogModel {
329
- id: string;
330
- name: string;
331
- description?: string;
332
- /** DSH's merge-extensible model modality ids; absent means capability is unknown. */
333
- inputModalities?: string[];
334
- }
335
- export interface TaskAgentModelCatalogGroup {
336
- id: string;
337
- name: string;
338
- models: TaskAgentModelCatalogModel[];
339
- }
340
- export interface TaskAgentModelCatalogFailure {
341
- id: string;
342
- name: string;
343
- message: string;
344
- }
345
- export interface TaskAgentModelCatalog {
346
- effective?: {
347
- provider: string;
348
- model: string;
349
- source: 'fixed' | 'session' | 'dsh-default' | 'active-agent';
350
- };
351
- defaultSelection?: {
352
- provider: string;
353
- model: string;
354
- };
355
- /**
356
- * The route the asking conversation's own delegated work takes while it is
357
- * open: the model of its last request, or the one it was created with before
358
- * its first. An inherited setting that follows its conversation takes it too;
359
- * it is reported whether or not that switch is on.
360
- */
361
- session?: {
362
- provider: string;
363
- model: string;
364
- };
365
- /**
366
- * The route an inherited setting takes without following a conversation: the
367
- * DSH new-session default, else an open Agent's creation route.
368
- */
369
- inherited?: {
370
- provider: string;
371
- model: string;
372
- source: 'dsh-default' | 'active-agent';
373
- };
374
- groups: TaskAgentModelCatalogGroup[];
375
- failures: TaskAgentModelCatalogFailure[];
376
- }
328
+ export type { TaskAgentModelCatalog, TaskAgentModelCatalogFailure, TaskAgentModelCatalogGroup, TaskAgentModelCatalogModel, } from '../core/contracts/task-agent-model.ts';
377
329
  export interface InteractionConfig {
378
330
  turnBar?: boolean;
379
331
  saveAction?: boolean;
@@ -418,6 +370,10 @@ export type SkillsConfig = Partial<ResolvedSkillsConfig>;
418
370
  export interface ResolvedSkillsConfig {
419
371
  /** Review finished conversations in the background and suggest new or better skills. */
420
372
  evolution: boolean;
373
+ /** Allow reviews to suggest new skills. Existing suggestions remain actionable. */
374
+ allowCreate: boolean;
375
+ /** Allow reviews to suggest changes to existing skills. */
376
+ allowUpdate: boolean;
421
377
  /** Backtest a draft against the current version as soon as it is written. */
422
378
  autoBacktest: boolean;
423
379
  /** The one-line skills bar under a finished turn. */
@@ -772,6 +728,8 @@ export interface MnemonPackTarget {
772
728
  root: string;
773
729
  scope: StorageScopeKind;
774
730
  defaultRoot: string;
731
+ /** Optional on older Hosts; only a native DSH backend can open an OS chooser. */
732
+ nativeDirectoryPicker?: boolean;
775
733
  }
776
734
  export interface MnemonPackExport {
777
735
  fileName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mnemon",
3
- "version": "0.5.35",
3
+ "version": "0.5.37",
4
4
  "description": "Composable, view-based context for DeepSeek Harness. Pluggable sources and strategies, with layered memory and self-evolving skills out of the box.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -161,13 +161,13 @@
161
161
  "dsh-mnemon-provider-honcho": "0.5.5",
162
162
  "dsh-mnemon-provider-mem0": "0.5.5",
163
163
  "dsh-mnemon-provider-mnemon-native": "0.5.10",
164
- "dsh-mnemon-provider-openviking": "0.5.9",
164
+ "dsh-mnemon-provider-openviking": "0.5.10",
165
165
  "dsh-mnemon-provider-retaindb": "0.5.6",
166
166
  "dsh-mnemon-provider-supermemory": "0.5.5",
167
167
  "dsh-mnemon-source-documents": "0.5.11",
168
- "dsh-mnemon-source-memory-spaces": "0.5.23",
168
+ "dsh-mnemon-source-memory-spaces": "0.5.24",
169
169
  "dsh-mnemon-source-runtime": "0.5.16",
170
- "dsh-mnemon-source-skills": "0.5.2",
170
+ "dsh-mnemon-source-skills": "0.5.4",
171
171
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
172
172
  "dsh-mnemon-strategy-default-three-tier": "0.5.11",
173
173
  "dsh-mnemon-strategy-general": "0.5.1",