dsh-perm-gate 1.0.0 → 2.0.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/CHANGELOG.ja.md +37 -0
- package/CHANGELOG.ko.md +36 -0
- package/CHANGELOG.md +48 -7
- package/INSTALL.ja.md +14 -7
- package/INSTALL.ko.md +13 -7
- package/INSTALL.md +15 -8
- package/INSTALL.zh.md +13 -7
- package/README.ja.md +29 -15
- package/README.ko.md +30 -16
- package/README.md +33 -21
- package/README.zh.md +23 -14
- package/cordis.patch.yml +15 -3
- package/lib/client.js +7 -117
- package/lib/client.js.map +1 -1
- package/lib/runtime.d.ts +22 -5
- package/lib/runtime.js +57 -8
- package/package.json +9 -8
package/lib/runtime.d.ts
CHANGED
|
@@ -27,13 +27,21 @@ export interface ToolExecutionLike {
|
|
|
27
27
|
* read from `agent.session.id` / `agent.session.header.cwd`.
|
|
28
28
|
*/
|
|
29
29
|
readonly agent?: {
|
|
30
|
+
/** The agent's own session id (DSH ≥ 0.1.2 exposes it beside `session`). */
|
|
31
|
+
readonly sessionId?: string;
|
|
30
32
|
readonly session?: {
|
|
31
33
|
readonly id?: string;
|
|
32
34
|
readonly header?: {
|
|
33
35
|
readonly cwd?: string;
|
|
34
36
|
};
|
|
35
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The durable event log, behind whichever accessor this DSH line ships:
|
|
39
|
+
* a plain `events` array (0.1.1), or the `snapshotEvents()` / `ownEvents()`
|
|
40
|
+
* readers 0.1.2 replaced it with. See {@link sessionEventsOf}.
|
|
41
|
+
*/
|
|
36
42
|
readonly events?: readonly SessionEventLike[];
|
|
43
|
+
readonly snapshotEvents?: () => readonly SessionEventLike[];
|
|
44
|
+
readonly ownEvents?: () => readonly SessionEventLike[];
|
|
37
45
|
};
|
|
38
46
|
};
|
|
39
47
|
}
|
|
@@ -157,7 +165,14 @@ export declare class PermGateRuntime {
|
|
|
157
165
|
private readonly gatePresets;
|
|
158
166
|
/** Extra tool names the user classified as safe (config `autoAllowTools`). */
|
|
159
167
|
private readonly autoAllowExtra;
|
|
160
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* Cache of the permission-preset fold. The key is the log's length plus the
|
|
170
|
+
* identity of its LAST event: a session's log only appends, so "same length
|
|
171
|
+
* and same last event" means the last `permission/preset` event is unchanged.
|
|
172
|
+
* Identity of the tail — not of the array — is the key because DSH 0.1.2's
|
|
173
|
+
* `snapshotEvents()` answers every read with a fresh array over the same
|
|
174
|
+
* frozen events, which would otherwise re-fold on every tool call.
|
|
175
|
+
*/
|
|
161
176
|
private presetCache;
|
|
162
177
|
private readonly learning;
|
|
163
178
|
private readonly events?;
|
|
@@ -206,9 +221,11 @@ export declare class PermGateRuntime {
|
|
|
206
221
|
private livePermissive;
|
|
207
222
|
/**
|
|
208
223
|
* The session's selected permission preset, folded from its event log. The
|
|
209
|
-
* fold is cached
|
|
210
|
-
*
|
|
211
|
-
*
|
|
224
|
+
* fold is cached on the log's length plus the identity of its last event: a
|
|
225
|
+
* session's log only appends, so an unchanged tail means the last
|
|
226
|
+
* `permission/preset` event is unchanged too. Tail identity — rather than the
|
|
227
|
+
* array's — is the key because DSH 0.1.2's snapshot accessor returns a fresh
|
|
228
|
+
* array over the same frozen events on every read.
|
|
212
229
|
*/
|
|
213
230
|
private presetOf;
|
|
214
231
|
/**
|
package/lib/runtime.js
CHANGED
|
@@ -25,11 +25,50 @@ import { compileDocument, documentHash, extractPathCandidates, parsePermissionsD
|
|
|
25
25
|
import { DEFAULT_DENY_KEYWORDS } from './deny-defaults.js';
|
|
26
26
|
import { permissionPresetOf, presetInScope } from './preset.js';
|
|
27
27
|
import { resolveGatePresets } from './config.js';
|
|
28
|
+
/**
|
|
29
|
+
* One execution's session event log, or `undefined` when the host exposes none.
|
|
30
|
+
*
|
|
31
|
+
* The accessor moved between DSH lines: 0.1.1 exposed `session.events` as a
|
|
32
|
+
* plain array, while 0.1.2 made the log private behind `snapshotEvents()` /
|
|
33
|
+
* `ownEvents()`. Reading only `.events` therefore made {@link
|
|
34
|
+
* PermGateRuntime.presetOf} return `undefined` on 0.1.2, which silently stood
|
|
35
|
+
* the whole gate down (no rule, grant, deny-keyword, or P0 hard-deny decision,
|
|
36
|
+
* and no audit event). Every carrier is probed, array-first; a throwing or
|
|
37
|
+
* missing accessor just moves to the next one, so an unknown future shape
|
|
38
|
+
* degrades to "no events" instead of breaking the decision path.
|
|
39
|
+
*
|
|
40
|
+
* @param exec - the execution whose owning session is inspected.
|
|
41
|
+
* @returns the session's events in log order, or `undefined`.
|
|
42
|
+
*/
|
|
43
|
+
function sessionEventsOf(exec) {
|
|
44
|
+
const session = exec.agent?.session;
|
|
45
|
+
for (const carrier of [session, exec]) {
|
|
46
|
+
if (carrier === undefined)
|
|
47
|
+
continue;
|
|
48
|
+
const direct = carrier.events;
|
|
49
|
+
if (Array.isArray(direct))
|
|
50
|
+
return direct;
|
|
51
|
+
for (const read of ['snapshotEvents', 'ownEvents']) {
|
|
52
|
+
const accessor = carrier[read];
|
|
53
|
+
if (typeof accessor !== 'function')
|
|
54
|
+
continue;
|
|
55
|
+
try {
|
|
56
|
+
const events = accessor.call(carrier);
|
|
57
|
+
if (Array.isArray(events))
|
|
58
|
+
return events;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// An accessor that throws is no accessor: try the next carrier.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
28
67
|
/** The session id of one execution (direct field, else the agent's session). */
|
|
29
68
|
function sessionIdOf(exec) {
|
|
30
69
|
if (typeof exec.sessionId === 'string' && exec.sessionId !== '')
|
|
31
70
|
return exec.sessionId;
|
|
32
|
-
const id = exec.agent?.session?.id;
|
|
71
|
+
const id = exec.agent?.sessionId ?? exec.agent?.session?.id;
|
|
33
72
|
return typeof id === 'string' ? id : '';
|
|
34
73
|
}
|
|
35
74
|
/** The workspace cwd of one execution (direct field, else the session header). */
|
|
@@ -73,7 +112,14 @@ export class PermGateRuntime {
|
|
|
73
112
|
gatePresets;
|
|
74
113
|
/** Extra tool names the user classified as safe (config `autoAllowTools`). */
|
|
75
114
|
autoAllowExtra;
|
|
76
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Cache of the permission-preset fold. The key is the log's length plus the
|
|
117
|
+
* identity of its LAST event: a session's log only appends, so "same length
|
|
118
|
+
* and same last event" means the last `permission/preset` event is unchanged.
|
|
119
|
+
* Identity of the tail — not of the array — is the key because DSH 0.1.2's
|
|
120
|
+
* `snapshotEvents()` answers every read with a fresh array over the same
|
|
121
|
+
* frozen events, which would otherwise re-fold on every tool call.
|
|
122
|
+
*/
|
|
77
123
|
presetCache;
|
|
78
124
|
learning;
|
|
79
125
|
events;
|
|
@@ -224,18 +270,21 @@ export class PermGateRuntime {
|
|
|
224
270
|
}
|
|
225
271
|
/**
|
|
226
272
|
* The session's selected permission preset, folded from its event log. The
|
|
227
|
-
* fold is cached
|
|
228
|
-
*
|
|
229
|
-
*
|
|
273
|
+
* fold is cached on the log's length plus the identity of its last event: a
|
|
274
|
+
* session's log only appends, so an unchanged tail means the last
|
|
275
|
+
* `permission/preset` event is unchanged too. Tail identity — rather than the
|
|
276
|
+
* array's — is the key because DSH 0.1.2's snapshot accessor returns a fresh
|
|
277
|
+
* array over the same frozen events on every read.
|
|
230
278
|
*/
|
|
231
279
|
presetOf(exec) {
|
|
232
|
-
const events = exec
|
|
280
|
+
const events = sessionEventsOf(exec);
|
|
233
281
|
const length = events?.length ?? -1;
|
|
282
|
+
const last = length > 0 ? events?.[length - 1] : undefined;
|
|
234
283
|
const cached = this.presetCache;
|
|
235
|
-
if (cached !== undefined && cached.
|
|
284
|
+
if (cached !== undefined && cached.length === length && cached.last === last)
|
|
236
285
|
return cached.preset;
|
|
237
286
|
const preset = permissionPresetOf(events);
|
|
238
|
-
this.presetCache = {
|
|
287
|
+
this.presetCache = { length, last, preset };
|
|
239
288
|
return preset;
|
|
240
289
|
}
|
|
241
290
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-perm-gate",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "DSH permission-gate: a single self-sufficient, deterministic-first, fail-closed gate covering P0 hard-deny -> P1 session grant -> P2 static rule (allow/deny) chain -> P3 optional LLM semantic classifier -> P4 ask, with command whitelist/blacklist.",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "DSH permission-gate for the DeepSeek Harness 0.1.2+ line: a single self-sufficient, deterministic-first, fail-closed gate covering P0 hard-deny -> P1 session grant -> P2 static rule (allow/deny) chain -> P3 optional LLM semantic classifier -> P4 ask, with command whitelist/blacklist.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"lib",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=20",
|
|
61
|
-
"dsh": ">=0.1.
|
|
61
|
+
"dsh": ">=0.1.2-alpha.1 <0.2.0-0"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
64
|
"@deepseek-ai/cordis": "^4.0.1"
|
|
@@ -69,10 +69,10 @@
|
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
72
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.
|
|
72
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
|
|
76
76
|
"@eslint/js": "^10.0.1",
|
|
77
77
|
"@types/node": "^22.0.0",
|
|
78
78
|
"@types/react": "~18.3.1",
|
|
@@ -89,9 +89,10 @@
|
|
|
89
89
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.client.json --noEmit",
|
|
90
90
|
"lint": "eslint src test scripts tsdown.config.ts eslint.config.mjs",
|
|
91
91
|
"test": "vitest run",
|
|
92
|
-
"verify:line": "node scripts/verify-line.mjs
|
|
92
|
+
"verify:line": "node scripts/verify-line.mjs new",
|
|
93
93
|
"verify:lines": "node scripts/verify-line.mjs both",
|
|
94
94
|
"release:latest": "npm publish --tag latest",
|
|
95
|
+
"release:2x": "npm publish --tag 2.x",
|
|
95
96
|
"release:legacy": "npm publish --tag legacy"
|
|
96
97
|
},
|
|
97
98
|
"license": "MIT",
|