@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 +46 -9
- package/lib/types/doctor.d.ts +3 -0
- package/package.json +5 -5
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
|
|
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
|
|
65
|
-
const layered = host &&
|
|
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
|
|
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
|
|
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 =
|
|
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(
|
|
505
|
+
const planTail = /\s--plan\s+(\S+)\s*$/.exec(rawInputTrimmed) ?? null;
|
|
495
506
|
const planRunId = planTail?.[1] ?? void 0;
|
|
496
|
-
const rest = planTail ?
|
|
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, [{
|
package/lib/types/doctor.d.ts
CHANGED
|
@@ -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.
|
|
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.
|
|
35
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
36
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
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.
|
|
39
|
+
"@lmzhen/dsh-evolution-agent-preset": "^0.3.67"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@deepseek-ai/cordis": "^4.0.1",
|