@lmzhen/dsh-evolution-review 0.3.66 → 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/README.md +1 -1
- package/lib/index.js +168 -63
- package/lib/types/index.d.ts +3 -2
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
25
25
|
- Review subagents run as `spawn` children on the deployment default preset rather than inheriting the parent agent's composition (`fork`): a fork child is always promoted by the Anchored Standard bootstrap and its narrowed resident catalog would drop the plain `skill` tool from the review allow-list.
|
|
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
|
-
- 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.
|
|
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 — bounded by `REVIEW_STATE_SESSION_CAP` (500, seam constant): the least-recently-active sessions are pruned on save, so a very old session restarting resumes from a fresh cadence baseline rather than an unbounded store.
|
|
29
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
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.
|
|
31
31
|
- Read-before-write can see the review subagent's own `skill` reads only when the subagent backend exposes `localAgent` (the in-process driver does; out-of-process backends such as ACP and the CLI providers set `localAgent: undefined`). With a remote backend the subagent's reads are invisible, so a plan item patching a skill the subagent itself loaded is dropped as "unread" — the review then falls back to the parent session's reads only. Documented rather than worked around: recovering the child read set needs a `SubagentLike` contract change (v14 P2-6).
|
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();
|
|
@@ -93,6 +86,16 @@ function apply(ctx, rawConfig = {}) {
|
|
|
93
86
|
const cadenceResetWarned = /* @__PURE__ */ new Set();
|
|
94
87
|
let reviewInFlight = false;
|
|
95
88
|
const policy = () => ctx.get("evolutionPolicy")?.get();
|
|
89
|
+
const reviewStateLocks = /* @__PURE__ */ new Map();
|
|
90
|
+
async function withReviewStateLock(id, task) {
|
|
91
|
+
const next = (reviewStateLocks.get(id) ?? Promise.resolve()).catch(() => {}).then(task);
|
|
92
|
+
reviewStateLocks.set(id, next);
|
|
93
|
+
try {
|
|
94
|
+
return await next;
|
|
95
|
+
} finally {
|
|
96
|
+
if (reviewStateLocks.get(id) === next) reviewStateLocks.delete(id);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
96
99
|
ctx.on("session/event", (session, event) => {
|
|
97
100
|
if (event.type === "turn/start" && session.header.origin !== "subagent") turnStarts.set(session.id, session.seq - 1);
|
|
98
101
|
if (event.type !== "turn/end") return;
|
|
@@ -132,24 +135,32 @@ function apply(ctx, rawConfig = {}) {
|
|
|
132
135
|
statelessReviewStateWarned = true;
|
|
133
136
|
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).");
|
|
134
137
|
}
|
|
135
|
-
const
|
|
138
|
+
const snapshot = policy();
|
|
139
|
+
const skipFire = skipNextCadenceFire.get(session.id) ?? false;
|
|
140
|
+
if (skipFire) skipNextCadenceFire.delete(session.id);
|
|
141
|
+
let state = {
|
|
136
142
|
turnsSinceMemory: 0,
|
|
137
143
|
turnsSinceSkill: 0,
|
|
138
144
|
lastTurn: -1
|
|
139
145
|
};
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
const advanced = { kind: null };
|
|
147
|
+
await withReviewStateLock(session.id, async () => {
|
|
148
|
+
state = await stateService?.loadReviewState(session.id) ?? {
|
|
149
|
+
turnsSinceMemory: 0,
|
|
150
|
+
turnsSinceSkill: 0,
|
|
151
|
+
lastTurn: -1
|
|
152
|
+
};
|
|
153
|
+
advanced.kind = advanceReview(state, event.data.turn, signal, {
|
|
154
|
+
memoryInterval: snapshot?.reviewMemoryInterval ?? config.memoryInterval,
|
|
155
|
+
skillInterval: snapshot?.reviewSkillInterval ?? config.skillInterval,
|
|
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,
|
|
159
|
+
resetOnFire: false
|
|
160
|
+
});
|
|
161
|
+
await stateService?.saveReviewState(session.id, state);
|
|
150
162
|
});
|
|
151
|
-
const kind = skipFire ? null :
|
|
152
|
-
await stateService?.saveReviewState(session.id, state);
|
|
163
|
+
const kind = skipFire ? null : advanced.kind;
|
|
153
164
|
const cumulative = (cumulativeToolCalls.get(session.id) ?? 0) + signal.toolCalls;
|
|
154
165
|
cumulativeToolCalls.set(session.id, cumulative);
|
|
155
166
|
if (event.data.reason.kind === "completed") {
|
|
@@ -158,6 +169,18 @@ function apply(ctx, rawConfig = {}) {
|
|
|
158
169
|
pendingCadenceReviews.delete(session.id);
|
|
159
170
|
if ((policy()?.reviewMode ?? config.reviewMode) === "inject") try {
|
|
160
171
|
deliverMessage(agent, reviewPrompt(pendingKind), "auto-review");
|
|
172
|
+
try {
|
|
173
|
+
ctx.emit("evolution/review-scheduled", {
|
|
174
|
+
sessionId: session.id,
|
|
175
|
+
kind: pendingKind,
|
|
176
|
+
toolCalls: signal.toolCalls,
|
|
177
|
+
userChars: signal.userChars,
|
|
178
|
+
assistantChars: signal.assistantChars,
|
|
179
|
+
channel: "inject"
|
|
180
|
+
});
|
|
181
|
+
} catch (emitError) {
|
|
182
|
+
ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
183
|
+
}
|
|
161
184
|
} catch (injectError) {
|
|
162
185
|
ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
|
|
163
186
|
}
|
|
@@ -169,21 +192,40 @@ function apply(ctx, rawConfig = {}) {
|
|
|
169
192
|
kind: pendingKind,
|
|
170
193
|
toolCalls: signal.toolCalls,
|
|
171
194
|
userChars: signal.userChars,
|
|
172
|
-
assistantChars: signal.assistantChars
|
|
195
|
+
assistantChars: signal.assistantChars,
|
|
196
|
+
channel: "subagent"
|
|
173
197
|
});
|
|
174
198
|
} catch (emitError) {
|
|
175
199
|
ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
176
200
|
}
|
|
177
|
-
else if (reviewOutcome
|
|
201
|
+
else if (reviewOutcome === "dropped") {
|
|
202
|
+
pendingCadenceReviews.set(session.id, pendingKind);
|
|
203
|
+
return;
|
|
204
|
+
} else if (reviewOutcome !== "deferred") try {
|
|
178
205
|
deliverMessage(agent, reviewPrompt(pendingKind), "auto-review");
|
|
206
|
+
try {
|
|
207
|
+
ctx.emit("evolution/review-scheduled", {
|
|
208
|
+
sessionId: session.id,
|
|
209
|
+
kind: pendingKind,
|
|
210
|
+
toolCalls: signal.toolCalls,
|
|
211
|
+
userChars: signal.userChars,
|
|
212
|
+
assistantChars: signal.assistantChars,
|
|
213
|
+
channel: "inject"
|
|
214
|
+
});
|
|
215
|
+
} catch (emitError) {
|
|
216
|
+
ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
217
|
+
}
|
|
179
218
|
} catch (injectError) {
|
|
180
219
|
ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
|
|
181
220
|
}
|
|
182
221
|
}
|
|
183
|
-
state.turnsSinceMemory = 0;
|
|
184
|
-
state.turnsSinceSkill = 0;
|
|
185
222
|
try {
|
|
186
|
-
await
|
|
223
|
+
await withReviewStateLock(session.id, async () => {
|
|
224
|
+
const fresh = await stateService?.loadReviewState(session.id) ?? state;
|
|
225
|
+
fresh.turnsSinceMemory = 0;
|
|
226
|
+
fresh.turnsSinceSkill = 0;
|
|
227
|
+
await stateService?.saveReviewState(session.id, fresh);
|
|
228
|
+
});
|
|
187
229
|
} catch (resetError) {
|
|
188
230
|
if (!cadenceResetWarned.has(session.id)) {
|
|
189
231
|
cadenceResetWarned.add(session.id);
|
|
@@ -207,6 +249,22 @@ function apply(ctx, rawConfig = {}) {
|
|
|
207
249
|
if (completionInjected.has(session.id)) return;
|
|
208
250
|
if (!shouldCompletionReview(event.data.reason, cumulative, config.skillReviewCompletionMinToolCalls)) return;
|
|
209
251
|
completionInjected.add(session.id);
|
|
252
|
+
if (reviewInFlight) {
|
|
253
|
+
if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP) deferredFallbackReviews.push({
|
|
254
|
+
agent,
|
|
255
|
+
sessionId: session.id,
|
|
256
|
+
kind: "skill",
|
|
257
|
+
prompt: COMPLETION_SKILL_REVIEW_PROMPT,
|
|
258
|
+
label: "completion review",
|
|
259
|
+
channel: "completion",
|
|
260
|
+
counts: signal
|
|
261
|
+
});
|
|
262
|
+
else {
|
|
263
|
+
completionInjected.delete(session.id);
|
|
264
|
+
ctx.logger.warn(`dsh-evolution-review: deferred-review queue at cap (${DEFERRED_REVIEW_CAP}) — dropped one deferred completion review prompt; the completion gate re-arms for the next completed turn`);
|
|
265
|
+
}
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
210
268
|
try {
|
|
211
269
|
deliverMessage(agent, COMPLETION_SKILL_REVIEW_PROMPT, "completion review");
|
|
212
270
|
} catch (injectError) {
|
|
@@ -220,7 +278,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
220
278
|
kind: "skill",
|
|
221
279
|
toolCalls: signal.toolCalls,
|
|
222
280
|
userChars: signal.userChars,
|
|
223
|
-
assistantChars: signal.assistantChars
|
|
281
|
+
assistantChars: signal.assistantChars,
|
|
282
|
+
channel: "completion"
|
|
224
283
|
});
|
|
225
284
|
} catch (emitError) {
|
|
226
285
|
ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
@@ -271,17 +330,25 @@ function apply(ctx, rawConfig = {}) {
|
|
|
271
330
|
const subagents = ctx.get("subagents");
|
|
272
331
|
if (!subagents) return false;
|
|
273
332
|
if (reviewInFlight) {
|
|
274
|
-
if (deferredFallbackReviews.length < DEFERRED_REVIEW_CAP)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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";
|
|
280
347
|
}
|
|
281
348
|
reviewInFlight = true;
|
|
282
349
|
try {
|
|
283
350
|
const snapshot = policy();
|
|
284
|
-
const model = kind === "memory" ? snapshot?.memoryReviewModel ??
|
|
351
|
+
const model = kind === "memory" ? snapshot?.memoryReviewModel ?? DEFAULT_MEMORY_REVIEW_MODEL : snapshot?.skillReviewModel ?? DEFAULT_SKILL_REVIEW_MODEL;
|
|
285
352
|
const reviewText = redactSecrets(buildReviewRequest(session, kind, signal, config.reviewContextMessages, config.reviewMessageChars));
|
|
286
353
|
const agentOptions = { model };
|
|
287
354
|
if (config.reviewProvider) agentOptions.provider = config.reviewProvider;
|
|
@@ -323,9 +390,38 @@ function apply(ctx, rawConfig = {}) {
|
|
|
323
390
|
});
|
|
324
391
|
const acceptedSkillOps = validation.accepted.skillOps ?? [];
|
|
325
392
|
const skippedUnread = filterUnreadSkillOps(acceptedSkillOps, new Set([...collectReadSkillNames(session), ...childReads]));
|
|
326
|
-
const executed = await withTimeout(executePlan(validation.accepted, session), config.reviewTimeoutMs, "review plan execution");
|
|
327
|
-
const actions = executed.actions;
|
|
328
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;
|
|
329
425
|
if (actions.length > 0) {
|
|
330
426
|
const applied = actions.join(" · ");
|
|
331
427
|
const note = executed.ok ? "" : "\n部分操作失败。以下操作已应用,请勿重复执行。";
|
|
@@ -347,23 +443,11 @@ function apply(ctx, rawConfig = {}) {
|
|
|
347
443
|
} catch (injectError) {
|
|
348
444
|
ctx.logger.warn(`dsh-evolution-review: zero-landing notice inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
|
|
349
445
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
memoryApplied: actions.filter((action) => action.startsWith("Memory")).length,
|
|
356
|
-
skillApplied: actions.filter((action) => action.startsWith("Skill ")).length,
|
|
357
|
-
rejectedOps: validation.rejected.length + skippedUnread,
|
|
358
|
-
executionFailures: executed.failedOps.length,
|
|
359
|
-
...executed.aborted !== void 0 ? { executionError: executed.aborted } : {},
|
|
360
|
-
...executed.failedOps[0] !== void 0 && executed.aborted === void 0 ? { executionError: executed.failedOps[0] } : {},
|
|
361
|
-
evidenceQuotes,
|
|
362
|
-
estimatedInputChars: reviewText.length
|
|
363
|
-
});
|
|
364
|
-
} catch (emitError) {
|
|
365
|
-
ctx.logger.warn(`dsh-evolution-review: plan-applied emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
366
|
-
}
|
|
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
|
+
});
|
|
367
451
|
return true;
|
|
368
452
|
} finally {
|
|
369
453
|
try {
|
|
@@ -379,14 +463,30 @@ function apply(ctx, rawConfig = {}) {
|
|
|
379
463
|
} finally {
|
|
380
464
|
reviewInFlight = false;
|
|
381
465
|
const deferred = deferredFallbackReviews.splice(0);
|
|
382
|
-
for (const { agent: waitingAgent, kind: waitingKind } of deferred)
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
466
|
+
for (const { agent: waitingAgent, sessionId: entrySession, kind: waitingKind, prompt, label, channel, counts: entryCounts } of deferred) {
|
|
467
|
+
try {
|
|
468
|
+
deliverMessage(waitingAgent, prompt, label);
|
|
469
|
+
} catch (injectError) {
|
|
470
|
+
if (channel === "completion") completionInjected.delete(entrySession);
|
|
471
|
+
ctx.logger.warn(`dsh-evolution-review: deferred review inject failed: ${injectError instanceof Error ? injectError.message : String(injectError)}`);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
ctx.emit("evolution/review-scheduled", {
|
|
476
|
+
sessionId: entrySession,
|
|
477
|
+
kind: waitingKind,
|
|
478
|
+
toolCalls: entryCounts.toolCalls,
|
|
479
|
+
userChars: entryCounts.userChars,
|
|
480
|
+
assistantChars: entryCounts.assistantChars,
|
|
481
|
+
channel
|
|
482
|
+
});
|
|
483
|
+
} catch (emitError) {
|
|
484
|
+
ctx.logger.warn(`dsh-evolution-review: review-scheduled emit failed: ${emitError instanceof Error ? emitError.message : String(emitError)}`);
|
|
485
|
+
}
|
|
386
486
|
}
|
|
387
487
|
}
|
|
388
488
|
}
|
|
389
|
-
async function executePlan(plan, session) {
|
|
489
|
+
async function executePlan(plan, session, onLanded) {
|
|
390
490
|
const sessionId = session?.id;
|
|
391
491
|
const memory = ctx.get("memory");
|
|
392
492
|
const approval = ctx.get("evolutionApproval");
|
|
@@ -413,8 +513,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
413
513
|
old_text: op.old_text
|
|
414
514
|
};
|
|
415
515
|
const result = approval ? await runApproved("memory", `memory ${normalized.target} ${normalized.action}`, normalized, normalized, session) : await memory?.applyBatch(normalized.target, [normalized]);
|
|
416
|
-
if (result?.ok)
|
|
417
|
-
|
|
516
|
+
if (result?.ok) {
|
|
517
|
+
actions.push("Memory updated");
|
|
518
|
+
onLanded?.("Memory updated");
|
|
519
|
+
} else {
|
|
418
520
|
ok = false;
|
|
419
521
|
failedOps.push(`memory ${normalized.action} ${normalized.target}: ${result?.message ?? "service unavailable"}`);
|
|
420
522
|
}
|
|
@@ -438,8 +540,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
438
540
|
origin: origins.library
|
|
439
541
|
};
|
|
440
542
|
const result = approval ? await runApproved("skill", `skill ${op.action ?? "patch"} ${op.name}`, runnerArgs, runnerArgs, session) : await executeSkillDirect(args);
|
|
441
|
-
if (result?.ok)
|
|
442
|
-
|
|
543
|
+
if (result?.ok) {
|
|
544
|
+
actions.push(`Skill ${op.name} ${op.action ?? "patch"}`);
|
|
545
|
+
onLanded?.(`Skill ${op.name} ${op.action ?? "patch"}`);
|
|
546
|
+
} else {
|
|
443
547
|
ok = false;
|
|
444
548
|
failedOps.push(`skill ${op.action ?? "patch"} ${op.name}: ${result?.message ?? "service unavailable"}`);
|
|
445
549
|
}
|
|
@@ -559,6 +663,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
559
663
|
pendingCadenceWarned.clear();
|
|
560
664
|
skipNextCadenceFire.clear();
|
|
561
665
|
cadenceResetWarned.clear();
|
|
666
|
+
reviewStateLocks.clear();
|
|
562
667
|
}, "dsh-evolution-review.cleanup");
|
|
563
668
|
}
|
|
564
669
|
/** Completion-channel decision: task finished normally AND the session is proven long. */
|
package/lib/types/index.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
51
|
-
*
|
|
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.
|
|
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.
|
|
35
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
36
|
-
"@lmzhen/dsh-evolution-plan-validator": "^0.3.
|
|
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.
|
|
46
|
-
"@lmzhen/dsh-evolution-policy": "^0.3.
|
|
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.
|
|
58
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
59
|
-
"@lmzhen/dsh-evolution-plan-validator": "^0.3.
|
|
60
|
-
"@lmzhen/dsh-evolution-state": "^0.3.
|
|
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
|
}
|