agentshield-sdk 10.0.0 → 12.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.
@@ -1,812 +1,664 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * Agent Shield — Smart Configuration System (v8)
4
+ * Agent Shield — Smart Configuration / Policy Engine (v12.0)
5
5
  *
6
- * Unified configuration for all Shield features.
7
- * Three ways to use:
6
+ * Auto-configures Agent Shield based on deployment context. Provides presets
7
+ * for common deployment scenarios (chatbot, coding agent, RAG pipeline,
8
+ * MCP server, enterprise, paranoid) and generates complete configurations
9
+ * with security recommendations.
8
10
  *
9
- * 1. Quick start (3 lines):
10
- * const { createShield } = require('agentshield-sdk');
11
- * const shield = createShield('chatbot');
12
- * const result = shield.scan(text);
13
- *
14
- * 2. Builder pattern:
15
- * const shield = createShield()
16
- * .preset('rag_pipeline')
17
- * .enableIntent({ purpose: 'Answer questions from docs' })
18
- * .enableLearning({ persist: true })
19
- * .enableEnsemble()
20
- * .build();
21
- *
22
- * 3. Config object:
23
- * const shield = createShield({
24
- * preset: 'high_security',
25
- * intent: { purpose: 'Flight booking agent', allowedTools: ['searchFlights', 'bookFlight'] },
26
- * learning: { persist: true, feedbackEnabled: true },
27
- * ensemble: true,
28
- * goalDrift: true,
29
- * crossTurn: { windowSize: 20 },
30
- * toolSequence: true,
31
- * adaptiveThresholds: true
32
- * });
11
+ * All processing runs locally — no data ever leaves your environment.
33
12
  *
34
13
  * @module smart-config
35
14
  */
36
15
 
37
- /** Valid preset names */
38
- const VALID_PRESETS = [
39
- 'chatbot',
40
- 'coding_agent',
41
- 'rag_pipeline',
42
- 'customer_support',
43
- 'internal_tool',
44
- 'multi_agent',
45
- 'high_security',
46
- 'minimal',
47
- 'mcp_server'
48
- ];
49
-
50
- /** Valid sensitivity levels */
51
- const VALID_SENSITIVITIES = ['low', 'medium', 'high'];
16
+ // =========================================================================
17
+ // DEPLOYMENT PRESETS
18
+ // =========================================================================
52
19
 
