@lmzhen/dsh-evolution-review 0.4.0 → 0.5.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/lib/index.js +69 -22
- package/lib/types/index.d.ts +21 -12
- package/package.json +11 -11
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, 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";
|
|
6
6
|
import { validateEvolutionPlan } from "@lmzhen/dsh-evolution-plan-validator";
|
|
7
7
|
//#region lib/types/session-state.js
|
|
8
8
|
/**
|
|
@@ -149,9 +149,6 @@ 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
|
}
|
|
@@ -213,7 +210,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
213
210
|
return;
|
|
214
211
|
}
|
|
215
212
|
if (event.type !== "turn/end") return;
|
|
216
|
-
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 || lastTurnStart.size >= COUNTER_SWEEP_THRESHOLD) {
|
|
213
|
+
if (turnStarts.size >= COUNTER_SWEEP_THRESHOLD || pendingCadenceWarned.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 || lastTurnStart.size >= COUNTER_SWEEP_THRESHOLD) {
|
|
217
214
|
const isAlive = (id) => ctx.agents.get(id) !== void 0;
|
|
218
215
|
sweepDeadSessionEntries(turnStarts, isAlive);
|
|
219
216
|
sweepDeadSessionEntries(lastTurnStart, isAlive);
|
|
@@ -904,6 +901,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
904
901
|
io: evolutionIoAdapter(() => io.provider()),
|
|
905
902
|
limits: {
|
|
906
903
|
...DEFAULT_SKILL_LIMITS,
|
|
904
|
+
...policyStageLimits(policySnapshot),
|
|
907
905
|
maxSkillContentChars: policySnapshot?.skillContentChars ?? DEFAULT_SKILL_LIMITS.maxSkillContentChars
|
|
908
906
|
},
|
|
909
907
|
ctx
|
|
@@ -1113,24 +1111,56 @@ function resultCallIdOf(event) {
|
|
|
1113
1111
|
return null;
|
|
1114
1112
|
}
|
|
1115
1113
|
/**
|
|
1116
|
-
* V10-10 (P2-11): render one `[result]` evidence line from
|
|
1117
|
-
* event payload. The former read (`data.output`) targeted a
|
|
1118
|
-
* not exist on the
|
|
1119
|
-
*
|
|
1120
|
-
*
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
*
|
|
1114
|
+
* V10-10 (P2-11) / A1 (audit P1-1): render one `[result]` evidence line from
|
|
1115
|
+
* a tool-result event payload. The former read (`data.output`) targeted a
|
|
1116
|
+
* field that does not exist on the rc.2 payload; the current contract covers
|
|
1117
|
+
* BOTH rc.2 result shapes:
|
|
1118
|
+
* - native `tool/result`: the outcome lives in `message.content`
|
|
1119
|
+
* tool-result blocks (inner text blocks joined); a failure is marked by
|
|
1120
|
+
* the payload-level `error` OR a block-level `isError`;
|
|
1121
|
+
* - PTC `tool/ptc-dispatch` settle: the outcome lives at the TOP level —
|
|
1122
|
+
* `content` is the logged ContentBlock list and `isError` the flag; there
|
|
1123
|
+
* is no `message` wrapper. (Before A1 this shape rendered an empty line,
|
|
1124
|
+
* so every PTC session's evidence block starved while its plan prompt
|
|
1125
|
+
* still demanded evidence.)
|
|
1126
|
+
* The legacy pre-rc.2 shape (neither `message` nor a PTC settle marker) is
|
|
1127
|
+
* tolerated as an empty payload — it never throws. `identity`, when given, is
|
|
1128
|
+
* the dispatched tool's name + raw arguments, prepended so a settled dispatch
|
|
1129
|
+
* keeps the call identity its `[call]` line would have had. Budget: 500 chars
|
|
1130
|
+
* per line (the 12-line cap lives in buildReviewRequest and is unchanged).
|
|
1127
1131
|
*/
|
|
1128
|
-
function renderToolResultLine(data) {
|
|
1132
|
+
function renderToolResultLine(data, identity) {
|
|
1129
1133
|
const shape = data;
|
|
1130
|
-
const
|
|
1131
|
-
const resultBlocks =
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
+
const nativeContent = shape?.message?.content;
|
|
1135
|
+
const resultBlocks = Array.isArray(nativeContent) ? nativeContent.filter((block) => block.type === "tool-result") : [];
|
|
1136
|
+
let output;
|
|
1137
|
+
let failed;
|
|
1138
|
+
if (shape?.message !== void 0) {
|
|
1139
|
+
output = resultBlocks.map((block) => Array.isArray(block.content) ? block.content.map((inner) => inner.type === "text" && typeof inner.text === "string" ? inner.text : "").join(" ") : typeof block.text === "string" ? block.text : "").join(" ").trim();
|
|
1140
|
+
failed = Boolean(shape.error) || resultBlocks.some((block) => block.isError === true);
|
|
1141
|
+
} else if (typeof shape?.subCallId === "string" && typeof shape.isError === "boolean") {
|
|
1142
|
+
output = textOfLoggedContent(shape.content);
|
|
1143
|
+
failed = shape.isError;
|
|
1144
|
+
} else {
|
|
1145
|
+
output = "";
|
|
1146
|
+
failed = false;
|
|
1147
|
+
}
|
|
1148
|
+
const head = identity === void 0 ? "" : `${identity.name} ${identity.argsRaw.slice(0, 200)} → `;
|
|
1149
|
+
return `[result]${failed ? " [ERROR]" : ""} ${head}${output.slice(0, 500)}`;
|
|
1150
|
+
}
|
|
1151
|
+
/** Text of a PTC settle `content` payload (the logged ContentBlock list):
|
|
1152
|
+
* text blocks and plain strings joined, anything else skipped. */
|
|
1153
|
+
function textOfLoggedContent(content) {
|
|
1154
|
+
if (typeof content === "string") return content.trim();
|
|
1155
|
+
if (!Array.isArray(content)) return "";
|
|
1156
|
+
return content.map((block) => {
|
|
1157
|
+
if (typeof block === "string") return block;
|
|
1158
|
+
if (block !== null && typeof block === "object") {
|
|
1159
|
+
const candidate = block;
|
|
1160
|
+
if (candidate.type === "text" && typeof candidate.text === "string") return candidate.text;
|
|
1161
|
+
}
|
|
1162
|
+
return "";
|
|
1163
|
+
}).join(" ").trim();
|
|
1134
1164
|
}
|
|
1135
1165
|
/**
|
|
1136
1166
|
* PLAN S4.1 (2026-09-16, audit P2-12): text of one persisted content block,
|
|
@@ -1157,6 +1187,17 @@ function buildReviewRequest(session, kind, signal, maxMessages, maxMessageChars)
|
|
|
1157
1187
|
}
|
|
1158
1188
|
const toolLines = [];
|
|
1159
1189
|
const events = session.snapshotEvents();
|
|
1190
|
+
const callIdentity = /* @__PURE__ */ new Map();
|
|
1191
|
+
const identityWindowStart = Math.max(0, events.length - 2e3);
|
|
1192
|
+
for (let index = events.length - 1; index >= identityWindowStart; index -= 1) {
|
|
1193
|
+
const event = events[index];
|
|
1194
|
+
const opened = readDispatchSignal(event);
|
|
1195
|
+
if (opened === null || callIdentity.has(opened.callId)) continue;
|
|
1196
|
+
callIdentity.set(opened.callId, {
|
|
1197
|
+
name: opened.name,
|
|
1198
|
+
argsRaw: typeof opened.arguments === "string" ? opened.arguments : JSON.stringify(opened.arguments ?? {})
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1160
1201
|
const openedCallIds = /* @__PURE__ */ new Set();
|
|
1161
1202
|
for (let index = events.length - 1; index >= 0 && toolLines.length < 12; index -= 1) {
|
|
1162
1203
|
const event = events[index];
|
|
@@ -1164,7 +1205,13 @@ function buildReviewRequest(session, kind, signal, maxMessages, maxMessageChars)
|
|
|
1164
1205
|
if (answeredCallId !== null) {
|
|
1165
1206
|
if (openedCallIds.has(answeredCallId)) continue;
|
|
1166
1207
|
openedCallIds.add(answeredCallId);
|
|
1167
|
-
|
|
1208
|
+
const rawSettle = event?.data;
|
|
1209
|
+
const settleData = rawSettle !== null && typeof rawSettle === "object" ? rawSettle : void 0;
|
|
1210
|
+
const identity = settleData !== void 0 && typeof settleData.name === "string" && settleData.name !== "" ? {
|
|
1211
|
+
name: settleData.name,
|
|
1212
|
+
argsRaw: typeof settleData.arguments === "string" ? settleData.arguments : JSON.stringify(settleData.arguments ?? {})
|
|
1213
|
+
} : callIdentity.get(answeredCallId);
|
|
1214
|
+
toolLines.push(renderToolResultLine(event?.data, identity));
|
|
1168
1215
|
continue;
|
|
1169
1216
|
}
|
|
1170
1217
|
const opened = readDispatchSignal(event);
|
package/lib/types/index.d.ts
CHANGED
|
@@ -151,19 +151,28 @@ export declare function filterUnreadSkillOps(ops: Array<{
|
|
|
151
151
|
name?: string;
|
|
152
152
|
}>, readNames: ReadonlySet<string>): number;
|
|
153
153
|
/**
|
|
154
|
-
* V10-10 (P2-11): render one `[result]` evidence line from
|
|
155
|
-
* event payload. The former read (`data.output`) targeted a
|
|
156
|
-
* not exist on the
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
154
|
+
* V10-10 (P2-11) / A1 (audit P1-1): render one `[result]` evidence line from
|
|
155
|
+
* a tool-result event payload. The former read (`data.output`) targeted a
|
|
156
|
+
* field that does not exist on the rc.2 payload; the current contract covers
|
|
157
|
+
* BOTH rc.2 result shapes:
|
|
158
|
+
* - native `tool/result`: the outcome lives in `message.content`
|
|
159
|
+
* tool-result blocks (inner text blocks joined); a failure is marked by
|
|
160
|
+
* the payload-level `error` OR a block-level `isError`;
|
|
161
|
+
* - PTC `tool/ptc-dispatch` settle: the outcome lives at the TOP level —
|
|
162
|
+
* `content` is the logged ContentBlock list and `isError` the flag; there
|
|
163
|
+
* is no `message` wrapper. (Before A1 this shape rendered an empty line,
|
|
164
|
+
* so every PTC session's evidence block starved while its plan prompt
|
|
165
|
+
* still demanded evidence.)
|
|
166
|
+
* The legacy pre-rc.2 shape (neither `message` nor a PTC settle marker) is
|
|
167
|
+
* tolerated as an empty payload — it never throws. `identity`, when given, is
|
|
168
|
+
* the dispatched tool's name + raw arguments, prepended so a settled dispatch
|
|
169
|
+
* keeps the call identity its `[call]` line would have had. Budget: 500 chars
|
|
170
|
+
* per line (the 12-line cap lives in buildReviewRequest and is unchanged).
|
|
165
171
|
*/
|
|
166
|
-
export declare function renderToolResultLine(data: unknown
|
|
172
|
+
export declare function renderToolResultLine(data: unknown, identity?: {
|
|
173
|
+
name: string;
|
|
174
|
+
argsRaw: string;
|
|
175
|
+
}): string;
|
|
167
176
|
export declare function buildReviewRequest(session: Session, kind: ReviewKind, signal: {
|
|
168
177
|
toolCalls: number;
|
|
169
178
|
userChars: number;
|
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
|
+
"version": "0.5.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.
|
|
31
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
32
|
-
"@lmzhen/dsh-evolution-plan-validator": "^0.
|
|
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"
|
|
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.
|
|
41
|
-
"@lmzhen/dsh-evolution-policy": "^0.
|
|
40
|
+
"@lmzhen/dsh-evolution-state": "^0.5.0",
|
|
41
|
+
"@lmzhen/dsh-evolution-policy": "^0.5.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.
|
|
52
|
-
"@lmzhen/dsh-evolution-core": "^0.
|
|
53
|
-
"@lmzhen/dsh-evolution-curator": "^0.
|
|
54
|
-
"@lmzhen/dsh-evolution-plan-validator": "^0.
|
|
55
|
-
"@lmzhen/dsh-evolution-state": "^0.
|
|
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"
|
|
56
56
|
}
|
|
57
57
|
}
|