@lmzhen/dsh-evolution-review 0.5.0 → 0.6.1

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/README.md CHANGED
@@ -33,6 +33,10 @@ own, and the delivery mode lives on the `evolution-policy` row, not here.
33
33
  - `reviewToolAllow`: the allow-list review subagents are spawned with (default `[skill]`).
34
34
  - `reviewWakeInject` (default `true`): waking (`agent.followup`) delivery instead of the non-waking `agent.inject`.
35
35
  - `skillReviewTrigger` (`'cadence'` default, or `'completion'` / `'both'`): gates only the completion channel.
36
+ - Deprecated names (G0/S0.3): `memoryInterval` and `skillInterval` are the legacy
37
+ spellings of the policy row's `reviewMemoryInterval` / `reviewSkillInterval`
38
+ (the snapshot shadows them in every shipped composition). Reading them still
39
+ works; writing them is refused, and both are removed in 0.7.0.
36
40
 
37
41
  ### Review delivery contract
38
42
 
package/lib/index.js CHANGED
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import z from "@deepseek-ai/schemastery";
3
3
  import { createUserMessage } from "@deepseek-ai/dsh-llm";
4
4
  import { SessionId } from "@deepseek-ai/dsh-session";
5
- 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_LIMITS, 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, advanceReview, assertSkillsRootAliasRetired, clampedNumber, clearReviewChannel, contentHash, evolutionIoAdapter, foldToolDispatches, foldTurn, markReviewChannel, newSkillLibrary, policyStageLimits, readDispatchSignal, redactSecrets, resolveOrigins, resolveRootConfig, reviewPrompt, sessionAudited, skillReadNameOf, sweepReviewChannelSessions, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
5
+ 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_LIMITS, 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, PARAM_NAMESPACES, PROMPT_BUNDLE, advanceReview, assertSkillsRootAliasRetired, clampedNumber, clearReviewChannel, contentHash, evolutionIoAdapter, foldToolDispatches, foldTurn, installParamSection, markReviewChannel, newSkillLibrary, policyStageLimits, readDispatchSignal, readNumberParam, redactSecrets, resolveOrigins, resolveRootConfig, reviewPrompt, sessionAudited, skillReadNameOf, sweepReviewChannelSessions, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
6
6
  import { validateEvolutionPlan } from "@lmzhen/dsh-evolution-plan-validator";
7
7
  //#region lib/types/session-state.js
