@lmzhen/dsh-evolution-commands 0.3.65 → 0.3.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import z from "@deepseek-ai/schemastery";
3
+ import { effectiveSessionPolicy } from "@lmzhen/dsh-evolution-approval";
3
4
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
5
  import { SkillLibrary, appendEvolutionEvent, buildLearnPrompt, clampedNumber, composePresetComposition, eventsFile, evolutionRoot, resolveRootConfig, resolveSkillsRoot } from "@lmzhen/dsh-evolution-core";
5
6
  import { buildEnrichment, buildMaintainFacts, runMaintain, snapshotFromLibrary } from "@lmzhen/dsh-evolution-maintenance";
@@ -11,7 +12,8 @@ import { fileURLToPath } from "node:url";
11
12
  * `/evolution doctor` — read-only self-check (0.3.55, WB2).
12
13
  *
13
14
  * Turns "what the README would explain" into "the tool tells you directly":
14
- * install form, three-way conflict detection (all/host/preset + layered), the
15
+ * install form, three-way conflict detection (all/host/preset + layered,
16
+ * including preset-bundle × layered via the preset's delivered agent.cordis.yml), the
15
17
  * environment surfaces that bit us before (v10 P2-12/13), and which evolution
16
18
  * services are actually mounted in this runtime. Always ends with suggested
17
19
  * actions so any finding carries its next step.
@@ -61,13 +63,14 @@ async function diagnose(ctx, options = {}) {
61
63
  const full = bundles.some((name) => tailOf(name) === "dsh-evolution-all");
62
64
  const host = bundles.some((name) => tailOf(name) === "dsh-evolution-host");
63
65
  const preset = bundles.some((name) => tailOf(name) === "dsh-evolution-preset");
64
- const presetDir = join(home, ".agent-presets", "evolution");
65
- const layered = host && existsSync(presetDir);
66
+ const presetDirInstalled = existsSync(join(join(home, ".agent-presets", "evolution"), "agent.cordis.yml"));
67
+ const layered = host && presetDirInstalled;
66
68
  const conflicts = [];
67
69
  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.");
68
70
  if (full && preset) conflicts.push("evolution-all and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
69
71
  if (host && preset) conflicts.push("evolution-host and evolution-preset are installed together — the infra rows double-mount. Keep ONE.");
70
72
  if (full && layered) 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).");
73
+ 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).");
71
74
  const services = {
72
75
  review: full || host || preset,
73
76
  curator: has("evolutionCurator"),
@@ -76,12 +79,16 @@ async function diagnose(ctx, options = {}) {
76
79
  io: has("evolutionIo")
77
80
  };
78
81
  let pendingCount = null;
82
+ let executingCount = null;
79
83
  const approvalService = ctx.get("evolutionApproval");
80
84
  if (approvalService?.list) try {
81
85
  const rows = await approvalService.list("pending");
82
86
  pendingCount = Array.isArray(rows) ? rows.length : null;
87
+ const executing = await approvalService.list("executing");
88
+ executingCount = Array.isArray(executing) ? executing.length : null;
83
89
  } catch {
84
90
  pendingCount = null;
91
+ executingCount = null;
85
92
  }
86
93
  const installForm = full ? "full" : preset ? "preset" : layered ? "layered" : host ? "host" : "none";
87
94
  const actions = [];
@@ -92,6 +99,7 @@ async function diagnose(ctx, options = {}) {
92
99
  if (env.length > 0) actions.push("Fix the DSH_EVOLUTION_* variable listed above.");
93
100
  if (services.review && !services.curator) actions.push("Curator service is not mounted — automatic curation is off; verify the host/all bundle row set is complete.");
94
101
  if (pendingCount === null && services.approval) actions.push("Approval service is mounted but pending listing failed — check the evolution state service.");
102
+ if ((executingCount ?? 0) > 0) actions.push(`${executingCount} staged write(s) are stuck EXECUTING (the approving run crashed) — inspect with /evolution pending and reject them after verifying the write effect.`);
95
103
  return {
96
104
  installForm,
97
105
  bundles,
@@ -99,6 +107,7 @@ async function diagnose(ctx, options = {}) {
99
107
  envIssues: env,
100
108
  services,
101
109
  pendingCount,
110
+ executingCount,
102
111
  actions
103
112
  };
104
113
  }
@@ -107,7 +116,8 @@ function renderDoctorText(report) {
107
116
  `Evolution doctor — install form: ${report.installForm}`,
108
117
  `bundles (all profiles): ${report.bundles.length > 0 ? report.bundles.join(", ") : "(none)"}`,
109
118
  `services: review=${report.services.review} curator=${report.services.curator} approval=${report.services.approval} skillUsage=${report.services.skillUsage} io=${report.services.io}`,
110
- `pending: ${report.pendingCount === null ? "unknown" : report.pendingCount}`
119
+ `pending: ${report.pendingCount === null ? "unknown" : report.pendingCount}`,
120
+ `executing: ${report.executingCount === null ? "unknown" : report.executingCount}`
111
121
  ];
112
122
  if (report.conflicts.length > 0) lines.push("conflicts:", ...report.conflicts.map((line) => ` ! ${line}`));
113
123
  if (report.envIssues.length > 0) lines.push("env:", ...report.envIssues.map((line) => ` ! ${line}`));
@@ -179,7 +189,7 @@ const COMMAND_ENTRIES = [
179
189
  },
180
190
  {
181
191
  usage: "replay",
182
- summary: "compare prompt-bundle replay for this session"
192
+ summary: "compare plan outcomes across sessions and restarts (backfilled from the activity store)"
183
193
  }
184
194
  ];
185
195
  /** The input-declaration hint (single line, ` | `-separated). */
@@ -221,7 +231,8 @@ function apply(ctx, rawConfig = {}) {
221
231
  recordInput: false,
222
232
  input: { hint: renderHint() },
223
233
  async handler(invocation) {
224
- const input = invocation.rawInput?.trim() ?? "";
234
+ const rawInputTrimmed = invocation.rawInput?.trim() ?? "";
235
+ const input = rawInputTrimmed.replace(/\s+/g, " ");
225
236
  const ok = (text) => ({
226
237
  kind: "success",
227
238
  text
@@ -373,7 +384,7 @@ function apply(ctx, rawConfig = {}) {
373
384
  return ok("Skill catalog refresh requested: caches dropped, catalog re-read on next lookup.");
374
385
  }
375
386
  if (input === "learn" || input.startsWith("learn ")) {
376
- const request = input === "learn" ? "" : input.slice(6).trim();
387
+ const request = rawInputTrimmed === "learn" ? "" : rawInputTrimmed.slice(6).trim();
377
388
  invocation.agent.inject(createUserMessage({
378
389
  content: [{
379
390
  type: "text",
@@ -491,9 +502,9 @@ function apply(ctx, rawConfig = {}) {
491
502
  }
492
503
  }
493
504
  if (input.startsWith("restructure ")) {
494
- const planTail = /\s--plan\s+(\S+)\s*$/.exec(input) ?? null;
505
+ const planTail = /\s--plan\s+(\S+)\s*$/.exec(rawInputTrimmed) ?? null;
495
506
  const planRunId = planTail?.[1] ?? void 0;
496
- const rest = planTail ? input.slice(0, planTail.index) : input;
507
+ const rest = planTail ? rawInputTrimmed.slice(0, planTail.index) : rawInputTrimmed;
497
508
  const match = /^restructure\s+(\S+)\s+"([^"]+)"\s+(\S+)$/.exec(rest);
498
509
  if (!match) return err("Usage: /evolution restructure <name> \"<## heading>\" <to_file>");
499
510
  const name = match[1] ?? "";
@@ -502,6 +513,32 @@ function apply(ctx, rawConfig = {}) {
502
513
  if (!toFile.startsWith("references/")) return err("to_file must live under references/ (log/detail destination).");
503
514
  const ioRegistry = ctx.get("evolutionIo");
504
515
  if (!ioRegistry) return err("Evolution IO registry not mounted — restructure unavailable.");
516
+ if (approval) {
517
+ const session = invocation.agent.session;
518
+ const sessionPolicy = effectiveSessionPolicy(ctx, session);
519
+ if (approval.isEnabled !== false && sessionPolicy !== "never" && approval.stageForeground !== false && !approval.hasRunner("skill")) return err("Restructure cannot be staged: no skill replay runner is registered — mount the tool-skill-manage row (evolution-agent preset, or evolution-all) or disable evolution-approval.");
520
+ const decision = await approval.request({
521
+ kind: "skill",
522
+ summary: `/evolution restructure ${name}${planRunId ? ` (plan ${planRunId})` : ""}`,
523
+ args: {
524
+ operation: {
525
+ action: "restructure",
526
+ name,
527
+ restructure: [{
528
+ heading,
529
+ to_file: toFile
530
+ }]
531
+ },
532
+ origin: "foreground",
533
+ libraryOrigin: "foreground"
534
+ },
535
+ origin: "foreground",
536
+ ...session?.id ? { sessionId: session.id } : {},
537
+ ...session ? { session } : {},
538
+ ...sessionPolicy !== void 0 ? { sessionPolicy } : {}
539
+ });
540
+ if (decision.action === "staged") return ok(`${decision.message}${planRunId ? `\n[audit] plan=${planRunId}` : ""}`);
541
+ }
505
542
  const result = await new SkillLibrary(resolveSkillsRoot({ root: skillsRootValue }), ioRegistry.provider(), void 0, (event) => {
506
543
  ctx.emit("evolution/skill-mutated", event);
507
544
  }, void 0, config.threatExemptLabels ?? []).restructure(name, [{
@@ -16,6 +16,9 @@ export interface DoctorReport {
16
16
  io: boolean;
17
17
  };
18
18
  pendingCount: number | null;
19
+ /** v23 (AP-2): claimed-but-crashed records — the only state that needs an
20
+ * operator action (reject) to clear; surfaced separately from pending. */
21
+ executingCount: number | null;
19
22
  actions: string[];
20
23
  }
21
24
  /** Profile bundle rows for evolution-family packages across all profiles. */
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.65",
4
+ "version": "0.3.67",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,12 +31,12 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.65",
35
- "@lmzhen/dsh-evolution-core": "^0.3.65",
36
- "@lmzhen/dsh-evolution-maintenance": "^0.3.65"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.67",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.67",
36
+ "@lmzhen/dsh-evolution-maintenance": "^0.3.67"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@lmzhen/dsh-evolution-agent-preset": "^0.3.65"
39
+ "@lmzhen/dsh-evolution-agent-preset": "^0.3.67"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "@deepseek-ai/cordis": "^4.0.1",