@lmzhen/dsh-evolution-commands 0.3.79 → 0.3.80

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.
Files changed (2) hide show
  1. package/lib/index.js +57 -7
  2. package/package.json +5 -5
package/lib/index.js CHANGED
@@ -119,6 +119,9 @@ function resolveAgentPresetAsset(asset) {
119
119
  }
120
120
  /** The base rows of bases.json: the comparison iterates the same table the
121
121
  * installer reads, so doctor cannot check a different set of variants.
122
+ * S1-F1: rows also carry the optional `unsupported` note — the layered-form
123
+ * detection below must not treat a base that carries NO family model rows
124
+ * (minimal) as a double-mount participant.
122
125
  * @param path - resolved bases.json.
123
126
  * @returns the rows, or null when the table is unreadable or malformed. */
124
127
  function readAgentPresetBases$1(path) {
@@ -131,7 +134,8 @@ function readAgentPresetBases$1(path) {
131
134
  if (typeof entry.name !== "string" || typeof entry.id !== "string") return null;
132
135
  rows.push({
133
136
  name: entry.name,
134
- id: entry.id
137
+ id: entry.id,
138
+ ...typeof entry.unsupported === "string" ? { unsupported: entry.unsupported } : {}
135
139
  });
136
140
  }
137
141
  return rows.length > 0 ? rows : null;
@@ -155,6 +159,38 @@ function installedPresetIds(root) {
155
159
  return ids;
156
160
  }
157
161
  /**
162
+ * S1-F1: ids of the family's DELIVERED layered presets — every
163
+ * `.agent-presets/<id>/agent.cordis.yml` artifact that can actually carry the
164
+ * family model rows. The detection used to probe ONLY the default base id
165
+ * (`evolution`), so a `--base ptc|cordis` layered install was invisible:
166
+ * installForm degraded to `none`/`host` and the action ladder recommended
167
+ * installing `evolution-all` on top — the exact double-mount the doctor exists
168
+ * to prevent (V27 G6.4 closed it for the default id only).
169
+ *
170
+ * A directory counts only when the base table names it as a SUPPORTED family
171
+ * base (the `unsupported` minimal base carries no model rows and cannot
172
+ * double-mount anything). When the table itself cannot be read, every
173
+ * delivered artifact counts — the visible-by-directory posture of
174
+ * {@link probePresetFreshness}: flag rather than stay silent.
175
+ * @param root - the `.agent-presets` home directory.
176
+ * @returns installed family base ids whose `agent.cordis.yml` is on disk.
177
+ */
178
+ function installedLayeredPresetIds(root) {
179
+ const delivered = installedPresetIds(root).filter((id) => existsSync(join(root, id, "agent.cordis.yml")));
180
+ if (delivered.length === 0) return [];
181
+ const basesPath = resolveAgentPresetAsset("bases.json");
182
+ const bases = basesPath === null ? null : readAgentPresetBases$1(basesPath);
183
+ if (bases === null) return delivered.filter((id) => {
184
+ try {
185
+ return readFileSync(join(root, id, "agent.cordis.yml"), "utf8").includes("@deepseek-ai/dsh-");
186
+ } catch {
187
+ return false;
188
+ }
189
+ });
190
+ const supported = new Set(bases.filter((row) => row.unsupported === void 0).map((row) => row.id));
191
+ return delivered.filter((id) => supported.has(id));
192
+ }
193
+ /**
158
194
  * G3-② (B2): recompute what a fresh generation would write for every
159
195
  * installed variant and compare it byte-for-byte with the file on disk.
160
196
  *
@@ -278,13 +314,15 @@ async function diagnose(ctx, options = {}) {
278
314
  const full = bundles.some((name) => tailOf(name) === "dsh-evolution-all");
279
315
  const host = bundles.some((name) => tailOf(name) === "dsh-evolution-host");
280
316
  const preset = bundles.some((name) => tailOf(name) === "dsh-evolution-preset");
281
- const presetDirInstalled = existsSync(join(join(home, ".agent-presets", "evolution"), "agent.cordis.yml"));
317
+ const layeredIds = installedLayeredPresetIds(join(home, ".agent-presets"));
318
+ const presetDirInstalled = layeredIds.length > 0;
319
+ const layeredArtifactLabel = presetDirInstalled ? layeredIds.join(", ") : "evolution";
282
320
  const layered = host && presetDirInstalled;
283
321
  if (full && host) conflicts.push("evolution-all and evolution-host are installed together — the infra rows double-mount and startup fails loud. Keep ONE: remove the other bundle.");
284
322
  if (full && preset) conflicts.push("evolution-all and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
285
323
  if (host && preset) conflicts.push("evolution-host and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
286
- if (full && presetDirInstalled) conflicts.push("evolution-all and the layered Evolution preset are both present — the model rows double-mount. Keep ONE (use layered without all, or drop the preset).");
287
- if (preset && presetDirInstalled) conflicts.push("evolution-preset and the layered Evolution preset are both present — the model rows double-mount (the preset bundle carries the same model rows as all). Keep ONE (drop the preset bundle, or remove the layered preset).");
324
+ if (full && presetDirInstalled) conflicts.push(`evolution-all and the layered Evolution preset (${layeredArtifactLabel}) are both present — the model rows double-mount. Keep ONE (use layered without all, or drop the preset).`);
325
+ if (preset && presetDirInstalled) conflicts.push(`evolution-preset and the layered Evolution preset (${layeredArtifactLabel}) are both present — the model rows double-mount (the preset bundle carries the same model rows as all). Keep ONE (drop the preset bundle, or remove the layered preset).`);
288
326
  const services = {
289
327
  review: full || host || preset,
290
328
  curator: has("evolutionCurator"),
@@ -312,7 +350,7 @@ async function diagnose(ctx, options = {}) {
312
350
  if (mountConflicts.length > 0) actions.push("Resolve the conflict first: keep exactly one of evolution-all / evolution-host / evolution-preset / layered.");
313
351
  else if (undecidable) actions.push("The install form is UNDECIDABLE (see the DEGRADED row above): an unreadable profiles directory or profile manifest hides whatever is installed, so this report must not add or remove a bundle. Fix the reported read failure, then re-run /evolution doctor.");
314
352
  else if (installForm === "none") actions.push("Install the default full bundle: dsh plugin --profile web add @lmzhen/dsh-evolution-all");
315
- else if (installForm === "preset-only") actions.push("The Evolution preset is delivered but no profile mounts an evolution bundle — re-add @lmzhen/dsh-evolution-host for the layered layout (do NOT add all on top of the preset: that double-mounts the model rows), or remove .agent-presets/evolution if the layered layout is no longer wanted.");
353
+ else if (installForm === "preset-only") actions.push(`The Evolution preset is delivered but no profile mounts an evolution bundle — re-add @lmzhen/dsh-evolution-host for the layered layout (do NOT add all on top of the preset: that double-mounts the model rows), or remove .agent-presets/${layeredArtifactLabel} if the layered layout is no longer wanted.`);
316
354
  else if (installForm === "layered") actions.push("Variant form (session opt-in): model tools follow the Evolution preset, and a session on a platform original preset carries no family rows; add @lmzhen/dsh-evolution-all instead if every session should have them.");
317
355
  const env = envIssues();
318
356
  if (env.length > 0) actions.push("Fix the DSH_EVOLUTION_* variable listed above.");
@@ -320,7 +358,7 @@ async function diagnose(ctx, options = {}) {
320
358
  if (pendingCount === null && services.approval) actions.push("Approval service is mounted but pending listing failed — check the evolution state service.");
321
359
  const memoryIssues = memoryInterpolationIssues(home);
322
360
  if (memoryIssues.length > 0) actions.push("Rewrite the memory entries listed above (or run a build with the render-time neutralization) — they broke prompt assembly on older builds.");
323
- if ((executingCount ?? 0) > 0) actions.push(`${executingCount} staged write(s) are EXECUTING (an approve crashed mid-run — or one is still in flight). Inspect with /evolution pending: if you started the approve, verify the landed write and do not reject it; only reject after verifying no write is intended.`);
361
+ if ((executingCount ?? 0) > 0) actions.push(`${executingCount} staged write(s) are EXECUTING (an approve crashed mid-run — or one is still in flight). Inspect with /evolution pending: if you started the approve, verify the landed write and do not reject it; only reject after verifying no write is intended. For a verified orphan (this build: S2-P2-22), /evolution release <id> returns it to the pending window instead.`);
324
362
  for (const row of presetFreshness) {
325
363
  if (row.status !== "differs") continue;
326
364
  actions.push(`Preset variant ${row.destination} DIFFERS from a fresh generation — it is an install-time snapshot; re-run the installer (/evolution preset install) to regenerate`);
@@ -373,6 +411,10 @@ const COMMAND_ENTRIES = [
373
411
  usage: "reject <id>",
374
412
  summary: "drop a staged write without running it"
375
413
  },
414
+ {
415
+ usage: "release <id>",
416
+ summary: "return an orphaned EXECUTING write to the pending window (verify the effect first, then approve or reject)"
417
+ },
376
418
  {
377
419
  usage: "doctor [--json]",
378
420
  summary: "read-only self-check: install form, conflicts, env, services (--json feeds scripts)"
@@ -477,7 +519,8 @@ function apply(ctx, rawConfig = {}) {
477
519
  const approval = ctx.get("evolutionApproval");
478
520
  const pendingMatch = /^pending(?: --detail)?$/.exec(input);
479
521
  if (pendingMatch) {
480
- const listed = approval ? [...await approval.list("pending"), ...await approval.list("executing")] : [];
522
+ if (!approval) return err("E-301: approval service not mounted — pending writes cannot be listed or replayed. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted.");
523
+ const listed = [...await approval.list("pending"), ...await approval.list("executing")];
481
524
  const pending = [...new Map(listed.map((row) => [row.id, row])).values()];
482
525
  if (pending.length === 0) return ok("No pending evolution writes.");
483
526
  const detailed = pendingMatch[0] === "pending --detail";
@@ -504,6 +547,13 @@ function apply(ctx, rawConfig = {}) {
504
547
  };
505
548
  return result.ok ? ok(result.message) : err(result.message);
506
549
  }
550
+ if (input.startsWith("release ")) {
551
+ const id = input.slice(8).trim();
552
+ if (!approval) return err("E-301: approval service not mounted. Next: the evolution-approval row ships with evolution-host/evolution-all — run /evolution doctor to see which services are mounted.");
553
+ if (!approval.release) return err("E-304: the mounted approval service predates the release capability — reject the record instead.");
554
+ const result = await approval.release(id);
555
+ return result.ok ? ok(result.message) : err(result.message);
556
+ }
507
557
  if (input === "curator run") {
508
558
  const curator = ctx.get("evolutionCurator");
509
559
  if (!curator) return err("E-302: curator service not mounted. Next: mount the evolution-curator row (evolution-host/evolution-all) and run /evolution doctor.");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-commands",
3
3
  "description": "Human commands for the evolution family (/evolution pending|curator|maintain|doctor) (community build)",
4
- "version": "0.3.79",
4
+ "version": "0.3.80",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,12 +27,12 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.1",
30
- "@lmzhen/dsh-evolution-approval": "^0.3.79",
31
- "@lmzhen/dsh-evolution-core": "^0.3.79",
32
- "@lmzhen/dsh-evolution-maintenance": "^0.3.79"
30
+ "@lmzhen/dsh-evolution-approval": "^0.3.80",
31
+ "@lmzhen/dsh-evolution-core": "^0.3.80",
32
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.80"
33
33
  },
34
34
  "optionalDependencies": {
35
- "@lmzhen/dsh-evolution-agent-preset": "^0.3.79"
35
+ "@lmzhen/dsh-evolution-agent-preset": "^0.3.80"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@deepseek-ai/cordis": "^4.0.1",