agentshield-sdk 7.4.0 → 10.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -21
  3. package/README.md +30 -37
  4. package/bin/agentshield-audit +51 -0
  5. package/package.json +7 -9
  6. package/src/adaptive.js +330 -330
  7. package/src/agent-intent.js +807 -0
  8. package/src/alert-tuning.js +480 -480
  9. package/src/audit-streaming.js +1 -1
  10. package/src/badges.js +196 -196
  11. package/src/behavioral-dna.js +12 -0
  12. package/src/canary.js +2 -3
  13. package/src/certification.js +563 -563
  14. package/src/circuit-breaker.js +2 -2
  15. package/src/confused-deputy.js +4 -0
  16. package/src/conversation.js +494 -494
  17. package/src/cross-turn.js +649 -0
  18. package/src/ctf.js +462 -462
  19. package/src/detector-core.js +71 -152
  20. package/src/document-scanner.js +795 -795
  21. package/src/drift-monitor.js +344 -0
  22. package/src/encoding.js +429 -429
  23. package/src/ensemble.js +523 -0
  24. package/src/enterprise.js +405 -405
  25. package/src/flight-recorder.js +2 -0
  26. package/src/i18n-patterns.js +523 -523
  27. package/src/index.js +19 -0
  28. package/src/main.js +79 -6
  29. package/src/mcp-guard.js +974 -0
  30. package/src/micro-model.js +762 -0
  31. package/src/ml-detector.js +316 -0
  32. package/src/model-finetuning.js +884 -884
  33. package/src/multimodal.js +296 -296
  34. package/src/nist-mapping.js +2 -2
  35. package/src/observability.js +330 -330
  36. package/src/openclaw.js +450 -450
  37. package/src/otel.js +544 -544
  38. package/src/owasp-2025.js +1 -1
  39. package/src/owasp-agentic.js +420 -0
  40. package/src/persistent-learning.js +677 -0
  41. package/src/plugin-marketplace.js +628 -628
  42. package/src/plugin-system.js +349 -349
  43. package/src/policy-extended.js +635 -635
  44. package/src/policy.js +443 -443
  45. package/src/prompt-leakage.js +2 -2
  46. package/src/real-attack-datasets.js +2 -2
  47. package/src/redteam-cli.js +439 -0
  48. package/src/self-training.js +772 -0
  49. package/src/smart-config.js +812 -0
  50. package/src/supply-chain-scanner.js +691 -0
  51. package/src/testing.js +5 -1
  52. package/src/threat-encyclopedia.js +629 -629
  53. package/src/threat-intel-network.js +1017 -1017
  54. package/src/token-analysis.js +467 -467
  55. package/src/tool-output-validator.js +354 -354
  56. package/src/watermark.js +1 -2
  57. package/types/index.d.ts +660 -0
