@lmzhen/dsh-evolution-review 0.4.1 → 0.6.0

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, 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),
@@ -149,12 +149,26 @@ function clampReviewConfig(rawConfig, ctx) {
149
149
  if (clamped.length > 0) ctx.logger.warn(`dsh-evolution-review: ${clamped.join(", ")} provided an invalid value; falling back to the default`);
150
150
  return config;
151
151
  }
152
- /** v30 REV-04/REV-02: read the policy snapshot off the (optional) policy
153
- * service through an `unknown` boundary — the Context augmentation types the
154
- * getter non-optionally, but at runtime the row can be absent. */
155
152
  function policySnapshotOf(source) {
156
153
  return source?.get?.();
157
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
+ });
158
172
  function apply(ctx, rawConfig = {}) {
159
173
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to schedule review work");
160
174
  const config = clampReviewConfig(rawConfig, ctx);
@@ -173,6 +187,30 @@ function apply(ctx, rawConfig = {}) {
173
187
  const cadenceResetWarned = sessionState.add("cadenceResetWarned", /* @__PURE__ */ new Set());
174
188
  let reviewInFlight = false;
175
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
+ };
176
214
  const schemaDefaults = Config["~standard"].validate({}).value;
177
215
  const shadowedRowFields = [
178
216
  "reviewMode",
@@ -191,7 +229,7 @@ function apply(ctx, rawConfig = {}) {
191
229
  warnShadowed();
192
230
  });
193
231
  }
194
- 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.");
195
233
  const reviewStateLocks = sessionState.add("reviewStateLocks", /* @__PURE__ */ new Map());
196
234
  async function withReviewStateLock(id, task) {
197
235
  const next = (reviewStateLocks.get(id) ?? Promise.resolve()).catch(() => {}).then(task);
@@ -240,7 +278,7 @@ function apply(ctx, rawConfig = {}) {
240
278
  }
241
279
  }
