@goodandready/dsh-approval-gate 0.1.16 → 0.1.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 CHANGED
@@ -2,6 +2,7 @@ import { SecurityEngine } from "./engine.js";
2
2
  import { BandsEngine } from "./bands.js";
3
3
  import { AuditLogger } from "./audit-log.js";
4
4
  import { DeclarativeRulesEngine } from "./rules-loader.js";
5
+ import { LlmClassifier } from "./classifier.js";
5
6
  import Schema from "@deepseek-ai/schemastery";
6
7
 
7
8
  if (typeof Schema.prototype?.volatile !== "function") {
@@ -217,6 +218,26 @@ export const Config = Schema.object({
217
218
  auditFailClosed: Schema.boolean().default(false).volatile(),
218
219
  /** Optional custom declarative rules YAML file path (#38). */
219
220
  rulesFile: Schema.string().default("").volatile(),
221
+ /** Semantic LLM classifier endpoint URL (#127). */
222
+ classifierEndpoint: Schema.string().default("").volatile(),
223
+ /** API key for classifier endpoint (#115). */
224
+ classifierApiKey: Schema.string().default("").volatile(),
225
+ /** Model name for semantic classification. */
226
+ classifierModel: Schema.string().default("gpt-4o-mini").volatile(),
227
+ /** Request timeout in ms for classifier (#118). */
228
+ classifierTimeout: Schema.number().default(3000).volatile(),
229
+ /** Policy on classifier timeout: 'strict' or 'permissive' (#118). */
230
+ classifierTimeoutPolicy: Schema.union(["strict", "permissive"]).default("strict").volatile(),
231
+ /** Maximum context characters for classifier prompt (#117). */
232
+ classifierMaxContext: Schema.number().default(1000).volatile(),
233
+ /** Maximum payload bytes for tool arguments (#71). */
234
+ maxPayloadBytes: Schema.number().default(65536).volatile(),
235
+ /** Isolate temporary directories to workspace/.dsh/tmp (#102). */
236
+ isolateTempRoots: Schema.boolean().default(true).volatile(),
237
+ /** Maximum allowed created files per session to prevent overengineering (#89). */
238
+ maxFilesPerSession: Schema.number().default(10).volatile(),
239
+ /** Task contract boundary rules (#87). */
240
+ taskContract: Schema.any().volatile(),
220
241
  /** Security engine instance (internal/testing). */
221
242
  engine: Schema.any().hidden(),
222
243
  });
@@ -256,9 +277,30 @@ export function apply(ctx, config) {
256
277
  };
257
278
  const unattendedPolicies = cfg.unattendedPolicies || DEFAULT_UNATTENDED_POLICIES.slice();
258
279
  const forcedUnattended = cfg.unattended === true;
280
+ const classifier = cfg.classifierEndpoint
281
+ ? new LlmClassifier({
282
+ endpoint: cfg.classifierEndpoint,
283
+ apiKey: cfg.classifierApiKey,
284
+ model: cfg.classifierModel,
285
+ timeoutMs: cfg.classifierTimeout,
286
+ timeoutPolicy: cfg.classifierTimeoutPolicy,
287
+ maxContextChars: cfg.classifierMaxContext,
288
+ })
289
+ : null;
290
+
259
291
  const engine = (config && config.engine) || cfg.engine || new SecurityEngine({
260
- inspectConfig: { toolName: toolName, fileWriteTools: fileWriteTools, workspaceDir: cfg.workspaceDir || "" },
292
+ inspectConfig: {
293
+ toolName: toolName,
294
+ fileWriteTools: fileWriteTools,
295
+ workspaceDir: cfg.workspaceDir || "",
296
+ maxPayloadBytes: cfg.maxPayloadBytes !== undefined ? cfg.maxPayloadBytes : 65536,
297
+ isolateTempRoots: cfg.isolateTempRoots !== false,
298
+ },
299
+ taskContract: cfg.taskContract || undefined,
300
+ maxFilesPerSession: cfg.maxFilesPerSession !== undefined ? cfg.maxFilesPerSession : 10,
261
301
  bands: new BandsEngine({ toolRegistry: ctx && ctx.tools }),
302
+ classifier,
303
+ timeoutPolicy: cfg.classifierTimeoutPolicy || "strict",
262
304
  defaultVerdict: cfg.defaultVerdict || "allow",
263
305
  dryRun: Boolean(cfg.dryRun || cfg.shadowMode),
264
306
  auditFailClosed: Boolean(cfg.auditFailClosed),
@@ -288,6 +330,8 @@ export function apply(ctx, config) {
288
330
  active: true,
289
331
  dryRun: Boolean(cfg.dryRun || cfg.shadowMode),
290
332
  auditFailClosed: Boolean(cfg.auditFailClosed),
333
+ classifierActive: Boolean(classifier || engine.classifier),
334
+ timeoutPolicy: engine.timeoutPolicy || "strict",
291
335
  counters: engine.getCounters(),
292
336
  rulesCount: engine.declarativeRules ? engine.declarativeRules.count() : 0,
293
337
  cacheStats: engine.cache ? engine.cache.stats() : null,
@@ -519,3 +563,15 @@ export {
519
563
  WORKSPACE_RULES_REL,
520
564
  USER_RULES_REL,
521
565
  } from "./rules-loader.js";
566
+ export {
567
+ normalizeChatCompletionsUrl,
568
+ withLlmRetry,
569
+ sanitizeClassifierInput,
570
+ truncateClassifierContext,
571
+ isLowRiskExecution,
572
+ LlmClassifier,
573
+ } from "./classifier.js";
574
+ export { ContractGuard } from "./contract-guard.js";
575
+ export { OverengineeringGuard, DEFAULT_MAX_FILES_PER_SESSION } from "./overengineering.js";
576
+ export { resolveTempRoot, isGlobalTempPath, checkTempIsolation } from "./paths.js";
577
+ export { computePayloadSize, scanPromptInjection } from "./inspect.js";