dsh-perm-gate 0.2.1-beta.5 → 1.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/lib/runtime.d.ts CHANGED
@@ -27,21 +27,13 @@ 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;
32
30
  readonly session?: {
33
31
  readonly id?: string;
34
32
  readonly header?: {
35
33
  readonly cwd?: string;
36
34
  };
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
- */
35
+ /** The durable event log; the permission-preset fold reads it. */
42
36
  readonly events?: readonly SessionEventLike[];
43
- readonly snapshotEvents?: () => readonly SessionEventLike[];
44
- readonly ownEvents?: () => readonly SessionEventLike[];
45
37
  };
46
38
  };
47
39
  }
@@ -165,14 +157,7 @@ export declare class PermGateRuntime {
165
157
  private readonly gatePresets;
166
158
  /** Extra tool names the user classified as safe (config `autoAllowTools`). */
167
159
  private readonly autoAllowExtra;
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
- */
160
+ /** Cache of the permission-preset fold, keyed by log identity + length. */
176
161
  private presetCache;
177
162
  private readonly learning;
178
163
  private readonly events?;
@@ -221,11 +206,9 @@ export declare class PermGateRuntime {
221
206
  private livePermissive;
222
207
  /**
223
208
  * The session's selected permission preset, folded from its event log. The
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.
209
+ * fold is cached by the log array identity plus its length: a session's log
210
+ * is a stable, append-only array, so a changed reference or a grown log
211
+ * invalidates the cache while repeated calls on the same log stay cheap.
229
212
  */
230
213
  private presetOf;
231
214
  /**
package/lib/runtime.js CHANGED
@@ -25,50 +25,11 @@ 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
- }
67
28
  /** The session id of one execution (direct field, else the agent's session). */
68
29
  function sessionIdOf(exec) {
69
30
  if (typeof exec.sessionId === 'string' && exec.sessionId !== '')
70
31
  return exec.sessionId;
71
- const id = exec.agent?.sessionId ?? exec.agent?.session?.id;
32
+ const id = exec.agent?.session?.id;
72
33
  return typeof id === 'string' ? id : '';
73
34
  }
74
35
  /** The workspace cwd of one execution (direct field, else the session header). */
@@ -112,14 +73,7 @@ export class PermGateRuntime {
112
73
  gatePresets;
113
74
  /** Extra tool names the user classified as safe (config `autoAllowTools`). */
114
75
  autoAllowExtra;
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
- */
76
+ /** Cache of the permission-preset fold, keyed by log identity + length. */
123
77
  presetCache;
124
78
  learning;
125
79
  events;
@@ -270,21 +224,18 @@ export class PermGateRuntime {
270
224
  }
271
225
  /**
272
226
  * The session's selected permission preset, folded from its event log. The
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.
227
+ * fold is cached by the log array identity plus its length: a session's log
228
+ * is a stable, append-only array, so a changed reference or a grown log
229
+ * invalidates the cache while repeated calls on the same log stay cheap.
278
230
  */
279
231
  presetOf(exec) {
280
- const events = sessionEventsOf(exec);
232
+ const events = exec.agent?.session?.events;
281
233
  const length = events?.length ?? -1;
282
- const last = length > 0 ? events?.[length - 1] : undefined;
283
234
  const cached = this.presetCache;
284
- if (cached !== undefined && cached.length === length && cached.last === last)
235
+ if (cached !== undefined && cached.events === events && cached.length === length)
285
236
  return cached.preset;
286
237
  const preset = permissionPresetOf(events);
287
- this.presetCache = { length, last, preset };
238
+ this.presetCache = { events, length, preset };
288
239
  return preset;
289
240
  }
290
241
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-perm-gate",
3
- "version": "0.2.1-beta.5",
3
+ "version": "1.0.0",
4
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.",
5
5
  "type": "module",
6
6
  "files": [
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "engines": {
60
60
  "node": ">=20",
61
- "dsh": ">=0.1.2-alpha.1 <0.2.0-0"
61
+ "dsh": ">=0.1.0-rc.7 <0.1.2-alpha.1"
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.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",
72
+ "@deepseek-ai/dsh-client-locale": "^0.1.1-rc.2",
73
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.1-rc.2",
74
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
75
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.1-rc.2",
76
76
  "@eslint/js": "^10.0.1",
77
77
  "@types/node": "^22.0.0",
78
78
  "@types/react": "~18.3.1",
@@ -89,7 +89,7 @@
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 new",
92
+ "verify:line": "node scripts/verify-line.mjs legacy",
93
93
  "verify:lines": "node scripts/verify-line.mjs both",
94
94
  "release:latest": "npm publish --tag latest",
95
95
  "release:legacy": "npm publish --tag legacy"