242
280
  async function runOnTurnEnd(session, event) {
243
- if (!config.reviewEnabled) return;
281
+ if (!params().reviewEnabled) return;
244
282
  if (session.header.origin === "subagent") return;
245
283
  const agent = ctx.agents.get(session.id);
246
284
  if (!agent) return;
@@ -272,8 +310,8 @@ function apply(ctx, rawConfig = {}) {
272
310
  lastTurn: -1
273
311
  };
274
312
  advanced.kind = advanceReview(state, event.data.turn, signal, {
275
- memoryInterval: snapshot?.reviewMemoryInterval ?? config.memoryInterval,
276
- skillInterval: snapshot?.reviewSkillInterval ?? config.skillInterval,
313
+ memoryInterval: params().reviewMemoryInterval,
314
+ skillInterval: params().reviewSkillInterval,
277
315
  substantiveMinToolCalls: snapshot?.substantiveMinToolCalls ?? DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS,
278
316
  substantiveMinUserChars: snapshot?.substantiveMinUserChars ?? DEFAULT_SUBSTANTIVE_MIN_USER_CHARS,
279
317
  substantiveMinAgentChars: snapshot?.substantiveMinAgentChars ?? DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS,
@@ -288,7 +326,7 @@ function apply(ctx, rawConfig = {}) {
288
326
  const pendingKind = kind ?? pendingCadenceReviews.get(session.id) ?? void 0;
289
327
  if (pendingKind !== void 0) {
290
328
  pendingCadenceReviews.delete(session.id);
291
- if ((policy()?.reviewMode ?? config.reviewMode) === "inject") {
329
+ if (params().reviewMode === "inject") {
292
330
  if (!deliverMessage(agent, reviewPrompt(pendingKind), cadenceSummary(pendingKind), true)) {
293
331
  pendingCadenceReviews.set(session.id, pendingKind);
294
332
  return;
@@ -366,10 +404,10 @@ function apply(ctx, rawConfig = {}) {
366
404
  return;
367
405
  }
368
406
  if (skipFire) return;
369
- const trigger = config.skillReviewTrigger;
407
+ const trigger = params().skillReviewTrigger;
370
408
  if (trigger !== "completion" && trigger !== "both") return;
371
409
  if (completionInjected.has(session.id)) return;
372
- if (!shouldCompletionReview(event.data.reason, cumulative, config.skillReviewCompletionMinToolCalls)) return;
410
+ if (!shouldCompletionReview(event.data.reason, cumulative, params().skillReviewCompletionMinToolCalls)) return;
373
411
  completionInjected.add(session.id);
374
412
  if (reviewInFlight) {
375
413
  if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) deferredFallbackReviews.push({
@@ -486,7 +524,7 @@ function apply(ctx, rawConfig = {}) {
486
524
  ctx.logger.warn(`dsh-evolution-review: inbox coalescing failed (${error instanceof Error ? error.message : String(error)}) — delivering a fresh message instead`);
487
525
  }
488
526
  try {
489
- if (config.reviewWakeInject && typeof wake.followup === "function") {
527
+ if (params().reviewWakeInject && typeof wake.followup === "function") {
490
528
  wake.followup(message);
491
529
  skipNextCadenceFire.set(agent.session.id, { afterTurn: lastTurnStart.get(agent.session.id) ?? -1 });
492
530
  } else agent.inject(message);
@@ -560,7 +598,7 @@ function apply(ctx, rawConfig = {}) {
560
598
  return hashes;
561
599
  }
562
600
  async function trySubagentReview(session, agent, kind, signal) {
563
- if ((policy()?.reviewMode ?? config.reviewMode) === "inject") return false;
601
+ if (params().reviewMode === "inject") return false;
564
602
  const subagents = ctx.get("subagents");
565
603
  if (!subagents) return false;
566
604
  if (reviewInFlight) {
@@ -904,6 +942,7 @@ function apply(ctx, rawConfig = {}) {
904
942
  io: evolutionIoAdapter(() => io.provider()),
905
943
  limits: {
906
944
  ...DEFAULT_SKILL_LIMITS,
945
+ ...policyStageLimits(policySnapshot),
907
946
  maxSkillContentChars: policySnapshot?.skillContentChars ?? DEFAULT_SKILL_LIMITS.maxSkillContentChars
908
947
  },
909
948
  ctx
@@ -1234,4 +1273,4 @@ function buildReviewRequest(session, kind, signal, maxMessages, maxMessageChars)
1234
1273
  ].join("\n");
1235
1274
  }
1236
1275
  //#endregion
1237
- 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.4.1",
4
+ "version": "0.6.0",
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.4.1",
31
- "@lmzhen/dsh-evolution-core": "^0.4.1",
32
- "@lmzhen/dsh-evolution-plan-validator": "^0.4.1"
30
+ "@lmzhen/dsh-evolution-approval": "^0.6.0",
31
+ "@lmzhen/dsh-evolution-core": "^0.6.0",
32
+ "@lmzhen/dsh-evolution-plan-validator": "^0.6.0"
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.4.1",
41
- "@lmzhen/dsh-evolution-policy": "^0.4.1"
40
+ "@lmzhen/dsh-evolution-state": "^0.6.0",
41
+ "@lmzhen/dsh-evolution-policy": "^0.6.0"
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.4.1",
52
- "@lmzhen/dsh-evolution-core": "^0.4.1",
53
- "@lmzhen/dsh-evolution-curator": "^0.4.1",
54
- "@lmzhen/dsh-evolution-plan-validator": "^0.4.1",
55
- "@lmzhen/dsh-evolution-state": "^0.4.1"
51
+ "@lmzhen/dsh-evolution-approval": "^0.6.0",
52
+ "@lmzhen/dsh-evolution-core": "^0.6.0",
53
+ "@lmzhen/dsh-evolution-curator": "^0.6.0",
54
+ "@lmzhen/dsh-evolution-plan-validator": "^0.6.0",
55
+ "@lmzhen/dsh-evolution-state": "^0.6.0"
56
56
  }
57
57
  }