@lmzhen/dsh-evolution-approval 0.3.16 → 0.3.18
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 +60 -17
- package/lib/types/index.d.ts +20 -2
- package/package.json +8 -8
package/lib/index.js
CHANGED
|
@@ -52,8 +52,18 @@ var EvolutionApproval = class extends Service {
|
|
|
52
52
|
hasRunner(kind) {
|
|
53
53
|
return this.runners.has(kind);
|
|
54
54
|
}
|
|
55
|
-
/**
|
|
56
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Trusted plan-executor entry point: replay a write through the registered
|
|
57
|
+
* runner exactly once. 0.3.17 (S3.2, E-23): this is the BACKGROUND-REVIEW
|
|
58
|
+
* replay channel only — the caller must declare the intent (the review
|
|
59
|
+
* pipeline is the sole production consumer); a bare call is refused so the
|
|
60
|
+
* staging boundary cannot be bypassed by accident from another surface.
|
|
61
|
+
*/
|
|
62
|
+
async run(kind, args, intent) {
|
|
63
|
+
if (intent?.interface !== "background_review") return {
|
|
64
|
+
ok: false,
|
|
65
|
+
message: "approval.run is the background-review replay channel; direct execution must use the tool path (staging is the only gate bypass for foreground)."
|
|
66
|
+
};
|
|
57
67
|
const runner = this.runners.get(kind);
|
|
58
68
|
if (!runner) return {
|
|
59
69
|
ok: false,
|
|
@@ -68,7 +78,7 @@ var EvolutionApproval = class extends Service {
|
|
|
68
78
|
action: "allow",
|
|
69
79
|
message: "Approval disabled."
|
|
70
80
|
};
|
|
71
|
-
if (input.sessionPolicy === "never") return {
|
|
81
|
+
if ((this.deriveSessionPolicy(input.sessionId) ?? (this.ctx.get("approval") ? void 0 : input.sessionPolicy)) === "never") return {
|
|
72
82
|
action: "allow",
|
|
73
83
|
message: "Session approval policy is \"never\"; write allowed without staging."
|
|
74
84
|
};
|
|
@@ -80,7 +90,9 @@ var EvolutionApproval = class extends Service {
|
|
|
80
90
|
summary,
|
|
81
91
|
args: input.args,
|
|
82
92
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
83
|
-
status: "pending"
|
|
93
|
+
status: "pending",
|
|
94
|
+
origin: input.origin,
|
|
95
|
+
...input.sessionId ? { sessionId: input.sessionId } : {}
|
|
84
96
|
};
|
|
85
97
|
await this.state().savePending(record);
|
|
86
98
|
return {
|
|
@@ -103,21 +115,46 @@ var EvolutionApproval = class extends Service {
|
|
|
103
115
|
async reject(id) {
|
|
104
116
|
return await this.dedupe(`reject:${id}`, async () => {
|
|
105
117
|
const claimId = randomUUID();
|
|
106
|
-
if (!await this.state().claimPending(id, claimId))
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
118
|
+
if (!await this.state().claimPending(id, claimId)) {
|
|
119
|
+
if ((await this.state().listPending("executing")).find((item) => item.id === id)) return (await this.state().tryResolvePending(id, "rejected")).applied ? {
|
|
120
|
+
ok: true,
|
|
121
|
+
message: `Rejected executing write "${id}" (crashed approve cleaned up; verify the write state manually).`
|
|
122
|
+
} : {
|
|
123
|
+
ok: false,
|
|
124
|
+
message: `Pending write "${id}" could not be rejected: it resolved concurrently.`
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
ok: false,
|
|
128
|
+
message: `Pending write "${id}" is already being resolved by another writer.`
|
|
129
|
+
};
|
|
130
|
+
}
|
|
110
131
|
const resolution = await this.state().tryResolvePending(id, "rejected");
|
|
111
|
-
if (!resolution.applied || !resolution.record)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
if (!resolution.applied || !resolution.record) {
|
|
133
|
+
await this.state().releasePendingClaim(id, claimId);
|
|
134
|
+
return {
|
|
135
|
+
ok: false,
|
|
136
|
+
message: `Pending write "${id}" is not pending (already resolved or missing).`
|
|
137
|
+
};
|
|
138
|
+
}
|
|
115
139
|
return {
|
|
116
140
|
ok: true,
|
|
117
141
|
message: `Rejected ${resolution.record.kind} write "${id}".`
|
|
118
142
|
};
|
|
119
143
|
});
|
|
120
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* 0.3.17 (S3.1): platform-side session policy derivation — the platform's
|
|
147
|
+
* approval service (`overrideOf`) is the authority; a caller's self-reported
|
|
148
|
+
* sessionPolicy is a fallback only without it (E-22). The mount check is
|
|
149
|
+
* lazy: the platform service can start before or after this plugin.
|
|
150
|
+
*/
|
|
151
|
+
deriveSessionPolicy(sessionId) {
|
|
152
|
+
if (!sessionId) return void 0;
|
|
153
|
+
const platformApproval = this.ctx.get("approval");
|
|
154
|
+
if (!platformApproval) return void 0;
|
|
155
|
+
const override = platformApproval.overrideOf?.(sessionId);
|
|
156
|
+
return override === "never" || override === "ask" ? override : void 0;
|
|
157
|
+
}
|
|
121
158
|
dedupe(id, task) {
|
|
122
159
|
const existing = this.inFlight.get(id);
|
|
123
160
|
if (existing) return existing;
|
|
@@ -130,10 +167,16 @@ var EvolutionApproval = class extends Service {
|
|
|
130
167
|
async doApprove(id) {
|
|
131
168
|
const claimId = randomUUID();
|
|
132
169
|
const record = await this.state().claimPending(id, claimId);
|
|
133
|
-
if (!record)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
170
|
+
if (!record) {
|
|
171
|
+
if ((await this.state().listPending("executing")).find((item) => item.id === id)) return {
|
|
172
|
+
ok: false,
|
|
173
|
+
message: `Pending write "${id}" is executing (a previous approve may have run before a crash). Verify its effect manually, then reject it — /evolution pending shows it as EXECUTING.`
|
|
174
|
+
};
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
message: `Pending write "${id}" is already being resolved by another writer.`
|
|
178
|
+
};
|
|
179
|
+
}
|
|
137
180
|
const runner = this.runners.get(record.kind);
|
|
138
181
|
if (!runner) {
|
|
139
182
|
if (record.kind === "capability") {
|
|
@@ -175,7 +218,7 @@ var EvolutionApproval = class extends Service {
|
|
|
175
218
|
};
|
|
176
219
|
return {
|
|
177
220
|
ok: true,
|
|
178
|
-
message: `Approved ${record.kind}
|
|
221
|
+
message: `Approved ${record.kind} write "${id}" (${record.summary}).`
|
|
179
222
|
};
|
|
180
223
|
}
|
|
181
224
|
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -31,6 +31,9 @@ export interface ApprovalRequest {
|
|
|
31
31
|
* (no session / no approval service) keeps the previous behavior.
|
|
32
32
|
*/
|
|
33
33
|
sessionPolicy?: 'ask' | 'never';
|
|
34
|
+
/** 0.3.17 (E-25): the requesting session id, kept on the record for
|
|
35
|
+
* audit attribution (staged AND resolved history). */
|
|
36
|
+
sessionId?: string;
|
|
34
37
|
}
|
|
35
38
|
export interface ApprovalDecision {
|
|
36
39
|
action: 'allow' | 'staged';
|
|
@@ -68,8 +71,16 @@ export declare class EvolutionApproval extends Service {
|
|
|
68
71
|
* design, so they are exempt from the staging pre-check.
|
|
69
72
|
*/
|
|
70
73
|
hasRunner(kind: PendingKind): boolean;
|
|
71
|
-
/**
|
|
72
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Trusted plan-executor entry point: replay a write through the registered
|
|
76
|
+
* runner exactly once. 0.3.17 (S3.2, E-23): this is the BACKGROUND-REVIEW
|
|
77
|
+
* replay channel only — the caller must declare the intent (the review
|
|
78
|
+
* pipeline is the sole production consumer); a bare call is refused so the
|
|
79
|
+
* staging boundary cannot be bypassed by accident from another surface.
|
|
80
|
+
*/
|
|
81
|
+
run(kind: PendingKind, args: unknown, intent?: {
|
|
82
|
+
interface: 'background_review';
|
|
83
|
+
}): Promise<{
|
|
73
84
|
ok: boolean;
|
|
74
85
|
message: string;
|
|
75
86
|
}>;
|
|
@@ -84,6 +95,13 @@ export declare class EvolutionApproval extends Service {
|
|
|
84
95
|
ok: boolean;
|
|
85
96
|
message: string;
|
|
86
97
|
}>;
|
|
98
|
+
/**
|
|
99
|
+
* 0.3.17 (S3.1): platform-side session policy derivation — the platform's
|
|
100
|
+
* approval service (`overrideOf`) is the authority; a caller's self-reported
|
|
101
|
+
* sessionPolicy is a fallback only without it (E-22). The mount check is
|
|
102
|
+
* lazy: the platform service can start before or after this plugin.
|
|
103
|
+
*/
|
|
104
|
+
private deriveSessionPolicy;
|
|
87
105
|
private dedupe;
|
|
88
106
|
private doApprove;
|
|
89
107
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-approval",
|
|
3
3
|
"description": "Stage/pending approval service for Hermes-style self-evolution writes (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.18",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,15 +37,15 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
41
|
-
"@lmzhen/dsh-evolution-state": "^0.3.
|
|
40
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.18",
|
|
41
|
+
"@lmzhen/dsh-evolution-state": "^0.3.18"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
45
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-evolution-state": "^0.3.
|
|
47
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
48
|
-
"@lmzhen/dsh-evolution-io-node": "^0.3.
|
|
49
|
-
"@lmzhen/dsh-evolution-state-json": "^0.3.
|
|
45
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.18",
|
|
46
|
+
"@lmzhen/dsh-evolution-state": "^0.3.18",
|
|
47
|
+
"@lmzhen/dsh-evolution-io": "^0.3.18",
|
|
48
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.18",
|
|
49
|
+
"@lmzhen/dsh-evolution-state-json": "^0.3.18"
|
|
50
50
|
}
|
|
51
51
|
}
|