@lmzhen/dsh-evolution-review 0.3.62 → 0.3.64

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,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
- import { COMPLETION_SKILL_REVIEW_PROMPT, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_USER_CHAR_LIMIT, PROMPT_BUNDLE, SkillLibrary, advanceReview, clampedNumber, evolutionIoAdapter, foldTurn, redactSecrets, resolveOrigins, resolveSkillsRoot, reviewPrompt, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
4
+ import { COMPLETION_SKILL_REVIEW_PROMPT, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_USER_CHAR_LIMIT, PROMPT_BUNDLE, SkillLibrary, advanceReview, clampedNumber, evolutionIoAdapter, foldTurn, redactSecrets, resolveOrigins, resolveRootConfig, resolveSkillsRoot, reviewPrompt, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
5
5
  import { validateEvolutionPlan } from "@lmzhen/dsh-evolution-plan-validator";
6
6
  //#region lib/types/index.js
7
7
  /**
@@ -10,13 +10,20 @@ import { validateEvolutionPlan } from "@lmzhen/dsh-evolution-plan-validator";
10
10
  */
11
11
  const name = "evolution-review";
12
12
  const inject = ["agents"];
13
+ /** Node's 32-bit timer-delay ceiling (`AbortSignal.timeout`/`setTimeout`):
14
+ * a larger value throws RangeError. B-2 (v18): without a max, a misconfigured
15
+ * `reviewTimeoutMs` made `AbortSignal.timeout` throw inside the subagent
16
+ * start call; the outer catch logged it and silently degraded the review to
17
+ * the inject path. The schema and the assembly clamp both reject it (same
18
+ * bound as commands/maintenance). */
19
+ const MAX_TIMER_DELAY_MS = 4294967295;
13
20
  const Config = z.object({
14
21
  reviewEnabled: z.boolean().default(true),
15
22
  reviewMode: z.union([z.const("subagent"), z.const("inject")]).default("subagent"),
16
23
  memoryInterval: z.number().min(1).default(DEFAULT_REVIEW_MEMORY_INTERVAL),
17
24
  skillInterval: z.number().min(1).default(DEFAULT_REVIEW_SKILL_INTERVAL),
18
25
  reviewToolAllow: z.array(z.string()).default(["skill"]),
19
- reviewTimeoutMs: z.number().min(1).default(DEFAULT_REVIEW_TIMEOUT_MS),
26
+ reviewTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS).default(DEFAULT_REVIEW_TIMEOUT_MS),
20
27
  reviewContextMessages: z.number().min(1).default(DEFAULT_REVIEW_CONTEXT_MESSAGES),
21
28
  reviewMessageChars: z.number().min(1).default(DEFAULT_REVIEW_MESSAGE_CHARS),
22
29
  reviewMaxDepth: z.number().min(1).default(1),
@@ -28,6 +35,7 @@ const Config = z.object({
28
35
  ]).default(DEFAULT_SKILL_REVIEW_TRIGGER),
29
36
  reviewWakeInject: z.boolean().default(true),
30
37
  skillReviewCompletionMinToolCalls: z.number().min(1).default(DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS),
38
+ root: z.string().default(""),
31
39
  skillsRoot: z.string().default("")
32
40
  });
33
41
  /** V8-01 (0.3.45): the review subagent's structured-output contract. The
@@ -48,18 +56,20 @@ const REVIEW_OUTPUT_SCHEMA = {
48
56
  summary: { type: "string" }
49
57
  }
50
58
  };
51
- let statelessReviewStateWarned = false;
52
59
  function clampReviewConfig(rawConfig, ctx) {
53
60
  const clamped = [];
54
- const field = (name, value, fallback, min) => {
55
- const result = clampedNumber(value, fallback, { min });
61
+ const field = (name, value, fallback, min, max) => {
62
+ const result = clampedNumber(value, fallback, max === void 0 ? { min } : {
63
+ min,
64
+ max
65
+ });
56
66
  if (value !== void 0 && result !== value) clamped.push(name);
57
67
  return result;
58
68
  };
59
69
  const config = Object.assign({}, rawConfig, {
60
70
  memoryInterval: field("memoryInterval", rawConfig.memoryInterval, DEFAULT_REVIEW_MEMORY_INTERVAL, 1),
61
71
  skillInterval: field("skillInterval", rawConfig.skillInterval, DEFAULT_REVIEW_SKILL_INTERVAL, 1),
62
- reviewTimeoutMs: field("reviewTimeoutMs", rawConfig.reviewTimeoutMs, DEFAULT_REVIEW_TIMEOUT_MS, 1),
72
+ reviewTimeoutMs: field("reviewTimeoutMs", rawConfig.reviewTimeoutMs, DEFAULT_REVIEW_TIMEOUT_MS, 1, MAX_TIMER_DELAY_MS),
63
73
  reviewContextMessages: field("reviewContextMessages", rawConfig.reviewContextMessages, DEFAULT_REVIEW_CONTEXT_MESSAGES, 1),
64
74
  reviewMessageChars: field("reviewMessageChars", rawConfig.reviewMessageChars, DEFAULT_REVIEW_MESSAGE_CHARS, 1),
65
75
  reviewMaxDepth: field("reviewMaxDepth", rawConfig.reviewMaxDepth, 1, 1),
@@ -68,10 +78,13 @@ function clampReviewConfig(rawConfig, ctx) {
68
78
  if (clamped.length > 0) ctx.logger.warn(`dsh-evolution-review: ${clamped.join(", ")} provided an invalid value; falling back to the default`);
69
79
  return config;
70
80
  }
71
- function apply(ctx, rawConfig) {
81
+ function apply(ctx, rawConfig = {}) {
72
82
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to schedule review work");
73
83
  const config = clampReviewConfig(rawConfig, ctx);
84
+ const rootConfig = resolveRootConfig(rawConfig);
85
+ if (rootConfig.usedDeprecatedAlias) ctx.logger.warn("evolution-review: config \"skillsRoot\" is deprecated (E-7); use \"root\" — the alias is honoured until 0.3.65");
74
86
  const turnStarts = /* @__PURE__ */ new Map();
87
+ let statelessReviewStateWarned = false;
75
88
  const cumulativeToolCalls = /* @__PURE__ */ new Map();
76
89
  const completionInjected = /* @__PURE__ */ new Set();
77
90
  const pendingCadenceReviews = /* @__PURE__ */ new Map();
@@ -185,6 +198,7 @@ function apply(ctx, rawConfig) {
185
198
  }
186
199
  return;
187
200
  }
