@lmzhen/dsh-evolution-review 0.3.22 → 0.3.24

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 (3) hide show
  1. package/README.md +2 -0
  2. package/lib/index.js +34 -24
  3. package/package.json +10 -10
package/README.md CHANGED
@@ -26,6 +26,8 @@ Independent of request-prefix construction. This package does not alter the asse
26
26
  - The review request text is redacted for credential-shaped patterns before it reaches the subagent, but redaction is pattern-based and best-effort, not a security boundary.
27
27
  - Read-before-write tracks only reads through the `skill` tool. `skill_manage` has no per-skill read action (`list`/`review` are whole-library, not targeted at one name), so a skill that was only listed via `skill_manage` is not marked as read — a background review may still reject a patch to it until it is actually loaded.
28
28
  - The completion-channel counters (`cumulativeToolCalls` / `completionInjected`) are in-memory only. A process restart resets them, which is accepted behavior: the completion review is a one-per-session post-task adaptation and a restart is treated as a fresh conversation boundary. The cadence state (`turnsSinceMemory` / `turnsSinceSkill`) is persisted via `ReviewState` and survives restart.
29
+ - `evolution/review-scheduled` and `evolution/review-error` are emitted for platform/user wiring only — this family has no in-repo production `ctx.on` consumer for them. They are declared externally owned (the platform side wires consumption), which matches the `EXEMPT_ORPHANS` set in `scripts/verify-event-pairing.mjs`.
30
+ - When the `evolution-state` service is not mounted, the memory/skill cadence state is not persisted and every turn restarts from a clean `{ turnsSinceMemory: 0, turnsSinceSkill: 0 }` baseline — the review schedule is stateless and re-decided each turn rather than accumulating across the conversation. The loss is surfaced once per process as a logger warning at the first turn/end.
29
31
 
30
32
  ## Configuration
31
33
 
package/lib/index.js CHANGED
@@ -25,6 +25,7 @@ const Config = z.object({
25
25
  skillReviewTrigger: z.string().default(DEFAULT_SKILL_REVIEW_TRIGGER),
26
26
  skillReviewCompletionMinToolCalls: z.number().default(DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS)
27
27
  });
