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.
- package/CHANGELOG.md +48 -0
- package/LICENSE +21 -21
- package/README.md +30 -37
- package/bin/agentshield-audit +51 -0
- package/package.json +7 -9
- package/src/adaptive.js +330 -330
- package/src/agent-intent.js +807 -0
- package/src/alert-tuning.js +480 -480
- package/src/audit-streaming.js +1 -1
- package/src/badges.js +196 -196
- package/src/behavioral-dna.js +12 -0
- package/src/canary.js +2 -3
- package/src/certification.js +563 -563
- package/src/circuit-breaker.js +2 -2
- package/src/confused-deputy.js +4 -0
- package/src/conversation.js +494 -494
- package/src/cross-turn.js +649 -0
- package/src/ctf.js +462 -462
- package/src/detector-core.js +71 -152
- package/src/document-scanner.js +795 -795
- package/src/drift-monitor.js +344 -0
- package/src/encoding.js +429 -429
- package/src/ensemble.js +523 -0
- package/src/enterprise.js +405 -405
- package/src/flight-recorder.js +2 -0
- package/src/i18n-patterns.js +523 -523
- package/src/index.js +19 -0
- package/src/main.js +79 -6
- package/src/mcp-guard.js +974 -0
- package/src/micro-model.js +762 -0
- package/src/ml-detector.js +316 -0
- package/src/model-finetuning.js +884 -884
- package/src/multimodal.js +296 -296
- package/src/nist-mapping.js +2 -2
- package/src/observability.js +330 -330
- package/src/openclaw.js +450 -450
- package/src/otel.js +544 -544
- package/src/owasp-2025.js +1 -1
- package/src/owasp-agentic.js +420 -0
- package/src/persistent-learning.js +677 -0
- package/src/plugin-marketplace.js +628 -628
- package/src/plugin-system.js +349 -349
- package/src/policy-extended.js +635 -635
- package/src/policy.js +443 -443
- package/src/prompt-leakage.js +2 -2
- package/src/real-attack-datasets.js +2 -2
- package/src/redteam-cli.js +439 -0
- package/src/self-training.js +772 -0
- package/src/smart-config.js +812 -0
- package/src/supply-chain-scanner.js +691 -0
- package/src/testing.js +5 -1
- package/src/threat-encyclopedia.js +629 -629
- package/src/threat-intel-network.js +1017 -1017
- package/src/token-analysis.js +467 -467
- package/src/tool-output-validator.js +354 -354
- package/src/watermark.js +1 -2
- 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
|
+
};
|