8
8
  /**
@@ -138,8 +138,8 @@ function clampReviewConfig(rawConfig, ctx) {
138
138
  return result;
139
139
  };
140
140
  const config = Object.assign({}, rawConfig, {
141
- memoryInterval: field("memoryInterval", rawConfig.memoryInterval, DEFAULT_REVIEW_MEMORY_INTERVAL, 1),
142
- skillInterval: field("skillInterval", rawConfig.skillInterval, DEFAULT_REVIEW_SKILL_INTERVAL, 1),
141
+ memoryInterval: field("memoryInterval", readNumberParam(rawConfig, "reviewMemoryInterval"), DEFAULT_REVIEW_MEMORY_INTERVAL, 1),
142
+ skillInterval: field("skillInterval", readNumberParam(rawConfig, "reviewSkillInterval"), DEFAULT_REVIEW_SKILL_INTERVAL, 1),
143
143
  reviewTimeoutMs: field("reviewTimeoutMs", rawConfig.reviewTimeoutMs, DEFAULT_REVIEW_TIMEOUT_MS, 1, MAX_TIMER_DELAY_MS),
144
144
  reviewContextMessages: field("reviewContextMessages", rawConfig.reviewContextMessages, DEFAULT_REVIEW_CONTEXT_MESSAGES, 1),
145
145
  reviewMessageChars: field("reviewMessageChars", rawConfig.reviewMessageChars, DEFAULT_REVIEW_MESSAGE_CHARS, 1),
@@ -152,6 +152,23 @@ function clampReviewConfig(rawConfig, ctx) {
152
152
  function policySnapshotOf(source) {
153
153
  return source?.get?.();
154
154
  }
155
+ /** Namespace the review group's user-writable knobs live in (core's PARAM_NAMESPACES). */
156
+ const REVIEW_SETTINGS_NAMESPACE = "evolution-review";
157
+ /** Schema the platform validates the user layer against; defaults mirror the
158
+ * core constants so an empty document resolves to today's behaviour. */
159
+ const REVIEW_SETTINGS_SCHEMA = z.object({
160
+ reviewSkillInterval: z.number().min(1).default(DEFAULT_REVIEW_SKILL_INTERVAL),
161
+ reviewMemoryInterval: z.number().min(1).default(DEFAULT_REVIEW_MEMORY_INTERVAL),
162
+ skillReviewTrigger: z.union([
163
+ z.const("cadence"),
164
+ z.const("completion"),
165
+ z.const("both")
166
+ ]).default(DEFAULT_SKILL_REVIEW_TRIGGER),
167
+ skillReviewCompletionMinToolCalls: z.number().min(1).default(DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS),
168
+ reviewEnabled: z.boolean().default(true),
169
+ reviewMode: z.union([z.const("subagent"), z.const("inject")]).default("inject"),
170
+ reviewWakeInject: z.boolean().default(true)
171
+ });
155
172
  function apply(ctx, rawConfig = {}) {
156
173
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to schedule review work");
157
174
  const config = clampReviewConfig(rawConfig, ctx);
@@ -170,6 +187,30 @@ function apply(ctx, rawConfig = {}) {
170
187
  const cadenceResetWarned = sessionState.add("cadenceResetWarned", /* @__PURE__ */ new Set());
171
188
  let reviewInFlight = false;
172
189
  const policy = () => ctx.get("evolutionPolicy")?.get();
190
+ const settingsBase = {
191
+ reviewSkillInterval: config.skillInterval,
192
+ reviewMemoryInterval: config.memoryInterval,
193
+ skillReviewTrigger: config.skillReviewTrigger ?? DEFAULT_SKILL_REVIEW_TRIGGER,
194
+ skillReviewCompletionMinToolCalls: config.skillReviewCompletionMinToolCalls,
195
+ reviewEnabled: config.reviewEnabled ?? true,
196
+ reviewMode: config.reviewMode ?? "inject",
197
+ reviewWakeInject: config.reviewWakeInject ?? true
198
+ };
199
+ const overrides = installParamSection(ctx, PARAM_NAMESPACES["evolution-review"] ?? "evolution-review", REVIEW_SETTINGS_SCHEMA, settingsBase, { warn: (message) => {
200
+ ctx.logger.warn("dsh-evolution-review: " + message);
201
+ } });
202
+ const params = () => {
203
+ const snapshot = policy();
204
+ return {
205
+ reviewSkillInterval: overrides.get("reviewSkillInterval") ?? snapshot?.reviewSkillInterval ?? config.skillInterval,
206
+ reviewMemoryInterval: overrides.get("reviewMemoryInterval") ?? snapshot?.reviewMemoryInterval ?? config.memoryInterval,
207
+ skillReviewTrigger: overrides.get("skillReviewTrigger") ?? config.skillReviewTrigger ?? DEFAULT_SKILL_REVIEW_TRIGGER,
208
+ skillReviewCompletionMinToolCalls: overrides.get("skillReviewCompletionMinToolCalls") ?? config.skillReviewCompletionMinToolCalls,
209
+ reviewEnabled: overrides.get("reviewEnabled") ?? config.reviewEnabled ?? true,
210
+ reviewMode: overrides.get("reviewMode") ?? snapshot?.reviewMode ?? config.reviewMode ?? "inject",
211
+ reviewWakeInject: overrides.get("reviewWakeInject") ?? config.reviewWakeInject ?? true
212
+ };
213
+ };
173
214
  const schemaDefaults = Config["~standard"].validate({}).value;
174
215
  const shadowedRowFields = [
175
216
  "reviewMode",
@@ -188,7 +229,7 @@ function apply(ctx, rawConfig = {}) {
188
229
  warnShadowed();
189
230
  });
190
231
  }
191
- if ((policy()?.reviewMode ?? config.reviewMode) === "inject") ctx.logger.warn("dsh-evolution-review: reviewMode \"inject\" (default) runs the review in the parent session and emits NO evolution/plan-applied ledger entry — evolution-activity and evolution-replay stay empty in this mode (see the evolution-review README, \"Known Limitations and Deferred Work\"). Set reviewMode: \"subagent\" on the evolution-policy row to keep the audited plan path.");
232
+ if (params().reviewMode === "inject") ctx.logger.warn("dsh-evolution-review: reviewMode \"inject\" (default) runs the review in the parent session and emits NO evolution/plan-applied ledger entry — evolution-activity and evolution-replay stay empty in this mode (see the evolution-review README, \"Known Limitations and Deferred Work\"). Set reviewMode: \"subagent\" on the evolution-policy row to keep the audited plan path.");
192
233
  const reviewStateLocks = sessionState.add("reviewStateLocks", /* @__PURE__ */ new Map());
193
234
  async function withReviewStateLock(id, task) {
194
235
  const next = (reviewStateLocks.get(id) ?? Promise.resolve()).catch(() => {}).then(task);
@@ -237,7 +278,7 @@ function apply(ctx, rawConfig = {}) {
237
278
  }
238
279
  }
239
280
  async function runOnTurnEnd(session, event) {
240
- if (!config.reviewEnabled) return;
281
+ if (!params().reviewEnabled) return;
241
282
  if (session.header.origin === "subagent") return;
242
283
  const agent = ctx.agents.get(session.id);
243
284
  if (!agent) return;
@@ -269,8 +310,8 @@ function apply(ctx, rawConfig = {}) {
269
310
  lastTurn: -1
270
311
  };
271
312
  advanced.kind = advanceReview(state, event.data.turn, signal, {
272
- memoryInterval: snapshot?.reviewMemoryInterval ?? config.memoryInterval,
273
- skillInterval: snapshot?.reviewSkillInterval ?? config.skillInterval,
313
+ memoryInterval: params().reviewMemoryInterval,
314
+ skillInterval: params().reviewSkillInterval,
274
315
  substantiveMinToolCalls: snapshot?.substantiveMinToolCalls ?? DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS,
275
316
  substantiveMinUserChars: snapshot?.substantiveMinUserChars ?? DEFAULT_SUBSTANTIVE_MIN_USER_CHARS,
276
317
  substantiveMinAgentChars: snapshot?.substantiveMinAgentChars ?? DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS,
@@ -285,7 +326,7 @@ function apply(ctx, rawConfig = {}) {
285
326
  const pendingKind = kind ?? pendingCadenceReviews.get(session.id) ?? void 0;
286
327
  if (pendingKind !== void 0) {
287
328
  pendingCadenceReviews.delete(session.id);
288
- if ((policy()?.reviewMode ?? config.reviewMode) === "inject") {
329
+ if (params().reviewMode === "inject") {
289
330
  if (!deliverMessage(agent, reviewPrompt(pendingKind), cadenceSummary(pendingKind), true)) {
290
331
  pendingCadenceReviews.set(session.id, pendingKind);
291
332
  return;
@@ -363,10 +404,10 @@ function apply(ctx, rawConfig = {}) {
363
404
  return;
364
405
  }
365
406
  if (skipFire) return;
366
- const trigger = config.skillReviewTrigger;
407
+ const trigger = params().skillReviewTrigger;
367
408
  if (trigger !== "completion" && trigger !== "both") return;
368
409
  if (completionInjected.has(session.id)) return;
369
- if (!shouldCompletionReview(event.data.reason, cumulative, config.skillReviewCompletionMinToolCalls)) return;
410
+ if (!shouldCompletionReview(event.data.reason, cumulative, params().skillReviewCompletionMinToolCalls)) return;
370
411
  completionInjected.add(session.id);
371
412
  if (reviewInFlight) {
372
413
  if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) deferredFallbackReviews.push({
@@ -483,7 +524,7 @@ function apply(ctx, rawConfig = {}) {
483
524
  ctx.logger.warn(`dsh-evolution-review: inbox coalescing failed (${error instanceof Error ? error.message : String(error)}) — delivering a fresh message instead`);
484
525
  }
485
526
  try {
486
- if (config.reviewWakeInject && typeof wake.followup === "function") {
527
+ if (params().reviewWakeInject && typeof wake.followup === "function") {
487
528
  wake.followup(message);
488
529
  skipNextCadenceFire.set(agent.session.id, { afterTurn: lastTurnStart.get(agent.session.id) ?? -1 });
489
530
  } else agent.inject(message);
@@ -557,7 +598,7 @@ function apply(ctx, rawConfig = {}) {
557
598
  return hashes;
558
599
  }
559
600
  async function trySubagentReview(session, agent, kind, signal) {
560
- if ((policy()?.reviewMode ?? config.reviewMode) === "inject") return false;
601
+ if (params().reviewMode === "inject") return false;
561
602
  const subagents = ctx.get("subagents");
562
603
  if (!subagents) return false;
563
604
  if (reviewInFlight) {
@@ -1232,4 +1273,4 @@ function buildReviewRequest(session, kind, signal, maxMessages, maxMessageChars)
1232
1273
  ].join("\n");
1233
1274
  }
1234
1275
  //#endregion
1235
- export { Config, REVIEW_OUTPUT_SCHEMA, apply, buildReviewRequest, clampReviewConfig, filterUnreadSkillOps, inject, name, renderToolResultLine, shouldCompletionReview, sweepDeadSessionEntries };
1276
+ export { Config, REVIEW_OUTPUT_SCHEMA, REVIEW_SETTINGS_NAMESPACE, REVIEW_SETTINGS_SCHEMA, apply, buildReviewRequest, clampReviewConfig, filterUnreadSkillOps, inject, name, renderToolResultLine, shouldCompletionReview, sweepDeadSessionEntries };
@@ -24,8 +24,12 @@ export interface Config {
24
24
  * row's value, which the plugin reports once at load (v37 P2-24). */
25
25
  reviewMode?: 'subagent' | 'inject';
26
26
  /** Shadowed by the policy snapshot in every shipped composition — configure
27
- * `reviewMemoryInterval` on the `evolution-policy` row instead (v37 P2-24). */
27
+ * `reviewMemoryInterval` on the `evolution-policy` row instead (v37 P2-24).
28
+ * Deprecated alias (G0/S0.3): still readable, refused by writes; removed 0.7.0. */
28
29
  memoryInterval?: number;
30
+ /** Shadowed by the policy snapshot in every shipped composition — configure
31
+ * `reviewSkillInterval` on the `evolution-policy` row instead. Deprecated
32
+ * alias (G0/S0.3): still readable, refused by writes; removed 0.7.0. */
29
33
  skillInterval?: number;
30
34
  /**
31
35
  * Tools the one-shot review subagent may use. Only actually-existing tools
@@ -125,6 +129,30 @@ type ClampedReviewConfig = Config & {
125
129
  skillReviewCompletionMinToolCalls: number;
126
130
  };
127
131
  export declare function clampReviewConfig(rawConfig: Config, ctx: Context): ClampedReviewConfig;
132
+ /** Namespace the review group's user-writable knobs live in (core's PARAM_NAMESPACES). */
133
+ export declare const REVIEW_SETTINGS_NAMESPACE = "evolution-review";
134
+ /** Review behaviour a user may change (G3/S3.1). Field names are the CANONICAL
135
+ * parameter ids from the registry, so the settings document, the params output,
136
+ * the doctor report and the cards all spell one name. */
137
+ export interface ReviewSettings {
138
+ /** Activity units between skill-review injections. */
139
+ reviewSkillInterval: number;
140
+ /** Activity units between memory-review injections. */
141
+ reviewMemoryInterval: number;
142
+ /** Which channel may inject a skill review. */
143
+ skillReviewTrigger: 'cadence' | 'completion' | 'both';
144
+ /** Tool calls a task needs before the completion channel injects. */
145
+ skillReviewCompletionMinToolCalls: number;
146
+ /** Master switch for the review plugin. */
147
+ reviewEnabled: boolean;
148
+ /** Run the review in the parent session (inject) or on a subagent. */
149
+ reviewMode: 'subagent' | 'inject';
150
+ /** Deliver the deferred review as a waking follow-up message. */
151
+ reviewWakeInject: boolean;
152
+ }
153
+ /** Schema the platform validates the user layer against; defaults mirror the
154
+ * core constants so an empty document resolves to today's behaviour. */
155
+ export declare const REVIEW_SETTINGS_SCHEMA: z<ReviewSettings>;
128
156
  export declare function apply(ctx: Context, rawConfig?: Config): void;
129
157
  /** Completion-channel decision: task finished normally AND the session is proven long. */
130
158
  export declare function shouldCompletionReview(reason: {
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.5.0",
4
+ "version": "0.6.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,9 +27,9 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.1",
30
- "@lmzhen/dsh-evolution-approval": "^0.5.0",
31
- "@lmzhen/dsh-evolution-core": "^0.5.0",
32
- "@lmzhen/dsh-evolution-plan-validator": "^0.5.0"
30
+ "@lmzhen/dsh-evolution-approval": "^0.6.1",
31
+ "@lmzhen/dsh-evolution-core": "^0.6.1",
32
+ "@lmzhen/dsh-evolution-plan-validator": "^0.6.1"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@deepseek-ai/cordis": "^4.0.1",
@@ -37,8 +37,8 @@
37
37
  "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
38
38
  "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
39
39
  "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
40
- "@lmzhen/dsh-evolution-state": "^0.5.0",
41
- "@lmzhen/dsh-evolution-policy": "^0.5.0"
40
+ "@lmzhen/dsh-evolution-state": "^0.6.1",
41
+ "@lmzhen/dsh-evolution-policy": "^0.6.1"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
@@ -48,10 +48,10 @@
48
48
  "@deepseek-ai/dsh-session-persistence": "^0.1.5-rc.2",
49
49
  "@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.5-rc.2",
50
50
  "@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
51
- "@lmzhen/dsh-evolution-approval": "^0.5.0",
52
- "@lmzhen/dsh-evolution-core": "^0.5.0",
53
- "@lmzhen/dsh-evolution-curator": "^0.5.0",
54
- "@lmzhen/dsh-evolution-plan-validator": "^0.5.0",
55
- "@lmzhen/dsh-evolution-state": "^0.5.0"
51
+ "@lmzhen/dsh-evolution-approval": "^0.6.1",
52
+ "@lmzhen/dsh-evolution-core": "^0.6.1",
53
+ "@lmzhen/dsh-evolution-curator": "^0.6.1",
54
+ "@lmzhen/dsh-evolution-plan-validator": "^0.6.1",
55
+ "@lmzhen/dsh-evolution-state": "^0.6.1"
56
56
  }
57
57
  }