@@ -0,0 +1,812 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Agent Shield — Smart Configuration System (v8)
5
+ *
6
+ * Unified configuration for all Shield features.
7
+ * Three ways to use:
8
+ *
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
+ * });
33
+ *
34
+ * @module smart-config
35
+ */
36
+
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'];
52
+
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
82
+ },
83
+ goalDrift: {
84
+ checkInterval: 5,
85
+ driftThreshold: 0.6,
86
+ windowSize: 10
87
+ },
88
+ crossTurn: {
89
+ windowSize: 20,
90
+ scanInterval: 3,
91
+ accumulateAll: true
92
+ },
93
+ toolSequence: {
94
+ learningPeriod: 50,
95
+ anomalyThreshold: 0.15,
96
+ maxChainLength: 10
97
+ },
98
+ adaptiveThresholds: {
99
+ calibrationSamples: 100,
100
+ adjustInterval: 50,
101
+ minConfidence: 0.3
102
+ },
103
+ selfTraining: {
104
+ generations: 10,
105
+ populationSize: 20,
106
+ mutationRate: 0.3,
107
+ interval: 0
108
+ }
109
+ };
110
+
111
+ /**
112
+ * Preset configurations that set sensible defaults for common use cases.
113
+ * @private
114
+ */
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
123
+ },
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
131
+ },
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
140
+ },
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
149
+ },
150
+ internal_tool: {
151
+ sensitivity: 'low',
152
+ blockOnThreat: false,
153
+ blockThreshold: 'high',
154
+ intent: { purpose: 'Internal tooling agent' },
155
+ toolSequence: true
156
+ },
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
166
+ },
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
179
+ },
180
+ minimal: {
181
+ sensitivity: 'low',
182
+ blockOnThreat: false,
183
+ blockThreshold: 'critical'
184
+ },
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
+ }
216
+ }
217
+ return result;
218
+ }
219
+
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
+ }
235
+
236
+ /**
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}
243
+ */
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
+ }
254
+
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 = [];
262
+
263
+ if (!config || typeof config !== 'object') {
264
+ return { valid: false, errors: ['Config must be a non-null object'] };
265
+ }
266
+
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(', ')}`);
271
+ }
272
+ }
273
+
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(', ')}`);
278
+ }
279
+ }
280
+
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(', ')}`);
285
+ }
286
+ }
287
+
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
+ }
299
+ }
300
+ }
301
+
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');
310
+ }
311
+ }
312
+ }
313
+
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
+ }
326
+ }
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
+
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');
339
+ }
340
+ }
341
+ }
342
+
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
+ }
349
+ }
350
+ }
351
+
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
+ }
358
+ }
359
+ }
360
+
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
+ }
367
+ }
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
+
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
+ }
428
+
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
+ }
471
+
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
+ }
500
+ };
501
+ }
502
+
503
+ /**
504
+ * Start from a named preset.
505
+ * @param {string} name - One of the VALID_PRESETS.
506
+ * @returns {ShieldBuilder} this
507
+ */
508
+ preset(name) {
509
+ if (!VALID_PRESETS.includes(name)) {
510
+ throw new Error(`[Agent Shield] Unknown preset "${name}". Valid presets: ${VALID_PRESETS.join(', ')}`);
511
+ }
512
+ this._config.preset = name;
513
+ const presetCfg = PRESET_CONFIGS[name];
514
+ if (presetCfg) {
515
+ this._applyPreset(presetCfg);
516
+ }
517
+ return this;
518
+ }
519
+
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
+ }
538
+
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
+ }
551
+
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;
560
+ }
561
+
562
+ /**
563
+ * Set the severity threshold at which blocking occurs.
564
+ * @param {string} level - 'low', 'medium', 'high', or 'critical'.
565
+ * @returns {ShieldBuilder} this
566
+ */
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(', ')}`);
570
+ }
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
+
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
+ }
628
+
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
+ }
638
+
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
+ }
648
+
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
+ }
658
+
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;
667
+ }
668
+
669
+ /**
670
+ * Set a callback invoked when a threat is detected.
671
+ * @param {function} callback - fn(threatInfo)
672
+ * @returns {ShieldBuilder} this
673
+ */
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;
680
+ }
681
+
682
+ /**
683
+ * Set a callback invoked when goal drift is detected.
684
+ * @param {function} callback - fn(driftInfo)
685
+ * @returns {ShieldBuilder} this
686
+ */
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;
693
+ }
694
+
695
+ /**
696
+ * Set a callback invoked when an anomaly is detected.
697
+ * @param {function} callback - fn(anomalyInfo)
698
+ * @returns {ShieldBuilder} this
699
+ */
700
+ onAnomaly(callback) {
701
+ if (typeof callback !== 'function') {
702
+ throw new Error('[Agent Shield] onAnomaly callback must be a function');
703
+ }
704
+ this._config.callbacks.onAnomaly = callback;
705
+ return this;
706
+ }
707
+
708
+ /**
709
+ * Finalize and return a frozen configuration object.
710
+ * Validates the config and throws on errors.
711
+ * @returns {object} Frozen config object.
712
+ */
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 - ')}`);
723
+ }
724
+
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);
791
+ }
792
+ }
793
+
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);
798
+
799
+ return builder.build();
800
+ }
801
+
802
+ throw new Error('[Agent Shield] createShield() accepts a string, object, ShieldBuilder, or no arguments');
803
+ }
804
+
805
+ module.exports = {
806
+ ShieldBuilder,
807
+ createShield,
808
+ validateConfig,
809
+ describeConfig,
810
+ FEATURE_DEFAULTS,
811
+ VALID_PRESETS
812
+ };