dsh-perm-gate 1.0.0 → 2.1.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 +28 -14
- package/README.ko.md +29 -15
- package/README.md +32 -20
- package/README.zh.md +22 -13
- package/cordis.patch.yml +16 -3
- package/lib/client.js +7 -117
- package/lib/client.js.map +1 -1
- package/lib/risk.js +26 -3
- package/lib/runtime.d.ts +22 -5
- package/lib/runtime.js +156 -9
- package/package.json +9 -8
package/lib/risk.js
CHANGED
|
@@ -43,9 +43,9 @@ const RISK_SYSTEM_PROMPT = [
|
|
|
43
43
|
' {"risk":"risky","category":"<category>","reason":"short"}',
|
|
44
44
|
'',
|
|
45
45
|
'category must be exactly one of:',
|
|
46
|
-
' deletion — deletes or overwrites data that cannot be regenerated',
|
|
46
|
+
' deletion — deletes or overwrites data that cannot be regenerated (IMPORTANT: see exclusion rules below)',
|
|
47
47
|
' credential — creates/modifies/exposes credentials, keys, tokens, auth config',
|
|
48
|
-
' remote —
|
|
48
|
+
' remote — WRITES to or MODIFIES remote systems (git push, git deploy, kubectl apply, terraform apply, production database writes, messaging, billing, publishing). Must have a write/modify/delete effect on the remote side.',
|
|
49
49
|
' system — system-level paths or configuration (/etc, /usr, boot items), shutdown/reboot',
|
|
50
50
|
' bulk — mass overwrite of many files, formatting, dd-style irreversible writes',
|
|
51
51
|
' neutral — none of the above clearly applies (ordinary edits outside the obvious scope, or you are unsure but see no hard-risk sign)',
|
|
@@ -54,6 +54,17 @@ const RISK_SYSTEM_PROMPT = [
|
|
|
54
54
|
'- safe is only for reversible, in-scope operations that touch no sensitive resource.',
|
|
55
55
|
'- When unsure between safe and neutral, answer neutral; never guess safe.',
|
|
56
56
|
'- Judge the operation content, not merely its location.',
|
|
57
|
+
'',
|
|
58
|
+
'DELETION exclusion rules (these override the deletion category):',
|
|
59
|
+
'- Cleanup of temporary files, build outputs, or failed clone directories is SAFE — even if using rm/rmdir/Remove-Item. Look for temp paths: paths containing "temp", "tmp", "test-clone", "node_modules", ".cache", "dist", "build", or numbered/timestamped directories.',
|
|
60
|
+
'- Removal of session-local artifacts (files the session itself created) is SAFE.',
|
|
61
|
+
'- rm -rf on node_modules, .next, __pycache__, .pytest_cache, .git, dist, build is SAFE — these are always regenerable.',
|
|
62
|
+
'- Deletion inside the workspace that targets non-existent or already-empty paths is SAFE (idempotent cleanup).',
|
|
63
|
+
'- Only flag as deletion when the target is: (a) user data with no backup, (b) configuration files, (c) database records, (d) files outside the workspace that were not created by this session, or (e) a destructive rm -rf on a directory containing unique, irreplaceable content.',
|
|
64
|
+
'',
|
|
65
|
+
'READ-ONLY remote operations are safe: git fetch, git pull, git clone, git log, git diff, git status, git show, npm install (read-only), pip install (read-only), apt-get update, curl/wget (read, no write flag).',
|
|
66
|
+
'- Operations that only READ from remote systems without modifying them are safe, even if they involve network traffic.',
|
|
67
|
+
'- Only flag as remote when the operation WRITES, DEPLOYS, PUSHES, or DELETES on the remote side.',
|
|
57
68
|
].join('\n');
|
|
58
69
|
/** Parse one assistant message into a verdict; anything off-protocol → undefined (caller retries/fails). */
|
|
59
70
|
export function parseRiskVerdict(content) {
|
|
@@ -92,9 +103,21 @@ export async function classifyRisk(cfg, req, nowFetch = fetch) {
|
|
|
92
103
|
}
|
|
93
104
|
/** The bounded user prompt for one risk grading (shared by every transport). */
|
|
94
105
|
export function riskUserText(req) {
|
|
106
|
+
// Enrich the prompt with full path context so the LLM can judge
|
|
107
|
+
// whether a deletion targets a temporary/build artifact vs real data.
|
|
108
|
+
const raw = req.args ?? {};
|
|
109
|
+
const argsRecord = (typeof raw === 'object' && raw !== null ? raw : {});
|
|
110
|
+
const enriched = { ...argsRecord };
|
|
111
|
+
// Surface the working directory if present (helps LLM see temp paths).
|
|
112
|
+
if (typeof enriched.cwd === 'string' && enriched.cwd !== '') {
|
|
113
|
+
enriched.__cwd = enriched.cwd;
|
|
114
|
+
}
|
|
115
|
+
if (typeof enriched.workdir === 'string' && enriched.workdir !== '') {
|
|
116
|
+
enriched.__cwd = enriched.workdir;
|
|
117
|
+
}
|
|
95
118
|
return JSON.stringify({
|
|
96
119
|
tool: req.tool,
|
|
97
|
-
args:
|
|
120
|
+
args: enriched,
|
|
98
121
|
reason: req.reason,
|
|
99
122
|
instruction: 'Reply strictly as one JSON object: {"risk":"safe"} or {"risk":"risky","category":"..."} plus a short "reason".',
|
|
100
123
|
});
|
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
|
@@ -18,6 +18,7 @@ import { decideRules } from './evaluate.js';
|
|
|
18
18
|
import { canonicalizeCall, GrantRegistry } from './grant.js';
|
|
19
19
|
import { learnKey, operationFingerprint, RiskLearning } from './learning.js';
|
|
20
20
|
import { ArtifactRegistry } from './path.js';
|
|
21
|
+
import { decomposeShellCommand } from './shell.js';
|
|
21
22
|
import { classifyRisk, classifyRiskWith } from './risk.js';
|
|
22
23
|
import { completeViaHost, DEFAULT_HOST_MODEL } from './host-llm.js';
|
|
23
24
|
import { chatCompletion } from './classifier.js';
|
|
@@ -25,11 +26,50 @@ import { compileDocument, documentHash, extractPathCandidates, parsePermissionsD
|
|
|
25
26
|
import { DEFAULT_DENY_KEYWORDS } from './deny-defaults.js';
|
|
26
27
|
import { permissionPresetOf, presetInScope } from './preset.js';
|
|
27
28
|
import { resolveGatePresets } from './config.js';
|
|
29
|
+
/**
|
|
30
|
+
* One execution's session event log, or `undefined` when the host exposes none.
|
|
31
|
+
*
|
|
32
|
+
* The accessor moved between DSH lines: 0.1.1 exposed `session.events` as a
|
|
33
|
+
* plain array, while 0.1.2 made the log private behind `snapshotEvents()` /
|
|
34
|
+
* `ownEvents()`. Reading only `.events` therefore made {@link
|
|
35
|
+
* PermGateRuntime.presetOf} return `undefined` on 0.1.2, which silently stood
|
|
36
|
+
* the whole gate down (no rule, grant, deny-keyword, or P0 hard-deny decision,
|
|
37
|
+
* and no audit event). Every carrier is probed, array-first; a throwing or
|
|
38
|
+
* missing accessor just moves to the next one, so an unknown future shape
|
|
39
|
+
* degrades to "no events" instead of breaking the decision path.
|
|
40
|
+
*
|
|
41
|
+
* @param exec - the execution whose owning session is inspected.
|
|
42
|
+
* @returns the session's events in log order, or `undefined`.
|
|
43
|
+
*/
|
|
44
|
+
function sessionEventsOf(exec) {
|
|
45
|
+
const session = exec.agent?.session;
|
|
46
|
+
for (const carrier of [session, exec]) {
|
|
47
|
+
if (carrier === undefined)
|
|
48
|
+
continue;
|
|
49
|
+
const direct = carrier.events;
|
|
50
|
+
if (Array.isArray(direct))
|
|
51
|
+
return direct;
|
|
52
|
+
for (const read of ['snapshotEvents', 'ownEvents']) {
|
|
53
|
+
const accessor = carrier[read];
|
|
54
|
+
if (typeof accessor !== 'function')
|
|
55
|
+
continue;
|
|
56
|
+
try {
|
|
57
|
+
const events = accessor.call(carrier);
|
|
58
|
+
if (Array.isArray(events))
|
|
59
|
+
return events;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// An accessor that throws is no accessor: try the next carrier.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
28
68
|
/** The session id of one execution (direct field, else the agent's session). */
|
|
29
69
|
function sessionIdOf(exec) {
|
|
30
70
|
if (typeof exec.sessionId === 'string' && exec.sessionId !== '')
|
|
31
71
|
return exec.sessionId;
|
|
32
|
-
const id = exec.agent?.session?.id;
|
|
72
|
+
const id = exec.agent?.sessionId ?? exec.agent?.session?.id;
|
|
33
73
|
return typeof id === 'string' ? id : '';
|
|
34
74
|
}
|
|
35
75
|
/** The workspace cwd of one execution (direct field, else the session header). */
|
|
@@ -60,6 +100,70 @@ const CLEARED_CALL_TTL_MS = 5 * 60_000;
|
|
|
60
100
|
* unrecognized wording fails closed (the request is forwarded to the human).
|
|
61
101
|
*/
|
|
62
102
|
const SANDBOX_ESCALATION_REASON = /^escalate sandbox to (workspace-write|danger-full-access): /;
|
|
103
|
+
// --- Deterministic cleanup pre-screen ----------------------------------------
|
|
104
|
+
// Pattern for shell deletion commands (rm, rmdir, Remove-Item) with flags.
|
|
105
|
+
const SHELL_DELETE_RE = /^(?:rm|rmdir|Remove-Item)\b/i;
|
|
106
|
+
// Temp/build artifact directory name patterns (case-insensitive match on each path segment).
|
|
107
|
+
const REGENERABLE_SEGMENT = /(?:^|[\\/])(?:temp|tmp|test[-_]clone|node_modules|\.cache|dist|build|__pycache__|\.pytest_cache|\.git|\.next|\.nuxt|coverage|\.tox|\.venv|venv|env|\.mypy_cache|\.parcel-cache)(?:[\\/]|$)/i;
|
|
108
|
+
// Workspace-relative path tokens that are always regenerable.
|
|
109
|
+
const REGENERABLE_TOKENS = /^(?:node_modules|\.next|__pycache__|\.cache|dist|build|\.git|coverage|\.pytest_cache|\.tox|\.venv|venv|env)$/i;
|
|
110
|
+
/**
|
|
111
|
+
* Deterministic pre-screen for deletion commands. Returns a reason string when
|
|
112
|
+
* the deletion is clearly safe (targeting a regenerable/temp artifact inside the
|
|
113
|
+
* workspace), or undefined when the LLM should decide.
|
|
114
|
+
*
|
|
115
|
+
* This runs BEFORE the LLM risk grader to avoid the LLM's unconditional
|
|
116
|
+
* "Remove-Item → risky:deletion" bias. The LLM is still the fallback for
|
|
117
|
+
* ambiguous cases.
|
|
118
|
+
*/
|
|
119
|
+
function isCleanupSafe(exec) {
|
|
120
|
+
const tool = exec.name;
|
|
121
|
+
const args = exec.arguments ?? {};
|
|
122
|
+
// Only applies to shell/pwsh tools.
|
|
123
|
+
if (!/^(?:pwsh|bash|sh|cmd|shell|terminal)$/i.test(tool))
|
|
124
|
+
return undefined;
|
|
125
|
+
const commandText = typeof args.command === 'string' ? args.command : undefined;
|
|
126
|
+
if (commandText === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
let commands = [];
|
|
129
|
+
try {
|
|
130
|
+
commands = decomposeShellCommand(commandText).commands;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
const cwd = typeof args.cwd === 'string' ? args.cwd : cwdOf(exec) ?? '';
|
|
136
|
+
const posixCwd = cwd.replace(/\\/g, '/').toLowerCase();
|
|
137
|
+
for (const cmd of commands) {
|
|
138
|
+
if (!SHELL_DELETE_RE.test(cmd.command))
|
|
139
|
+
continue;
|
|
140
|
+
// Collect all non-flag argument tokens as potential targets.
|
|
141
|
+
const targets = cmd.args.filter((a) => !a.startsWith('-'));
|
|
142
|
+
if (targets.length === 0)
|
|
143
|
+
continue;
|
|
144
|
+
let allSafe = true;
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
const posixTarget = target.replace(/\\/g, '/').toLowerCase();
|
|
147
|
+
// Resolve relative to cwd.
|
|
148
|
+
const fullPath = posixTarget.startsWith('/') || /^[a-z]:\//i.test(posixTarget)
|
|
149
|
+
? posixTarget
|
|
150
|
+
: posixCwd !== '' ? `${posixCwd}/${posixTarget}` : posixTarget;
|
|
151
|
+
// Check if the target path contains a regenerable segment.
|
|
152
|
+
if (REGENERABLE_SEGMENT.test(fullPath))
|
|
153
|
+
continue;
|
|
154
|
+
// Check bare token (e.g. "node_modules" without path separators).
|
|
155
|
+
if (REGENERABLE_TOKENS.test(target))
|
|
156
|
+
continue;
|
|
157
|
+
// Not clearly regenerable — let the LLM decide.
|
|
158
|
+
allSafe = false;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
if (allSafe) {
|
|
162
|
+
return `deletion targets regenerable/temp artifacts: ${targets.join(', ')}`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
63
167
|
export class PermGateRuntime {
|
|
64
168
|
options;
|
|
65
169
|
ruleset;
|
|
@@ -73,7 +177,14 @@ export class PermGateRuntime {
|
|
|
73
177
|
gatePresets;
|
|
74
178
|
/** Extra tool names the user classified as safe (config `autoAllowTools`). */
|
|
75
179
|
autoAllowExtra;
|
|
76
|
-
/**
|
|
180
|
+
/**
|
|
181
|
+
* Cache of the permission-preset fold. The key is the log's length plus the
|
|
182
|
+
* identity of its LAST event: a session's log only appends, so "same length
|
|
183
|
+
* and same last event" means the last `permission/preset` event is unchanged.
|
|
184
|
+
* Identity of the tail — not of the array — is the key because DSH 0.1.2's
|
|
185
|
+
* `snapshotEvents()` answers every read with a fresh array over the same
|
|
186
|
+
* frozen events, which would otherwise re-fold on every tool call.
|
|
187
|
+
*/
|
|
77
188
|
presetCache;
|
|
78
189
|
learning;
|
|
79
190
|
events;
|
|
@@ -224,18 +335,21 @@ export class PermGateRuntime {
|
|
|
224
335
|
}
|
|
225
336
|
/**
|
|
226
337
|
* The session's selected permission preset, folded from its event log. The
|
|
227
|
-
* fold is cached
|
|
228
|
-
*
|
|
229
|
-
*
|
|
338
|
+
* fold is cached on the log's length plus the identity of its last event: a
|
|
339
|
+
* session's log only appends, so an unchanged tail means the last
|
|
340
|
+
* `permission/preset` event is unchanged too. Tail identity — rather than the
|
|
341
|
+
* array's — is the key because DSH 0.1.2's snapshot accessor returns a fresh
|
|
342
|
+
* array over the same frozen events on every read.
|
|
230
343
|
*/
|
|
231
344
|
presetOf(exec) {
|
|
232
|
-
const events = exec
|
|
345
|
+
const events = sessionEventsOf(exec);
|
|
233
346
|
const length = events?.length ?? -1;
|
|
347
|
+
const last = length > 0 ? events?.[length - 1] : undefined;
|
|
234
348
|
const cached = this.presetCache;
|
|
235
|
-
if (cached !== undefined && cached.
|
|
349
|
+
if (cached !== undefined && cached.length === length && cached.last === last)
|
|
236
350
|
return cached.preset;
|
|
237
351
|
const preset = permissionPresetOf(events);
|
|
238
|
-
this.presetCache = {
|
|
352
|
+
this.presetCache = { length, last, preset };
|
|
239
353
|
return preset;
|
|
240
354
|
}
|
|
241
355
|
/**
|
|
@@ -489,6 +603,19 @@ export class PermGateRuntime {
|
|
|
489
603
|
};
|
|
490
604
|
const raw = decide(ctx, { decide: (c) => decideRules(this.ruleset, c) }, grantResolver, this.autoAllowExtra);
|
|
491
605
|
const decision = this.applyPermissive(raw);
|
|
606
|
+
// Deterministic cleanup pre-screen: if a deletion command targets a
|
|
607
|
+
// regenerable/temporary artifact inside the workspace, allow it directly.
|
|
608
|
+
// This runs BEFORE the ask/passthrough logic so it works even under
|
|
609
|
+
// `approval: never` (where refineAsk is never called).
|
|
610
|
+
if (decision.action === 'ask') {
|
|
611
|
+
const cleanupSafe = isCleanupSafe(exec);
|
|
612
|
+
if (cleanupSafe !== undefined) {
|
|
613
|
+
const reason = `cleanup-safe: ${cleanupSafe}`;
|
|
614
|
+
this.audit.append(makeEntry({ callId, tool: exec.name, outcome: 'allow', source: 'classifier', reason, at: Date.now() }));
|
|
615
|
+
this.recordEvent(exec, 'auto', reason, { risk: 'safe', verdict: 'cleanup-safe', category: 'safe' });
|
|
616
|
+
return undefined; // passthrough (allow)
|
|
617
|
+
}
|
|
618
|
+
}
|
|
492
619
|
let outcome;
|
|
493
620
|
let source;
|
|
494
621
|
switch (decision.stage) {
|
|
@@ -595,6 +722,19 @@ export class PermGateRuntime {
|
|
|
595
722
|
return undefined;
|
|
596
723
|
}
|
|
597
724
|
}
|
|
725
|
+
// Defensive cleanup pre-screen: normally this is handled in decideExecution
|
|
726
|
+
// before refineAsk is ever called, but refineAsk is public and may be called
|
|
727
|
+
// directly by tests or future embedders. Keep the same deterministic check
|
|
728
|
+
// here as a safety net — identical logic to decideExecution's pre-screen.
|
|
729
|
+
const cleanupSafe = isCleanupSafe(exec);
|
|
730
|
+
if (cleanupSafe !== undefined) {
|
|
731
|
+
const reason = `cleanup-safe: ${cleanupSafe}`;
|
|
732
|
+
this.audit.append(makeEntry({ callId: randomId(), tool: exec.name, outcome: 'allow', source: 'classifier', reason, at: Date.now() }));
|
|
733
|
+
this.recordEvent(exec, 'auto', reason, { risk: 'safe', verdict: 'cleanup-safe', category: 'safe' });
|
|
734
|
+
this.untrackAsk(exec);
|
|
735
|
+
this.clearCall(exec, reason, 'cleanup-safe', 'classifier');
|
|
736
|
+
return undefined;
|
|
737
|
+
}
|
|
598
738
|
if (!permissive.strategies.llmAssist)
|
|
599
739
|
return decision;
|
|
600
740
|
// One live read of the classifier setup. Without a usable receiver (a
|
|
@@ -607,7 +747,14 @@ export class PermGateRuntime {
|
|
|
607
747
|
&& (cfg?.endpoint === undefined || cfg?.endpoint === '' || cfg?.model === undefined || cfg?.model === '')) {
|
|
608
748
|
return decision;
|
|
609
749
|
}
|
|
610
|
-
|
|
750
|
+
// Inject cwd into the risk request so the LLM can see the full path context
|
|
751
|
+
// (e.g. that a Remove-Item targets a temp directory, not user data).
|
|
752
|
+
const riskArgs = { ...(exec.arguments ?? {}) };
|
|
753
|
+
const cwd = cwdOf(exec);
|
|
754
|
+
if (typeof cwd === 'string' && cwd !== '' && typeof riskArgs.cwd !== 'string') {
|
|
755
|
+
riskArgs.cwd = cwd;
|
|
756
|
+
}
|
|
757
|
+
const req = { tool: exec.name, args: riskArgs, reason: decision.reason };
|
|
611
758
|
let risk;
|
|
612
759
|
if (this.options.riskHook !== undefined) {
|
|
613
760
|
risk = await this.options.riskHook(req);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-perm-gate",
|
|
3
|
-
"version": "1.0
|
|
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.1.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",
|