@lmzhen/dsh-evolution-review 0.3.69 → 0.3.71
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 +53 -39
- package/package.json +24 -23
package/lib/index.js
CHANGED
|
@@ -342,13 +342,10 @@ function apply(ctx, rawConfig = {}) {
|
|
|
342
342
|
const io = ctx.get("evolutionIo");
|
|
343
343
|
if (!io) return hashes;
|
|
344
344
|
const library = new SkillLibrary(resolveSkillsRoot({ root: rootConfig.root }), evolutionIoAdapter(() => io.provider()));
|
|
345
|
-
for (const summary of await library.list().catch((error) => {
|
|
345
|
+
for (const summary of await library.list({ withContent: true }).catch((error) => {
|
|
346
346
|
ctx.logger.warn(`dsh-evolution-review: skill tree scan failed (${error instanceof Error ? error.message : String(error)}) — pre-run staleness hashes are unavailable; full-content updates will not be drift-checked this review`);
|
|
347
347
|
return [];
|
|
348
|
-
}))
|
|
349
|
-
const text = await library.read(summary.name).catch(() => null);
|
|
350
|
-
if (text !== null) hashes.set(summary.name, contentHash(text));
|
|
351
|
-
}
|
|
348
|
+
})) if (summary.content !== void 0) hashes.set(summary.name, contentHash(summary.content));
|
|
352
349
|
return hashes;
|
|
353
350
|
}
|
|
354
351
|
async function trySubagentReview(session, agent, kind, signal) {
|
|
@@ -559,8 +556,9 @@ function apply(ctx, rawConfig = {}) {
|
|
|
559
556
|
...op,
|
|
560
557
|
evidence: op.evidence
|
|
561
558
|
};
|
|
559
|
+
let stageCurrent = null;
|
|
562
560
|
if ((args.action === "update" || args.action === "edit") && hashLibrary && typeof args.name === "string" && args.name !== "") {
|
|
563
|
-
|
|
561
|
+
stageCurrent = await hashLibrary.read(args.name).catch(() => null);
|
|
564
562
|
if (stageCurrent !== null) args.staged_from_sha256 = contentHash(stageCurrent);
|
|
565
563
|
}
|
|
566
564
|
if ((args.action === "write_file" || args.action === "remove_file") && hashLibrary && typeof args.name === "string" && args.name !== "" && typeof args.file_path === "string" && args.file_path !== "") {
|
|
@@ -570,7 +568,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
570
568
|
const opName = typeof args.name === "string" ? args.name : "";
|
|
571
569
|
const hashChecked = (args.action === "update" || args.action === "edit") && opName !== "" && preRunHashes?.has(opName) === true;
|
|
572
570
|
if (hashChecked) {
|
|
573
|
-
const live =
|
|
571
|
+
const live = stageCurrent;
|
|
574
572
|
const preRun = preRunHashes.get(opName);
|
|
575
573
|
if (live === null || preRun !== void 0 && contentHash(live) !== preRun) {
|
|
576
574
|
ok = false;
|
|
@@ -679,32 +677,14 @@ function apply(ctx, rawConfig = {}) {
|
|
|
679
677
|
ok: false,
|
|
680
678
|
message: `Skill "${name}" is protected by the current policy (protectedSkillNames); autonomous writes are refused.`
|
|
681
679
|
};
|
|
682
|
-
if (op.action === "write_file" || op.action === "remove_file") {
|
|
683
|
-
const expected = op.staged_from_sha256;
|
|
684
|
-
if (typeof expected === "string" && expected !== "") {
|
|
685
|
-
const current = await library.readSupportFile(name, op.file_path ?? "").catch(() => void 0);
|
|
686
|
-
const actual = current === void 0 || current === null ? "absent" : contentHash(current);
|
|
687
|
-
if (current === void 0 || actual !== expected) return {
|
|
688
|
-
ok: false,
|
|
689
|
-
message: `Support file "${op.file_path ?? ""}" of "${name}" changed since this plan was produced — the staged file operation was refused as stale. Re-read the skill tree and produce a fresh plan.`
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
680
|
if (op.action === "create") {
|
|
694
681
|
const created = await library.create(name, op.content ?? "", origin);
|
|
695
682
|
if (created.ok) await ctx.get("skillUsage")?.markAgentCreated?.(name);
|
|
696
683
|
return created;
|
|
697
684
|
}
|
|
698
685
|
if (op.action === "edit" || op.action === "update") {
|
|
699
|
-
const
|
|
700
|
-
if (
|
|
701
|
-
const current = await library.read(name).catch(() => null);
|
|
702
|
-
if (current !== null && contentHash(current) !== expected) return {
|
|
703
|
-
ok: false,
|
|
704
|
-
message: `Skill "${name}" changed since this plan was produced — the full-content update was refused as stale. Re-read the skill and produce a fresh plan.`
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
const updated = await library.update(name, op.content ?? "", origin);
|
|
686
|
+
const updated = await library.update(name, op.content ?? "", origin, anchorOf(op));
|
|
687
|
+
if (updated.stale === true) return staleRefusal(updated, name, op.file_path);
|
|
708
688
|
if (updated.ok) await ctx.get("skillUsage")?.record?.(name, "patch").catch(() => {});
|
|
709
689
|
return updated;
|
|
710
690
|
}
|
|
@@ -724,17 +704,14 @@ function apply(ctx, rawConfig = {}) {
|
|
|
724
704
|
return archived;
|
|
725
705
|
}
|
|
726
706
|
if (op.action === "write_file" || op.action === "remove_file") {
|
|
727
|
-
const
|
|
728
|
-
if (
|
|
729
|
-
const
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
ok: false,
|
|
733
|
-
message: `Support file "${op.file_path ?? ""}" of "${name}" changed since this plan was produced — the staged file operation was refused as stale. Re-read the skill tree and produce a fresh plan.`
|
|
734
|
-
};
|
|
707
|
+
const anchor = anchorOf(op);
|
|
708
|
+
if (op.action === "write_file") {
|
|
709
|
+
const written = await library.writeSupportFile(name, op.file_path ?? "", op.file_content ?? "", origin, anchor);
|
|
710
|
+
if (written.stale === true) return staleRefusal(written, name, op.file_path);
|
|
711
|
+
return written;
|
|
735
712
|
}
|
|
736
|
-
|
|
737
|
-
|
|
713
|
+
const removedSupport = await library.removeSupportFile(name, op.file_path ?? "", origin, anchor);
|
|
714
|
+
if (removedSupport.stale === true) return staleRefusal(removedSupport, name, op.file_path);
|
|
738
715
|
if (removedSupport.ok) await ctx.get("skillUsage")?.record?.(name, "patch").catch(() => {});
|
|
739
716
|
return removedSupport;
|
|
740
717
|
}
|
|
@@ -771,10 +748,47 @@ function shouldCompletionReview(reason, sessionToolCalls, minToolCalls) {
|
|
|
771
748
|
* `skill_search` discovery pair, so that branch was dead. `skill_manage` has no
|
|
772
749
|
* per-skill read action (its `list`/`review` are whole-library), so a specific
|
|
773
750
|
* skill read through it cannot be tracked — see README Known Limitations. */
|
|
751
|
+
/**
|
|
752
|
+
* v35 C9: the staging anchor of one accepted plan op, in the shape the library
|
|
753
|
+
* verifies against the bytes it reads under the write's own lock. The `absent`
|
|
754
|
+
* sentinel is the same one the tool's staging attaches for a support file that
|
|
755
|
+
* did not exist when the plan was produced.
|
|
756
|
+
* The staged field rides OUTSIDE the plan schema (the staging channel attaches
|
|
757
|
+
* it to the op payload), so it is read defensively rather than typed on SkillOp.
|
|
758
|
+
* @param op - one accepted plan op.
|
|
759
|
+
* @returns the anchor, or `undefined` when the op carries none.
|
|
760
|
+
*/
|
|
761
|
+
function anchorOf(op) {
|
|
762
|
+
const expected = op.staged_from_sha256;
|
|
763
|
+
if (typeof expected !== "string" || expected === "") return void 0;
|
|
764
|
+
return expected === "absent" ? { absent: true } : { sha256: expected };
|
|
765
|
+
}
|
|
766
|
+
/**
|
|
767
|
+
* Translate the library's anchor refusal into this channel's staging wording.
|
|
768
|
+
* The library reports WHICH drift it saw (drift / missing / unverifiable); the
|
|
769
|
+
* vocabulary ("this plan was produced", "produce a fresh plan") is the review
|
|
770
|
+
* channel's. A body op whose skill vanished keeps the historical not-found
|
|
771
|
+
* report: the former lock-free check only refused on a DRIFTED body, so a
|
|
772
|
+
* deleted target fell through to the write's own not-found result.
|
|
773
|
+
* @param result - the library's stale result.
|
|
774
|
+
* @param name - the skill the op targets.
|
|
775
|
+
* @param filePath - the support-file path for file ops; omitted for body ops.
|
|
776
|
+
* @returns the refusal to return from the direct path.
|
|
777
|
+
*/
|
|
778
|
+
function staleRefusal(result, name, filePath) {
|
|
779
|
+
if (filePath === void 0 && result.anchor === "missing") return {
|
|
780
|
+
ok: false,
|
|
781
|
+
message: `Skill "${name}" not found.`
|
|
782
|
+
};
|
|
783
|
+
return {
|
|
784
|
+
ok: false,
|
|
785
|
+
message: filePath === void 0 ? `Skill "${name}" changed since this plan was produced — the full-content update was refused as stale. Re-read the skill and produce a fresh plan.` : `Support file "${filePath}" of "${name}" changed since this plan was produced — the staged file operation was refused as stale. Re-read the skill tree and produce a fresh plan.`
|
|
786
|
+
};
|
|
787
|
+
}
|
|
774
788
|
function collectReadSkillNames(session) {
|
|
775
789
|
const callNames = /* @__PURE__ */ new Map();
|
|
776
790
|
const okCallIds = /* @__PURE__ */ new Set();
|
|
777
|
-
for (const event of session.
|
|
791
|
+
for (const event of session.snapshotEvents()) if (event.type === "tool/call") {
|
|
778
792
|
if (event.data.name !== "skill") continue;
|
|
779
793
|
const raw = event.data.arguments;
|
|
780
794
|
let parsed = {};
|
|
@@ -880,7 +894,7 @@ function buildReviewRequest(session, kind, signal, maxMessages, maxMessageChars)
|
|
|
880
894
|
if (text) messages.push(`${message.role.toUpperCase()}: ${text.slice(0, maxMessageChars)}`);
|
|
881
895
|
}
|
|
882
896
|
const toolLines = [];
|
|
883
|
-
const events = session.
|
|
897
|
+
const events = session.snapshotEvents();
|
|
884
898
|
for (let index = events.length - 1; index >= 0 && toolLines.length < 12; index -= 1) {
|
|
885
899
|
const event = events[index];
|
|
886
900
|
if (event?.type === "tool/call") {
|
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.71",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,32 +31,33 @@
|
|
|
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.71",
|
|
35
|
+
"@lmzhen/dsh-evolution-core": "^0.3.71",
|
|
36
|
+
"@lmzhen/dsh-evolution-plan-validator": "^0.3.71"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
41
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
42
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
43
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
44
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
45
|
-
"@lmzhen/dsh-evolution-state": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-evolution-policy": "^0.3.
|
|
40
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
|
|
41
|
+
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
42
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-session": "^0.1.5-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
|
|
45
|
+
"@lmzhen/dsh-evolution-state": "^0.3.71",
|
|
46
|
+
"@lmzhen/dsh-evolution-policy": "^0.3.71"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@deepseek-ai/dsh-agent": "^0.1.
|
|
50
|
-
"@deepseek-ai/dsh-agent-loop-testkit": "^0.1.
|
|
51
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
52
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
53
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
54
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
55
|
-
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
57
|
-
"@lmzhen/dsh-evolution-approval": "^0.3.
|
|
58
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
59
|
-
"@lmzhen/dsh-evolution-
|
|
60
|
-
"@lmzhen/dsh-evolution-
|
|
49
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "^0.1.5-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-invariants": "^0.1.5-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-session": "^0.1.5-rc.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.5-rc.2",
|
|
55
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.5-rc.2",
|
|
56
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
|
|
57
|
+
"@lmzhen/dsh-evolution-approval": "^0.3.71",
|
|
58
|
+
"@lmzhen/dsh-evolution-core": "^0.3.71",
|
|
59
|
+
"@lmzhen/dsh-evolution-curator": "^0.3.71",
|
|
60
|
+
"@lmzhen/dsh-evolution-plan-validator": "^0.3.71",
|
|
61
|
+
"@lmzhen/dsh-evolution-state": "^0.3.71"
|
|
61
62
|
}
|
|
62
63
|
}
|