@dsh-cc/permission-rules 0.5.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.
Files changed (65) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +86 -0
  4. package/README.zh.md +86 -0
  5. package/lib/approval-listener.d.ts +50 -0
  6. package/lib/approval-listener.d.ts.map +1 -0
  7. package/lib/approval-listener.js +58 -0
  8. package/lib/approval-listener.js.map +1 -0
  9. package/lib/auto-stage.d.ts +138 -0
  10. package/lib/auto-stage.d.ts.map +1 -0
  11. package/lib/auto-stage.js +284 -0
  12. package/lib/auto-stage.js.map +1 -0
  13. package/lib/classifier.d.ts +57 -0
  14. package/lib/classifier.d.ts.map +1 -0
  15. package/lib/classifier.js +129 -0
  16. package/lib/classifier.js.map +1 -0
  17. package/lib/decide.d.ts +80 -0
  18. package/lib/decide.d.ts.map +1 -0
  19. package/lib/decide.js +127 -0
  20. package/lib/decide.js.map +1 -0
  21. package/lib/domain.d.ts +46 -0
  22. package/lib/domain.d.ts.map +1 -0
  23. package/lib/domain.js +103 -0
  24. package/lib/domain.js.map +1 -0
  25. package/lib/evaluate.d.ts +32 -0
  26. package/lib/evaluate.d.ts.map +1 -0
  27. package/lib/evaluate.js +176 -0
  28. package/lib/evaluate.js.map +1 -0
  29. package/lib/index.d.ts +123 -0
  30. package/lib/index.d.ts.map +1 -0
  31. package/lib/index.js +380 -0
  32. package/lib/index.js.map +1 -0
  33. package/lib/invariant.d.ts +28 -0
  34. package/lib/invariant.d.ts.map +1 -0
  35. package/lib/invariant.js +54 -0
  36. package/lib/invariant.js.map +1 -0
  37. package/lib/llm-classifier.d.ts +107 -0
  38. package/lib/llm-classifier.d.ts.map +1 -0
  39. package/lib/llm-classifier.js +231 -0
  40. package/lib/llm-classifier.js.map +1 -0
  41. package/lib/matchers.d.ts +18 -0
  42. package/lib/matchers.d.ts.map +1 -0
  43. package/lib/matchers.js +43 -0
  44. package/lib/matchers.js.map +1 -0
  45. package/lib/mode.d.ts +91 -0
  46. package/lib/mode.d.ts.map +1 -0
  47. package/lib/mode.js +133 -0
  48. package/lib/mode.js.map +1 -0
  49. package/lib/parser.d.ts +91 -0
  50. package/lib/parser.d.ts.map +1 -0
  51. package/lib/parser.js +282 -0
  52. package/lib/parser.js.map +1 -0
  53. package/lib/session-allowlist.d.ts +76 -0
  54. package/lib/session-allowlist.d.ts.map +1 -0
  55. package/lib/session-allowlist.js +122 -0
  56. package/lib/session-allowlist.js.map +1 -0
  57. package/lib/settings-schema.d.ts +99 -0
  58. package/lib/settings-schema.d.ts.map +1 -0
  59. package/lib/settings-schema.js +64 -0
  60. package/lib/settings-schema.js.map +1 -0
  61. package/lib/types.d.ts +150 -0
  62. package/lib/types.d.ts.map +1 -0
  63. package/lib/types.js +33 -0
  64. package/lib/types.js.map +1 -0
  65. package/package.json +71 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/lib/index.js ADDED