53
- /** Valid block threshold levels */
54
- const VALID_BLOCK_THRESHOLDS = ['low', 'medium', 'high', 'critical'];
55
-
56
- /** Valid ensemble voter names */
57
- const VALID_VOTERS = ['pattern', 'tfidf', 'entropy', 'ipia', 'semantic', 'behavioral', 'heuristic'];
58
-
59
- /** Default configurations for each v8 feature */
60
- const FEATURE_DEFAULTS = {
61
- intent: {
62
- purpose: '',
63
- allowedTools: [],
64
- allowedTopics: [],
65
- maxDriftScore: 0.6
66
- },
67
- learning: {
68
- persist: false,
69
- persistPath: './.agentshield/learned-patterns.json',
70
- promotionThreshold: 3,
71
- maxPatterns: 500
72
- },
73
- feedback: {
74
- autoRetrain: true,
75
- maxPending: 100,
76
- cooldownMs: 5000
77
- },
78
- ensemble: {
79
- voters: ['pattern', 'tfidf', 'entropy', 'ipia'],
80
- threshold: 0.5,
81
- requireUnanimous: false
20
+ /**
21
+ * Predefined deployment presets with tuned security settings.
22
+ * @type {Object<string, object>}
23
+ */
24
+ const DEPLOYMENT_PRESETS = {
25
+ chatbot: {
26
+ name: 'Chatbot',
27
+ description: 'Customer-facing conversational agent with moderate security',
28
+ detection: {
29
+ enablePatternScanner: true,
30
+ enableMicroModel: true,
31
+ enableIntentGraph: false,
32
+ enableOWASP: false,
33
+ sensitivity: 'medium',
34
+ scanTimeout: 50
35
+ },
36
+ protection: {
37
+ blockOnThreat: true,
38
+ piiRedaction: true,
39
+ canaryTokens: false,
40
+ rateLimiting: true,
41
+ rateLimit: { maxRequests: 60, windowMs: 60000 },
42
+ circuitBreaker: { threshold: 10, resetMs: 30000 }
43
+ },
44
+ monitoring: {
45
+ auditLog: true,
46
+ metricsExport: false,
47
+ alerting: false,
48
+ shadowMode: false
49
+ },
50
+ compliance: {
51
+ frameworks: ['gdpr'],
52
+ dataRetention: '30d'
53
+ }
82
54
  },
83
- goalDrift: {
84
- checkInterval: 5,
85
- driftThreshold: 0.6,
86
- windowSize: 10
55
+
56
+ coding_agent: {
57
+ name: 'Coding Agent',
58
+ description: 'AI coding assistant with tool execution capabilities',
59
+ detection: {
60
+ enablePatternScanner: true,
61
+ enableMicroModel: true,
62
+ enableIntentGraph: true,
63
+ enableOWASP: true,
64
+ sensitivity: 'high',
65
+ scanTimeout: 100
66
+ },
67
+ protection: {
68
+ blockOnThreat: true,
69
+ piiRedaction: true,
70
+ canaryTokens: true,
71
+ rateLimiting: true,
72
+ rateLimit: { maxRequests: 120, windowMs: 60000 },
73
+ circuitBreaker: { threshold: 5, resetMs: 60000 },
74
+ toolGuard: {
75
+ enabled: true,
76
+ blockedTools: ['exec', 'shell', 'eval'],
77
+ requireApproval: ['writeFile', 'deleteFile', 'networkRequest'],
78
+ maxChainDepth: 5
79
+ }
80
+ },
81
+ monitoring: {
82
+ auditLog: true,
83
+ metricsExport: true,
84
+ alerting: true,
85
+ shadowMode: false
86
+ },
87
+ compliance: {
88
+ frameworks: ['soc2'],
89
+ dataRetention: '90d'
90
+ }
87
91
  },
88
- crossTurn: {
89
- windowSize: 20,
90
- scanInterval: 3,
91
- accumulateAll: true
92
+
93
+ rag_pipeline: {
94
+ name: 'RAG Pipeline',
95
+ description: 'Retrieval-augmented generation with document ingestion',
96
+ detection: {
97
+ enablePatternScanner: true,
98
+ enableMicroModel: true,
99
+ enableIntentGraph: true,
100
+ enableOWASP: true,
101
+ sensitivity: 'high',
102
+ scanTimeout: 150,
103
+ enableIPIA: true,
104
+ enableDocumentScanner: true
105
+ },
106
+ protection: {
107
+ blockOnThreat: true,
108
+ piiRedaction: true,
109
+ canaryTokens: true,
110
+ rateLimiting: true,
111
+ rateLimit: { maxRequests: 30, windowMs: 60000 },
112
+ circuitBreaker: { threshold: 5, resetMs: 60000 },
113
+ semanticIsolation: {
114
+ enabled: true,
115
+ trustLevels: { system: 'trusted', user: 'semi-trusted', rag_chunk: 'untrusted' }
116
+ }
117
+ },
118
+ monitoring: {
119
+ auditLog: true,
120
+ metricsExport: true,
121
+ alerting: true,
122
+ shadowMode: false
123
+ },
124
+ compliance: {
125
+ frameworks: ['soc2', 'gdpr'],
126
+ dataRetention: '90d'
127
+ }
92
128
  },
93
- toolSequence: {
94
- learningPeriod: 50,
95
- anomalyThreshold: 0.15,
96
- maxChainLength: 10
129
+
130
+ mcp_server: {
131
+ name: 'MCP Server',
132
+ description: 'Model Context Protocol server with tool exposure',
133
+ detection: {
134
+ enablePatternScanner: true,
135
+ enableMicroModel: true,
136
+ enableIntentGraph: true,
137
+ enableOWASP: true,
138
+ sensitivity: 'high',
139
+ scanTimeout: 100,
140
+ enableSupplyChainScan: true
141
+ },
142
+ protection: {
143
+ blockOnThreat: true,
144
+ piiRedaction: true,
145
+ canaryTokens: true,
146
+ rateLimiting: true,
147
+ rateLimit: { maxRequests: 100, windowMs: 60000 },
148
+ circuitBreaker: { threshold: 3, resetMs: 120000 },
149
+ mcpGuard: {
150
+ enabled: true,
151
+ attestation: true,
152
+ ssrfFirewall: true,
153
+ oauthEnforcement: true,
154
+ crossServerIsolation: true,
155
+ behaviorBaseline: true
156
+ },
157
+ intentBinding: { enabled: true },
158
+ messageIntegrity: { enabled: true }
159
+ },
160
+ monitoring: {
161
+ auditLog: true,
162
+ metricsExport: true,
163
+ alerting: true,
164
+ shadowMode: false,
165
+ driftMonitor: { enabled: true, windowSize: 100 }
166
+ },
167
+ compliance: {
168
+ frameworks: ['soc2', 'owasp-agentic'],
169
+ dataRetention: '180d'
170
+ }
97
171
  },
98
- adaptiveThresholds: {
99
- calibrationSamples: 100,
100
- adjustInterval: 50,
101
- minConfidence: 0.3
172
+
173
+ enterprise: {
174
+ name: 'Enterprise',
175
+ description: 'Enterprise deployment with full security, compliance, and monitoring',
176
+ detection: {
177
+ enablePatternScanner: true,
178
+ enableMicroModel: true,
179
+ enableIntentGraph: true,
180
+ enableOWASP: true,
181
+ sensitivity: 'high',
182
+ scanTimeout: 200,
183
+ enableIPIA: true,
184
+ enableDocumentScanner: true,
185
+ enableSupplyChainScan: true
186
+ },
187
+ protection: {
188
+ blockOnThreat: true,
189
+ piiRedaction: true,
190
+ canaryTokens: true,
191
+ rateLimiting: true,
192
+ rateLimit: { maxRequests: 200, windowMs: 60000 },
193
+ circuitBreaker: { threshold: 5, resetMs: 60000 },
194
+ toolGuard: {
195
+ enabled: true,
196
+ blockedTools: ['exec', 'shell', 'eval'],
197
+ requireApproval: [],
198
+ maxChainDepth: 10
199
+ },
200
+ mcpGuard: {
201
+ enabled: true,
202
+ attestation: true,
203
+ ssrfFirewall: true,
204
+ oauthEnforcement: true,
205
+ crossServerIsolation: true,
206
+ behaviorBaseline: true
207
+ },
208
+ semanticIsolation: { enabled: true },
209
+ intentBinding: { enabled: true },
210
+ messageIntegrity: { enabled: true },
211
+ multiTenant: { enabled: true },
212
+ rbac: { enabled: true }
213
+ },
214
+ monitoring: {
215
+ auditLog: true,
216
+ metricsExport: true,
217
+ alerting: true,
218
+ shadowMode: false,
219
+ driftMonitor: { enabled: true, windowSize: 200 },
220
+ socIntegration: { enabled: true },
221
+ immutableAudit: { enabled: true }
222
+ },
223
+ compliance: {
224
+ frameworks: ['soc2', 'hipaa', 'gdpr', 'nist', 'owasp-agentic', 'eu-ai-act'],
225
+ dataRetention: '365d',
226
+ certificationLevel: 'gold'
227
+ }
102
228
  },
103
- selfTraining: {
104
- generations: 10,
105
- populationSize: 20,
106
- mutationRate: 0.3,
107
- interval: 0
229
+
230
+ paranoid: {
231
+ name: 'Paranoid',
232
+ description: 'Maximum security — all detectors, all protections, strictest thresholds',
233
+ detection: {
234
+ enablePatternScanner: true,
235
+ enableMicroModel: true,
236
+ enableIntentGraph: true,
237
+ enableOWASP: true,
238
+ sensitivity: 'critical',
239
+ scanTimeout: 500,
240
+ enableIPIA: true,
241
+ enableDocumentScanner: true,
242
+ enableSupplyChainScan: true,
243
+ enableBehaviorProfiling: true,
244
+ enableHoneypot: true,
245
+ enableSelfTraining: true,
246
+ enableAttackSurface: true
247
+ },
248
+ protection: {
249
+ blockOnThreat: true,
250
+ piiRedaction: true,
251
+ canaryTokens: true,
252
+ rateLimiting: true,
253
+ rateLimit: { maxRequests: 30, windowMs: 60000 },
254
+ circuitBreaker: { threshold: 2, resetMs: 300000 },
255
+ toolGuard: {
256
+ enabled: true,
257
+ blockedTools: ['exec', 'shell', 'eval', 'spawn', 'fork'],
258
+ requireApproval: ['writeFile', 'deleteFile', 'networkRequest', 'databaseQuery'],
259
+ maxChainDepth: 3
260
+ },
261
+ mcpGuard: {
262
+ enabled: true,
263
+ attestation: true,
264
+ ssrfFirewall: true,
265
+ oauthEnforcement: true,
266
+ crossServerIsolation: true,
267
+ behaviorBaseline: true
268
+ },
269
+ semanticIsolation: { enabled: true },
270
+ intentBinding: { enabled: true },
271
+ messageIntegrity: { enabled: true },
272
+ promptHardening: { enabled: true, level: 4 },
273
+ multiTenant: { enabled: true },
274
+ rbac: { enabled: true }
275
+ },
276
+ monitoring: {
277
+ auditLog: true,
278
+ metricsExport: true,
279
+ alerting: true,
280
+ shadowMode: true,
281
+ driftMonitor: { enabled: true, windowSize: 50 },
282
+ socIntegration: { enabled: true },
283
+ immutableAudit: { enabled: true },
284
+ continuousSecurity: { enabled: true, intervalMs: 60000 }
285
+ },
286
+ compliance: {
287
+ frameworks: ['soc2', 'hipaa', 'gdpr', 'nist', 'owasp-agentic', 'eu-ai-act'],
288
+ dataRetention: '365d',
289
+ certificationLevel: 'platinum'
290
+ }
108
291
  }
109
292
  };
110
293
 
294
+ // =========================================================================
295
+ // VALIDATION RULES
296
+ // =========================================================================
297
+
111
298
  /**
112
- * Preset configurations that set sensible defaults for common use cases.
113
- * @private
299
+ * Configuration validation rules.
300
+ * @type {Array<{ check: function, message: string, severity: string }>}
114
301
  */
115
- const PRESET_CONFIGS = {
116
- chatbot: {
117
- sensitivity: 'medium',
118
- blockOnThreat: true,
119
- blockThreshold: 'medium',
120
- intent: { purpose: 'General-purpose chatbot' },
121
- crossTurn: true,
122
- goalDrift: true
302
+ const VALIDATION_RULES = [
303
+ {
304
+ check: (cfg) => !cfg.detection?.enablePatternScanner && !cfg.detection?.enableMicroModel,
305
+ message: 'No detection engine enabled — all threats will be missed',
306
+ severity: 'critical'
123
307
  },
124
- coding_agent: {
125
- sensitivity: 'high',
126
- blockOnThreat: true,
127
- blockThreshold: 'high',
128
- intent: { purpose: 'Code generation and assistance' },
129
- toolSequence: true,
130
- ensemble: true
308
+ {
309
+ check: (cfg) => !cfg.protection?.blockOnThreat,
310
+ message: 'Threat blocking is disabled — threats will be logged but not stopped',
311
+ severity: 'high'
131
312
  },
132
- rag_pipeline: {
133
- sensitivity: 'high',
134
- blockOnThreat: true,
135
- blockThreshold: 'medium',
136
- intent: { purpose: 'Answer questions from documents' },
137
- ensemble: true,
138
- crossTurn: true,
139
- adaptiveThresholds: true
313
+ {
314
+ check: (cfg) => !cfg.protection?.piiRedaction,
315
+ message: 'PII redaction is disabled — personal data may leak to LLM providers',
316
+ severity: 'high'
140
317
  },
141
- customer_support: {
142
- sensitivity: 'medium',
143
- blockOnThreat: true,
144
- blockThreshold: 'medium',
145
- intent: { purpose: 'Customer support agent' },
146
- goalDrift: true,
147
- crossTurn: true,
148
- feedback: true
318
+ {
319
+ check: (cfg) => !cfg.monitoring?.auditLog,
320
+ message: 'Audit logging is disabled — no forensic trail for incidents',
321
+ severity: 'medium'
149
322
  },
150
- internal_tool: {
151
- sensitivity: 'low',
152
- blockOnThreat: false,
153
- blockThreshold: 'high',
154
- intent: { purpose: 'Internal tooling agent' },
155
- toolSequence: true
323
+ {
324
+ check: (cfg) => cfg.protection?.rateLimiting && !cfg.protection?.rateLimit?.maxRequests,
325
+ message: 'Rate limiting enabled but no maxRequests set',
326
+ severity: 'medium'
156
327
  },
157
- multi_agent: {
158
- sensitivity: 'high',
159
- blockOnThreat: true,
160
- blockThreshold: 'medium',
161
- intent: { purpose: 'Multi-agent orchestration' },
162
- ensemble: true,
163
- toolSequence: true,
164
- crossTurn: true,
165
- goalDrift: true
328
+ {
329
+ check: (cfg) => cfg.protection?.mcpGuard?.enabled && !cfg.protection?.mcpGuard?.ssrfFirewall,
330
+ message: 'MCP Guard enabled without SSRF firewall — CVE-2026-26118 risk',
331
+ severity: 'high'
166
332
  },
167
- high_security: {
168
- sensitivity: 'high',
169
- blockOnThreat: true,
170
- blockThreshold: 'low',
171
- intent: { purpose: 'High-security agent' },
172
- ensemble: { voters: ['pattern', 'tfidf', 'entropy', 'ipia', 'semantic', 'behavioral'], requireUnanimous: false },
173
- learning: { persist: true },
174
- goalDrift: { driftThreshold: 0.4 },
175
- crossTurn: true,
176
- toolSequence: true,
177
- adaptiveThresholds: true,
178
- selfTraining: true
333
+ {
334
+ check: (cfg) => cfg.protection?.mcpGuard?.enabled && !cfg.protection?.mcpGuard?.attestation,
335
+ message: 'MCP Guard enabled without server attestation — tool rug-pull risk',
336
+ severity: 'high'
179
337
  },
180
- minimal: {
181
- sensitivity: 'low',
182
- blockOnThreat: false,
183
- blockThreshold: 'critical'
338
+ {
339
+ check: (cfg) => cfg.protection?.toolGuard?.enabled && (!cfg.protection?.toolGuard?.blockedTools || cfg.protection.toolGuard.blockedTools.length === 0),
340
+ message: 'Tool guard enabled with empty blocklist — exec/shell/eval should be blocked',
341
+ severity: 'medium'
184
342
  },
185
- mcp_server: {
186
- sensitivity: 'high',
187
- blockOnThreat: true,
188
- blockThreshold: 'medium',
189
- intent: { purpose: 'MCP tool server' },
190
- ensemble: true,
191
- toolSequence: { learningPeriod: 30, anomalyThreshold: 0.1, maxChainLength: 15 },
192
- crossTurn: true,
193
- goalDrift: { driftThreshold: 0.5 },
194
- adaptiveThresholds: true
195
- }
196
- };
197
-
198
- /**
199
- * Deep-merge source into target. Arrays are replaced, not concatenated.
200
- * @private
201
- * @param {object} target
202
- * @param {object} source
203
- * @returns {object}
204
- */
205
- function deepMerge(target, source) {
206
- const result = Object.assign({}, target);
207
- for (const key of Object.keys(source)) {
208
- const srcVal = source[key];
209
- const tgtVal = result[key];
210
- if (srcVal && typeof srcVal === 'object' && !Array.isArray(srcVal) &&
211
- tgtVal && typeof tgtVal === 'object' && !Array.isArray(tgtVal)) {
212
- result[key] = deepMerge(tgtVal, srcVal);
213
- } else {
214
- result[key] = srcVal;
215
- }
343
+ {
344
+ check: (cfg) => cfg.detection?.enableIPIA && !cfg.protection?.semanticIsolation?.enabled,
345
+ message: 'IPIA detection enabled without semantic isolation — reduced effectiveness',
346
+ severity: 'medium'
347
+ },
348
+ {
349
+ check: (cfg) => !cfg.protection?.circuitBreaker,
350
+ message: 'No circuit breaker configured — sustained attacks may overwhelm the system',
351
+ severity: 'medium'
352
+ },
353
+ {
354
+ check: (cfg) => cfg.detection?.scanTimeout && cfg.detection.scanTimeout > 300,
355
+ message: 'Scan timeout exceeds 300ms — may impact user experience',
356
+ severity: 'low'
357
+ },
358
+ {
359
+ check: (cfg) => cfg.compliance?.frameworks?.includes('hipaa') && !cfg.monitoring?.immutableAudit?.enabled,
360
+ message: 'HIPAA compliance requires immutable audit logging',
361
+ severity: 'high'
216
362
  }
217
- return result;
218
- }
363
+ ];
219
364
 
220
- /**
221
- * Deep-freeze an object and all nested objects.
222
- * @private
223
- * @param {object} obj
224
- * @returns {object}
225
- */
226
- function deepFreeze(obj) {
227
- Object.freeze(obj);
228
- for (const val of Object.values(obj)) {
229
- if (val && typeof val === 'object' && !Object.isFrozen(val)) {
230
- deepFreeze(val);
231
- }
232
- }
233
- return obj;
234
- }
365
+ // =========================================================================
366
+ // SMART CONFIG CLASS
367
+ // =========================================================================
235
368
 
236
369
  /**
237
- * Resolve a feature value: `true` becomes the defaults, an object merges with defaults,
238
- * falsy stays null.
239
- * @private
240
- * @param {string} featureName
241
- * @param {*} value
242
- * @returns {object|null}
370
+ * Smart Configuration and Policy Engine.
371
+ *
372
+ * Analyzes deployment context and generates optimized security configurations.
373
+ *
374
+ * @example
375
+ * const config = new SmartConfig();
376
+ * const policy = config.generatePolicy('mcp_server');
377
+ * const analysis = config.analyzeDeployment({
378
+ * tools: ['readFile', 'writeFile', 'exec'],
379
+ * model: 'claude-3-opus',
380
+ * mcpServers: ['filesystem', 'github']
381
+ * });
243
382
  */
244
- function resolveFeature(featureName, value) {
245
- if (!value) return null;
246
- const defaults = FEATURE_DEFAULTS[featureName];
247
- if (!defaults) return null;
248
- if (value === true) return Object.assign({}, defaults);
249
- if (typeof value === 'object' && !Array.isArray(value)) {
250
- return Object.assign({}, defaults, value);
251
- }
252
- return Object.assign({}, defaults);
253
- }
383
+ class SmartConfig {
384
+ /**
385
+ * @param {object} [options]
386
+ * @param {Object<string, object>} [options.customPresets] - Additional presets to register
387
+ */
388
+ constructor(options = {}) {
389
+ this.presets = { ...DEPLOYMENT_PRESETS };
254
390
 
255
- /**
256
- * Validate a configuration object.
257
- * @param {object} config - The configuration to validate.
258
- * @returns {{ valid: boolean, errors: string[] }} Validation result.
259
- */
260
- function validateConfig(config) {
261
- const errors = [];
391
+ if (options.customPresets) {
392
+ for (const [name, preset] of Object.entries(options.customPresets)) {
393
+ this.presets[name] = preset;
394
+ }
395
+ }
262
396
 
263
- if (!config || typeof config !== 'object') {
264
- return { valid: false, errors: ['Config must be a non-null object'] };
397
+ console.log(`[Agent Shield] SmartConfig initialized with ${Object.keys(this.presets).length} presets`);
265
398
  }
266
399
 
267
- // Preset
268
- if (config.preset !== undefined && config.preset !== null) {
269
- if (!VALID_PRESETS.includes(config.preset)) {
270
- errors.push(`Invalid preset "${config.preset}". Valid presets: ${VALID_PRESETS.join(', ')}`);
400
+ /**
401
+ * Analyze a deployment configuration and recommend security settings.
402
+ * @param {object} deployment - Deployment context
403
+ * @param {string[]} [deployment.tools] - Available tools
404
+ * @param {string[]} [deployment.mcpServers] - Connected MCP servers
405
+ * @param {string} [deployment.model] - LLM model name
406
+ * @param {boolean} [deployment.hasRAG] - Whether RAG pipeline is present
407
+ * @param {boolean} [deployment.isPublicFacing] - Whether agent faces external users
408
+ * @param {string[]} [deployment.complianceNeeds] - Required compliance frameworks
409
+ * @param {number} [deployment.expectedQPS] - Expected queries per second
410
+ * @returns {{ recommendedPreset: string, reasons: string[], config: object, warnings: string[] }}
411
+ */
412
+ analyzeDeployment(deployment = {}) {
413
+ const reasons = [];
414
+ const warnings = [];
415
+ let recommendedPreset = 'chatbot';
416
+ let riskScore = 0;
417
+
418
+ const tools = deployment.tools || [];
419
+ const mcpServers = deployment.mcpServers || [];
420
+ const complianceNeeds = deployment.complianceNeeds || [];
421
+
422
+ // Analyze tool risk
423
+ const dangerousTools = ['exec', 'shell', 'eval', 'spawn', 'fork', 'system'];
424
+ const hasDangerousTools = tools.some(t => dangerousTools.includes(t.toLowerCase()));
425
+ if (hasDangerousTools) {
426
+ riskScore += 3;
427
+ reasons.push('Dangerous tools detected (exec/shell/eval) — elevated protection required');
271
428
  }
272
- }
273
429
 
274
- // Sensitivity
275
- if (config.sensitivity !== undefined) {
276
- if (!VALID_SENSITIVITIES.includes(config.sensitivity)) {
277
- errors.push(`Invalid sensitivity "${config.sensitivity}". Must be one of: ${VALID_SENSITIVITIES.join(', ')}`);
430
+ const fileTools = ['readFile', 'writeFile', 'deleteFile', 'listDir', 'mkdir'];
431
+ const hasFileTools = tools.some(t => fileTools.includes(t));
432
+ if (hasFileTools) {
433
+ riskScore += 1;
434
+ reasons.push('File system tools present — path traversal protection recommended');
278
435
  }
279
- }
280
436
 
281
- // Block threshold
282
- if (config.blockThreshold !== undefined) {
283
- if (!VALID_BLOCK_THRESHOLDS.includes(config.blockThreshold)) {
284
- errors.push(`Invalid blockThreshold "${config.blockThreshold}". Must be one of: ${VALID_BLOCK_THRESHOLDS.join(', ')}`);
437
+ const networkTools = ['fetch', 'httpRequest', 'networkRequest', 'curl'];
438
+ const hasNetworkTools = tools.some(t => networkTools.includes(t));
439
+ if (hasNetworkTools) {
440
+ riskScore += 2;
441
+ reasons.push('Network tools present — SSRF protection and domain allowlisting recommended');
285
442
  }
286
- }
287
443
 
288
- // Intent
289
- if (config.intent) {
290
- if (typeof config.intent === 'object') {
291
- if (config.intent.purpose !== undefined && (typeof config.intent.purpose !== 'string' || config.intent.purpose.trim() === '')) {
292
- errors.push('intent.purpose must be a non-empty string when provided');
293
- }
294
- if (config.intent.maxDriftScore !== undefined) {
295
- if (typeof config.intent.maxDriftScore !== 'number' || config.intent.maxDriftScore < 0 || config.intent.maxDriftScore > 1) {
296
- errors.push('intent.maxDriftScore must be a number between 0 and 1');
297
- }
298
- }
444
+ // MCP servers
445
+ if (mcpServers.length > 0) {
446
+ riskScore += 2;
447
+ reasons.push(`${mcpServers.length} MCP server(s) detected MCP Guard recommended`);
448
+ recommendedPreset = 'mcp_server';
299
449
  }
300
- }
301
450
 
302
- // Learning
303
- if (config.learning && typeof config.learning === 'object') {
304
- if (config.learning.persistPath !== undefined && typeof config.learning.persistPath !== 'string') {
305
- errors.push('learning.persistPath must be a string');
306
- }
307
- if (config.learning.promotionThreshold !== undefined) {
308
- if (typeof config.learning.promotionThreshold !== 'number' || config.learning.promotionThreshold < 0) {
309
- errors.push('learning.promotionThreshold must be a non-negative number');
451
+ // RAG pipeline
452
+ if (deployment.hasRAG) {
453
+ riskScore += 2;
454
+ reasons.push('RAG pipeline detected IPIA detection and semantic isolation recommended');
455
+ if (recommendedPreset === 'chatbot') {
456
+ recommendedPreset = 'rag_pipeline';
310
457
  }
311
458
  }
312
- }
313
459
 
314
- // Ensemble
315
- if (config.ensemble && typeof config.ensemble === 'object') {
316
- if (config.ensemble.voters !== undefined) {
317
- if (!Array.isArray(config.ensemble.voters)) {
318
- errors.push('ensemble.voters must be an array');
319
- } else {
320
- for (const voter of config.ensemble.voters) {
321
- if (!VALID_VOTERS.includes(voter)) {
322
- errors.push(`Invalid ensemble voter "${voter}". Valid voters: ${VALID_VOTERS.join(', ')}`);
323
- }
324
- }
325
- }
460
+ // Public facing
461
+ if (deployment.isPublicFacing) {
462
+ riskScore += 1;
463
+ reasons.push('Public-facing deployment — stricter rate limiting and PII redaction recommended');
326
464
  }
327
- if (config.ensemble.threshold !== undefined) {
328
- if (typeof config.ensemble.threshold !== 'number' || config.ensemble.threshold < 0 || config.ensemble.threshold > 1) {
329
- errors.push('ensemble.threshold must be a number between 0 and 1');
330
- }
331
- }
332
- }
333
465
 
334
- // Goal drift
335
- if (config.goalDrift && typeof config.goalDrift === 'object') {
336
- if (config.goalDrift.driftThreshold !== undefined) {
337
- if (typeof config.goalDrift.driftThreshold !== 'number' || config.goalDrift.driftThreshold < 0 || config.goalDrift.driftThreshold > 1) {
338
- errors.push('goalDrift.driftThreshold must be a number between 0 and 1');
466
+ // Compliance
467
+ if (complianceNeeds.length > 0) {
468
+ riskScore += 1;
469
+ reasons.push(`Compliance requirements: ${complianceNeeds.join(', ')}`);
470
+ if (complianceNeeds.includes('hipaa') || complianceNeeds.includes('soc2')) {
471
+ recommendedPreset = 'enterprise';
339
472
  }
340
473
  }
341
- }
342
474
 
343
- // Adaptive thresholds
344
- if (config.adaptiveThresholds && typeof config.adaptiveThresholds === 'object') {
345
- if (config.adaptiveThresholds.minConfidence !== undefined) {
346
- if (typeof config.adaptiveThresholds.minConfidence !== 'number' || config.adaptiveThresholds.minConfidence < 0 || config.adaptiveThresholds.minConfidence > 1) {
347
- errors.push('adaptiveThresholds.minConfidence must be a number between 0 and 1');
348
- }
475
+ // Coding agent detection
476
+ const codeTools = ['readFile', 'writeFile', 'exec', 'shell', 'runCode', 'compile'];
477
+ const hasCodeTools = tools.filter(t => codeTools.includes(t)).length >= 2;
478
+ if (hasCodeTools && recommendedPreset === 'chatbot') {
479
+ recommendedPreset = 'coding_agent';
480
+ reasons.push('Code execution tools detected — coding agent preset recommended');
349
481
  }
350
- }
351
482
 
352
- // Self-training
353
- if (config.selfTraining && typeof config.selfTraining === 'object') {
354
- if (config.selfTraining.mutationRate !== undefined) {
355
- if (typeof config.selfTraining.mutationRate !== 'number' || config.selfTraining.mutationRate < 0 || config.selfTraining.mutationRate > 1) {
356
- errors.push('selfTraining.mutationRate must be a number between 0 and 1');
357
- }
483
+ // High risk override
484
+ if (riskScore >= 7) {
485
+ recommendedPreset = 'paranoid';
486
+ reasons.push(`Risk score ${riskScore}/10 paranoid preset recommended`);
487
+ } else if (riskScore >= 5 && recommendedPreset !== 'enterprise') {
488
+ recommendedPreset = 'enterprise';
489
+ reasons.push(`Risk score ${riskScore}/10 — enterprise preset recommended`);
358
490
  }
359
- }
360
491
 
361
- // Tool sequence
362
- if (config.toolSequence && typeof config.toolSequence === 'object') {
363
- if (config.toolSequence.anomalyThreshold !== undefined) {
364
- if (typeof config.toolSequence.anomalyThreshold !== 'number' || config.toolSequence.anomalyThreshold < 0 || config.toolSequence.anomalyThreshold > 1) {
365
- errors.push('toolSequence.anomalyThreshold must be a number between 0 and 1');
366
- }
492
+ // QPS warnings
493
+ if (deployment.expectedQPS && deployment.expectedQPS > 100) {
494
+ warnings.push('High QPS expected — consider enabling worker scanner for non-blocking scans');
367
495
  }
368
- }
369
-
370
- return { valid: errors.length === 0, errors };
371
- }
372
-
373
- /**
374
- * Return a human-readable summary of a Shield configuration.
375
- * @param {object} config - A built configuration object.
376
- * @returns {string} Multi-line summary string.
377
- */
378
- function describeConfig(config) {
379
- if (!config || typeof config !== 'object') {
380
- return '[Agent Shield] No configuration provided.';
381
- }
382
-
383
- const lines = [];
384
- lines.push('Agent Shield Configuration:');
385
- if (config.preset) {
386
- lines.push(` Preset: ${config.preset}`);
387
- }
388
-
389
- const sens = config.sensitivity || 'medium';
390
- const block = config.blockOnThreat ? 'yes' : 'no';
391
- const thresh = config.blockThreshold || 'medium';
392
- lines.push(` Sensitivity: ${sens} | Block: ${block} (threshold: ${thresh})`);
393
- lines.push('');
394
- lines.push(' Features enabled:');
395
-
396
- // Intent
397
- if (config.intent) {
398
- const purpose = config.intent.purpose || '(not specified)';
399
- lines.push(` \u2713 Agent Intent \u2014 "${purpose}"`);
400
- } else {
401
- lines.push(' \u2717 Agent Intent \u2014 disabled');
402
- }
403
-
404
- // Learning
405
- if (config.learning) {
406
- const dest = config.learning.persist
407
- ? `saving to ${config.learning.persistPath || FEATURE_DEFAULTS.learning.persistPath}`
408
- : 'in-memory only';
409
- lines.push(` \u2713 Persistent Learning \u2014 ${dest}`);
410
- } else {
411
- lines.push(' \u2717 Persistent Learning \u2014 disabled');
412
- }
413
-
414
- // Feedback
415
- if (config.feedback) {
416
- lines.push(` \u2713 Feedback API \u2014 autoRetrain: ${config.feedback.autoRetrain !== false}`);
417
- } else {
418
- lines.push(' \u2717 Feedback API \u2014 disabled');
419
- }
420
496
 
421
- // Ensemble
422
- if (config.ensemble) {
423
- const voters = (config.ensemble.voters || FEATURE_DEFAULTS.ensemble.voters).join(', ');
424
- lines.push(` \u2713 Ensemble Detection \u2014 voters: ${voters}`);
425
- } else {
426
- lines.push(' \u2717 Ensemble Detection \u2014 disabled');
427
- }
497
+ // Generate config from recommended preset
498
+ const config = this.generatePolicy(recommendedPreset);
428
499
 
429
- // Goal Drift
430
- if (config.goalDrift) {
431
- const dt = config.goalDrift.driftThreshold !== undefined ? config.goalDrift.driftThreshold : FEATURE_DEFAULTS.goalDrift.driftThreshold;
432
- lines.push(` \u2713 Goal Drift Detection \u2014 threshold: ${dt}`);
433
- } else {
434
- lines.push(' \u2717 Goal Drift Detection \u2014 disabled');
435
- }
436
-
437
- // Cross-Turn
438
- if (config.crossTurn) {
439
- const ws = config.crossTurn.windowSize !== undefined ? config.crossTurn.windowSize : FEATURE_DEFAULTS.crossTurn.windowSize;
440
- lines.push(` \u2713 Cross-Turn Tracking \u2014 window: ${ws} turns`);
441
- } else {
442
- lines.push(' \u2717 Cross-Turn Tracking \u2014 disabled');
443
- }
444
-
445
- // Tool Sequence
446
- if (config.toolSequence) {
447
- const at = config.toolSequence.anomalyThreshold !== undefined ? config.toolSequence.anomalyThreshold : FEATURE_DEFAULTS.toolSequence.anomalyThreshold;
448
- lines.push(` \u2713 Tool Sequence Modeling \u2014 anomaly threshold: ${at}`);
449
- } else {
450
- lines.push(' \u2717 Tool Sequence Modeling \u2014 disabled');
451
- }
452
-
453
- // Adaptive Thresholds
454
- if (config.adaptiveThresholds) {
455
- const cs = config.adaptiveThresholds.calibrationSamples !== undefined ? config.adaptiveThresholds.calibrationSamples : FEATURE_DEFAULTS.adaptiveThresholds.calibrationSamples;
456
- lines.push(` \u2713 Adaptive Thresholds \u2014 calibration samples: ${cs}`);
457
- } else {
458
- lines.push(' \u2717 Adaptive Thresholds \u2014 disabled');
459
- }
460
-
461
- // Self-Training
462
- if (config.selfTraining) {
463
- const gen = config.selfTraining.generations !== undefined ? config.selfTraining.generations : FEATURE_DEFAULTS.selfTraining.generations;
464
- lines.push(` \u2713 Self-Training \u2014 ${gen} generations`);
465
- } else {
466
- lines.push(' \u2717 Self-Training \u2014 disabled');
467
- }
468
-
469
- return lines.join('\n');
470
- }
500
+ // Apply deployment-specific overrides
501
+ if (complianceNeeds.length > 0) {
502
+ config.compliance = config.compliance || {};
503
+ config.compliance.frameworks = [...new Set([...(config.compliance.frameworks || []), ...complianceNeeds])];
504
+ }
471
505
 
472
- /**
473
- * Fluent builder for Agent Shield configuration.
474
- *
475
- * Use method chaining to enable features, then call `.build()` to get
476
- * a frozen, validated config object.
477
- */
478
- class ShieldBuilder {
479
- constructor() {
480
- /** @private */
481
- this._config = {
482
- preset: null,
483
- sensitivity: 'medium',
484
- blockOnThreat: true,
485
- blockThreshold: 'medium',
486
- intent: null,
487
- learning: null,
488
- feedback: null,
489
- ensemble: null,
490
- goalDrift: null,
491
- crossTurn: null,
492
- toolSequence: null,
493
- adaptiveThresholds: null,
494
- selfTraining: null,
495
- callbacks: {
496
- onThreat: null,
497
- onDrift: null,
498
- onAnomaly: null
499
- }
506
+ return {
507
+ recommendedPreset,
508
+ reasons,
509
+ config,
510
+ warnings,
511
+ riskScore
500
512
  };
501
513
  }
502
514
 
503
515
  /**
504
- * Start from a named preset.
505
- * @param {string} name - One of the VALID_PRESETS.
506
- * @returns {ShieldBuilder} this
516
+ * Generate a complete security policy configuration from a preset.
517
+ * @param {string} preset - Preset name
518
+ * @returns {object} Complete security configuration
507
519
  */
508
- preset(name) {
509
- if (!VALID_PRESETS.includes(name)) {
510
- throw new Error(`[Agent Shield] Unknown preset "${name}". Valid presets: ${VALID_PRESETS.join(', ')}`);
520
+ generatePolicy(preset) {
521
+ const presetConfig = this.presets[preset];
522
+ if (!presetConfig) {
523
+ const available = Object.keys(this.presets).join(', ');
524
+ throw new Error(`Unknown preset "${preset}". Available: ${available}`);
511
525
  }
512
- this._config.preset = name;
513
- const presetCfg = PRESET_CONFIGS[name];
514
- if (presetCfg) {
515
- this._applyPreset(presetCfg);
516
- }
517
- return this;
518
- }
519
526
 
520
- /**
521
- * Apply a preset config to the internal state.
522
- * @private
523
- * @param {object} presetCfg
524
- */
525
- _applyPreset(presetCfg) {
526
- if (presetCfg.sensitivity) this._config.sensitivity = presetCfg.sensitivity;
527
- if (presetCfg.blockOnThreat !== undefined) this._config.blockOnThreat = presetCfg.blockOnThreat;
528
- if (presetCfg.blockThreshold) this._config.blockThreshold = presetCfg.blockThreshold;
529
-
530
- const features = ['intent', 'learning', 'feedback', 'ensemble', 'goalDrift',
531
- 'crossTurn', 'toolSequence', 'adaptiveThresholds', 'selfTraining'];
532
- for (const feat of features) {
533
- if (presetCfg[feat] !== undefined) {
534
- this._config[feat] = resolveFeature(feat, presetCfg[feat]);
535
- }
536
- }
537
- }
527
+ // Deep clone the preset config
528
+ const config = JSON.parse(JSON.stringify(presetConfig));
538
529
 
539
- /**
540
- * Set detection sensitivity.
541
- * @param {string} level - 'low', 'medium', or 'high'.
542
- * @returns {ShieldBuilder} this
543
- */
544
- sensitivity(level) {
545
- if (!VALID_SENSITIVITIES.includes(level)) {
546
- throw new Error(`[Agent Shield] Invalid sensitivity "${level}". Must be one of: ${VALID_SENSITIVITIES.join(', ')}`);
547
- }
548
- this._config.sensitivity = level;
549
- return this;
550
- }
530
+ // Add metadata
531
+ config._metadata = {
532
+ preset,
533
+ generatedAt: new Date().toISOString(),
534
+ generatedBy: 'Agent Shield SmartConfig',
535
+ version: '12.0'
536
+ };
551
537
 
552
- /**
553
- * Enable or disable blocking on threat detection.
554
- * @param {boolean} bool
555
- * @returns {ShieldBuilder} this
556
- */
557
- blockOnThreat(bool) {
558
- this._config.blockOnThreat = !!bool;
559
- return this;
538
+ return config;
560
539
  }
561
540
 
562
541
  /**
563
- * Set the severity threshold at which blocking occurs.
564
- * @param {string} level - 'low', 'medium', 'high', or 'critical'.
565
- * @returns {ShieldBuilder} this
542
+ * Validate a configuration for misconfigurations and security gaps.
543
+ * @param {object} config - Configuration to validate
544
+ * @returns {{ valid: boolean, issues: Array<{ message: string, severity: string }>, score: number }}
566
545
  */
567
- blockThreshold(level) {
568
- if (!VALID_BLOCK_THRESHOLDS.includes(level)) {
569
- throw new Error(`[Agent Shield] Invalid blockThreshold "${level}". Must be one of: ${VALID_BLOCK_THRESHOLDS.join(', ')}`);
546
+ validateConfig(config) {
547
+ if (!config || typeof config !== 'object') {
548
+ return {
549
+ valid: false,
550
+ issues: [{ message: 'Configuration is empty or invalid', severity: 'critical' }],
551
+ score: 0
552
+ };
570
553
  }
571
- this._config.blockThreshold = level;
572
- return this;
573
- }
574
-
575
- /**
576
- * Enable agent intent declaration.
577
- * @param {object} [opts] - { purpose, allowedTools, allowedTopics, maxDriftScore }
578
- * @returns {ShieldBuilder} this
579
- */
580
- enableIntent(opts) {
581
- this._config.intent = resolveFeature('intent', opts || true);
582
- if (opts && opts.purpose) this._config.intent.purpose = opts.purpose;
583
- if (opts && opts.allowedTools) this._config.intent.allowedTools = opts.allowedTools;
584
- if (opts && opts.allowedTopics) this._config.intent.allowedTopics = opts.allowedTopics;
585
- if (opts && opts.maxDriftScore !== undefined) this._config.intent.maxDriftScore = opts.maxDriftScore;
586
- return this;
587
- }
588
-
589
- /**
590
- * Enable persistent learning.
591
- * @param {object} [opts] - { persist, persistPath, promotionThreshold, maxPatterns }
592
- * @returns {ShieldBuilder} this
593
- */
594
- enableLearning(opts) {
595
- this._config.learning = resolveFeature('learning', opts || true);
596
- return this;
597
- }
598
-
599
- /**
600
- * Enable the feedback API.
601
- * @param {object} [opts] - { autoRetrain, maxPending, cooldownMs }
602
- * @returns {ShieldBuilder} this
603
- */
604
- enableFeedback(opts) {
605
- this._config.feedback = resolveFeature('feedback', opts || true);
606
- return this;
607
- }
608
-
609
- /**
610
- * Enable ensemble voting detection.
611
- * @param {object} [opts] - { voters, threshold, requireUnanimous }
612
- * @returns {ShieldBuilder} this
613
- */
614
- enableEnsemble(opts) {
615
- this._config.ensemble = resolveFeature('ensemble', opts || true);
616
- return this;
617
- }
618
554
 
619
- /**
620
- * Enable goal drift detection.
621
- * @param {object} [opts] - { checkInterval, driftThreshold, windowSize }
622
- * @returns {ShieldBuilder} this
623
- */
624
- enableGoalDrift(opts) {
625
- this._config.goalDrift = resolveFeature('goalDrift', opts || true);
626
- return this;
627
- }
555
+ const issues = [];
628
556
 
629
- /**
630
- * Enable cross-turn injection tracking.
631
- * @param {object} [opts] - { windowSize, scanInterval, accumulateAll }
632
- * @returns {ShieldBuilder} this
633
- */
634
- enableCrossTurn(opts) {
635
- this._config.crossTurn = resolveFeature('crossTurn', opts || true);
636
- return this;
637
- }
557
+ for (const rule of VALIDATION_RULES) {
558
+ try {
559
+ if (rule.check(config)) {
560
+ issues.push({ message: rule.message, severity: rule.severity });
561
+ }
562
+ } catch (_e) {
563
+ // Rule check failed skip silently
564
+ }
565
+ }
638
566
 
639
- /**
640
- * Enable tool sequence modeling.
641
- * @param {object} [opts] - { learningPeriod, anomalyThreshold, maxChainLength }
642
- * @returns {ShieldBuilder} this
643
- */
644
- enableToolSequence(opts) {
645
- this._config.toolSequence = resolveFeature('toolSequence', opts || true);
646
- return this;
647
- }
567
+ // Compute security score (0-100)
568
+ const severityPenalty = { critical: 25, high: 15, medium: 8, low: 3 };
569
+ let penalty = 0;
570
+ for (const issue of issues) {
571
+ penalty += severityPenalty[issue.severity] || 5;
572
+ }
573
+ const score = Math.max(0, 100 - penalty);
648
574
 
649
- /**
650
- * Enable adaptive entropy thresholds.
651
- * @param {object} [opts] - { calibrationSamples, adjustInterval, minConfidence }
652
- * @returns {ShieldBuilder} this
653
- */
654
- enableAdaptiveThresholds(opts) {
655
- this._config.adaptiveThresholds = resolveFeature('adaptiveThresholds', opts || true);
656
- return this;
657
- }
575
+ const hasCritical = issues.some(i => i.severity === 'critical');
576
+ const valid = !hasCritical && score >= 50;
658
577
 
659
- /**
660
- * Enable adversarial self-training.
661
- * @param {object} [opts] - { generations, populationSize, mutationRate, interval }
662
- * @returns {ShieldBuilder} this
663
- */
664
- enableSelfTraining(opts) {
665
- this._config.selfTraining = resolveFeature('selfTraining', opts || true);
666
- return this;
578
+ return { valid, issues, score };
667
579
  }
668
580
 
669
581
  /**
670
- * Set a callback invoked when a threat is detected.
671
- * @param {function} callback - fn(threatInfo)
672
- * @returns {ShieldBuilder} this
582
+ * List all available preset names.
583
+ * @returns {string[]}
673
584
  */
674
- onThreat(callback) {
675
- if (typeof callback !== 'function') {
676
- throw new Error('[Agent Shield] onThreat callback must be a function');
677
- }
678
- this._config.callbacks.onThreat = callback;
679
- return this;
585
+ listPresets() {
586
+ return Object.keys(this.presets);
680
587
  }
681
588
 
682
589
  /**
683
- * Set a callback invoked when goal drift is detected.
684
- * @param {function} callback - fn(driftInfo)
685
- * @returns {ShieldBuilder} this
590
+ * Get a preset by name.
591
+ * @param {string} name - Preset name
592
+ * @returns {object|null}
686
593
  */
687
- onDrift(callback) {
688
- if (typeof callback !== 'function') {
689
- throw new Error('[Agent Shield] onDrift callback must be a function');
690
- }
691
- this._config.callbacks.onDrift = callback;
692
- return this;
594
+ getPreset(name) {
595
+ return this.presets[name] || null;
693
596
  }
694
597
 
695
598
  /**
696
- * Set a callback invoked when an anomaly is detected.
697
- * @param {function} callback - fn(anomalyInfo)
698
- * @returns {ShieldBuilder} this
599
+ * Register a custom preset.
600
+ * @param {string} name - Preset name
601
+ * @param {object} config - Preset configuration
699
602
  */
700
- onAnomaly(callback) {
701
- if (typeof callback !== 'function') {
702
- throw new Error('[Agent Shield] onAnomaly callback must be a function');
603
+ registerPreset(name, config) {
604
+ if (!name || typeof name !== 'string') {
605
+ throw new Error('Preset name is required');
703
606
  }
704
- this._config.callbacks.onAnomaly = callback;
705
- return this;
607
+ this.presets[name] = config;
608
+ console.log(`[Agent Shield] Custom preset "${name}" registered`);
706
609
  }
707
610
 
708
611
  /**
709
- * Finalize and return a frozen configuration object.
710
- * Validates the config and throws on errors.
711
- * @returns {object} Frozen config object.
612
+ * Compare two presets and show differences.
613
+ * @param {string} presetA - First preset name
614
+ * @param {string} presetB - Second preset name
615
+ * @returns {object} Comparison summary
712
616
  */
713
- build() {
714
- const config = Object.assign({}, this._config);
715
-
716
- // Extract callbacks they can't be frozen in the same way
717
- const callbacks = config.callbacks;
718
- delete config.callbacks;
719
-
720
- const validation = validateConfig(config);
721
- if (!validation.valid) {
722
- throw new Error(`[Agent Shield] Invalid configuration:\n - ${validation.errors.join('\n - ')}`);
617
+ comparePresets(presetA, presetB) {
618
+ const a = this.presets[presetA];
619
+ const b = this.presets[presetB];
620
+ if (!a || !b) {
621
+ throw new Error(`Preset not found: ${!a ? presetA : presetB}`);
723
622
  }
724
623
 
725
- // Attach callbacks as a non-enumerable property so they survive freezing
726
- Object.defineProperty(config, 'callbacks', {
727
- value: Object.freeze(callbacks),
728
- enumerable: false,
729
- writable: false,
730
- configurable: false
731
- });
732
-
733
- console.log('[Agent Shield] Configuration built' + (config.preset ? ` (preset: ${config.preset})` : ''));
734
-
735
- return deepFreeze(config);
736
- }
737
- }
738
-
739
- /**
740
- * Factory function for creating Shield configurations.
741
- *
742
- * @param {string|object|ShieldBuilder} [input] - Preset name, config object, or ShieldBuilder.
743
- * @returns {object|ShieldBuilder} Built config (if string/object) or new ShieldBuilder (if no input).
744
- */
745
- function createShield(input) {
746
- // No input — return builder for chaining
747
- if (input === undefined || input === null) {
748
- return new ShieldBuilder();
749
- }
750
-
751
- // String — preset name, auto-build
752
- if (typeof input === 'string') {
753
- return new ShieldBuilder().preset(input).build();
754
- }
755
-
756
- // ShieldBuilder instance — build it
757
- if (input instanceof ShieldBuilder) {
758
- return input.build();
759
- }
760
-
761
- // Object — treat as raw config
762
- if (typeof input === 'object' && !Array.isArray(input)) {
763
- const builder = new ShieldBuilder();
764
-
765
- // Apply preset first if specified
766
- if (input.preset) {
767
- builder.preset(input.preset);
768
- }
769
-
770
- // Override with explicit values
771
- if (input.sensitivity) builder.sensitivity(input.sensitivity);
772
- if (input.blockOnThreat !== undefined) builder.blockOnThreat(input.blockOnThreat);
773
- if (input.blockThreshold) builder.blockThreshold(input.blockThreshold);
774
-
775
- const featureMap = {
776
- intent: 'enableIntent',
777
- learning: 'enableLearning',
778
- feedback: 'enableFeedback',
779
- ensemble: 'enableEnsemble',
780
- goalDrift: 'enableGoalDrift',
781
- crossTurn: 'enableCrossTurn',
782
- toolSequence: 'enableToolSequence',
783
- adaptiveThresholds: 'enableAdaptiveThresholds',
784
- selfTraining: 'enableSelfTraining'
785
- };
786
-
787
- for (const [key, method] of Object.entries(featureMap)) {
788
- if (input[key] !== undefined && input[key] !== false && input[key] !== null) {
789
- const val = input[key] === true ? undefined : input[key];
790
- builder[method](val);
624
+ const differences = [];
625
+
626
+ const compare = (objA, objB, path = '') => {
627
+ const allKeys = new Set([...Object.keys(objA || {}), ...Object.keys(objB || {})]);
628
+ for (const key of allKeys) {
629
+ const fullPath = path ? `${path}.${key}` : key;
630
+ const va = objA?.[key];
631
+ const vb = objB?.[key];
632
+
633
+ if (va !== undefined && vb === undefined) {
634
+ differences.push({ path: fullPath, inA: va, inB: undefined });
635
+ } else if (va === undefined && vb !== undefined) {
636
+ differences.push({ path: fullPath, inA: undefined, inB: vb });
637
+ } else if (typeof va === 'object' && va !== null && typeof vb === 'object' && vb !== null && !Array.isArray(va)) {
638
+ compare(va, vb, fullPath);
639
+ } else if (JSON.stringify(va) !== JSON.stringify(vb)) {
640
+ differences.push({ path: fullPath, inA: va, inB: vb });
641
+ }
791
642
  }
792
- }
643
+ };
793
644
 
794
- // Callbacks
795
- if (typeof input.onThreat === 'function') builder.onThreat(input.onThreat);
796
- if (typeof input.onDrift === 'function') builder.onDrift(input.onDrift);
797
- if (typeof input.onAnomaly === 'function') builder.onAnomaly(input.onAnomaly);
645
+ compare(a, b);
798
646
 
799
- return builder.build();
647
+ return {
648
+ presetA,
649
+ presetB,
650
+ differences,
651
+ differenceCount: differences.length
652
+ };
800
653
  }
801
-
802
- throw new Error('[Agent Shield] createShield() accepts a string, object, ShieldBuilder, or no arguments');
803
654
  }
804
655
 
656
+ // =========================================================================
657
+ // EXPORTS
658
+ // =========================================================================
659
+
805
660
  module.exports = {
806
- ShieldBuilder,
807
- createShield,
808
- validateConfig,
809
- describeConfig,
810
- FEATURE_DEFAULTS,
811
- VALID_PRESETS
661
+ SmartConfig,
662
+ DEPLOYMENT_PRESETS,
663
+ VALIDATION_RULES
812
664
  };