@lmzhen/dsh-evolution-review 0.3.64 → 0.3.66

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.
Files changed (2) hide show
  1. package/lib/index.js +63 -20
  2. package/package.json +10 -10
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, 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_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";
5
5
  import { validateEvolutionPlan } from "@lmzhen/dsh-evolution-plan-validator";
6
6
  //#region lib/types/index.js
7
7
  /**
@@ -16,7 +16,7 @@ const inject = ["agents"];
16
16
  * start call; the outer catch logged it and silently degraded the review to
17
17
  * the inject path. The schema and the assembly clamp both reject it (same
18
18
  * bound as commands/maintenance). */
19
- const MAX_TIMER_DELAY_MS = 4294967295;
19
+ const MAX_TIMER_DELAY_MS = 2147483647;
20
20
  const Config = z.object({
21
21
  reviewEnabled: z.boolean().default(true),
22
22
  reviewMode: z.union([z.const("subagent"), z.const("inject")]).default("subagent"),
@@ -94,7 +94,7 @@ function apply(ctx, rawConfig = {}) {
94
94
  let reviewInFlight = false;
95
95
  const policy = () => ctx.get("evolutionPolicy")?.get();
96
96
  ctx.on("session/event", (session, event) => {
97
- if (event.type === "turn/start") turnStarts.set(session.id, session.seq - 1);
97
+ if (event.type === "turn/start" && session.header.origin !== "subagent") turnStarts.set(session.id, session.seq - 1);
98
98
  if (event.type !== "turn/end") return;
99
99
  if (turnStarts.size >= COUNTER_SWEEP_THRESHOLD || cumulativeToolCalls.size >= COUNTER_SWEEP_THRESHOLD || completionInjected.size >= COUNTER_SWEEP_THRESHOLD || pendingCadenceReviews.size >= COUNTER_SWEEP_THRESHOLD || skipNextCadenceFire.size >= COUNTER_SWEEP_THRESHOLD || cadenceResetWarned.size >= COUNTER_SWEEP_THRESHOLD) {
100
100
  const isAlive = (id) => ctx.agents.get(id) !== void 0;
@@ -161,21 +161,24 @@ function apply(ctx, rawConfig = {}) {
161
161
  } catch (injectError) {
162
162
  ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
163
163
  }
164
- else if (await trySubagentReview(session, agent, pendingKind, signal)) try {
165
- ctx.emit("evolution/review-scheduled", {
166
- sessionId: session.id,
167
- kind: pendingKind,
168
- toolCalls: signal.toolCalls,
169
- userChars: signal.userChars,
170
- assistantChars: signal.assistantChars
171
- });
172
- } catch (emitError) {
173
- ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
174
- }
175
- else try {
176
- deliverMessage(agent, reviewPrompt(pendingKind), "auto-review");
177
- } catch (injectError) {
178
- ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
164
+ else {
165
+ const reviewOutcome = await trySubagentReview(session, agent, pendingKind, signal);
166
+ if (reviewOutcome === true) try {
167
+ ctx.emit("evolution/review-scheduled", {
168
+ sessionId: session.id,
169
+ kind: pendingKind,
170
+ toolCalls: signal.toolCalls,
171
+ userChars: signal.userChars,
172
+ assistantChars: signal.assistantChars
173
+ });
174
+ } catch (emitError) {
175
+ ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
176
+ }
177
+ else if (reviewOutcome !== "deferred") try {
178
+ deliverMessage(agent, reviewPrompt(pendingKind), "auto-review");
179
+ } catch (injectError) {
180
+ ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
181
+ }
179
182
  }
180
183
  state.turnsSinceMemory = 0;
181
184
  state.turnsSinceSkill = 0;
@@ -246,11 +249,35 @@ function apply(ctx, rawConfig = {}) {
246
249
  skipNextCadenceFire.set(agent.session.id, true);
247
250
  } else agent.inject(message);
248
251
  };
252
+ const withTimeout = (promise, ms, label) => new Promise((resolve, reject) => {
253
+ const timer = setTimeout(() => {
254
+ reject(/* @__PURE__ */ new Error(`dsh-evolution-review: ${label} timed out after ${ms}ms`));
255
+ }, ms);
256
+ promise.then((value) => {
257
+ clearTimeout(timer);
258
+ resolve(value);
259
+ }, (error) => {
260
+ clearTimeout(timer);
261
+ reject(error instanceof Error ? error : new Error(String(error)));
262
+ });
263
+ });
264
+ const deferredFallbackReviews = [];
265
+ const DEFERRED_REVIEW_CAP = 16;
266
+ ctx.effect(() => () => {
267
+ deferredFallbackReviews.length = 0;
268
+ }, "dsh-evolution-review.deferred-drain");
249
269
  async function trySubagentReview(session, agent, kind, signal) {
250
270
  if ((policy()?.reviewMode ?? config.reviewMode) === "inject") return false;
251
271
  const subagents = ctx.get("subagents");
252
272
  if (!subagents) return false;
253
- if (reviewInFlight) return false;
273
+ if (reviewInFlight) {
274
+ if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) deferredFallbackReviews.push({
275
+ agent,
276
+ kind
277
+ });
278
+ else ctx.logger.warn(`dsh-evolution-review: deferred-review queue at cap (${DEFERRED_REVIEW_CAP}) — dropping one fallback review prompt`);
279
+ return "deferred";
280
+ }
254
281
  reviewInFlight = true;
255
282
  try {
256
283
  const snapshot = policy();
@@ -296,7 +323,7 @@ function apply(ctx, rawConfig = {}) {
296
323
  });
297
324
  const acceptedSkillOps = validation.accepted.skillOps ?? [];
298
325
  const skippedUnread = filterUnreadSkillOps(acceptedSkillOps, new Set([...collectReadSkillNames(session), ...childReads]));
299
- const executed = await executePlan(validation.accepted, session);
326
+ const executed = await withTimeout(executePlan(validation.accepted, session), config.reviewTimeoutMs, "review plan execution");
300
327
  const actions = executed.actions;
301
328
  const evidenceQuotes = [...validation.accepted.memoryOps ?? [], ...acceptedSkillOps].reduce((total, op) => total + (Array.isArray(op.evidence) ? op.evidence.length : 0), 0);
302
329
  if (actions.length > 0) {
@@ -347,15 +374,27 @@ function apply(ctx, rawConfig = {}) {
347
374
  }
348
375
  } catch (error) {
349
376
  ctx.logger.warn(`dsh-evolution-review: subagent review failed: ${error instanceof Error ? error.message : String(error)}`);
377
+ if (error instanceof Error && error.message.includes("plan execution timed out")) ctx.logger.warn("dsh-evolution-review: plan execution abandoned on timeout — any late write it lands has NO plan-applied record and races the fallback inject; inspect the skill tree and usage sidecar");
350
378
  return false;
351
379
  } finally {
352
380
  reviewInFlight = false;
381
+ const deferred = deferredFallbackReviews.splice(0);
382
+ for (const { agent: waitingAgent, kind: waitingKind } of deferred) try {
383
+ deliverMessage(waitingAgent, reviewPrompt(waitingKind), "auto-review");
384
+ } catch (injectError) {
385
+ ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
386
+ }
353
387
  }
354
388
  }
355
389
  async function executePlan(plan, session) {
356
390
  const sessionId = session?.id;
357
391
  const memory = ctx.get("memory");
358
392
  const approval = ctx.get("evolutionApproval");
393
+ const hashLibrary = (() => {
394
+ const io = ctx.get("evolutionIo");
395
+ if (!io) return null;
396
+ return new SkillLibrary(resolveSkillsRoot({ root: rootConfig.root }), evolutionIoAdapter(() => io.provider()));
397
+ })();
359
398
  const origins = resolveOrigins(void 0, true);
360
399
  const actions = [];
361
400
  const failedOps = [];
@@ -390,6 +429,10 @@ function apply(ctx, rawConfig = {}) {
390
429
  ...op,
391
430
  evidence: op.evidence
392
431
  };
432
+ if ((args.action === "update" || args.action === "edit") && hashLibrary && typeof args.name === "string" && args.name !== "") {
433
+ const stageCurrent = await hashLibrary.read(args.name).catch(() => null);
434
+ if (stageCurrent !== null) args.staged_from_sha256 = contentHash(stageCurrent);
435
+ }
393
436
  const runnerArgs = {
394
437
  operation: args,
395
438
  origin: origins.library
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.64",
4
+ "version": "0.3.66",
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.64",
35
- "@lmzhen/dsh-evolution-core": "^0.3.64",
36
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.64"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.66",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.66",
36
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.66"
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.64",
46
- "@lmzhen/dsh-evolution-policy": "^0.3.64"
45
+ "@lmzhen/dsh-evolution-state": "^0.3.66",
46
+ "@lmzhen/dsh-evolution-policy": "^0.3.66"
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.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"
57
+ "@lmzhen/dsh-evolution-approval": "^0.3.66",
58
+ "@lmzhen/dsh-evolution-core": "^0.3.66",
59
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.66",
60
+ "@lmzhen/dsh-evolution-state": "^0.3.66"
61
61
  }
62
62
  }