28
+ let statelessReviewStateWarned = false;
28
29
  function apply(ctx, rawConfig) {
29
30
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to schedule review work");
30
31
  const config = rawConfig;
@@ -60,6 +61,10 @@ function apply(ctx, rawConfig) {
60
61
  const signal = foldTurn(session, turnStarts.get(session.id) ?? Math.max(0, session.seq - 1));
61
62
  turnStarts.delete(session.id);
62
63
  const stateService = ctx.get("evolutionState");
64
+ if (!stateService && !statelessReviewStateWarned) {
65
+ statelessReviewStateWarned = true;
66
+ ctx.logger.warn("dsh-evolution-review: evolution-state service not mounted — memory/skill review cadence is not persisted and resets every turn (see README Known Limitations).");
67
+ }
63
68
  const state = await stateService?.loadReviewState(session.id) ?? {
64
69
  turnsSinceMemory: 0,
65
70
  turnsSinceSkill: 0,
@@ -103,13 +108,6 @@ function apply(ctx, rawConfig) {
103
108
  if (completionInjected.has(session.id)) return;
104
109
  if (!shouldCompletionReview(event.data.reason, cumulative, config.skillReviewCompletionMinToolCalls)) return;
105
110
  completionInjected.add(session.id);
106
- ctx.emit("evolution/review-scheduled", {
107
- sessionId: session.id,
108
- kind: "skill",
109
- toolCalls: signal.toolCalls,
110
- userChars: signal.userChars,
111
- assistantChars: signal.assistantChars
112
- });
113
111
  agent.inject(createUserMessage({
114
112
  content: [{
115
113
  type: "text",
@@ -122,6 +120,13 @@ function apply(ctx, rawConfig) {
122
120
  summary: "completion review"
123
121
  }
124
122
  }));
123
+ ctx.emit("evolution/review-scheduled", {
124
+ sessionId: session.id,
125
+ kind: "skill",
126
+ toolCalls: signal.toolCalls,
127
+ userChars: signal.userChars,
128
+ assistantChars: signal.assistantChars
129
+ });
125
130
  }
126
131
  async function trySubagentReview(session, agent, kind, signal) {
127
132
  if ((policy()?.reviewMode ?? config.reviewMode) === "inject") return false;
@@ -186,6 +191,26 @@ function apply(ctx, rawConfig) {
186
191
  const executed = await executePlan(validation.accepted, session.id);
187
192
  const actions = executed.actions;
188
193
  const evidenceQuotes = [...validation.accepted.memoryOps ?? [], ...acceptedSkillOps].reduce((total, op) => total + (Array.isArray(op.evidence) ? op.evidence.length : 0), 0);
194
+ if (actions.length > 0) {
195
+ const applied = actions.join(" · ");
196
+ const note = executed.ok ? "" : "\n部分操作失败。以下操作已应用,请勿重复执行。";
197
+ try {
198
+ agent.inject(createUserMessage({
199
+ content: [{
200
+ type: "text",
201
+ text: `💾 Self-improvement review: ${applied}${note}`
202
+ }],
203
+ source: {
204
+ kind: "plugin",
205
+ plugin: "dsh-evolution-review",
206
+ form: "notice",
207
+ summary: "self-improvement review"
208
+ }
209
+ }));
210
+ } catch (injectError) {
211
+ ctx.logger.warn(`dsh-evolution-review: result notice inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
212
+ }
213
+ }
189
214
  ctx.emit("evolution/plan-applied", {
190
215
  sessionId: session.id,
191
216
  planId: randomUUID(),
@@ -196,22 +221,6 @@ function apply(ctx, rawConfig) {
196
221
  evidenceQuotes,
197
222
  estimatedInputChars: reviewText.length
198
223
  });
199
- if (actions.length > 0) {
200
- const applied = actions.join(" · ");
201
- const note = executed.ok ? "" : "\n部分操作失败。以下操作已应用,请勿重复执行。";
202
- agent.inject(createUserMessage({
203
- content: [{
204
- type: "text",
205
- text: `💾 Self-improvement review: ${applied}${note}`
206
- }],
207
- source: {
208
- kind: "plugin",
209
- plugin: "dsh-evolution-review",
210
- form: "notice",
211
- summary: "self-improvement review"
212
- }
213
- }));
214
- }
215
224
  return true;
216
225
  } finally {
217
226
  try {
@@ -379,7 +388,8 @@ function collectReadSkillNames(session) {
379
388
  continue;
380
389
  }
381
390
  else parsed = raw ?? {};
382
- const name = typeof parsed.name === "string" ? parsed.name : typeof parsed.skill === "string" ? parsed.skill : "";
391
+ const parsedObj = typeof parsed === "object" && parsed !== null ? parsed : {};
392
+ const name = typeof parsedObj.name === "string" ? parsedObj.name : typeof parsedObj.skill === "string" ? parsedObj.skill : "";
383
393
  if (name) names.add(name);
384
394
  }
385
395
  return names;
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.22",
4
+ "version": "0.3.24",
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.22",
35
- "@lmzhen/dsh-evolution-core": "^0.3.22",
36
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.22"
34
+ "@lmzhen/dsh-evolution-approval": "^0.3.24",
35
+ "@lmzhen/dsh-evolution-core": "^0.3.24",
36
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.24"
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.22",
46
- "@lmzhen/dsh-evolution-policy": "^0.3.22"
45
+ "@lmzhen/dsh-evolution-state": "^0.3.24",
46
+ "@lmzhen/dsh-evolution-policy": "^0.3.24"
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.22",
58
- "@lmzhen/dsh-evolution-core": "^0.3.22",
59
- "@lmzhen/dsh-evolution-plan-validator": "^0.3.22",
60
- "@lmzhen/dsh-evolution-state": "^0.3.22"
57
+ "@lmzhen/dsh-evolution-approval": "^0.3.24",
58
+ "@lmzhen/dsh-evolution-core": "^0.3.24",
59
+ "@lmzhen/dsh-evolution-plan-validator": "^0.3.24",
60
+ "@lmzhen/dsh-evolution-state": "^0.3.24"
61
61
  }
62
62
  }