@lmzhen/dsh-evolution-commands 0.3.51 → 0.3.52
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 +13 -36
- package/lib/types/index.d.ts +9 -13
- package/package.json +6 -6
package/lib/index.js
CHANGED
|
@@ -23,7 +23,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
23
23
|
name: "evolution",
|
|
24
24
|
description: "Self-evolution status and approval controls",
|
|
25
25
|
recordInput: false,
|
|
26
|
-
input: { hint: "pending [--detail] | approve <id> | reject <id> | curator run|pause|resume|status|report|scope | mutations | restore | consolidate <target> <sources...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout
|
|
26
|
+
input: { hint: "pending [--detail] | approve <id> | reject <id> | curator run|pause|resume|status|report|scope | mutations | restore | consolidate <target> <sources...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout=<ms> | --facts] | preset install | restructure <name> \"<heading>\" <to_file> | replay" },
|
|
27
27
|
async handler(invocation) {
|
|
28
28
|
const input = invocation.rawInput?.trim() ?? "";
|
|
29
29
|
const ok = (text) => ({
|
|
@@ -74,7 +74,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
74
74
|
if (!curator) return err("Curator service not mounted.");
|
|
75
75
|
const paused = input === "curator pause";
|
|
76
76
|
await curator.setPaused(paused);
|
|
77
|
-
|
|
77
|
+
const notPersisted = ctx.get("evolutionState") !== void 0 ? "" : "\nNOTE: no evolution state service is mounted — this setting is NOT persisted and will not survive this process.";
|
|
78
|
+
return ok((paused ? "Curator automatic curation paused. Manual /evolution curator run is unaffected; resume with /evolution curator resume." : "Curator automatic curation resumed. The next scheduled pass waits one interval (first-run defer semantics).") + notPersisted);
|
|
78
79
|
}
|
|
79
80
|
if (input === "curator status") {
|
|
80
81
|
const curator = ctx.get("evolutionCurator");
|
|
@@ -210,14 +211,14 @@ function apply(ctx, rawConfig = {}) {
|
|
|
210
211
|
}), enrichment.usageObservedValue, void 0);
|
|
211
212
|
return ok(`Maintenance facts (0-token preview):\n${facts}`);
|
|
212
213
|
}
|
|
213
|
-
const maintainArgs = /^maintain(
|
|
214
|
+
const maintainArgs = /^maintain(?:\s+--timeout[ =](\d+))?\s*$/.exec(input);
|
|
214
215
|
if (maintainArgs) {
|
|
215
216
|
const runTimeoutMs = maintainArgs[1] ? Number(maintainArgs[1]) : config.maintainTimeoutMs ?? 6e5;
|
|
216
217
|
if (!Number.isSafeInteger(runTimeoutMs) || runTimeoutMs <= 0 || runTimeoutMs > 4294967295) return err("Invalid --timeout value: expected a positive integer number of milliseconds up to 4294967295 (e.g. /evolution maintain --timeout 600000).");
|
|
217
|
-
if (maintainInFlightSince > 0) return
|
|
218
|
+
if (maintainInFlightSince > 0) return err(`Maintenance scan is already running (since ~${Math.max(1, Math.round((Date.now() - maintainInFlightSince) / 1e3))}s ago) — re-submitting now would cancel it. Wait for it to settle; the result appears when it finishes.`);
|
|
218
219
|
const cooldownMs = clampedNumber(config.maintainCooldownMs, 3e4, { min: 0 });
|
|
219
220
|
const sinceLast = Date.now() - lastMaintainAt;
|
|
220
|
-
if (cooldownMs > 0 && sinceLast < cooldownMs) return
|
|
221
|
+
if (cooldownMs > 0 && sinceLast < cooldownMs) return err(`Maintenance cooldown active (${Math.ceil((cooldownMs - sinceLast) / 1e3)}s) — latest scan ${lastMaintainRunId}; re-running now would spend another model call.`);
|
|
221
222
|
const ioRegistry = ctx.get("evolutionIo");
|
|
222
223
|
const subagents = ctx.get("subagents");
|
|
223
224
|
if (!ioRegistry) return err("Evolution IO registry not mounted — maintenance scan unavailable.");
|
|
@@ -231,6 +232,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
231
232
|
subagents,
|
|
232
233
|
parent: invocation.agent,
|
|
233
234
|
evolutionPolicy: { get: () => ctx.get("evolutionPolicy")?.get() },
|
|
235
|
+
tools: { get: (name) => ctx.get("tools")?.get(name) },
|
|
234
236
|
logger: { warn: (message) => {
|
|
235
237
|
ctx.logger.warn(message);
|
|
236
238
|
} }
|
|
@@ -250,7 +252,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
250
252
|
source: "manual",
|
|
251
253
|
runId: outcome.runId,
|
|
252
254
|
verdict: outcome.verdict,
|
|
253
|
-
recommendations:
|
|
255
|
+
recommendations: outcome.recommendationCount
|
|
254
256
|
}).catch((error) => {
|
|
255
257
|
ctx.logger.warn(`evolution-commands: failed to record maintain event: ${String(error)}`);
|
|
256
258
|
});
|
|
@@ -262,7 +264,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
262
264
|
lastMaintainAt = Date.now();
|
|
263
265
|
}
|
|
264
266
|
}
|
|
265
|
-
if (/^maintain\b/.test(input)) return err("Unknown maintain arguments: expected bare `maintain` or `maintain --timeout
|
|
267
|
+
if (/^maintain\b/.test(input)) return err("Unknown maintain arguments: expected bare `maintain`, `maintain --timeout <ms>` or `maintain --timeout=<ms>` (a positive integer). Got: " + input);
|
|
266
268
|
if (input === "preset install") {
|
|
267
269
|
const target = join(evolutionRoot(), ".agent-presets", "evolution");
|
|
268
270
|
try {
|
|
@@ -300,7 +302,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
300
302
|
if (!ioRegistry) return err("Evolution IO registry not mounted — restructure unavailable.");
|
|
301
303
|
const result = await new SkillLibrary(resolveSkillsRoot({ root: config.skillsRoot }), ioRegistry.provider(), void 0, (event) => {
|
|
302
304
|
ctx.emit("evolution/skill-mutated", event);
|
|
303
|
-
}).restructure(name, [{
|
|
305
|
+
}, void 0, config.threatExemptLabels ?? []).restructure(name, [{
|
|
304
306
|
heading,
|
|
305
307
|
toFile
|
|
306
308
|
}], "foreground");
|
|
@@ -313,7 +315,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
313
315
|
if (!replay) return err("Replay service not mounted.");
|
|
314
316
|
return ok(replay.compare().report);
|
|
315
317
|
}
|
|
316
|
-
return ok("Evolution: memory, skills, review, curator. Use /evolution pending | approve <id> | reject <id> | curator run | curator status | curator pause | curator resume | curator report | curator scope | mutations | restore | consolidate <target> <source...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout
|
|
318
|
+
return ok("Evolution: memory, skills, review, curator. Use /evolution pending | approve <id> | reject <id> | curator run | curator status | curator pause | curator resume | curator report | curator scope | mutations | restore | consolidate <target> <source...> | skill restore <name> | skills health | skills refresh | learn [request] | maintain [--timeout=<ms> | --facts] | preset install | restructure <name> \"<heading>\" <to_file> | replay.");
|
|
317
319
|
}
|
|
318
320
|
}));
|
|
319
321
|
});
|
|
@@ -442,32 +444,6 @@ function atomicWriteFiles(targetDir, writes, fs = defaultFs) {
|
|
|
442
444
|
throw error;
|
|
443
445
|
}
|
|
444
446
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Count the recommendation bullets in a rendered maintain plan.
|
|
447
|
-
* `formatPlan` renders each recommendation as a line starting with `- [<kind>]`;
|
|
448
|
-
* this matches that bullet (anchored at line start) instead of any string
|
|
449
|
-
* prefix, so a `- note` line can never inflate the count.
|
|
450
|
-
*
|
|
451
|
-
* @param text - the rendered maintain result text, or `undefined`.
|
|
452
|
-
* @returns the number of recommendation bullets in `text`.
|
|
453
|
-
* @remarks the count is still text-derived — MaintainOutcome does not expose the
|
|
454
|
-
* structured plan array, so a structured count would require a
|
|
455
|
-
* evolution-maintenance outcome change (out of this package's scope).
|
|
456
|
-
*/
|
|
457
|
-
function countMaintainRecommendations(text) {
|
|
458
|
-
if (!text) return 0;
|
|
459
|
-
return beforeNotesHeader(text).match(/^- \[/gm)?.length ?? 0;
|
|
460
|
-
}
|
|
461
|
-
/** Locate the maintain-plan `Notes:` section header as a standalone line.
|
|
462
|
-
* Returns everything before it (the plan section), or the whole text when the
|
|
463
|
-
* header is absent. formatPlan emits the header via `lines.push('Notes:')`, so
|
|
464
|
-
* it is always a line that is exactly `Notes:` (never an indented/embedded
|
|
465
|
-
* continuation), letting us ignore a `Notes:` that appears inside a field. */
|
|
466
|
-
function beforeNotesHeader(text) {
|
|
467
|
-
const header = /(^|\n)Notes:(?=\n)/.exec(text);
|
|
468
|
-
if (!header) return text;
|
|
469
|
-
return text.slice(0, header.index);
|
|
470
|
-
}
|
|
471
447
|
/** F-328: render staged args for `pending --detail`, truncated to 500 chars.
|
|
472
448
|
* args may be non-JSON (a tool produced garbage), so the render is fail-safe.
|
|
473
449
|
* V4-19: a truncation is marked with `…(truncated N chars)` so the operator is
|
|
@@ -476,6 +452,7 @@ function beforeNotesHeader(text) {
|
|
|
476
452
|
function safeStagedArgs(args) {
|
|
477
453
|
try {
|
|
478
454
|
const json = JSON.stringify(args);
|
|
455
|
+
if (typeof json !== "string") return "(unserializable)";
|
|
479
456
|
if (json.length > 500) return `${json.slice(0, 500)}…(truncated ${json.length - 500} chars)`;
|
|
480
457
|
return json;
|
|
481
458
|
} catch {
|
|
@@ -483,4 +460,4 @@ function safeStagedArgs(args) {
|
|
|
483
460
|
}
|
|
484
461
|
}
|
|
485
462
|
//#endregion
|
|
486
|
-
export { apply, atomicWriteFiles,
|
|
463
|
+
export { apply, atomicWriteFiles, name };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -22,6 +22,15 @@ export interface Config {
|
|
|
22
22
|
* 4-8 min — the 13:38 successful run used --timeout 600000; the old 120s
|
|
23
23
|
* default deadlined bare runs mid-analysis (14:37 run aborted at 119.94s). */
|
|
24
24
|
maintainTimeoutMs?: number | undefined;
|
|
25
|
+
/** Threat-scan exemption labels for WRITE-path skill mutations (P2-18).
|
|
26
|
+
* Threaded into the SkillLibrary this package constructs for `restructure`
|
|
27
|
+
* (its only write path); the core-side constructor option carries the same
|
|
28
|
+
* field name `threatExemptLabels` (P2-18 core batch — the field name is the
|
|
29
|
+
* linkage contract, keep in sync). Default empty: strict-scan behavior is
|
|
30
|
+
* unchanged; a deployment opts specific labels out explicitly. Read-only
|
|
31
|
+
* paths (maintain / maintain --facts) never threat-scan, so this never
|
|
32
|
+
* widens them. */
|
|
33
|
+
threatExemptLabels?: string[] | undefined;
|
|
25
34
|
}
|
|
26
35
|
export declare function apply(ctx: Context, rawConfig?: Config): void;
|
|
27
36
|
/** F-211: the fs surface atomicWriteFiles uses — injectable so the commit-phase
|
|
@@ -65,18 +74,5 @@ export declare function atomicWriteFiles(targetDir: string, writes: Array<{
|
|
|
65
74
|
name: string;
|
|
66
75
|
content: string | Uint8Array;
|
|
67
76
|
}>, fs?: FsOps): void;
|
|
68
|
-
/**
|
|
69
|
-
* Count the recommendation bullets in a rendered maintain plan.
|
|
70
|
-
* `formatPlan` renders each recommendation as a line starting with `- [<kind>]`;
|
|
71
|
-
* this matches that bullet (anchored at line start) instead of any string
|
|
72
|
-
* prefix, so a `- note` line can never inflate the count.
|
|
73
|
-
*
|
|
74
|
-
* @param text - the rendered maintain result text, or `undefined`.
|
|
75
|
-
* @returns the number of recommendation bullets in `text`.
|
|
76
|
-
* @remarks the count is still text-derived — MaintainOutcome does not expose the
|
|
77
|
-
* structured plan array, so a structured count would require a
|
|
78
|
-
* evolution-maintenance outcome change (out of this package's scope).
|
|
79
|
-
*/
|
|
80
|
-
export declare function countMaintainRecommendations(text: string | undefined): number;
|
|
81
77
|
export {};
|
|
82
78
|
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-commands",
|
|
3
3
|
"description": "Human commands for the evolution family (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.52",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/lmzhen/dsh-evolution.git",
|
|
11
|
-
"directory": "packages/
|
|
11
|
+
"directory": "packages/evolution-commands"
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "lib/index.js",
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
],
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@lmzhen/dsh-evolution-approval": "^0.3.
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
35
|
-
"@lmzhen/dsh-evolution-maintenance": "^0.3.
|
|
33
|
+
"@lmzhen/dsh-evolution-approval": "^0.3.52",
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.3.52",
|
|
35
|
+
"@lmzhen/dsh-evolution-maintenance": "^0.3.52"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
|
-
"@lmzhen/dsh-evolution-agent-preset": "^0.3.
|
|
38
|
+
"@lmzhen/dsh-evolution-agent-preset": "^0.3.52"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@deepseek-ai/cordis": "^4.0.1",
|