dsh-mnemon 0.5.40 → 0.5.41

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/bundle.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as prepareStarterResolution } from "./starter-resolution-C5nNk-cs.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-xVjONc6g.js";
2
2
  //#region src/bundle.ts
3
3
  const groupKey = Symbol.for("cordis.group");
4
4
  const init = Symbol.for("cordis.init");
package/lib/client.js CHANGED
@@ -318,6 +318,8 @@ window.__ModuleLoader__.load({
318
318
  allowUpdate: true,
319
319
  targetScope: "user",
320
320
  autoBacktest: true,
321
+ draftMaxTokens: 32768,
322
+ draftRetryMaxTokens: 65536,
321
323
  hints: true,
322
324
  sidebar: false,
323
325
  reviewThreshold: 5,
@@ -3145,6 +3147,10 @@ window.__ModuleLoader__.load({
3145
3147
  "skills.autoBacktest": "草案自动回测",
3146
3148
  "skills.autoBacktestHint": "草案写好后,用测试用例与当前版本对比",
3147
3149
  "skills.parameters": "复盘参数",
3150
+ "skills.draftGeneration": "草案生成",
3151
+ "skills.draftMaxTokens": "草案输出 token 上限",
3152
+ "skills.draftRetryMaxTokens": "重试输出 token 上限",
3153
+ "skills.draftBudgetHint": "预算包含推理与完整 SKILL.md。截断时仅在重试上限更大时重新生成一次;设为相同或更小值可关闭扩容重试。两项均需符合模型提供方的输出限制,保存后用于下一次草案。",
3148
3154
  "skills.reviewThreshold": "复盘阈值",
3149
3155
  "skills.idleSeconds": "空闲等待(秒)",
3150
3156
  "skills.cooldownSeconds": "复盘间隔(秒)",
@@ -3965,6 +3971,10 @@ window.__ModuleLoader__.load({
3965
3971
  "skills.autoBacktest": "Backtest drafts",
3966
3972
  "skills.autoBacktestHint": "Compare a new draft with the current version on its test cases",
3967
3973
  "skills.parameters": "Review parameters",
3974
+ "skills.draftGeneration": "Draft generation",
3975
+ "skills.draftMaxTokens": "Draft output token limit",
3976
+ "skills.draftRetryMaxTokens": "Retry output token limit",
3977
+ "skills.draftBudgetHint": "Budgets include reasoning and the complete SKILL.md. Truncation triggers one fresh attempt only when the retry limit is higher; an equal or lower value disables it. Keep both within the model provider’s output limit. Saved changes apply to the next draft.",
3968
3978
  "skills.reviewThreshold": "Review threshold",
3969
3979
  "skills.idleSeconds": "Idle wait (s)",
3970
3980
  "skills.cooldownSeconds": "Review interval (s)",
@@ -12032,6 +12042,17 @@ window.__ModuleLoader__.load({
12032
12042
  integer: true
12033
12043
  }
12034
12044
  ];
12045
+ const DRAFT_PARAMETERS = [{
12046
+ key: "draftMaxTokens",
12047
+ min: 128,
12048
+ max: 131072,
12049
+ integer: true
12050
+ }, {
12051
+ key: "draftRetryMaxTokens",
12052
+ min: 128,
12053
+ max: 131072,
12054
+ integer: true
12055
+ }];
12035
12056
  /** The value a parameter stores, or undefined when the Host would refuse it. */
12036
12057
  function parameterValue(field, text) {
12037
12058
  if (text.trim() === "") return void 0;
@@ -12072,8 +12093,16 @@ window.__ModuleLoader__.load({
12072
12093
  const changed = PARAMETERS.filter((field) => parameterValue(field, draft[field.key]) !== saved[field.key]);
12073
12094
  if (changed.length > 0) await scope.mutate(changed.map((field) => set(field.key, parameterValue(field, draft[field.key]))));
12074
12095
  });
12096
+ const draftBudgets = useStaged({
12097
+ draftMaxTokens: String(saved.draftMaxTokens),
12098
+ draftRetryMaxTokens: String(saved.draftRetryMaxTokens)
12099
+ }, async (draft) => {
12100
+ const changed = DRAFT_PARAMETERS.filter((field) => parameterValue(field, draft[field.key]) !== saved[field.key]);
12101
+ if (changed.length > 0) await scope.mutate(changed.map((field) => set(field.key, parameterValue(field, draft[field.key]))));
12102
+ });
12075
12103
  const disabled = !page.writable || snapshot.status === "loading" || !snapshot.writable;
12076
12104
  const invalid = PARAMETERS.find((field) => parameterValue(field, parameters.draft[field.key]) === void 0);
12105
+ const invalidDraft = DRAFT_PARAMETERS.find((field) => parameterValue(field, draftBudgets.draft[field.key]) === void 0);
12077
12106
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
12078
12107
  className: MnemonSettingsCard_module_css_default.panelSection,
12079
12108
  "aria-label": t("skills.label"),
@@ -12133,6 +12162,47 @@ window.__ModuleLoader__.load({
12133
12162
  destination.apply();
12134
12163
  }
12135
12164
  }),
12165
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
12166
+ className: MnemonSettingsCard_module_css_default.advanced,
12167
+ children: [
12168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [t("skills.draftGeneration"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { size: 12 })] }),
12169
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
12170
+ className: MnemonSettingsCard_module_css_default.panelNote,
12171
+ children: t("skills.draftBudgetHint")
12172
+ }),
12173
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
12174
+ className: MnemonSettingsCard_module_css_default.fieldGrid,
12175
+ children: DRAFT_PARAMETERS.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [t(`skills.${field.key}`), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
12176
+ type: "number",
12177
+ step: 1,
12178
+ min: field.min,
12179
+ max: field.max,
12180
+ inputMode: "numeric",
12181
+ value: draftBudgets.draft[field.key],
12182
+ disabled: disabled || draftBudgets.saving,
12183
+ "aria-invalid": parameterValue(field, draftBudgets.draft[field.key]) === void 0,
12184
+ onChange: (event) => draftBudgets.edit({ [field.key]: event.target.value })
12185
+ })] }, field.key))
12186
+ }),
12187
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelActions, {
12188
+ dirty: draftBudgets.dirty,
12189
+ saving: draftBudgets.saving,
12190
+ applied: draftBudgets.applied,
12191
+ failed: draftBudgets.failed,
12192
+ disabled,
12193
+ t,
12194
+ invalid: invalidDraft === void 0 ? null : t("skills.invalid", {
12195
+ field: t(`skills.${invalidDraft.key}`),
12196
+ min: invalidDraft.min,
12197
+ max: invalidDraft.max
12198
+ }),
12199
+ onDiscard: draftBudgets.discard,
12200
+ onApply: () => {
12201
+ draftBudgets.apply();
12202
+ }
12203
+ })
12204
+ ]
12205
+ }),
12136
12206
  evolution.value && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToggleRow, {
12137
12207
  id: "mnemon-skills-backtest",
12138
12208
  label: t("skills.autoBacktest"),
package/lib/index.js CHANGED
@@ -389,6 +389,8 @@ const DEFAULT_SKILLS_CONFIG = Object.freeze({
389
389
  allowUpdate: true,
390
390
  targetScope: "user",
391
391
  autoBacktest: true,
392
+ draftMaxTokens: 32768,
393
+ draftRetryMaxTokens: 65536,
392
394
  hints: true,
393
395
  sidebar: false,
394
396
  reviewThreshold: 5,
@@ -481,6 +483,8 @@ const SkillsConfig = z.object({
481
483
  "project"
482
484
  ]).default(DEFAULT_SKILLS_CONFIG.targetScope),
483
485
  autoBacktest: z.boolean().default(DEFAULT_SKILLS_CONFIG.autoBacktest),
486
+ draftMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftMaxTokens),
487
+ draftRetryMaxTokens: z.number().step(1).min(128).max(131072).default(DEFAULT_SKILLS_CONFIG.draftRetryMaxTokens),
484
488
  hints: z.boolean().default(DEFAULT_SKILLS_CONFIG.hints),
485
489
  sidebar: z.boolean().default(DEFAULT_SKILLS_CONFIG.sidebar),
486
490
  reviewThreshold: z.number().step(1).min(1).max(50).default(DEFAULT_SKILLS_CONFIG.reviewThreshold),
@@ -640,6 +644,7 @@ const Config = z.object({
640
644
  skills: SkillsConfig.default({ ...DEFAULT_SKILLS_CONFIG }),
641
645
  persistenceStrategy: MemoryPersistenceStrategySchema,
642
646
  taskAgentModel: TaskAgentModelSchema,
647
+ subagentProviderNames: z.array(z.string()).default([]),
643
648
  syncEnabled: z.boolean().default(false)
644
649
  });
645
650
  function resolveInteractionConfig(config = {}) {
@@ -689,6 +694,11 @@ function resolveCustomPacks(value, legacyDataDir) {
689
694
  }
690
695
  return packs;
691
696
  }
697
+ function resolveSubagentProviderNames(value) {
698
+ if (value === void 0) return [];
699
+ if (!Array.isArray(value) || value.length > 32 || value.some((name) => typeof name !== "string" || !name.trim() || name.length > 128)) throw new Error("dsh-mnemon: subagentProviderNames must contain at most 32 nonempty provider names of at most 128 characters");
700
+ return [...new Set(value.map((name) => name.trim()))];
701
+ }
692
702
  function resolveTaskAgentModel(value) {
693
703
  const mode = value?.mode ?? "inherit";
694
704
  if (mode !== "inherit" && mode !== "fixed") throw new Error(`dsh-mnemon: unsupported task Agent model mode: ${String(mode)}`);
@@ -813,6 +823,7 @@ function resolveConfig(config = {}) {
813
823
  },
814
824
  persistenceStrategy: resolvePersistenceStrategy(config.persistenceStrategy),
815
825
  taskAgentModel: resolveTaskAgentModel(config.taskAgentModel),
826
+ subagentProviderNames: resolveSubagentProviderNames(config.subagentProviderNames),
816
827
  syncEnabled: config.syncEnabled ?? false
817
828
  };
818
829
  }
@@ -7187,9 +7198,9 @@ var MemoryExecutions = class {
7187
7198
  constructor(runtime) {
7188
7199
  this.runtime = runtime;
7189
7200
  }
7190
- async turn(agent, turn, signal, delegation) {
7201
+ async turn(agent, turn, signal, delegation, independent = false) {
7191
7202
  signal.throwIfAborted();
7192
- const slot = this.open(agent, "turn", `${agent.id}:${turn}`, "agent.root-turn", delegation);
7203
+ const slot = this.open(agent, "turn", `${agent.id}:${turn}`, "agent.root-turn", delegation, independent);
7193
7204
  const abort = () => slot.controller.abort(signal.reason);
7194
7205
  signal.addEventListener("abort", abort, { once: true });
7195
7206
  try {
@@ -7272,7 +7283,7 @@ var MemoryExecutions = class {
7272
7283
  if (slot.kind === "turn") slot.close();
7273
7284
  }
7274
7285
  }
7275
- open(agent, kind, turnId, purpose, delegation) {
7286
+ open(agent, kind, turnId, purpose, delegation, independent = false) {
7276
7287
  this.assertOpen();
7277
7288
  const previous = this.slots.get(agent.id);
7278
7289
  const done = deferred();
@@ -7307,7 +7318,7 @@ var MemoryExecutions = class {
7307
7318
  await previous?.done;
7308
7319
  slot.controller.signal.throwIfAborted();
7309
7320
  this.assertOpen();
7310
- const graph = delegation?.graph ?? this.runtime.forAgent(agent);
7321
+ const graph = delegation?.graph ?? (independent ? this.runtime.forAgent(agent, { independent: true }) : this.runtime.forAgent(agent));
7311
7322
  const manager = graph.composableTurns;
7312
7323
  const scope = delegation === void 0 ? agentScope(agent, graph.config) : {
7313
7324
  ...delegation.scope,
@@ -7619,13 +7630,15 @@ var LiveMnemonRuntime = class {
7619
7630
  for (const graph of graphs) graph.dispose();
7620
7631
  }
7621
7632
  /** Resolve the runtime that must serve one Agent execution. */
7622
- forAgent(agent) {
7633
+ forAgent(agent, options = {}) {
7623
7634
  this.assertOpen();
7624
- const pinned = this.agentGraphs.get(agent.id);
7625
- if (pinned !== void 0) return pinned.graph;
7626
- const parentSession = agent.session.header?.origin === "subagent" ? agent.session.header.parentSession?.trim() : void 0;
7627
- const inherited = parentSession === void 0 || parentSession === "" ? void 0 : this.agentGraphs.get(parentSession);
7628
- if (inherited !== void 0) return inherited.graph;
7635
+ if (!options.independent) {
7636
+ const pinned = this.agentGraphs.get(agent.id);
7637
+ if (pinned !== void 0) return pinned.graph;
7638
+ const parentSession = agent.session.header?.origin === "subagent" ? agent.session.header.parentSession?.trim() : void 0;
7639
+ const inherited = parentSession === void 0 || parentSession === "" ? void 0 : this.agentGraphs.get(parentSession);
7640
+ if (inherited !== void 0) return inherited.graph;
7641
+ }
7629
7642
  if (!isWorkspaceStorageScope(this.current.config.storageScope)) return this.current;
7630
7643
  const cwd = agent.session.header?.cwd?.trim();
7631
7644
  if (cwd === void 0 || cwd === "") throw new Error("the current DSH session has no workspace for Mnemon");
@@ -8461,6 +8474,72 @@ async function applyReconcileOperations(operations, applier, signal, selection)
8461
8474
  };
8462
8475
  }
8463
8476
  //#endregion
8477
+ //#region src/host/agent-compat.ts
8478
+ function configuredProviderNames(host) {
8479
+ const names = /* @__PURE__ */ new Set();
8480
+ for (const runtime of host?.registry?.values() ?? []) for (const fiber of runtime.fibers) {
8481
+ const config = fiber.effectiveConfig;
8482
+ if (config && typeof config === "object" && "providerName" in config && typeof config.providerName === "string") names.add(config.providerName);
8483
+ }
8484
+ return [...names];
8485
+ }
8486
+ /** Official 0.2.2-rc.1 has registry ownership; master fcc0ad4 moved it into Subagent. */
8487
+ function activationOwned(subagents) {
8488
+ return typeof subagents?.rootOf === "function";
8489
+ }
8490
+ /** Only live registry instances can be adopted; persisted lineage is not authority. */
8491
+ function isRootAgent(agents, subagents, agent) {
8492
+ if (agents.get(agent.id) !== agent) return false;
8493
+ if (typeof agents.roots === "function") return agents.roots().includes(agent);
8494
+ try {
8495
+ return subagents.rootOf?.(agent) === agent;
8496
+ } catch {
8497
+ return false;
8498
+ }
8499
+ }
8500
+ function rootAgents(agents, subagents) {
8501
+ if (typeof agents.roots === "function") return agents.roots();
8502
+ return (agents.list?.() ?? []).filter((agent) => isRootAgent(agents, subagents, agent));
8503
+ }
8504
+ /** Persisted IDs keep completed internal sessions out of conversation automation after restart. */
8505
+ function isTaskRecord(id) {
8506
+ return /^mnemon-(?:task|run)-v1-[0-9a-f-]{36}$/.test(id);
8507
+ }
8508
+ function titleTask(agent, title) {
8509
+ (agent.ctx.get?.("sessionTitle"))?.rename?.(agent.session, title);
8510
+ }
8511
+ const creatingChild = new AsyncLocalStorage();
8512
+ /** Share one reservation across guard, continuation and composition wrappers, never across nested runs. */
8513
+ function withReservedChild(parent, operation, fresh = false) {
8514
+ const current = creatingChild.getStore();
8515
+ if (!fresh && current?.parent === parent && !current.claimed) return operation();
8516
+ return creatingChild.run({
8517
+ parent,
8518
+ id: `mnemon-run-v1-${randomUUID()}`
8519
+ }, operation);
8520
+ }
8521
+ function reservedChildId(parent) {
8522
+ const current = creatingChild.getStore();
8523
+ return current?.parent === parent && !current.claimed ? current.id : void 0;
8524
+ }
8525
+ function claimReservedChild(parent) {
8526
+ const current = creatingChild.getStore();
8527
+ if (current?.parent !== parent || current.claimed) return void 0;
8528
+ current.claimed = true;
8529
+ return current.id;
8530
+ }
8531
+ function isCreatingChild(agents, child) {
8532
+ const current = creatingChild.getStore();
8533
+ return current !== void 0 && isReservedChild(agents, current.parent, child);
8534
+ }
8535
+ /** A lineage string or same-tree relationship is not a creation capability. */
8536
+ function isReservedChild(agents, parent, child) {
8537
+ const current = creatingChild.getStore();
8538
+ if (current?.parent !== parent || !current.claimed || current.id !== child.id || agents?.get?.(parent.id) !== parent || agents.get(child.id) !== child || current.child !== void 0 && current.child !== child) return false;
8539
+ current.child = child;
8540
+ return true;
8541
+ }
8542
+ //#endregion
8464
8543
  //#region src/host/review-tools.ts
8465
8544
  const startingChild$1 = new AsyncLocalStorage();
8466
8545
  /**
@@ -8470,7 +8549,11 @@ const startingChild$1 = new AsyncLocalStorage();
8470
8549
  * still requires startGuardedReview; it never removes another plugin's policy.
8471
8550
  */
8472
8551
  function idleReviewBlockReason(parent, agentTeams = "pause") {
8473
- return agentTeams !== "scoped" && parent.ctx?.get?.("agentTeams") !== void 0 && parent.ctx.tools?.get?.("spawn_teammate", parent) !== void 0 ? "agent-team" : void 0;
8552
+ if (agentTeams === "scoped") return void 0;
8553
+ const teams = parent.ctx?.get?.("agentTeams");
8554
+ if (teams === void 0) return void 0;
8555
+ const tools = parent.ctx.tools;
8556
+ return tools?.get?.("spawn_teammate", parent) !== void 0 || typeof teams?.isEnabled === "function" && teams.isEnabled(parent) && tools?.get?.("team_task_create", parent) !== void 0 ? "agent-team" : void 0;
8474
8557
  }
8475
8558
  /** The two writes an idle review can make, one per layer; both are rows of the tool table. */
8476
8559
  const DOCUMENT_WRITE = "mnemon_document_create";
@@ -8522,20 +8605,24 @@ function startGuardedReview(host, parent, toolNames, start, published, policy) {
8522
8605
  async function startGuardedChild(host, parent, start, guard, options) {
8523
8606
  const { purpose, published } = options;
8524
8607
  const agents = host.agents;
8525
- if (typeof agents?.isOwnedBy !== "function") {
8608
+ const reserved = activationOwned(host.subagents);
8609
+ if (reserved && typeof agents?.get !== "function") throw new Error(`Mnemon ${purpose} requires exact live Agent lookup`);
8610
+ if (!reserved && typeof agents?.isOwnedBy !== "function") {
8526
8611
  if (options.optional === true) return start();
8527
8612
  throw new Error(`Mnemon ${purpose} requires DSH Agent ownership and scoped tool guard support`);
8528
8613
  }
8529
8614
  const pending = Symbol(`Mnemon ${purpose} publication`);
8530
8615
  const guards = /* @__PURE__ */ new Map();
8531
8616
  let attachmentError;
8617
+ let expectedChildId;
8532
8618
  let run;
8533
8619
  const listener = host.on("agent/created", (({ agent }) => {
8534
- if (startingChild$1.getStore() !== pending || !agents.isOwnedBy(agent.id, parent)) return;
8620
+ if (startingChild$1.getStore() !== pending) return;
8621
+ if (reserved ? agent.id !== expectedChildId || !isReservedChild(agents, parent, agent) : !agents.isOwnedBy(agent.id, parent)) return;
8535
8622
  try {
8536
8623
  const tools = agent.ctx?.tools;
8537
8624
  if (typeof tools?.guard !== "function") {
8538
- if (options.optional === true) return;
8625
+ if (options.optional === true && !reserved) return;
8539
8626
  throw new Error(`Mnemon ${purpose} requires DSH scoped tool guard support`);
8540
8627
  }
8541
8628
  const dispose = tools.guard(guard);
@@ -8563,13 +8650,16 @@ async function startGuardedChild(host, parent, start, guard, options) {
8563
8650
  if (failed.length) throw new AggregateError(failed.map((outcome) => outcome.reason), `Mnemon ${purpose} guard cleanup failed`);
8564
8651
  };
8565
8652
  try {
8566
- run = await startingChild$1.run(pending, start);
8653
+ run = await startingChild$1.run(pending, () => reserved ? withReservedChild(parent, () => {
8654
+ expectedChildId = reservedChildId(parent);
8655
+ return start();
8656
+ }, true) : start());
8567
8657
  if (attachmentError !== void 0) throw attachmentError;
8568
8658
  const runId = run.id;
8569
8659
  const child = run.localAgent ?? [...guards.keys()].find((agent) => agent.id === runId);
8570
8660
  if (child === void 0 || !guards.has(child)) {
8571
- const local = run.localAgent ?? agents.get?.(runId);
8572
- if (options.optional === true && guards.size === 0 && typeof local?.ctx?.tools?.guard !== "function") {
8661
+ const local = run.localAgent ?? agents?.get?.(runId);
8662
+ if (!reserved && options.optional === true && guards.size === 0 && typeof local?.ctx?.tools?.guard !== "function") {
8573
8663
  await closeObserver();
8574
8664
  return run;
8575
8665
  }
@@ -8719,10 +8809,13 @@ function continueAfterRefusal(agent) {
8719
8809
  * that does not report creation leaves the child as it was.
8720
8810
  */
8721
8811
  async function startWithContinuationTurns(host, parent, start) {
8812
+ const reserved = activationOwned(host.subagents);
8722
8813
  const pending = Symbol("Mnemon delegated start");
8723
8814
  const stops = [];
8815
+ let expectedChildId;
8724
8816
  const listener = host.on("agent/created", (({ agent }) => {
8725
8817
  if (startingChild.getStore() !== pending) return;
8818
+ if (reserved && (agent.id !== expectedChildId || !isReservedChild(host.agents, parent, agent))) return;
8726
8819
  if (typeof host.agents?.isOwnedBy === "function" && !host.agents.isOwnedBy(agent.id, parent)) return;
8727
8820
  for (const stop of continueAfterRefusal(agent)) if (typeof stop === "function") stops.push(stop);
8728
8821
  }));
@@ -8731,7 +8824,10 @@ async function startWithContinuationTurns(host, parent, start) {
8731
8824
  };
8732
8825
  let run;
8733
8826
  try {
8734
- run = await startingChild.run(pending, start);
8827
+ run = await startingChild.run(pending, () => reserved ? withReservedChild(parent, () => {
8828
+ expectedChildId = reservedChildId(parent);
8829
+ return start();
8830
+ }) : start());
8735
8831
  } catch (error) {
8736
8832
  await release();
8737
8833
  throw error;
@@ -8790,6 +8886,7 @@ function childComposition(subagents, provider) {
8790
8886
  * step, which needs `publications`.
8791
8887
  */
8792
8888
  async function startSubagent(subagents, agents, provider, request, publications) {
8889
+ if (typeof subagents.startActivation === "function" && reservedChildId(request.parent) === void 0) return withReservedChild(request.parent, () => startSubagent(subagents, agents, provider, request, publications));
8793
8890
  const { toolFilter, persona, ...task } = request;
8794
8891
  if (toolFilter === void 0 && persona === void 0 || childComposition(subagents, subagents.getProvider(provider)) !== "publication") return startOnHost(subagents, agents, provider, request);
8795
8892
  if (publications === void 0) throw new Error("This DSH takes no subagent persona or tool filter at start, and dsh-mnemon cannot observe the child it publishes");
@@ -8797,14 +8894,26 @@ async function startSubagent(subagents, agents, provider, request, publications)
8797
8894
  ...toolFilter === void 0 ? {} : { toolFilter },
8798
8895
  ...persona === void 0 ? {} : { persona }
8799
8896
  };
8800
- return startComposed(publications, agents, request.parent, composition, () => startOnHost(subagents, agents, provider, task));
8897
+ return startComposed(publications, agents, request.parent, composition, () => startOnHost(subagents, agents, provider, task), activationOwned(subagents));
8801
8898
  }
8802
8899
  async function startOnHost(subagents, agents, provider, request) {
8803
8900
  if (typeof subagents.startActivation === "function") {
8804
8901
  const { label, signal, ...task } = request;
8805
- const activation = await subagents.startActivation({
8902
+ const modern = activationOwned(subagents);
8903
+ const childId = claimReservedChild(request.parent);
8904
+ if (modern && (childId === void 0 || agents?.get?.(request.parent.id) !== request.parent)) throw new Error("Mnemon requires an exact live parent and reserved child identity on an activation-owned DSH");
8905
+ const startCurrent = subagents.startActivation;
8906
+ const activation = modern ? await startCurrent.call(subagents, {
8907
+ ...task,
8908
+ provider,
8909
+ label,
8910
+ signal,
8911
+ childId,
8912
+ delivery: "caller"
8913
+ }) : await subagents.startActivation({
8806
8914
  provider,
8807
8915
  label,
8916
+ ...childId === void 0 ? {} : { childId },
8808
8917
  request: task,
8809
8918
  signal,
8810
8919
  delivery: "caller"
@@ -8818,7 +8927,9 @@ async function startOnHost(subagents, agents, provider, request) {
8818
8927
  activation.result.then(release, release);
8819
8928
  if (signal.aborted) stop();
8820
8929
  const child = agents?.get?.(activation.childId);
8821
- const localAgent = child !== void 0 && (typeof agents?.isOwnedBy !== "function" || agents.isOwnedBy(child.id, request.parent)) ? child : void 0;
8930
+ if (modern && (activation.childId !== childId || child === void 0 || !isReservedChild(agents, request.parent, child))) throw new Error("DSH did not return the reserved live Mnemon child");
8931
+ if (modern && child !== void 0) titleTask(child, label.startsWith("Mnemon ") ? label : `Mnemon · ${label}`);
8932
+ const localAgent = child !== void 0 && (modern || typeof agents?.isOwnedBy !== "function" || agents.isOwnedBy(child.id, request.parent)) ? child : void 0;
8822
8933
  return {
8823
8934
  id: activation.childId,
8824
8935
  ...localAgent === void 0 ? {} : { localAgent },
@@ -8858,12 +8969,14 @@ function compose(agent, { toolFilter, persona }) {
8858
8969
  * check confirms the parent. A failed composition makes DSH roll the child
8859
8970
  * back; a child that was not seen published is disposed and refused.
8860
8971
  */
8861
- async function startComposed(publications, agents, parent, composition, start) {
8972
+ async function startComposed(publications, agents, parent, composition, start, reserved = false) {
8862
8973
  const pending = Symbol("Mnemon child composition");
8974
+ const expectedChildId = reserved ? reservedChildId(parent) : void 0;
8863
8975
  const composed = /* @__PURE__ */ new Set();
8864
8976
  let failure;
8865
8977
  const listener = publications.on("agent/created", (({ agent }) => {
8866
8978
  if (composingChild.getStore() !== pending) return;
8979
+ if (reserved && (agent.id !== expectedChildId || !isReservedChild(agents, parent, agent))) return;
8867
8980
  if (typeof agents?.isOwnedBy === "function" && !agents.isOwnedBy(agent.id, parent)) return;
8868
8981
  try {
8869
8982
  compose(agent, composition);
@@ -10332,6 +10445,7 @@ var AgentMemoryTurn = class {
10332
10445
  agent;
10333
10446
  runtime;
10334
10447
  delegation;
10448
+ independent;
10335
10449
  pinned;
10336
10450
  /** A turn that runs without memory because no composition was serving when it began. */
10337
10451
  unavailable;
@@ -10342,10 +10456,11 @@ var AgentMemoryTurn = class {
10342
10456
  lastInspection;
10343
10457
  lastWorkspace;
10344
10458
  releaseDelegation;
10345
- constructor(agent, runtime, delegation) {
10459
+ constructor(agent, runtime, delegation, independent = false) {
10346
10460
  this.agent = agent;
10347
10461
  this.runtime = runtime;
10348
10462
  this.delegation = delegation;
10463
+ this.independent = independent;
10349
10464
  if (delegation === void 0) return;
10350
10465
  this.releaseDelegation = runtime.executions.retain(agent, delegation);
10351
10466
  }
@@ -10437,7 +10552,7 @@ var AgentMemoryTurn = class {
10437
10552
  }
10438
10553
  }
10439
10554
  async pin(turn, generation, signal) {
10440
- const execution = await this.runtime.executions.turn(this.agent, turn, signal, this.delegation);
10555
+ const execution = await this.runtime.executions.turn(this.agent, turn, signal, this.delegation, this.independent);
10441
10556
  const { graph, context } = execution;
10442
10557
  try {
10443
10558
  if (this.closed || this.generation !== generation) throw new Error("memory turn ended during View preparation");
@@ -10581,6 +10696,136 @@ var AutomaticReviewAdmission = class {
10581
10696
  }
10582
10697
  };
10583
10698
  //#endregion
10699
+ //#region src/host/subagent-errors.ts
10700
+ /** A provider stop, kept separate from schema, startup and cancellation errors. */
10701
+ var SubagentStoppedError = class extends Error {
10702
+ stopReason;
10703
+ maxTokens;
10704
+ constructor(stopReason, maxTokens, detail) {
10705
+ super(`memory subagent stopped with ${stopReason}${detail === void 0 ? "" : `: ${detail}`}`);
10706
+ this.stopReason = stopReason;
10707
+ this.maxTokens = maxTokens;
10708
+ this.name = "SubagentStoppedError";
10709
+ }
10710
+ };
10711
+ //#endregion
10712
+ //#region src/host/task-continuation.ts
10713
+ const FINISHED_TASK_REFUSAL = "This Mnemon task has finished. Send a new user message in this conversation to continue working with memory.";
10714
+ const CONTINUATION_GUIDANCE = "This is a new user request in a completed Mnemon task conversation. The previous task stays completed. Use the current memory View and ordinary memory tools for this request; require receipts for changes. Do not repeat the previous task or submit its expired completion request.";
10715
+ /** Older workers have no reserved ID: require their own Host descriptor and task contract. */
10716
+ function isLegacyMemoryTask(agent) {
10717
+ if (agent.session.header?.origin !== "subagent") return false;
10718
+ const events = agent.session.snapshotEvents(agent.session.inheritedEventCount ?? 0);
10719
+ const descriptor = events.findLastIndex((event) => event.type === "subagent/descriptor");
10720
+ if (descriptor < 0 || typeof events[descriptor].data.label !== "string" || !events[descriptor].data.label.startsWith("Mnemon ")) return false;
10721
+ const own = events.slice(descriptor + 1);
10722
+ const textOf = (value) => {
10723
+ return value?.content?.filter((block) => block.type === "text").map((block) => block.text ?? "").join("\n") ?? "";
10724
+ };
10725
+ const prompt = own.find((event) => event.type === "user/message" && (event.data.message ?? event.data).source?.kind === "user");
10726
+ if (prompt === void 0 || !/^\[dsh-mnemon:delegated [a-z-]+\](?:\n|$)/u.test(textOf(prompt.data.message ?? prompt.data))) return false;
10727
+ return own.some((event) => event.type === "system/message" && textOf(event.data.message).includes("Completion protocol: call `mnemon_subagent_result` exactly once with requestId `"));
10728
+ }
10729
+ /** A resumed task receives memory authority only for a newly accepted human turn. */
10730
+ var TaskContinuation = class {
10731
+ agent;
10732
+ runtime;
10733
+ liveAgent;
10734
+ memory;
10735
+ authorizedTurn;
10736
+ stops = [];
10737
+ views = /* @__PURE__ */ new Map();
10738
+ closed = false;
10739
+ constructor(agent, runtime, liveAgent) {
10740
+ this.agent = agent;
10741
+ this.runtime = runtime;
10742
+ this.liveAgent = liveAgent;
10743
+ this.memory = new AgentMemoryTurn(agent, runtime, void 0, true);
10744
+ }
10745
+ start() {
10746
+ try {
10747
+ const guard = this.agent.ctx.tools?.guard?.((execution) => this.guard(execution));
10748
+ if (typeof guard !== "function") throw new Error("Task continuation requires a scoped memory tool guard");
10749
+ this.stops.push(guard);
10750
+ this.stops.push(this.agent.ctx.on("agent/pre-step", ((step, next) => this.preStep(step, next)), { prepend: true }));
10751
+ this.stops.push(this.agent.ctx.on("system-prompt/assemble", (async (_assembly, context, next) => {
10752
+ const assembly = await next();
10753
+ if (context.agent !== void 0 && context.agent !== this.agent) return assembly;
10754
+ const pin = this.memory.current;
10755
+ return applyMemoryViewGuidance(assembly, pin?.context.view, pin?.graph.config.routingGuidance);
10756
+ })));
10757
+ this.stops.push(this.agent.ctx.on("session/event", ((session, event) => {
10758
+ if (session === this.agent.session && event.type === "turn/end" && event.data.turn === this.authorizedTurn) this.end();
10759
+ })));
10760
+ return () => this.dispose();
10761
+ } catch (error) {
10762
+ this.dispose();
10763
+ throw error;
10764
+ }
10765
+ }
10766
+ ownsHumanTurn(actor) {
10767
+ return !this.closed && actor === this.agent && this.liveAgent(actor.id) === actor && this.authorizedTurn !== void 0 && this.memory.current?.turn === this.authorizedTurn && openAgentTurn(actor) === this.authorizedTurn;
10768
+ }
10769
+ guard(execution) {
10770
+ if (!execution.name?.startsWith("mnemon_")) return void 0;
10771
+ const actor = execution.agent;
10772
+ if (this.closed || actor === void 0 || this.liveAgent(actor.id) !== actor) return FINISHED_TASK_REFUSAL;
10773
+ if (actor !== this.agent) {
10774
+ try {
10775
+ const graph = this.runtime.forAgent(actor);
10776
+ const pin = graph.composableTurns.activeTurn(actor.id);
10777
+ if (pin !== void 0 && this.views.get(pin.view.id) === graph) return void 0;
10778
+ } catch {}
10779
+ return FINISHED_TASK_REFUSAL;
10780
+ }
10781
+ if (this.authorizedTurn === void 0) return FINISHED_TASK_REFUSAL;
10782
+ if (execution.name === "mnemon_subagent_result") return "The previous Mnemon task result is already settled. Use ordinary memory tools for the new user request.";
10783
+ if (this.memory.current?.turn !== this.authorizedTurn) return this.memory.unavailableReason ?? "Memory is unavailable for this request.";
10784
+ }
10785
+ async preStep(step, next) {
10786
+ const decision = await next();
10787
+ if (step.agent !== this.agent) return decision;
10788
+ if (decision.kind === "reject" || step.signal.aborted || this.closed || this.liveAgent(this.agent.id) !== this.agent) {
10789
+ this.end();
10790
+ return decision;
10791
+ }
10792
+ const starting = this.authorizedTurn !== step.turn;
10793
+ if (starting) this.end();
10794
+ if (decision.messages.some((message) => message.source.kind === "user" && message.content.some((block) => block.type !== "text" || (block.text?.trim().length ?? 0) > 0))) this.authorizedTurn = step.turn;
10795
+ if (this.authorizedTurn !== step.turn || openAgentTurn(this.agent) !== step.turn) return decision;
10796
+ try {
10797
+ await this.memory.begin(step.turn, step.signal);
10798
+ } catch (error) {
10799
+ this.end();
10800
+ throw error;
10801
+ }
10802
+ const pin = this.memory.current;
10803
+ for (const [id, graph] of this.views) if (graph.composableTurns.get(id) === void 0) this.views.delete(id);
10804
+ if (pin !== void 0) this.views.set(pin.context.view.id, pin.graph);
10805
+ if (!starting && step.step !== 1) return decision;
10806
+ const wake = pin === void 0 ? "" : modelMemoryWake(pin.graph, pin.context).text;
10807
+ return {
10808
+ ...decision,
10809
+ messages: [...decision.messages, createPluginMessage(`${CONTINUATION_GUIDANCE}\n\n${wake}`, "instructions")]
10810
+ };
10811
+ }
10812
+ end() {
10813
+ this.authorizedTurn = void 0;
10814
+ this.memory.end();
10815
+ }
10816
+ dispose() {
10817
+ if (this.closed) return;
10818
+ this.closed = true;
10819
+ try {
10820
+ for (const stop of this.stops.splice(0).reverse()) stop();
10821
+ } finally {
10822
+ this.authorizedTurn = void 0;
10823
+ this.memory.dispose();
10824
+ this.views.clear();
10825
+ }
10826
+ }
10827
+ };
10828
+ //#endregion
10584
10829
  //#region src/host/subagent.ts
10585
10830
  function evidenceInsights(evidence) {
10586
10831
  return evidence.items.map((item) => {
@@ -10626,7 +10871,6 @@ const REVIEW_TOOLS = [
10626
10871
  const RESULT_TOOL_NAME = "mnemon_subagent_result";
10627
10872
  /** How many finished delegated children stay refused memory tools if DSH resumes one. */
10628
10873
  const FINISHED_CHILDREN = 1e3;
10629
- const FINISHED_CHILD_REFUSAL = "This Mnemon task has finished, so it no longer reads or changes memory. Reply without memory tools.";
10630
10874
  const RESULT_TOOL_INPUT_SCHEMA = {
10631
10875
  type: "object",
10632
10876
  properties: {
@@ -10763,8 +11007,13 @@ var MnemonSubagentCoordinator = class {
10763
11007
  resultRequests = /* @__PURE__ */ new Map();
10764
11008
  disposeResultTool;
10765
11009
  disposeResumeGuard;
11010
+ disposeTaskNotices;
10766
11011
  /** Delegated children whose run has ended, newest last, at most FINISHED_CHILDREN. */
10767
11012
  finishedChildren = /* @__PURE__ */ new Set();
11013
+ continuations = /* @__PURE__ */ new Map();
11014
+ continuationOwners = /* @__PURE__ */ new WeakMap();
11015
+ providerNames = /* @__PURE__ */ new Set(["spawn", "fork"]);
11016
+ providerObservers = [];
10768
11017
  disposed = false;
10769
11018
  reviewAdmission = new AutomaticReviewAdmission();
10770
11019
  constructor(subagents, runtimeSource, resultRuntime, taskAgentModelResolver, runtimeMaintenanceMaxTokensResolver, runtimeMaintenanceTaskRunner) {
@@ -10799,11 +11048,39 @@ var MnemonSubagentCoordinator = class {
10799
11048
  });
10800
11049
  if (typeof registration !== "function") throw new Error("dsh-mnemon subagent result tool registration did not return a disposer");
10801
11050
  this.disposeResultTool = registration;
11051
+ const notices = resultRuntime.on("agent/pre-step", (async (step, next) => {
11052
+ const ignored = new Set(step.messages.filter((message) => {
11053
+ const source = message.source;
11054
+ return source.kind === "subagent-settled" && typeof source.senderSessionId === "string" && (isTaskRecord(source.senderSessionId) || this.finishedChildren.has(source.senderSessionId));
11055
+ }).map((message) => message.id));
11056
+ if (ignored.size === 0) return next();
11057
+ if (step.step === 1 && ignored.size === step.messages.length) return {
11058
+ kind: "enter",
11059
+ messages: []
11060
+ };
11061
+ const decision = await next();
11062
+ return decision.kind === "reject" ? decision : {
11063
+ ...decision,
11064
+ messages: decision.messages.filter((message) => !ignored.has(message.id))
11065
+ };
11066
+ }), { prepend: true });
11067
+ if (typeof notices === "function") this.disposeTaskNotices = notices;
11068
+ if (typeof subagents.list !== "function") {
11069
+ for (const stop of [resultRuntime.on("subagent/provider-added", ((provider) => {
11070
+ this.providerNames.add(provider.name);
11071
+ })), resultRuntime.on("subagent/provider-removed", ((name) => {
11072
+ this.providerNames.delete(name);
11073
+ }))]) if (typeof stop === "function") this.providerObservers.push(stop);
11074
+ }
10802
11075
  }
10803
11076
  dispose() {
10804
11077
  this.disposed = true;
10805
11078
  this.reviewAdmission.dispose();
10806
11079
  this.resultRequests.clear();
11080
+ this.disposeTaskNotices?.();
11081
+ this.disposeTaskNotices = void 0;
11082
+ for (const stop of [...this.continuations.values()].reverse()) stop();
11083
+ for (const stop of this.providerObservers.splice(0)) stop();
10807
11084
  const resumeGuard = this.disposeResumeGuard;
10808
11085
  this.disposeResumeGuard = void 0;
10809
11086
  resumeGuard?.();
@@ -10814,10 +11091,68 @@ var MnemonSubagentCoordinator = class {
10814
11091
  snapshot() {
10815
11092
  return { ...this.counters };
10816
11093
  }
11094
+ isFinishedTask(id, agent) {
11095
+ if (this.finishedChildren.has(id)) return true;
11096
+ if (agent === void 0 || agent.id !== id || !isLegacyMemoryTask(agent)) return false;
11097
+ this.finished(id);
11098
+ return true;
11099
+ }
11100
+ isUserContinuation(agent) {
11101
+ return this.continuationOwners.get(agent)?.ownsHumanTurn(agent) === true;
11102
+ }
11103
+ delegationLimit(parent) {
11104
+ if (!this.isUserContinuation(parent)) return 1;
11105
+ const depths = [parent.session.header?.delegationDepth ?? 0, parent.options?.subagentDepth ?? 0];
11106
+ if (depths.some((depth) => !Number.isSafeInteger(depth) || depth < 0 || Object.is(depth, -0) || depth === Number.MAX_SAFE_INTEGER)) throw new Error("Cannot determine a safe delegation depth for this continued task");
11107
+ return Math.max(...depths) + 1;
11108
+ }
11109
+ /** User continuation is a fresh scoped turn, never revival of a delegated run. */
11110
+ continueTask(agent) {
11111
+ if (this.continuations.has(agent) || this.disposed) return;
11112
+ if (typeof agent.ctx.effect !== "function" || this.resultRuntime?.agents?.get === void 0) {
11113
+ agent.ctx.tools?.guard?.((execution) => execution.name?.startsWith("mnemon_") ? FINISHED_TASK_REFUSAL : void 0);
11114
+ return;
11115
+ }
11116
+ const continuation = new TaskContinuation(agent, this.runtimeSource, (id) => this.resultRuntime?.agents?.get?.(id));
11117
+ let dispose;
11118
+ dispose = agent.ctx.effect(() => {
11119
+ const stop = continuation.start();
11120
+ this.continuationOwners.set(agent, continuation);
11121
+ return () => {
11122
+ try {
11123
+ stop();
11124
+ } finally {
11125
+ if (this.continuations.get(agent) === dispose) this.continuations.delete(agent);
11126
+ this.continuationOwners.delete(agent);
11127
+ }
11128
+ };
11129
+ }, "dsh-mnemon.task-continuation()");
11130
+ this.continuations.set(agent, dispose);
11131
+ }
11132
+ /** Spawn may use an independent tool scope, so transfer the turn guard explicitly. */
11133
+ inheritTaskContinuation(parent, child) {
11134
+ const owner = this.continuationOwners.get(parent);
11135
+ if (owner === void 0 || this.continuations.has(child)) return;
11136
+ let dispose;
11137
+ dispose = child.ctx.effect(() => {
11138
+ const stop = child.ctx.tools?.guard?.((execution) => owner.guard(execution));
11139
+ if (typeof stop !== "function") throw new Error("A continued task child requires a scoped memory tool guard");
11140
+ this.continuationOwners.set(child, owner);
11141
+ return () => {
11142
+ try {
11143
+ stop();
11144
+ } finally {
11145
+ if (this.continuations.get(child) === dispose) this.continuations.delete(child);
11146
+ this.continuationOwners.delete(child);
11147
+ }
11148
+ };
11149
+ }, "dsh-mnemon.task-delegation()");
11150
+ this.continuations.set(child, dispose);
11151
+ }
10817
11152
  /**
10818
11153
  * DSH 0.2.1 can resume a finished child from its saved state when another Agent sends it a
10819
- * message: with its persona and tools, but without the guard its run held. A delegated child
10820
- * never reads or changes memory once its run has ended (#387).
11154
+ * message: with its persona and tools, but without the guard its run held. Only a fresh
11155
+ * human turn may acquire new authority; automatic continuations remain denied (#387).
10821
11156
  */
10822
11157
  finished(id) {
10823
11158
  this.finishedChildren.delete(id);
@@ -10826,9 +11161,7 @@ var MnemonSubagentCoordinator = class {
10826
11161
  if (this.disposeResumeGuard !== void 0 || this.disposed || this.resultRuntime === void 0) return;
10827
11162
  const resumed = this.resultRuntime.on("agent/created", (({ agent }) => {
10828
11163
  if (!this.finishedChildren.has(agent.id)) return;
10829
- try {
10830
- agent.ctx?.tools?.guard?.((execution) => execution.name?.startsWith("mnemon_") === true ? FINISHED_CHILD_REFUSAL : void 0);
10831
- } catch {}
11164
+ this.continueTask(agent);
10832
11165
  }));
10833
11166
  if (typeof resumed === "function") this.disposeResumeGuard = resumed;
10834
11167
  }
@@ -11267,8 +11600,8 @@ var MnemonSubagentCoordinator = class {
11267
11600
  * One tool-less structured run for skill evolution: a review, a draft, a
11268
11601
  * backtest step or a case derivation. The child sees only the prompt.
11269
11602
  */
11270
- async skillTask(parent, label, prompt, schema, persona, signal, route) {
11271
- const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona, void 0, void 0, route);
11603
+ async skillTask(parent, label, prompt, schema, persona, signal, route, maxTokens) {
11604
+ const { provider, runId, result } = await this.delegate(parent, "skill", label, prompt, [], schema, signal, "spawn", persona, void 0, void 0, route, void 0, maxTokens);
11272
11605
  return {
11273
11606
  provider,
11274
11607
  runId,
@@ -11429,9 +11762,9 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
11429
11762
  execution.release();
11430
11763
  }
11431
11764
  }
11432
- async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission) {
11765
+ async delegate(parent, operation, label, prompt, tools, outputSchema, signal, preferredProvider = "spawn", persona = WRITE_PERSONA, recovery, guard, route, admission, skillMaxTokens) {
11433
11766
  if (this.disposed) throw new Error("dsh-mnemon subagent coordinator is disposed");
11434
- const provider = this.provider(preferredProvider);
11767
+ const provider = this.provider(preferredProvider, this.runtimeSource.forAgent(parent).config.subagentProviderNames);
11435
11768
  assertDshOutputSchema(outputSchema);
11436
11769
  if (this.resultRuntime === void 0) throw new Error("dsh-mnemon subagent result tool runtime is unavailable");
11437
11770
  const resultToolName = RESULT_TOOL_NAME;
@@ -11537,7 +11870,7 @@ ${naturalRequest(request, target === void 0 ? REQUEST_LABELS : TARGETED_WRITE_LA
11537
11870
  Completion protocol: call \`${resultToolName}\` exactly once with requestId \`${requestId}\` and result matching this JSON schema:
11538
11871
  ${JSON.stringify(outputSchema)}
11539
11872
  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.`;
11540
- 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;
11873
+ 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" ? skillMaxTokens ?? 16384 : void 0;
11541
11874
  const fixed = route ?? this.taskAgentModelResolver?.();
11542
11875
  const baseAgentOptions = perOpMaxTokens === void 0 ? void 0 : { maxTokens: perOpMaxTokens };
11543
11876
  const resolvedAgentOptions = fixed === void 0 ? baseAgentOptions : {
@@ -11560,7 +11893,7 @@ This is the only completion channel for this run. Do not finish with a plain-tex
11560
11893
  parent,
11561
11894
  signal,
11562
11895
  ...resolvedAgentOptions === void 0 ? {} : { agentOptions: resolvedAgentOptions },
11563
- maxDepth: 1,
11896
+ maxDepth: this.delegationLimit(parent),
11564
11897
  toolFilter: { allow: [...this.availableTools(parent, tools), resultToolName] },
11565
11898
  persona: completionPersona
11566
11899
  }, host);
@@ -11576,11 +11909,11 @@ This is the only completion channel for this run. Do not finish with a plain-tex
11576
11909
  if (captured !== void 0 && captured.agentId !== activeRun.id) throw new Error("Mnemon subagent result was recorded by a different child");
11577
11910
  let structured = captured?.value ?? result.structured;
11578
11911
  if (structured === void 0 && result.stopReason === "completed") structured = recoverStructuredResult(recovery, committedReceipts.filter((receipt) => receipt.agentId === activeRun.id));
11579
- if (structured !== void 0) assertDshOutputValue(outputSchema, structured);
11580
11912
  if (result.stopReason !== "completed") {
11581
11913
  const detail = subagentFailureDetail(activeRun, result);
11582
- throw new Error(`memory subagent stopped with ${result.stopReason}${detail === void 0 ? "" : `: ${detail}`}`);
11914
+ throw new SubagentStoppedError(result.stopReason, perOpMaxTokens, detail);
11583
11915
  }
11916
+ if (structured !== void 0) assertDshOutputValue(outputSchema, structured);
11584
11917
  if (structured === void 0) throw new Error("memory subagent completed without recording its result");
11585
11918
  if (operation === "review" && [
11586
11919
  "failed",
@@ -11644,8 +11977,12 @@ This is the only completion channel for this run. Do not finish with a plain-tex
11644
11977
  this.counters.lastAt = (/* @__PURE__ */ new Date()).toISOString();
11645
11978
  return completed;
11646
11979
  }
11647
- provider(preferred) {
11648
- const names = this.subagents.list();
11980
+ provider(preferred, explicitNames = []) {
11981
+ const names = this.subagents.list?.() ?? [.../* @__PURE__ */ new Set([
11982
+ ...this.providerNames,
11983
+ ...configuredProviderNames(this.resultRuntime),
11984
+ ...explicitNames
11985
+ ])].filter((name) => this.subagents.getProvider(name) !== void 0);
11649
11986
  const compatible = (name) => {
11650
11987
  const provider = this.subagents.getProvider(name);
11651
11988
  return provider?.capabilities.depthLimit === true && childComposition(this.subagents, provider) !== void 0;
@@ -12765,7 +13102,7 @@ var MnemonLifecycle = class {
12765
13102
  }
12766
13103
  /** Model work for skill evolution under a fresh task Agent, outside any conversation. */
12767
13104
  runSkillTask(workspaceRoot, signal, operation, route) {
12768
- return this.runTaskAgent("", workspaceRoot, signal, operation, route);
13105
+ return this.runTaskAgent("", workspaceRoot, signal, operation, route, "Mnemon · Skills");
12769
13106
  }
12770
13107
  /**
12771
13108
  * What one conversation did with skills, for the bars under its turns. A
@@ -12808,7 +13145,7 @@ var MnemonLifecycle = class {
12808
13145
  const stopCreated = this.ctx.on("agent/created", (({ agent, source }) => {
12809
13146
  this.install(agent, source);
12810
13147
  }));
12811
- for (const agent of this.ctx.agents.roots()) this.install(agent, "adopted");
13148
+ for (const agent of rootAgents(this.ctx.agents, this.ctx.subagents)) this.install(agent, "adopted");
12812
13149
  return () => {
12813
13150
  stopChanged?.();
12814
13151
  stopCreated();
@@ -12988,7 +13325,7 @@ var MnemonLifecycle = class {
12988
13325
  /** Synthesize a Web Agent Query without borrowing a conversation Agent or its history. */
12989
13326
  answerTask(sessionId, query, evidence, workspaceRoot, signal = new AbortController().signal) {
12990
13327
  const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
12991
- return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.answer(agent, query, evidence, signal));
13328
+ return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.answer(agent, query, evidence, signal), void 0, "Mnemon · Ask Agent");
12992
13329
  }
12993
13330
  /** A write the user sent from the page: a space they picked that is switched off comes back on (#387). */
12994
13331
  remember(sessionId, request, signal = new AbortController().signal) {
@@ -13001,7 +13338,7 @@ var MnemonLifecycle = class {
13001
13338
  return this.coordinator.manageSource(graph, request);
13002
13339
  }
13003
13340
  runRuntimeMaintenanceTask(scope, signal, operation) {
13004
- return this.runTaskAgent(scope.sessionId ?? "", scope.workspaceId, signal, operation);
13341
+ return this.runTaskAgent(scope.sessionId ?? "", scope.workspaceId, signal, operation, void 0, "Mnemon · Memory maintenance");
13005
13342
  }
13006
13343
  /**
13007
13344
  * Read the merged memory and let a model propose what a human should review.
@@ -13030,12 +13367,12 @@ var MnemonLifecycle = class {
13030
13367
  mutateDocumentTask(sessionId, request, workspaceRoot, signal = new AbortController().signal) {
13031
13368
  const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
13032
13369
  if (root === void 0 || root.trim() === "") throw new Error("a selected DSH workspace is required to write a Mnemon Document");
13033
- return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.document(agent, request, signal));
13370
+ return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.document(agent, request, signal), void 0, "Mnemon · Document");
13034
13371
  }
13035
13372
  archiveDocument(sessionId, id, workspaceRoot, signal = new AbortController().signal) {
13036
13373
  const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
13037
13374
  if (root === void 0 || root.trim() === "") throw new Error("a selected DSH workspace is required to archive a Mnemon Document");
13038
- return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.archiveDocument(agent, id, signal));
13375
+ return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.archiveDocument(agent, id, signal), void 0, "Mnemon · Archive Document");
13039
13376
  }
13040
13377
  mutate(sessionId, operation, request, signal = new AbortController().signal) {
13041
13378
  return this.coordinator.write(this.liveAgent(sessionId), operation, request, signal);
@@ -13046,11 +13383,11 @@ var MnemonLifecycle = class {
13046
13383
  /** Choose a new Memory Space's Provider under a fresh task Agent, as Ask Agent does without a loaded conversation Agent. */
13047
13384
  placeProviderTask(sessionId, body, prepared, workspaceRoot, signal = new AbortController().signal) {
13048
13385
  const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
13049
- return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.placeProvider(agent, body, prepared, signal));
13386
+ return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.placeProvider(agent, body, prepared, signal), void 0, "Mnemon · Choose memory provider");
13050
13387
  }
13051
13388
  maintainMetadata(sessionId, memoryBodyIds, workspaceRoot, signal = new AbortController().signal) {
13052
13389
  const root = workspaceRoot?.trim() || this.workspaceRoot(sessionId);
13053
- return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.maintainMetadata(agent, memoryBodyIds, signal));
13390
+ return this.runTaskAgent(sessionId, root, signal, (agent) => this.coordinator.maintainMetadata(agent, memoryBodyIds, signal), void 0, "Mnemon · Memory metadata");
13054
13391
  }
13055
13392
  async supervise(sessionId, content, idempotencyKey, signal = new AbortController().signal) {
13056
13393
  const normalizedSessionId = sessionId.trim();
@@ -13062,7 +13399,7 @@ var MnemonLifecycle = class {
13062
13399
  const normalizedSessionId = sessionId.trim();
13063
13400
  const root = workspaceRoot?.trim() || this.workspaceRoot(normalizedSessionId);
13064
13401
  const scopeKey = root === void 0 ? `task:${normalizedSessionId || "global"}` : `task:${resolve(root)}`;
13065
- return this.superviseResolved(scopeKey, normalizedSessionId, content, idempotencyKey, signal, (operation) => this.runTaskAgent(normalizedSessionId, root, signal, operation));
13402
+ return this.superviseResolved(scopeKey, normalizedSessionId, content, idempotencyKey, signal, (operation) => this.runTaskAgent(normalizedSessionId, root, signal, operation, void 0, "Mnemon · Save to memory"));
13066
13403
  }
13067
13404
  async superviseResolved(replayScope, responseSessionId, content, idempotencyKey, signal, withAgent) {
13068
13405
  if (!this.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only (writeEnabled: false)");
@@ -13116,8 +13453,9 @@ var MnemonLifecycle = class {
13116
13453
  * is the explicit Web workbench scope, so LiveMnemonRuntime resolves the same
13117
13454
  * workspace graph without borrowing conversation history or ownership.
13118
13455
  */
13119
- async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation, route) {
13120
- const sessionId = randomUUID();
13456
+ async runTaskAgent(fallbackSessionId, workspaceRoot, signal, operation, route, title = "Mnemon · Background task") {
13457
+ const modern = activationOwned(this.ctx.subagents);
13458
+ const sessionId = `mnemon-task-v1-${randomUUID()}`;
13121
13459
  this.taskAgentIds.add(sessionId);
13122
13460
  let handle;
13123
13461
  let failure;
@@ -13128,6 +13466,7 @@ var MnemonLifecycle = class {
13128
13466
  ...creation,
13129
13467
  signal
13130
13468
  });
13469
+ if (modern) titleTask(handle.agent, title);
13131
13470
  if (route !== void 0) try {
13132
13471
  handle.agent[TASK_ROUTE] = route;
13133
13472
  } catch {}
@@ -13153,7 +13492,9 @@ var MnemonLifecycle = class {
13153
13492
  * a turn only while DSH's projection cache holds a record of it, so a finished task
13154
13493
  * could stay listed as an untitled Ungrouped conversation (#435). No `parentSession`
13155
13494
  * or `delegationDepth`: the task belongs to no conversation, and its children stay
13156
- * one level deep.
13495
+ * one level deep. Activation-owned master requires an ordinary root instead;
13496
+ * its persistent task ID and title identify the visible record without enrolling
13497
+ * it in conversation automation. Official rc.1 retains the hidden metadata.
13157
13498
  */
13158
13499
  async taskAgentCreation(fallbackSessionId, workspaceRoot, signal, route) {
13159
13500
  const baseOptions = this.taskAgentModelOptions(fallbackSessionId, workspaceRoot);
@@ -13171,7 +13512,7 @@ var MnemonLifecycle = class {
13171
13512
  const workingDirectory = workFrom === void 0 ? void 0 : this.ctx.get("workingDirectory");
13172
13513
  const meta = {
13173
13514
  cwd,
13174
- origin: "subagent",
13515
+ ...activationOwned(this.ctx.subagents) ? {} : { origin: "subagent" },
13175
13516
  ...presetId === void 0 ? {} : { agentPreset: presetId }
13176
13517
  };
13177
13518
  if (presets === void 0 && typeof workingDirectory?.set !== "function") return {
@@ -13309,11 +13650,15 @@ var MnemonLifecycle = class {
13309
13650
  }
13310
13651
  install(agent, source) {
13311
13652
  if (this.taskAgentIds.has(agent.id) || this.owners.has(agent) || this.children.has(agent)) return;
13653
+ if ((isTaskRecord(agent.id) || this.coordinator.isFinishedTask(agent.id, agent)) && !isCreatingChild(this.ctx.agents, agent)) {
13654
+ this.coordinator.continueTask(agent);
13655
+ return;
13656
+ }
13312
13657
  if (agent.session.header?.origin === "subagent") {
13313
13658
  this.installChild(agent);
13314
13659
  return;
13315
13660
  }
13316
- if (!this.ctx.agents.roots().includes(agent)) return;
13661
+ if (!isRootAgent(this.ctx.agents, this.ctx.subagents, agent)) return;
13317
13662
  const memoryTurn = this.runtimeSource === void 0 ? void 0 : new AgentMemoryTurn(agent, this.runtimeSource);
13318
13663
  const lifecycle = new MnemonAgentLifecycle(agent, this.coordinator, this.config, this.counters, source, memoryTurn, () => this.runtimeSource?.forAgent(agent).directory);
13319
13664
  let dispose;
@@ -13343,6 +13688,7 @@ var MnemonLifecycle = class {
13343
13688
  const parentId = agent.session.header?.parentSession?.trim();
13344
13689
  const parent = parentId === void 0 || parentId === "" ? void 0 : this.ctx.agents.get(parentId);
13345
13690
  if (runtime === void 0 || parent === void 0 || parent === agent) return;
13691
+ this.coordinator.inheritTaskContinuation(parent, agent);
13346
13692
  const parentMemory = this.memoryTurns.get(parent);
13347
13693
  let delegation;
13348
13694
  if (parentMemory !== void 0) delegation = parentMemory.delegate();
@@ -14606,10 +14952,30 @@ var SkillEvolution = class {
14606
14952
  const settings = this.host.settings();
14607
14953
  const { pack, targetRoot, guidance } = prepared;
14608
14954
  let prompt = skillDraftPrompt(pack, guidance, targetRoot);
14955
+ let maxTokens = settings.draftMaxTokens;
14956
+ const attemptedBudgets = [];
14609
14957
  let draft;
14610
14958
  for (let attempt = 0; draft === void 0; attempt++) {
14611
- 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);
14959
+ signal.throwIfAborted();
14960
+ attemptedBudgets.push(maxTokens);
14961
+ let answer;
14962
+ try {
14963
+ answer = await this.host.runTask(workspace, signal, (task) => this.host.skillTask(task, "Mnemon skill draft", prompt, SKILL_DRAFT_SCHEMA, SKILL_DRAFT_PERSONA, signal, input.route, maxTokens), input.route);
14964
+ signal.throwIfAborted();
14965
+ } catch (error) {
14966
+ signal.throwIfAborted();
14967
+ if (error instanceof SubagentStoppedError && error.maxTokens !== void 0) attemptedBudgets[attemptedBudgets.length - 1] = error.maxTokens;
14968
+ if (error instanceof SubagentStoppedError && error.stopReason === "max-tokens" && attempt === 0 && settings.draftRetryMaxTokens > maxTokens) {
14969
+ maxTokens = settings.draftRetryMaxTokens;
14970
+ prompt += "\n\nThe previous attempt exhausted its output budget and was discarded. Return a complete SKILL.md from the beginning; keep notes and changes concise.";
14971
+ continue;
14972
+ }
14973
+ const bytes = Buffer.byteLength(pack.skill?.content ?? "", "utf8");
14974
+ throw new Error(`Skill draft failed. Attempted budgets: ${attemptedBudgets.join(", ")} tokens; source SKILL.md: ${bytes} bytes (UTF-8). Adjust skills.draftMaxTokens / skills.draftRetryMaxTokens in Plugins → dsh-mnemon → Skills → Draft generation. Stay within provider/model caps: lower rejected budgets, shorten the skill or use a model with a higher output cap. Reason: ${message(error).slice(0, 100)}`, { cause: error });
14975
+ }
14976
+ const parsed = parseSkillDraft(answer.structured);
14612
14977
  try {
14978
+ signal.throwIfAborted();
14613
14979
  draft = await source.mutate("draft-record", {
14614
14980
  ...input.suggestionId === void 0 ? {} : { suggestionId: input.suggestionId },
14615
14981
  skillName: pack.skillName,
@@ -14620,6 +14986,7 @@ var SkillEvolution = class {
14620
14986
  ...pack.action === "create" ? { targetRoot } : {}
14621
14987
  }, signal);
14622
14988
  } catch (error) {
14989
+ signal.throwIfAborted();
14623
14990
  if (attempt > 0) throw error;
14624
14991
  prompt += `\n\nYour previous SKILL.md was rejected: ${message(error)}. Return a corrected complete file.`;
14625
14992
  }
@@ -16415,7 +16782,7 @@ function registerDocumentTools(ctx, { requireSource, config, sourceAction }, coo
16415
16782
  const agent = requireAgent(exec);
16416
16783
  if (args.action === "archive") {
16417
16784
  requireSource(exec, "documents", "archive");
16418
- if (isSubagent(agent)) throw new Error("idle document workers cannot cold-archive directly");
16785
+ if (isSubagent(agent) && !coordinator.isUserContinuation(agent)) throw new Error("idle document workers cannot cold-archive directly");
16419
16786
  if (args.id === void 0) throw new Error("document id is required for archive");
16420
16787
  return Promise.resolve(coordinator.archiveDocument(agent, args.id, exec.signal)).then(documentReceipt);
16421
16788
  }
@@ -18885,7 +19252,7 @@ function apply(rawContext, rawConfig = {}) {
18885
19252
  settings: () => skillSettings.get(),
18886
19253
  source: (agent) => skillsSourceFor(runtime, agent),
18887
19254
  runTask: (workspaceRoot, signal, operation, route) => lifecycle.runSkillTask(workspaceRoot, signal, operation, route),
18888
- skillTask: (parent, label, prompt, schema, persona, signal) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent))
19255
+ skillTask: (parent, label, prompt, schema, persona, signal, _route, maxTokens) => coordinator.skillTask(parent, label, prompt, schema, persona, signal, taskRoute(parent), maxTokens)
18889
19256
  });
18890
19257
  lifecycle.useSkillEvolution(skills);
18891
19258
  ctx.effect(() => {
@@ -17,7 +17,7 @@ var dependencies = {
17
17
  "dsh-mnemon-source-documents": "0.5.13",
18
18
  "dsh-mnemon-source-memory-spaces": "0.5.25",
19
19
  "dsh-mnemon-source-runtime": "0.5.17",
20
- "dsh-mnemon-source-skills": "0.5.7",
20
+ "dsh-mnemon-source-skills": "0.5.8",
21
21
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
22
22
  "dsh-mnemon-strategy-default-three-tier": "0.5.12",
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-C5nNk-cs.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-xVjONc6g.js";
2
2
  //#region src/starter.ts
3
3
  const name = "dsh-mnemon-starter";
4
4
  const provide = ["mnemonStarterReady"];
@@ -793,6 +793,10 @@ export declare const zh: {
793
793
  'skills.autoBacktest': string;
794
794
  'skills.autoBacktestHint': string;
795
795
  'skills.parameters': string;
796
+ 'skills.draftGeneration': string;
797
+ 'skills.draftMaxTokens': string;
798
+ 'skills.draftRetryMaxTokens': string;
799
+ 'skills.draftBudgetHint': string;
796
800
  'skills.reviewThreshold': string;
797
801
  'skills.idleSeconds': string;
798
802
  'skills.cooldownSeconds': string;
@@ -307,6 +307,8 @@ export interface Config {
307
307
  persistenceStrategy?: MemoryPersistenceStrategy;
308
308
  /** Model route used by clean, session-independent maintenance Agents. */
309
309
  taskAgentModel?: TaskAgentModelConfig;
310
+ /** Additional provider names for Hosts without enumeration and opaque provider plugins. */
311
+ subagentProviderNames?: string[];
310
312
  /**
311
313
  * Whether this profile runs Git repository sync at all. It lives here rather than
312
314
  * in the data directory because the settings page has to draw the block without
@@ -367,6 +369,7 @@ export interface ResolvedConfig {
367
369
  };
368
370
  persistenceStrategy: ResolvedMemoryPersistenceStrategy;
369
371
  taskAgentModel: ResolvedTaskAgentModelConfig;
372
+ subagentProviderNames: string[];
370
373
  syncEnabled: boolean;
371
374
  }
372
375
  export interface ResolvedInteractionConfig {
@@ -386,6 +389,10 @@ export interface ResolvedSkillsConfig {
386
389
  targetScope: 'storage' | 'user' | 'project';
387
390
  /** Backtest a draft against the current version as soon as it is written. */
388
391
  autoBacktest: boolean;
392
+ /** Output budget for a complete skill draft, including model reasoning. */
393
+ draftMaxTokens: number;
394
+ /** One fresh attempt after truncation, only when larger than draftMaxTokens. */
395
+ draftRetryMaxTokens: number;
389
396
  /** The one-line skills bar under a finished turn. */
390
397
  hints: boolean;
391
398
  /** 技能管理 as a sidebar entry of its own instead of a Memory System page. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mnemon",
3
- "version": "0.5.40",
3
+ "version": "0.5.41",
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",
@@ -167,7 +167,7 @@
167
167
  "dsh-mnemon-source-documents": "0.5.13",
168
168
  "dsh-mnemon-source-memory-spaces": "0.5.25",
169
169
  "dsh-mnemon-source-runtime": "0.5.17",
170
- "dsh-mnemon-source-skills": "0.5.7",
170
+ "dsh-mnemon-source-skills": "0.5.8",
171
171
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
172
172
  "dsh-mnemon-strategy-default-three-tier": "0.5.12",
173
173
  "dsh-mnemon-strategy-general": "0.5.1",