@lmzhen/dsh-evolution-review 0.3.67 → 0.3.68

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, contentHash, evolutionIoAdapter, foldTurn, redactSecrets, resolveOrigins, resolveRootConfig, resolveSkillsRoot, reviewPrompt, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
4
+ import { COMPLETION_SKILL_REVIEW_PROMPT, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, 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_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_USER_CHAR_LIMIT, MAX_TIMER_DELAY_MS, PROMPT_BUNDLE, SkillLibrary, advanceReview, assertSkillsRootAliasRetired, clampedNumber, contentHash, 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,6 @@ 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 = 2147483647;
20
13
  const Config = z.object({
21
14
  reviewEnabled: z.boolean().default(true),
22
15
  reviewMode: z.union([z.const("subagent"), z.const("inject")]).default("subagent"),
@@ -81,8 +74,8 @@ function clampReviewConfig(rawConfig, ctx) {
81
74
  function apply(ctx, rawConfig = {}) {
82
75
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to schedule review work");
83
76
  const config = clampReviewConfig(rawConfig, ctx);
77
+ assertSkillsRootAliasRetired(rawConfig);
84
78
  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");
86
79
  const turnStarts = /* @__PURE__ */ new Map();
87
80
  let statelessReviewStateWarned = false;
88
81
  const cumulativeToolCalls = /* @__PURE__ */ new Map();
@@ -160,9 +153,9 @@ function apply(ctx, rawConfig = {}) {
160
153
  advanced.kind = advanceReview(state, event.data.turn, signal, {
161
154
  memoryInterval: snapshot?.reviewMemoryInterval ?? config.memoryInterval,
162
155
  skillInterval: snapshot?.reviewSkillInterval ?? config.skillInterval,
163
- substantiveMinToolCalls: snapshot?.substantiveMinToolCalls ?? 3,
164
- substantiveMinUserChars: snapshot?.substantiveMinUserChars ?? 200,
165
- substantiveMinAgentChars: snapshot?.substantiveMinAgentChars ?? 500,
156
+ substantiveMinToolCalls: snapshot?.substantiveMinToolCalls ?? DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS,
157
+ substantiveMinUserChars: snapshot?.substantiveMinUserChars ?? DEFAULT_SUBSTANTIVE_MIN_USER_CHARS,
158
+ substantiveMinAgentChars: snapshot?.substantiveMinAgentChars ?? DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS,
166
159
  resetOnFire: false
167
160
  });
168
161
  await stateService?.saveReviewState(session.id, state);
@@ -205,7 +198,10 @@ function apply(ctx, rawConfig = {}) {
205
198
  } catch (emitError) {
206
199
  ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
207
200
  }
208
- else if (reviewOutcome !== "deferred") try {
201
+ else if (reviewOutcome === "dropped") {
202
+ pendingCadenceReviews.set(session.id, pendingKind);
203
+ return;
204
+ } else if (reviewOutcome !== "deferred") try {
209
205
  deliverMessage(agent, reviewPrompt(pendingKind), "auto-review");
210
206
  try {
211
207
  ctx.emit("evolution/review-scheduled", {
@@ -334,22 +330,25 @@ function apply(ctx, rawConfig = {}) {
334
330
  const subagents = ctx.get("subagents");
335
331
  if (!subagents) return false;
336
332
  if (reviewInFlight) {
337
- if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) deferredFallbackReviews.push({
338
- agent,
339
- sessionId: session.id,
340
- kind,
341
- prompt: reviewPrompt(kind),
342
- label: "auto-review",
343
- channel: "inject",
344
- counts: signal
345
- });
346
- else ctx.logger.warn(`dsh-evolution-review: deferred-review queue at cap (${DEFERRED_REVIEW_CAP}) — dropping one fallback review prompt`);
347
- return "deferred";
333
+ if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) {
334
+ deferredFallbackReviews.push({
335
+ agent,
336
+ sessionId: session.id,
337
+ kind,
338
+ prompt: reviewPrompt(kind),
339
+ label: "auto-review",
340
+ channel: "inject",
341
+ counts: signal
342
+ });
343
+ return "deferred";
344
+ }
345
+ ctx.logger.warn(`dsh-evolution-review: deferred-review queue at cap (${DEFERRED_REVIEW_CAP}) — dropping one fallback review prompt`);
346
+ return "dropped";
348
347
  }
349
348
  reviewInFlight = true;
350
349
  try {
351
350
  const snapshot = policy();
352
- const model = kind === "memory" ? snapshot?.memoryReviewModel ?? "deepseek-v4-flash" : snapshot?.skillReviewModel ?? "deepseek-v4-pro";
351
+ const model = kind === "memory" ? snapshot?.memoryReviewModel ?? DEFAULT_MEMORY_REVIEW_MODEL : snapshot?.skillReviewModel ?? DEFAULT_SKILL_REVIEW_MODEL;
353
352
  const reviewText = redactSecrets(buildReviewRequest(session, kind, signal, config.reviewContextMessages, config.reviewMessageChars));
354
353
  const agentOptions = { model };
355
354
  if (config.reviewProvider) agentOptions.provider = config.reviewProvider;
@@ -391,9 +390,38 @@ function apply(ctx, rawConfig = {}) {
391
390
  });
392
391
  const acceptedSkillOps = validation.accepted.skillOps ?? [];
393
392
  const skippedUnread = filterUnreadSkillOps(acceptedSkillOps, new Set([...collectReadSkillNames(session), ...childReads]));
394
- const executed = await withTimeout(executePlan(validation.accepted, session), config.reviewTimeoutMs, "review plan execution");
395
- const actions = executed.actions;
396
393
  const evidenceQuotes = [...validation.accepted.memoryOps ?? [], ...acceptedSkillOps].reduce((total, op) => total + (Array.isArray(op.evidence) ? op.evidence.length : 0), 0);
394
+ const emitApplied = (report) => {
395
+ try {
396
+ ctx.emit("evolution/plan-applied", {
397
+ sessionId: session.id,
398
+ planId: randomUUID(),
399
+ policyFingerprint,
400
+ memoryApplied: report.actions.filter((action) => action.startsWith("Memory")).length,
401
+ skillApplied: report.actions.filter((action) => action.startsWith("Skill ")).length,
402
+ rejectedOps: validation.rejected.length,
403
+ ...skippedUnread > 0 ? { skippedUnread } : {},
404
+ executionFailures: report.failedOps?.length ?? 0,
405
+ ...report.executionError !== void 0 ? { executionError: report.executionError } : {},
406
+ evidenceQuotes,
407
+ estimatedInputChars: reviewText.length
408
+ });
409
+ } catch (emitError) {
410
+ ctx.logger.warn(`dsh-evolution-review: plan-applied emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
411
+ }
412
+ };
413
+ const landed = [];
414
+ let executed;
415
+ try {
416
+ executed = await withTimeout(executePlan(validation.accepted, session, (action) => landed.push(action)), config.reviewTimeoutMs, "review plan execution");
417
+ } catch (error) {
418
+ emitApplied({
419
+ actions: landed,
420
+ executionError: `execution timed out after ${config.reviewTimeoutMs}ms`
421
+ });
422
+ throw error;
423
+ }
424
+ const actions = executed.actions;
397
425
  if (actions.length > 0) {
398
426
  const applied = actions.join(" · ");
399
427
  const note = executed.ok ? "" : "\n部分操作失败。以下操作已应用,请勿重复执行。";
@@ -415,23 +443,11 @@ function apply(ctx, rawConfig = {}) {
415
443
  } catch (injectError) {
416
444
  ctx.logger.warn(`dsh-evolution-review: zero-landing notice inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
417
445
  }
418
- try {
419
- ctx.emit("evolution/plan-applied", {
420
- sessionId: session.id,
421
- planId: randomUUID(),
422
- policyFingerprint,
423
- memoryApplied: actions.filter((action) => action.startsWith("Memory")).length,
424
- skillApplied: actions.filter((action) => action.startsWith("Skill ")).length,
425
- rejectedOps: validation.rejected.length + skippedUnread,
426
- executionFailures: executed.failedOps.length,
427
- ...executed.aborted !== void 0 ? { executionError: executed.aborted } : {},
428
- ...executed.failedOps[0] !== void 0 && executed.aborted === void 0 ? { executionError: executed.failedOps[0] } : {},
429
- evidenceQuotes,
430
- estimatedInputChars: reviewText.length
431
- });
432
- } catch (emitError) {
433
- ctx.logger.warn(`dsh-evolution-review: plan-applied emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
434
- }
446
+ emitApplied({
447
+ actions,
448
+ failedOps: executed.failedOps,
449
+ ...executed.aborted !== void 0 ? { executionError: executed.aborted } : executed.failedOps[0] !== void 0 ? { executionError: executed.failedOps[0] } : {}
450
+ });
435
451
  return true;
436
452
  } finally {
437
453
  try {
@@ -470,7 +486,7 @@ function apply(ctx, rawConfig = {}) {
470
486
  }
471
487
  }
472
488
  }
473
- async function executePlan(plan, session) {
489
+ async function executePlan(plan, session, onLanded) {
474
490
  const sessionId = session?.id;
475
491
  const memory = ctx.get("memory");
476
492
  const approval = ctx.get("evolutionApproval");
@@ -497,8 +513,10 @@ function apply(ctx, rawConfig = {}) {
497
513
  old_text: op.old_text
498
514
  };
499
515
  const result = approval ? await runApproved("memory", `memory ${normalized.target} ${normalized.action}`, normalized, normalized, session) : await memory?.applyBatch(normalized.target, [normalized]);
500
- if (result?.ok) actions.push("Memory updated");
501
- else {
516
+ if (result?.ok) {
517
+ actions.push("Memory updated");
518
+ onLanded?.("Memory updated");
519
+ } else {
502
520
  ok = false;
503
521
  failedOps.push(`memory ${normalized.action} ${normalized.target}: ${result?.message ?? "service unavailable"}`);
504
522
  }
@@ -522,8 +540,10 @@ function apply(ctx, rawConfig = {}) {
522
540
  origin: origins.library
523
541
  };
524
542
  const result = approval ? await runApproved("skill", `skill ${op.action ?? "patch"} ${op.name}`, runnerArgs, runnerArgs, session) : await executeSkillDirect(args);
525
- if (result?.ok) actions.push(`Skill ${op.name} ${op.action ?? "patch"}`);
526
- else {
543
+ if (result?.ok) {
544
+ actions.push(`Skill ${op.name} ${op.action ?? "patch"}`);
545
+ onLanded?.(`Skill ${op.name} ${op.action ?? "patch"}`);
546
+ } else {
527
547
  ok = false;
528
548
  failedOps.push(`skill ${op.action ?? "patch"} ${op.name}: ${result?.message ?? "service unavailable"}`);
529
549
  }
@@ -47,8 +47,9 @@ export interface Config {
47
47
  * skills in the SAME tree the catalog/tools read instead of writing a
48
48
  * parallel tree the rest of the family cannot see. E-7 (v18): canonical key. */
49
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. */
50
+ /** V27 G2.4 (M-08): the retired `skillsRoot` alias, kept in the schema so a
51
+ * config that still sets it reaches {@link assertSkillsRootAliasRetired} and
52
+ * fails the load instead of being silently dropped. Never read. */
52
53
  skillsRoot?: string;
53
54
  }
54
55
  export declare const Config: z<Config>;
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.67",
4
+ "version": "0.3.68",
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.67",
35
- "@lmzhen/dsh-evolution-core": "^0.3.67",
36
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.67"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.68",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.68",
36
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.68"
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.67",
46
- "@lmzhen/dsh-evolution-policy": "^0.3.67"
45
+ "@lmzhen/dsh-evolution-state": "^0.3.68",
46
+ "@lmzhen/dsh-evolution-policy": "^0.3.68"
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.67",
58
- "@lmzhen/dsh-evolution-core": "^0.3.67",
59
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.67",
60
- "@lmzhen/dsh-evolution-state": "^0.3.67"
57
+ "@lmzhen/dsh-evolution-approval": "^0.3.68",
58
+ "@lmzhen/dsh-evolution-core": "^0.3.68",
59
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.68",
60
+ "@lmzhen/dsh-evolution-state": "^0.3.68"
61
61
  }
62
62
  }