201
+ if (skipFire) return;
188
202
  const trigger = config.skillReviewTrigger;
189
203
  if (trigger !== "completion" && trigger !== "both") return;
190
204
  if (completionInjected.has(session.id)) return;
@@ -348,7 +362,11 @@ function apply(ctx, rawConfig) {
348
362
  let ok = true;
349
363
  try {
350
364
  for (const op of plan.memoryOps ?? []) {
351
- if (!Array.isArray(op.evidence) || op.evidence.length === 0) continue;
365
+ if (!Array.isArray(op.evidence) || op.evidence.length === 0) {
366
+ ok = false;
367
+ failedOps.push(`memory ${op.action ?? "add"} ${op.target}: missing evidence (defense-in-depth rejection)`);
368
+ continue;
369
+ }
352
370
  const normalized = {
353
371
  target: op.target === "user" ? "user" : "memory",
354
372
  action: op.action ?? "add",
@@ -363,7 +381,11 @@ function apply(ctx, rawConfig) {
363
381
  }
364
382
  }
365
383
  for (const op of plan.skillOps ?? []) {
366
- if (!Array.isArray(op.evidence) || op.evidence.length === 0 || !op.name) continue;
384
+ if (!Array.isArray(op.evidence) || op.evidence.length === 0 || !op.name) {
385
+ ok = false;
386
+ failedOps.push(`skill ${op.action ?? "patch"} ${op.name ?? "<unnamed>"}: missing evidence or name (defense-in-depth rejection)`);
387
+ continue;
388
+ }
367
389
  const args = {
368
390
  ...op,
369
391
  evidence: op.evidence
@@ -448,7 +470,7 @@ function apply(ctx, rawConfig) {
448
470
  ok: false,
449
471
  message: "evolution-io service not mounted"
450
472
  };
451
- const library = new SkillLibrary(resolveSkillsRoot({ root: config.skillsRoot }), evolutionIoAdapter(() => io.provider()), void 0, (event) => {
473
+ const library = new SkillLibrary(resolveSkillsRoot({ root: rootConfig.root }), evolutionIoAdapter(() => io.provider()), void 0, (event) => {
452
474
  ctx.emit("evolution/skill-mutated", event);
453
475
  });
454
476
  const op = skillArgs;
@@ -471,7 +493,7 @@ function apply(ctx, rawConfig) {
471
493
  if (archived.ok) await ctx.get("skillUsage")?.markArchived?.(name);
472
494
  return archived;
473
495
  }
474
- if (op.action === "write_file") return await library.writeSupportFile(name, op.file_path ?? "", op.file_content ?? op.content ?? "", origin);
496
+ if (op.action === "write_file") return await library.writeSupportFile(name, op.file_path ?? "", op.file_content ?? "", origin);
475
497
  if (op.action === "remove_file") return await library.removeSupportFile(name, op.file_path ?? "", origin);
476
498
  if (op.action === "restructure") {
477
499
  const moves = (op.restructure ?? []).filter((move) => move !== null).map((move) => ({
@@ -45,7 +45,10 @@ export interface Config {
45
45
  * Empty (the default) resolves through `resolveSkillsRoot` to the shared
46
46
  * default root — the historical behavior. A custom root keeps review-created
47
47
  * skills in the SAME tree the catalog/tools read instead of writing a
48
- * parallel tree the rest of the family cannot see. */
48
+ * parallel tree the rest of the family cannot see. E-7 (v18): canonical key. */
49
+ root?: string;
50
+ /** Deprecated alias of `root` (E-7, v18); honoured only while `root` is
51
+ * empty, with a warning; removed after 0.3.65. */
49
52
  skillsRoot?: string;
50
53
  }
51
54
  export declare const Config: z<Config>;
@@ -97,7 +100,7 @@ type ClampedReviewConfig = Config & {
97
100
  skillReviewCompletionMinToolCalls: number;
98
101
  };
99
102
  export declare function clampReviewConfig(rawConfig: Config, ctx: Context): ClampedReviewConfig;
100
- export declare function apply(ctx: Context, rawConfig: Config): void;
103
+ export declare function apply(ctx: Context, rawConfig?: Config): void;
101
104
  /** Completion-channel decision: task finished normally AND the session is proven long. */
102
105
  export declare function shouldCompletionReview(reason: {
103
106
  kind?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-review",
3
3
  "description": "Background review orchestration (community build)",
4
- "version": "0.3.62",
4
+ "version": "0.3.64",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -31,9 +31,9 @@
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
33
  "@deepseek-ai/schemastery": "^3.18.1",
34
- "@lmzhen/dsh-evolution-approval": "^0.3.62",
35
- "@lmzhen/dsh-evolution-core": "^0.3.62",
36
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.62"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.64",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.64",
36
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.64"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@deepseek-ai/cordis": "^4.0.1",
@@ -42,8 +42,8 @@
42
42
  "@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
43
43
  "@deepseek-ai/dsh-session": "^0.1.1-rc.2",
44
44
  "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
45
- "@lmzhen/dsh-evolution-state": "^0.3.62",
46
- "@lmzhen/dsh-evolution-policy": "^0.3.62"
45
+ "@lmzhen/dsh-evolution-state": "^0.3.64",
46
+ "@lmzhen/dsh-evolution-policy": "^0.3.64"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@deepseek-ai/dsh-agent": "^0.1.1-rc.2",
@@ -54,9 +54,9 @@
54
54
  "@deepseek-ai/dsh-session-persistence": "^0.1.1-rc.2",
55
55
  "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.1-rc.2",
56
56
  "@deepseek-ai/dsh-tools": "^0.1.1-rc.2",
57
- "@lmzhen/dsh-evolution-approval": "^0.3.62",
58
- "@lmzhen/dsh-evolution-core": "^0.3.62",
59
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.62",
60
- "@lmzhen/dsh-evolution-state": "^0.3.62"
57
+ "@lmzhen/dsh-evolution-approval": "^0.3.64",
58
+ "@lmzhen/dsh-evolution-core": "^0.3.64",
59
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.64",
60
+ "@lmzhen/dsh-evolution-state": "^0.3.64"
61
61
  }
62
62
  }