@@ -0,0 +1,380 @@
1
+ /**
2
+ * Claude Code-compatible permission-rule engine. Owns a source-labelled rule
3
+ * set (Config `rules` merged with the optional `permissions` settings section),
4
+ * a `tools/pre-execute` listener that folds a mode-aware decision, and the
5
+ * monotonic guard layer that enforces bypass-immune content rules so neither a
6
+ * mode switch nor `bypassPermissions` can override them. A risk-classifier
7
+ * escalation stage hard-denies catastrophic commands and asks on protected or
8
+ * out-of-scope file writes before the normal waterfall. Rules fail loud at
9
+ * load; settings hot-reloads by rebuilding merged state and re-registering
10
+ * guards.
11
+ *
12
+ * @module @dsh-cc/permission-rules
13
+ */
14
+ import { Service } from '@deepseek-ai/cordis';
15
+ import { BlockAssembler, createUserMessage } from '@deepseek-ai/dsh-llm';
16
+ import { foldPlanMode } from '@deepseek-ai/dsh-plan-mode';
17
+ import { effectiveSandboxMode, setSandboxMode } from '@deepseek-ai/dsh-sandbox-policy';
18
+ import { foldSessionCwd } from '@dsh-cc/session-cwd';
19
+ import { resolveAlias, toOneShotRoute } from '@dsh-cc/model-aliases';
20
+ import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings';
21
+ import { parseRule, ruleString } from "./parser.js";
22
+ import { mergeRuleSets } from "./evaluate.js";
23
+ import { decideCallVerbose } from "./decide.js";
24
+ import { createAutoStage, appendSessionClassifier } from "./auto-stage.js";
25
+ import { PERMISSION_MODES, } from "./types.js";
26
+ import { foldPermissionMode, setPermissionMode, switchSessionPermissionMode, } from "./mode.js";
27
+ import { ruleMatches, subjectOf } from "./matchers.js";
28
+ import { SessionAllowlist, foldSessionAllows } from "./session-allowlist.js";
29
+ import { createSandboxApprovalListener } from "./approval-listener.js";
30
+ export { SESSION_ALLOW_EVENT, SessionAllowlist, appendSessionAllow, foldSessionAllows, } from "./session-allowlist.js";
31
+ export { createSandboxApprovalListener, isSandboxEscalation, } from "./approval-listener.js";
32
+ export { foldPermissionMode, foldResumeSandbox, setPermissionMode, PERMISSION_MODE_EVENT, } from "./mode.js";
33
+ export { CLASSIFIER_EVENT, appendSessionClassifier, foldClassifiers, createAutoStage, } from "./auto-stage.js";
34
+ export { createLlmClassifier, expandSoftDeny, DEFAULT_SOFT_DENY, } from "./llm-classifier.js";
35
+ export { PERMISSION_MODES, SWITCHABLE_PERMISSION_MODES, PLAN_READONLY_REASON, } from "./types.js";
36
+ export { parseRuleString, ruleString, contentMatches, } from "./parser.js";
37
+ export { canonicalizeHostname, isWebFetchRuleTool } from "./domain.js";
38
+ /** The settings namespace carrying `permissions.allow/deny/ask/defaultMode`. */
39
+ export const PERMISSION_SETTINGS_NAMESPACE = settingsNamespace('permissions');
40
+ export { permissionSettingsSchema, ConfigSchema, DEFAULT_FILE_EDIT_TOOLS, DEFAULT_READ_ONLY_TOOLS, } from "./settings-schema.js";
41
+ import { ConfigSchema, permissionSettingsSchema, } from "./settings-schema.js";
42
+ /** Build a settings-resolved rule set from a settings section. */
43
+ function settingsRuleSet(settings, source) {
44
+ return {
45
+ allow: (settings.allow ?? []).map(raw => parseRule(raw, 'allow', source)),
46
+ deny: (settings.deny ?? []).map(raw => parseRule(raw, 'deny', source)),
47
+ ask: (settings.ask ?? []).map(raw => parseRule(raw, 'ask', source)),
48
+ bypassImmune: [],
49
+ };
50
+ }
51
+ /** One short model-facing sentence per permission mode for the prompt context. */
52
+ const MODE_SENTENCE = {
53
+ default: 'Permission mode: default. Tool calls follow allow/deny/ask rules; unmatched calls pass through.',
54
+ acceptEdits: 'Permission mode: acceptEdits. File edits are auto-allowed; other calls follow the rules.',
55
+ plan: 'Permission mode: plan. Only read-only tools may run; submit the plan via exit_plan_mode.',
56
+ auto: 'Permission mode: auto. Low-risk approval prompts are auto-allowed; medium-risk prompts still ask the user.',
57
+ bypassPermissions: 'Permission mode: bypassPermissions. Permission prompts are skipped and the sandbox is full access, except bypass-immune and catastrophic commands which remain denied.',
58
+ };
59
+ /** The engine's Service Definition plus the mode/rule write and read surface. */
60
+ export class PermissionRulesService extends Service {
61
+ config;
62
+ static Config = ConfigSchema;
63
+ static inject = ['tools'];
64
+ bashToolName;
65
+ fileEditTools;
66
+ readOnlyTools;
67
+ settingsSource;
68
+ rulesConfig;
69
+ bypassImmuneRules;
70
+ /** Reads the currently authoritative settings section (swapped by the settings hook). */
71
+ settingsRead = () => ({});
72
+ /** Live merged state; rebuilt on settings change so listeners read a fresh snapshot. */
73
+ state;
74
+ /** Disposers for the currently registered monotonic guards. */
75
+ guardDisposers = [];
76
+ /** Session-scoped approval memory (WS4-PR-B): rules granted via the UI's "Allow for this session". */
77
+ sessionAllowlist = new SessionAllowlist();
78
+ /** Session ids already seeded from their log's `permission/session-allow` audit events. */
79
+ allowlistSeeded = new Set();
80
+ /** The optional LLM classifier stage (armed per call from the live settings slice). */
81
+ autoStage;
82
+ constructor(ctx, config) {
83
+ super(ctx, 'permissionRules');
84
+ this.config = config;
85
+ // The schema applied the defaults, so these are non-optional at runtime.
86
+ this.bashToolName = config.bashToolName;
87
+ this.fileEditTools = new Set(config.fileEditTools);
88
+ this.readOnlyTools = new Set(config.readOnlyTools);
89
+ this.settingsSource = config.settingsSource;
90
+ this.rulesConfig = config.rules ?? {};
91
+ this.bypassImmuneRules = (this.rulesConfig.bypassImmune ?? []).map(raw => parseRule(raw, 'deny', 'config'));
92
+ this.state = { rules: this.configRuleSet(), defaultMode: config.defaultMode };
93
+ // Monotonic guard layer for bypass-immune rules: never overridable.
94
+ this.registerGuards();
95
+ // Optional settings inject: absent `ctx.settings` leaves only the Config
96
+ // rules in force, exactly as the fallback contract requires. A stored
97
+ // change re-enters reload() to rebuild merged state and the guards.
98
+ installSettingsSection(ctx, PERMISSION_SETTINGS_NAMESPACE, permissionSettingsSchema(), {}, {
99
+ setSource: (current) => { this.settingsRead = current; },
100
+ onChange: () => this.reload(),
101
+ validate: value => this.validateSettings(value),
102
+ });
103
+ // The decision waterfall lives in ./decide.ts as pure functions over this
104
+ // structural dependency face; the closures read live fields so a settings
105
+ // reload is observed on the next call.
106
+ const decideDeps = {
107
+ classifierEnabled: config.classifierEnabled !== false,
108
+ exemptSandboxedBashFromToolAsk: config.exemptSandboxedBashFromToolAsk === true,
109
+ bashToolName: this.bashToolName,
110
+ fileEditTools: this.fileEditTools,
111
+ readOnlyTools: this.readOnlyTools,
112
+ settings: () => this.settingsSection(),
113
+ defaultMode: () => this.state.defaultMode,
114
+ rules: () => this.state.rules,
115
+ bypassDisabled: () => this.bypassDisabled(),
116
+ sessionAllowMatches: (exec) => this.sessionAllowMatches(exec),
117
+ shellMode: () => this.ctx.get('shell')?.sandboxMode,
118
+ };
119
+ // The optional LLM classifier stage (§4.1/§4.4 of the LLM risk-classifier
120
+ // design). The llm stream seam is wired via ctx.inject so a missing llm
121
+ // service is a silent no-op rather than a required dependency (same
122
+ // optional-availability pattern as the systemPrompt injection below).
123
+ let llmStream;
124
+ ctx.inject(['llm'], (scope) => {
125
+ llmStream = async (opts) => {
126
+ const assembler = new BlockAssembler();
127
+ for await (const chunk of scope.llm.stream({
128
+ provider: opts.provider,
129
+ model: opts.model,
130
+ system: opts.system,
131
+ messages: [createUserMessage({
132
+ content: [{ type: 'text', text: opts.prompt }],
133
+ source: { kind: 'plugin', plugin: 'permission-rules' },
134
+ })],
135
+ maxTokens: opts.maxTokens,
136
+ ...(opts.signal === undefined ? {} : { signal: opts.signal }),
137
+ })) {
138
+ assembler.push(chunk);
139
+ }
140
+ return assembler.blocks()
141
+ .filter(block => block.type === 'text')
142
+ .map(block => block.text)
143
+ .join(' ')
144
+ .trim();
145
+ };
146
+ });
147
+ const autoStage = createAutoStage({
148
+ settingsRead: () => this.settingsSection(),
149
+ get stream() {
150
+ return llmStream;
151
+ },
152
+ resolveRoute: (exec) => {
153
+ const route = this.settingsSection().autoMode?.classifier?.route ?? 'haiku';
154
+ // The calling agent's logged request header fills the provider for a
155
+ // string-form (model-only) alias; a complete {provider, model} alias
156
+ // needs no parent (toOneShotRoute flow, session-title-provider precedent).
157
+ const parent = exec.agent?.session.requestHeader()?.config;
158
+ return toOneShotRoute(resolveAlias(this.ctx, route), parent);
159
+ },
160
+ warn: (message) => this.ctx.logger.warn(message),
161
+ // R5 debug channel: opt-in via DSH_PERMISSION_CLASSIFIER_DEBUG=1, from
162
+ // the plugin's scoped process logger — raw classifier output NEVER
163
+ // enters session events (the digest-only audit contract stands). Raw
164
+ // output may echo tool input (including secrets), so this stays an
165
+ // explicitly opt-in channel with no redaction machinery.
166
+ ...(process.env.DSH_PERMISSION_CLASSIFIER_DEBUG === '1'
167
+ ? { debug: (message) => this.ctx.logger.debug?.(`[permission-rules] ${message}`) }
168
+ : {}),
169
+ audit: (session, event) => {
170
+ appendSessionClassifier(session, event);
171
+ },
172
+ });
173
+ this.autoStage = autoStage;
174
+ ctx.on('tools/pre-execute', async (exec, next) => {
175
+ const decided = decideCallVerbose(decideDeps, exec);
176
+ // Armed + auto + LOW + ask/passthrough ⇒ the LLM stage decides (§4.1):
177
+ // verdict allow ⇒ allow, verdict ask/failure ⇒ ask(reason). Every other
178
+ // path falls through to today's exact mapping (disarmed ⇒ bit-for-bit).
179
+ const escalated = await autoStage.maybeEscalate(decided, exec);
180
+ if (escalated !== undefined) {
181
+ return escalated === 'allow' ? { kind: 'allow' } : { kind: 'ask', reason: escalated.reason };
182
+ }
183
+ const { decision, risk, mode } = decided;
184
+ if (decision.kind === 'allow')
185
+ return { kind: 'allow' };
186
+ if (decision.kind === 'deny')
187
+ return { kind: 'deny', reason: decision.reason };
188
+ if (decision.kind === 'ask') {
189
+ // auto proxies every LOW-risk ask (MEDIUM/HIGH returned above):
190
+ // identical to the previous decideCall post-processing.
191
+ if (mode === 'auto' && risk.level === 'LOW')
192
+ return { kind: 'allow' };
193
+ return { kind: 'ask', ...decision.reason === undefined ? {} : { reason: decision.reason } };
194
+ }
195
+ return next();
196
+ });
197
+ // WS3 sandbox integration: the approval-seam listener auto-approves
198
+ // sandbox escalations in `auto` mode when the session has a resolvable
199
+ // workspace root. Registered ahead of any UI provider so an eligible
200
+ // escalation never reaches the modal queue; every auto-approval is
201
+ // audit-logged to the session log (`scope: 'sandbox-auto'`).
202
+ ctx.on('approval/request', createSandboxApprovalListener({
203
+ modeOf: (agent) => {
204
+ if (foldPlanMode(agent.session.events))
205
+ return 'plan';
206
+ return foldPermissionMode(agent.session.events) ?? this.state.defaultMode;
207
+ },
208
+ workspaceOf: (agent) => this.sessionWorkspaceOf(agent),
209
+ }));
210
+ // Pin sessions created while the deployment default is a sandbox-affecting or
211
+ // plan mode so a fresh session inherits the default durably.
212
+ ctx.on('session/created', (session) => {
213
+ if (foldPermissionMode(session.events) !== undefined)
214
+ return;
215
+ if (foldPlanMode(session.events))
216
+ return;
217
+ const mode = this.state.defaultMode;
218
+ if (mode === 'bypassPermissions') {
219
+ if (this.bypassDisabled())
220
+ return;
221
+ const resume = effectiveSandboxMode(session.events)
222
+ ?? this.ctx.get('shell')?.sandboxMode;
223
+ setPermissionMode(session, 'bypassPermissions', resume);
224
+ if ((effectiveSandboxMode(session.events) ?? this.ctx.get('shell')?.sandboxMode) !== 'danger-full-access') {
225
+ setSandboxMode(session, 'danger-full-access');
226
+ }
227
+ return;
228
+ }
229
+ if (mode === 'plan') {
230
+ session.append('plan/mode', { active: true });
231
+ }
232
+ });
233
+ // Optional model-facing mode sentence. Injected via ctx.inject so a missing
234
+ // system-prompt seam is a silent no-op rather than a required dependency.
235
+ ctx.inject(['systemPrompt'], (scope) => {
236
+ scope.systemPrompt.context({
237
+ name: 'permission:mode',
238
+ order: 116,
239
+ text: (context) => {
240
+ const agent = context.agent;
241
+ if (agent === undefined)
242
+ return '';
243
+ const mode = foldPlanMode(agent.session.events)
244
+ ? 'plan'
245
+ : (foldPermissionMode(agent.session.events) ?? this.state.defaultMode);
246
+ return MODE_SENTENCE[mode];
247
+ },
248
+ });
249
+ });
250
+ }
251
+ /** The current settings-resolved section, defaulting to the schema default. */
252
+ settingsSection() {
253
+ return this.settingsRead();
254
+ }
255
+ /** Reject a settings section the engine could not act on — fail loud at the settings boundary. */
256
+ validateSettings(value) {
257
+ for (const raw of [...value.allow ?? [], ...value.deny ?? [], ...value.ask ?? []]) {
258
+ parseRule(raw, 'allow', this.settingsSource);
259
+ }
260
+ if (value.defaultMode !== undefined && !PERMISSION_MODES.includes(value.defaultMode)) {
261
+ throw new Error(`permission: unknown defaultMode ${JSON.stringify(value.defaultMode)}`);
262
+ }
263
+ }
264
+ /** Rebuild merged state and re-register guards (mount and settings change). */
265
+ reload() {
266
+ const settings = this.settingsSection();
267
+ this.state = {
268
+ rules: mergeRuleSets(settingsRuleSet(settings, this.settingsSource), this.configRuleSet()),
269
+ defaultMode: settings.defaultMode ?? this.config.defaultMode ?? 'default',
270
+ };
271
+ this.registerGuards();
272
+ // Drop the memoized LLM classifier when the autoMode slice changed, so
273
+ // the next armed call rebuilds it from fresh settings.
274
+ this.autoStage?.rebuild();
275
+ }
276
+ /** Parse the Config `rules` block into a source-`config` rule set. */
277
+ configRuleSet() {
278
+ const { allow = [], deny = [], ask = [] } = this.rulesConfig;
279
+ return {
280
+ allow: allow.map(raw => parseRule(raw, 'allow', 'config')),
281
+ deny: deny.map(raw => parseRule(raw, 'deny', 'config')),
282
+ ask: ask.map(raw => parseRule(raw, 'ask', 'config')),
283
+ bypassImmune: this.bypassImmuneRules,
284
+ };
285
+ }
286
+ /** (Re)register monotonic guards for the bypass-immune rules, idempotent. */
287
+ registerGuards() {
288
+ for (const dispose of this.guardDisposers)
289
+ dispose();
290
+ this.guardDisposers = this.bypassImmuneRules.map(rule => this.ctx.tools.guard((exec) => {
291
+ const subject = subjectOf(exec, this.bashToolName);
292
+ if (subject === undefined || !ruleMatches(rule, exec.name, subject))
293
+ return undefined;
294
+ return `denied by permission rule ${ruleString(rule.toolName, rule.content)} [${rule.source}] (bypass-immune)`;
295
+ }));
296
+ }
297
+ /**
298
+ * Whether the session-scoped allowlist matches this call. The session's
299
+ * rules are seeded once from its log's `permission/session-allow` audit
300
+ * events, so a resumed session keeps its grants. Agent-less calls never
301
+ * match (there is no session to scope to).
302
+ */
303
+ sessionAllowMatches(exec) {
304
+ const agent = exec.agent;
305
+ if (agent === undefined)
306
+ return false;
307
+ const id = String(agent.session.id);
308
+ if (!this.allowlistSeeded.has(id)) {
309
+ this.allowlistSeeded.add(id);
310
+ this.sessionAllowlist.seed(id, foldSessionAllows(agent.session.events));
311
+ }
312
+ return this.sessionAllowlist.matches(id, exec.name, subjectOf(exec, this.bashToolName));
313
+ }
314
+ /**
315
+ * The session's workspace root: the durable `worktree/entered` fold
316
+ * (session-cwd, WS1), falling back to the session header cwd. Undefined
317
+ * when the session never recorded a cwd — the sandbox listener then cannot
318
+ * verify an escalation is in-scope and falls through to the normal ask.
319
+ */
320
+ sessionWorkspaceOf(agent) {
321
+ return foldSessionCwd(agent.session.events) ?? agent.session.header?.cwd;
322
+ }
323
+ /**
324
+ * Grant a session-scoped allow rule on the agent's session: in-memory match
325
+ * for the rest of this session plus a `permission/session-allow` audit
326
+ * event. Never touches the `permissions` settings namespace.
327
+ * @param agent - the agent whose session is granted the rule.
328
+ * @param rule - the rule string (e.g. `Bash(npm )` or a whole-tool name).
329
+ */
330
+ addSessionAllow(agent, rule) {
331
+ this.sessionAllowlist.add(agent.session, rule);
332
+ }
333
+ /**
334
+ * Drop every session-scoped rule for the agent's session (audited clear
335
+ * record in the session log).
336
+ */
337
+ clearSessionAllows(agent) {
338
+ this.sessionAllowlist.clear(agent.session);
339
+ }
340
+ /**
341
+ * Whether switching to `bypassPermissions` is disabled by Config or the
342
+ * settings section.
343
+ */
344
+ bypassDisabled() {
345
+ return this.config.disableBypassPermissionsMode === true
346
+ || this.settingsSection().disableBypassPermissionsMode === 'disable';
347
+ }
348
+ /**
349
+ * The LIVE merged settings default (`config.defaultMode`, overridden by the
350
+ * settings section): rebuilt on every settings reload, so display surfaces
351
+ * reading this always follow the currently authoritative default.
352
+ */
353
+ get defaultMode() {
354
+ return this.state.defaultMode;
355
+ }
356
+ /**
357
+ * Switch a session's permission mode durably. Semantics live in
358
+ * `switchSessionPermissionMode` (./mode.ts): `plan` is owned by plan-mode
359
+ * and throws; entering `bypassPermissions` pins the session sandbox to
360
+ * `danger-full-access` and records the prior mode for restore; unknown or
361
+ * disabled modes throw.
362
+ * @param agent - the live agent whose session mode is changing.
363
+ * @param mode - the new permission mode.
364
+ */
365
+ setMode(agent, mode) {
366
+ switchSessionPermissionMode({
367
+ agent,
368
+ mode,
369
+ defaultMode: this.state.defaultMode,
370
+ bypassDisabled: this.bypassDisabled(),
371
+ shellMode: this.ctx.get('shell')?.sandboxMode,
372
+ });
373
+ }
374
+ /** The currently merged rule set (for introspection and host preview). */
375
+ get ruleSet() {
376
+ return this.state.rules;
377
+ }
378
+ }
379
+ export default PermissionRulesService;
380
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Package-owned invariant companion for `@dsh-cc/permission-rules`.
3
+ * Guards the closed vocabulary of durable `permission/mode` session events and
4
+ * rejects illegal payloads at the session boundary.
5
+ * @module @dsh-cc/permission-rules/invariant
6
+ */
7
+ import type { Context } from '@deepseek-ai/cordis';
8
+ import type { SessionEvent } from '@deepseek-ai/dsh-session';
9
+ import type { InvariantFailure } from '@deepseek-ai/dsh-invariants';
10
+ /** Cordis companion plugin name. */
11
+ export declare const name = "cc-permission-rules-invariant";
12
+ /** Service required before the companion can reserve package ownership. */
13
+ export declare const inject: string[];
14
+ /**
15
+ * Validate one `permission/mode` event against its closed vocabularies. `plan`
16
+ * is never writable through this event (it is owned by plan-mode's `plan/mode`),
17
+ * and `resumeSandbox` must be a known sandbox mode when present.
18
+ * @param event - the session event to validate.
19
+ * @param fail - reporter bound to this package, throwing on violation.
20
+ */
21
+ export declare function assertPermissionModeEvent(event: SessionEvent, fail: InvariantFailure): void;
22
+ /**
23
+ * Register this package's invariant companion.
24
+ * @param ctx - Cordis context carrying the invariant service.
25
+ * @returns the installed registration's disposer after setup succeeds.
26
+ */
27
+ export declare const apply: (ctx: Context) => Promise<() => void>;
28
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.d.ts","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAW,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAErE,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,6BAA6B,CAAA;AAMvF,oCAAoC;AACpC,eAAO,MAAM,IAAI,kCAAkC,CAAA;AACnD,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAiB,CAAA;AAEpC;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAU3F;AAgBD;;;;GAIG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,OAAO,KAAG,OAAO,CAAC,MAAM,IAAI,CACU,CAAA"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Package-owned invariant companion for `@dsh-cc/permission-rules`.
3
+ * Guards the closed vocabulary of durable `permission/mode` session events and
4
+ * rejects illegal payloads at the session boundary.
5
+ * @module @dsh-cc/permission-rules/invariant
6
+ */
7
+ import { SANDBOX_MODES } from '@deepseek-ai/dsh-sandbox-policy';
8
+ import { SWITCHABLE_PERMISSION_MODES } from "./types.js";
9
+ const PACKAGE_NAME = '@dsh-cc/permission-rules';
10
+ /** Cordis companion plugin name. */
11
+ export const name = 'cc-permission-rules-invariant';
12
+ /** Service required before the companion can reserve package ownership. */
13
+ export const inject = ['invariants'];
14
+ /**
15
+ * Validate one `permission/mode` event against its closed vocabularies. `plan`
16
+ * is never writable through this event (it is owned by plan-mode's `plan/mode`),
17
+ * and `resumeSandbox` must be a known sandbox mode when present.
18
+ * @param event - the session event to validate.
19
+ * @param fail - reporter bound to this package, throwing on violation.
20
+ */
21
+ export function assertPermissionModeEvent(event, fail) {
22
+ const wire = event;
23
+ if (wire.type !== 'permission/mode')
24
+ return;
25
+ const data = wire.data;
26
+ if (!SWITCHABLE_PERMISSION_MODES.includes(data.mode)) {
27
+ fail(`permission/mode carries unknown mode ${JSON.stringify(data.mode)} (plan is owned by plan-mode)`);
28
+ }
29
+ if (data.resumeSandbox !== undefined && !SANDBOX_MODES.includes(data.resumeSandbox)) {
30
+ fail(`permission/mode carries unknown resumeSandbox ${JSON.stringify(data.resumeSandbox)}`);
31
+ }
32
+ }
33
+ /** Install closed-vocabulary checks on durable permission/mode events. */
34
+ const install = Object.assign((ctx, fail) => {
35
+ const seed = (session) => {
36
+ for (const event of session.events)
37
+ assertPermissionModeEvent(event, fail);
38
+ };
39
+ for (const session of ctx.sessions.list())
40
+ seed(session);
41
+ ctx.on('session/created', (session) => { seed(session); }, { global: true });
42
+ ctx.on('session/event', (session, event) => {
43
+ void session;
44
+ assertPermissionModeEvent(event, fail);
45
+ }, { global: true });
46
+ }, { inject: ['sessions'] });
47
+ /* jscpd:ignore-end */
48
+ /**
49
+ * Register this package's invariant companion.
50
+ * @param ctx - Cordis context carrying the invariant service.
51
+ * @returns the installed registration's disposer after setup succeeds.
52
+ */
53
+ export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
54
+ //# sourceMappingURL=invariant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariant.js","sourceRoot":"","sources":["../src/invariant.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAA;AAE/D,OAAO,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAGxD,MAAM,YAAY,GAAG,0BAA0B,CAAA;AAE/C,oCAAoC;AACpC,MAAM,CAAC,MAAM,IAAI,GAAG,+BAA+B,CAAA;AACnD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,YAAY,CAAC,CAAA;AAEpC;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAmB,EAAE,IAAsB;IACnF,MAAM,IAAI,GAAG,KAAmE,CAAA;IAChF,IAAI,IAAI,CAAC,IAAI,KAAK,iBAAiB;QAAE,OAAM;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;IACtB,IAAI,CAAE,2BAAiD,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5E,IAAI,CAAC,wCAAwC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAA;IACxG,CAAC;IACD,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,CAAE,aAAmC,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3G,IAAI,CAAC,iDAAiD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,CAAA;IAC7F,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,MAAM,OAAO,GAAuB,MAAM,CAAC,MAAM,CAAC,CAAC,GAAY,EAAE,IAAsB,EAAE,EAAE;IACzF,MAAM,IAAI,GAAG,CAAC,OAAgB,EAAQ,EAAE;QACtC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM;YAAE,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IAC5E,CAAC,CAAA;IACD,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,IAAI,CAAC,OAAO,CAAC,CAAA;IACxD,GAAG,CAAC,EAAE,CAAC,iBAAiB,EAAE,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;IAC3E,GAAG,CAAC,EAAE,CAAC,eAAe,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACzC,KAAK,OAAO,CAAA;QACZ,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACxC,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;AACtB,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAA;AAC5B,sBAAsB;AAEtB;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,GAAY,EAAuB,EAAE,CACzD,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAA"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The LLM risk classifier for `auto` mode: a one-shot auxiliary-model verdict
3
+ * (`allow` | `ask`) over the tool name plus rendered parameters, escalate-only
4
+ * and fail-safe (every failure ⇒ `ask`, never `allow`, never a throw).
5
+ *
6
+ * Dependency-light by design: the model seam (`stream`) is structural — the
7
+ * listener (Stage C) injects the real dsh-llm/alias wiring. The per-call route
8
+ * is passed as data (`classify(exec, { route })`), never resolved from ambient
9
+ * state, so concurrent calls cannot cross-contaminate. The result carries all
10
+ * digest/identity metadata the caller needs to audit; this module performs no
11
+ * session access and no I/O beyond the injected stream. Only `node:crypto` is
12
+ * imported.
13
+ *
14
+ * @module @dsh-cc/permission-rules/llm-classifier
15
+ */
16
+ import type { ToolExecution } from '@dsh-cc/tools';
17
+ /** A model verdict. `ask` is the only escalation the stage can produce. */
18
+ export type LlmVerdict = {
19
+ verdict: 'allow';
20
+ reason: string;
21
+ } | {
22
+ verdict: 'ask';
23
+ reason: string;
24
+ };
25
+ /**
26
+ * Why a classification failed. `unarmed` marks an unresolvable model route;
27
+ * `cancelled` marks a caller abort mid-flight (host noise, never a lane
28
+ * fault — the breaker stage never counts it).
29
+ */
30
+ export type ClassifierFailure = 'timeout' | 'error' | 'malformed' | 'unarmed' | 'cancelled';
31
+ /** The per-call route, passed as data instead of resolved from ambient state. */
32
+ export type ClassifierRoute = {
33
+ provider: string;
34
+ model: string;
35
+ };
36
+ /** Durable audit record for one classify call. The raw input NEVER appears — only its digest. */
37
+ export type ClassifierAuditEvent = {
38
+ tool: string;
39
+ /** sha256 of the rendered classifier input. */
40
+ digest: string;
41
+ verdict: 'allow' | 'ask';
42
+ failure?: ClassifierFailure;
43
+ /** The route identity used for the call (`provider/model`), when the route was armed. */
44
+ routeAlias?: string;
45
+ provider?: string;
46
+ model?: string;
47
+ latencyMs: number;
48
+ cacheHit: boolean;
49
+ };
50
+ /**
51
+ * A classification result: a verdict tagged with the failure kind when it did
52
+ * not come from a parseable model output, plus every digest/identity field the
53
+ * caller needs to build its audit record.
54
+ */
55
+ export type LlmClassification = ClassifierAuditEvent & LlmVerdict;
56
+ /** Structural face the listener injects. No dsh-llm imports in this module. */
57
+ export type LlmClassifierDeps = {
58
+ stream(opts: {
59
+ provider: string;
60
+ model: string;
61
+ system: string;
62
+ prompt: string;
63
+ maxTokens: number;
64
+ signal?: AbortSignal;
65
+ }): Promise<string>;
66
+ /** Already $defaults-expanded prose rules. */
67
+ softDeny: readonly string[];
68
+ timeoutMs: number;
69
+ cacheMaxEntries: number;
70
+ /**
71
+ * Optional env-gated debug sink (process log — NEVER session events).
72
+ * When present, every raw model output is logged with the
73
+ * `[dsh:classifier:raw]` prefix, truncated to 2 KiB. Raw output may echo
74
+ * tool input (including secrets the agent was about to run), so this
75
+ * stays a deliberately opt-in channel with no redaction machinery.
76
+ */
77
+ debug?: (message: string) => void;
78
+ };
79
+ export type LlmClassifier = {
80
+ /**
81
+ * Never throws. Any failure ⇒ {verdict:'ask', reason} tagged with the
82
+ * failure kind. An undefined route ⇒ the 'unarmed' classification (the
83
+ * stream is never called).
84
+ */
85
+ classify(exec: ToolExecution, opts?: {
86
+ route?: ClassifierRoute;
87
+ }): Promise<LlmClassification>;
88
+ };
89
+ /**
90
+ * The documented CC classifier duties, as prose rules. Expanded into the
91
+ * config list wherever the literal `"$defaults"` appears (position-preserving).
92
+ */
93
+ export declare const DEFAULT_SOFT_DENY: readonly string[];
94
+ /**
95
+ * Expand the configured soft-deny list: every `"$defaults"` entry is replaced
96
+ * in place by {@link DEFAULT_SOFT_DENY}; a list without it replaces the
97
+ * built-ins entirely (CC semantics). Duplicates are preserved as written.
98
+ */
99
+ export declare function expandSoftDeny(list: readonly string[]): string[];
100
+ /** The session-scope cache key: tool, rendered input, and the soft-deny list all bust it. */
101
+ export declare function classificationKey(toolName: string, renderedInput: string, softDeny: readonly string[]): string;
102
+ /**
103
+ * Build the classifier. See the module doc for the contract: escalate-only,
104
+ * fail-to-ask, never throws, LRU-cached per (tool | input | soft-deny list).
105
+ */
106
+ export declare function createLlmClassifier(deps: LlmClassifierDeps): LlmClassifier;
107
+ //# sourceMappingURL=llm-classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm-classifier.d.ts","sourceRoot":"","sources":["../src/llm-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAElD,2EAA2E;AAC3E,MAAM,MAAM,UAAU,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAElG;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,OAAO,GAAG,WAAW,GAAG,SAAS,GAAG,WAAW,CAAA;AAE3F,iFAAiF;AACjF,MAAM,MAAM,eAAe,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjE,iGAAiG;AACjG,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,OAAO,GAAG,KAAK,CAAA;IACxB,OAAO,CAAC,EAAE,iBAAiB,CAAA;IAC3B,yFAAyF;IACzF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;CAClB,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,GAAG,UAAU,CAAA;AAEjE,+EAA+E;AAC/E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3I,8CAA8C;IAC9C,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,eAAe,CAAA;KAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;CAC9F,CAAA;AAeD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAO9C,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAOhE;AAMD,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAE9G;AAmFD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,aAAa,CA+F1E"}