musubi-sdd 3.8.0 → 3.10.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/README.ja.md CHANGED
@@ -71,7 +71,31 @@ musubi init --windsurf # Windsurf IDE
71
71
 
72
72
  ---
73
73
 
74
- ## 📊 v3.7.1 の新機能
74
+ ## 📊 v3.9.0 の新機能
75
+
76
+ - 🛡️ **Guardrailsシステム** - OpenAI Agents SDK inspired 入出力検証とセーフティチェック
77
+ - ✅ **InputGuardrail** - 入力検証、PII検出、インジェクション攻撃防止
78
+ - ✅ **OutputGuardrail** - 出力サニタイズ、機密データ墨消し、コンテンツポリシー適用
79
+ - ⚖️ **SafetyCheckGuardrail** - 憲法条項準拠、コンテンツ安全性分析
80
+ - 🔧 **GuardrailRules DSL** - RuleBuilderによる検証ルール構築のFluent API
81
+ - 🔗 **GuardrailChain** - 複数Guardrailの順次/並列実行
82
+ - 🖥️ **CLIコマンド** - `musubi-validate guardrails` と `guardrails-chain` コマンド
83
+
84
+ ```bash
85
+ # セキュリティプリセットで入力検証
86
+ npx musubi-validate guardrails "user input" --type input --preset security
87
+
88
+ # PII墨消しで出力検証
89
+ npx musubi-validate guardrails "output" --type output --redact
90
+
91
+ # 憲法準拠でセーフティチェック
92
+ npx musubi-validate guardrails "code" --type safety --constitutional --level high
93
+
94
+ # Guardrailチェーンを並列実行
95
+ npx musubi-validate guardrails-chain "content" --parallel
96
+ ```
97
+
98
+ ### 以前のバージョン (v3.7.1)
75
99
 
76
100
  - 🌐 **WebSocketリアルタイムGUI** - `musubi-browser`ダッシュボードでライブ更新
77
101
  - 📋 **GUIクイックアクション** - 新規要件モーダル、プロジェクト検証、レポートエクスポート
package/README.md CHANGED
@@ -71,7 +71,31 @@ musubi init --windsurf # Windsurf IDE
71
71
 
72
72
  ---
73
73
 
74
- ## 📊 What's New in v3.7.1
74
+ ## 📊 What's New in v3.9.0
75
+
76
+ - 🛡️ **Guardrails System** - OpenAI Agents SDK inspired input/output validation and safety checks
77
+ - ✅ **InputGuardrail** - Input validation, PII detection, injection attack prevention
78
+ - ✅ **OutputGuardrail** - Output sanitization, sensitive data redaction, content policy enforcement
79
+ - ⚖️ **SafetyCheckGuardrail** - Constitutional Articles compliance, content safety analysis
80
+ - 🔧 **GuardrailRules DSL** - Fluent API for building validation rules with RuleBuilder
81
+ - 🔗 **GuardrailChain** - Compose multiple guardrails with sequential/parallel execution
82
+ - 🖥️ **CLI Commands** - `musubi-validate guardrails` and `guardrails-chain` commands
83
+
84
+ ```bash
85
+ # Input validation with security preset
86
+ npx musubi-validate guardrails "user input" --type input --preset security
87
+
88
+ # Output validation with PII redaction
89
+ npx musubi-validate guardrails "output" --type output --redact
90
+
91
+ # Safety check with constitutional compliance
92
+ npx musubi-validate guardrails "code" --type safety --constitutional --level high
93
+
94
+ # Run guardrail chain in parallel
95
+ npx musubi-validate guardrails-chain "content" --parallel
96
+ ```
97
+
98
+ ### Previous (v3.7.1)
75
99
 
76
100
  - 🌐 **WebSocket Real-time GUI** - Live replanning updates with `musubi-browser` dashboard
77
101
  - 📋 **GUI Quick Actions** - Modal dialog for New Requirement, Validate Project, Export Report
@@ -11,12 +11,20 @@
11
11
  * musubi-validate article <1-9> # Validate specific article
12
12
  * musubi-validate gates # Validate Phase -1 Gates
13
13
  * musubi-validate complexity # Validate complexity limits
14
+ * musubi-validate guardrails # Validate content with guardrails
14
15
  * musubi-validate all # Run all validations
15
16
  */
16
17
 
17
18
  const { Command } = require('commander');
18
19
  const chalk = require('chalk');
19
20
  const ConstitutionValidator = require('../src/validators/constitution');
21
+ const {
22
+ createInputGuardrail,
23
+ createOutputGuardrail,
24
+ createSafetyCheckGuardrail,
25
+ GuardrailChain,
26
+ SafetyLevel
27
+ } = require('../src/orchestration/guardrails');
20
28
 
21
29
  const program = new Command();
22
30
 
@@ -107,6 +115,134 @@ program
107
115
  }
108
116
  });
109
117
 
118
+ // Guardrails validation
119
+ program
120
+ .command('guardrails')
121
+ .description('Validate content with input/output guardrails')
122
+ .argument('[content]', 'Content to validate (or use --file)')
123
+ .option('--file <path>', 'Read content from file')
124
+ .option('-t, --type <type>', 'Guardrail type (input|output|safety)', 'input')
125
+ .option('-l, --level <level>', 'Safety level (basic|standard|strict|paranoid)', 'standard')
126
+ .option('--constitutional', 'Enable constitutional compliance checks')
127
+ .option('--redact', 'Enable redaction for output guardrails')
128
+ .option('-f, --format <type>', 'Output format (console|json)', 'console')
129
+ .action(async (content, options) => {
130
+ try {
131
+ // Get content from argument, file, or stdin
132
+ let inputContent = content;
133
+
134
+ if (options.file) {
135
+ const fs = require('fs');
136
+ const path = require('path');
137
+ const filePath = path.resolve(process.cwd(), options.file);
138
+ inputContent = fs.readFileSync(filePath, 'utf-8');
139
+ } else if (!inputContent && !process.stdin.isTTY) {
140
+ // Read from stdin
141
+ const chunks = [];
142
+ for await (const chunk of process.stdin) {
143
+ chunks.push(chunk);
144
+ }
145
+ inputContent = Buffer.concat(chunks).toString('utf-8');
146
+ }
147
+
148
+ if (!inputContent) {
149
+ console.error(chalk.red('✗ No content provided. Use --file or pipe content.'));
150
+ process.exit(1);
151
+ }
152
+
153
+ let guardrail;
154
+ const guardrailType = options.type.toLowerCase();
155
+
156
+ switch (guardrailType) {
157
+ case 'input':
158
+ guardrail = createInputGuardrail('userInput', {
159
+ sanitize: true
160
+ });
161
+ break;
162
+
163
+ case 'output':
164
+ guardrail = createOutputGuardrail(options.redact ? 'redact' : 'safe');
165
+ break;
166
+
167
+ case 'safety':
168
+ guardrail = createSafetyCheckGuardrail(options.level, {
169
+ enforceConstitution: options.constitutional
170
+ });
171
+ break;
172
+
173
+ default:
174
+ console.error(chalk.red(`✗ Unknown guardrail type: ${guardrailType}`));
175
+ process.exit(1);
176
+ }
177
+
178
+ console.log(chalk.dim(`\n🛡️ Running ${guardrailType} guardrail validation...\n`));
179
+
180
+ const result = await guardrail.run(inputContent);
181
+
182
+ displayGuardrailResults(result, options);
183
+ process.exit(result.passed ? 0 : 1);
184
+ } catch (error) {
185
+ console.error(chalk.red('✗ Guardrail validation error:'), error.message);
186
+ process.exit(1);
187
+ }
188
+ });
189
+
190
+ // Guardrails chain validation
191
+ program
192
+ .command('guardrails-chain')
193
+ .description('Run a chain of guardrails on content')
194
+ .argument('[content]', 'Content to validate')
195
+ .option('--file <path>', 'Read content from file')
196
+ .option('--parallel', 'Run guardrails in parallel')
197
+ .option('--stop-on-failure', 'Stop on first failure')
198
+ .option('-f, --format <type>', 'Output format (console|json)', 'console')
199
+ .action(async (content, options) => {
200
+ try {
201
+ // Get content
202
+ let inputContent = content;
203
+
204
+ if (options.file) {
205
+ const fs = require('fs');
206
+ const path = require('path');
207
+ const filePath = path.resolve(process.cwd(), options.file);
208
+ inputContent = fs.readFileSync(filePath, 'utf-8');
209
+ } else if (!inputContent && !process.stdin.isTTY) {
210
+ const chunks = [];
211
+ for await (const chunk of process.stdin) {
212
+ chunks.push(chunk);
213
+ }
214
+ inputContent = Buffer.concat(chunks).toString('utf-8');
215
+ }
216
+
217
+ if (!inputContent) {
218
+ console.error(chalk.red('✗ No content provided. Use --file or pipe content.'));
219
+ process.exit(1);
220
+ }
221
+
222
+ // Create guardrail chain
223
+ const chain = new GuardrailChain({
224
+ name: 'ValidationChain',
225
+ parallel: options.parallel || false,
226
+ stopOnFirstFailure: options.stopOnFailure || false
227
+ });
228
+
229
+ // Add default guardrails
230
+ chain.add(createInputGuardrail('security'));
231
+ chain.add(createSafetyCheckGuardrail('standard'));
232
+ chain.add(createOutputGuardrail('safe'));
233
+
234
+ console.log(chalk.dim('\n🔗 Running guardrail chain validation...\n'));
235
+
236
+ const result = await chain.run(inputContent);
237
+
238
+ displayChainResults(result, options);
239
+ process.exit(result.passed ? 0 : 1);
240
+ } catch (error) {
241
+ console.error(chalk.red('✗ Guardrail chain error:'), error.message);
242
+ process.exit(1);
243
+ }
244
+ });
245
+
110
246
  // Score validation (numeric score output for CI/CD)
111
247
  program
112
248
  .command('score')
@@ -345,6 +481,104 @@ function displayMarkdown(title, results) {
345
481
  }
346
482
  }
347
483
 
484
+ /**
485
+ * Display guardrail validation results
486
+ */
487
+ function displayGuardrailResults(result, options) {
488
+ if (options.format === 'json') {
489
+ console.log(JSON.stringify(result, null, 2));
490
+ return;
491
+ }
492
+
493
+ console.log(chalk.bold('Guardrail Validation Results'));
494
+ console.log(chalk.bold('━'.repeat(50)));
495
+
496
+ if (result.passed) {
497
+ console.log(chalk.green(`\n✓ PASSED - ${result.guardrailName}\n`));
498
+ } else {
499
+ console.log(chalk.red(`\n✗ FAILED - ${result.guardrailName}\n`));
500
+ }
501
+
502
+ if (result.message) {
503
+ console.log(chalk.dim(`Message: ${result.message}`));
504
+ }
505
+
506
+ console.log(chalk.dim(`Execution time: ${result.executionTimeMs}ms\n`));
507
+
508
+ if (result.violations && result.violations.length > 0) {
509
+ console.log(chalk.bold.red('Violations:'));
510
+ result.violations.forEach(violation => {
511
+ const severityIcon = violation.severity === 'error' ? '✗' :
512
+ violation.severity === 'warning' ? '⚠' : 'ℹ';
513
+ const severityColor = violation.severity === 'error' ? chalk.red :
514
+ violation.severity === 'warning' ? chalk.yellow : chalk.blue;
515
+ console.log(severityColor(` ${severityIcon} [${violation.code}] ${violation.message}`));
516
+ });
517
+ console.log();
518
+ }
519
+
520
+ // Show metadata if redaction was applied
521
+ if (result.metadata?.redactionApplied) {
522
+ console.log(chalk.bold.blue('Redaction Applied:'));
523
+ console.log(chalk.dim(` ${result.metadata.redactionCount} item(s) redacted`));
524
+ if (result.metadata.redactions) {
525
+ result.metadata.redactions.forEach(r => {
526
+ console.log(chalk.dim(` - ${r.type}: ${r.count}`));
527
+ });
528
+ }
529
+ console.log();
530
+ }
531
+
532
+ // Show quality scores if available
533
+ if (result.metadata?.qualityScores) {
534
+ console.log(chalk.bold.blue('Quality Scores:'));
535
+ Object.entries(result.metadata.qualityScores).forEach(([name, score]) => {
536
+ console.log(chalk.dim(` ${name}: ${(score * 100).toFixed(1)}%`));
537
+ });
538
+ console.log();
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Display guardrail chain results
544
+ */
545
+ function displayChainResults(result, options) {
546
+ if (options.format === 'json') {
547
+ console.log(JSON.stringify(result, null, 2));
548
+ return;
549
+ }
550
+
551
+ console.log(chalk.bold('Guardrail Chain Results'));
552
+ console.log(chalk.bold('━'.repeat(50)));
553
+
554
+ if (result.passed) {
555
+ console.log(chalk.green(`\n✓ PASSED - ${result.chainName}\n`));
556
+ } else {
557
+ console.log(chalk.red(`\n✗ FAILED - ${result.chainName}\n`));
558
+ }
559
+
560
+ console.log(chalk.dim(`Guardrails executed: ${result.executedCount}/${result.guardrailCount}`));
561
+ console.log(chalk.dim(`Total time: ${result.executionTimeMs}ms\n`));
562
+
563
+ // Show individual guardrail results
564
+ console.log(chalk.bold('Individual Results:'));
565
+ result.results.forEach((r, index) => {
566
+ const icon = r.passed ? chalk.green('✓') : chalk.red('✗');
567
+ console.log(` ${icon} ${r.guardrailName} (${r.executionTimeMs}ms)`);
568
+ });
569
+ console.log();
570
+
571
+ // Show all violations
572
+ if (result.violations && result.violations.length > 0) {
573
+ console.log(chalk.bold.red('All Violations:'));
574
+ result.violations.forEach(violation => {
575
+ const severityColor = violation.severity === 'error' ? chalk.red : chalk.yellow;
576
+ console.log(severityColor(` • [${violation.code}] ${violation.message}`));
577
+ });
578
+ console.log();
579
+ }
580
+ }
581
+
348
582
  // Parse arguments
349
583
  program.parse(process.argv);
350
584
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubi-sdd",
3
- "version": "3.8.0",
3
+ "version": "3.10.0",
4
4
  "description": "Ultimate Specification Driven Development Tool with 27 Agents for 7 AI Coding Platforms + MCP Integration (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -0,0 +1,358 @@
1
+ /**
2
+ * @fileoverview Base Guardrail class for MUSUBI Orchestration
3
+ *
4
+ * Guardrails provide safety checks for agent inputs and outputs.
5
+ * Inspired by OpenAI Agents SDK guardrails pattern.
6
+ *
7
+ * @module orchestration/guardrails/base-guardrail
8
+ * @version 3.9.0
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ /**
14
+ * Guardrail execution result
15
+ * @typedef {Object} GuardrailResult
16
+ * @property {boolean} passed - Whether the guardrail check passed
17
+ * @property {string} guardrailName - Name of the guardrail that was executed
18
+ * @property {string} [message] - Optional message describing the result
19
+ * @property {Array<GuardrailViolation>} violations - List of violations found
20
+ * @property {Object} [metadata] - Additional metadata from the check
21
+ * @property {number} executionTimeMs - Time taken to execute the guardrail
22
+ */
23
+
24
+ /**
25
+ * Guardrail violation details
26
+ * @typedef {Object} GuardrailViolation
27
+ * @property {string} code - Violation code (e.g., 'PII_DETECTED', 'PROHIBITED_CONTENT')
28
+ * @property {string} message - Human-readable description
29
+ * @property {string} severity - 'error' | 'warning' | 'info'
30
+ * @property {Object} [context] - Additional context about the violation
31
+ */
32
+
33
+ /**
34
+ * Guardrail configuration options
35
+ * @typedef {Object} GuardrailConfig
36
+ * @property {string} name - Unique name for the guardrail
37
+ * @property {string} [description] - Description of what the guardrail checks
38
+ * @property {boolean} [enabled=true] - Whether the guardrail is enabled
39
+ * @property {boolean} [failFast=false] - Stop on first violation
40
+ * @property {string} [severity='error'] - Default severity level
41
+ * @property {Object} [options] - Guardrail-specific options
42
+ */
43
+
44
+ /**
45
+ * Tripwire exception - thrown when guardrail fails and tripwire is enabled
46
+ * @class
47
+ */
48
+ class GuardrailTripwireException extends Error {
49
+ /**
50
+ * @param {string} message - Error message
51
+ * @param {GuardrailResult} result - The guardrail result that triggered the tripwire
52
+ */
53
+ constructor(message, result) {
54
+ super(message);
55
+ this.name = 'GuardrailTripwireException';
56
+ this.result = result;
57
+ this.violations = result.violations;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Base class for all guardrails
63
+ * @abstract
64
+ */
65
+ class BaseGuardrail {
66
+ /**
67
+ * @param {GuardrailConfig} config - Guardrail configuration
68
+ */
69
+ constructor(config) {
70
+ if (new.target === BaseGuardrail) {
71
+ throw new Error('BaseGuardrail is abstract and cannot be instantiated directly');
72
+ }
73
+
74
+ this.name = config.name || this.constructor.name;
75
+ this.description = config.description || '';
76
+ this.enabled = config.enabled !== false;
77
+ this.failFast = config.failFast || false;
78
+ this.defaultSeverity = config.severity || 'error';
79
+ this.options = config.options || {};
80
+
81
+ // Tripwire: if true, throws exception on failure instead of returning result
82
+ this.tripwireEnabled = config.tripwireEnabled || false;
83
+ }
84
+
85
+ /**
86
+ * Execute the guardrail check
87
+ * @abstract
88
+ * @param {*} input - Input to validate
89
+ * @param {Object} [context] - Execution context
90
+ * @returns {Promise<GuardrailResult>}
91
+ */
92
+ async check(input, context = {}) {
93
+ throw new Error('Subclasses must implement check() method');
94
+ }
95
+
96
+ /**
97
+ * Run the guardrail with timing and error handling
98
+ * @param {*} input - Input to validate
99
+ * @param {Object} [context] - Execution context
100
+ * @returns {Promise<GuardrailResult>}
101
+ */
102
+ async run(input, context = {}) {
103
+ if (!this.enabled) {
104
+ return this.createResult(true, [], 'Guardrail is disabled');
105
+ }
106
+
107
+ const startTime = Date.now();
108
+
109
+ try {
110
+ const result = await this.check(input, context);
111
+ result.executionTimeMs = Date.now() - startTime;
112
+ result.guardrailName = this.name;
113
+
114
+ // Handle tripwire
115
+ if (this.tripwireEnabled && !result.passed) {
116
+ throw new GuardrailTripwireException(
117
+ `Guardrail '${this.name}' triggered tripwire: ${result.message || 'Validation failed'}`,
118
+ result
119
+ );
120
+ }
121
+
122
+ return result;
123
+ } catch (error) {
124
+ if (error instanceof GuardrailTripwireException) {
125
+ throw error; // Re-throw tripwire exceptions
126
+ }
127
+
128
+ // Wrap unexpected errors
129
+ return this.createResult(
130
+ false,
131
+ [{
132
+ code: 'GUARDRAIL_ERROR',
133
+ message: error.message,
134
+ severity: 'error',
135
+ context: { errorName: error.name, stack: error.stack }
136
+ }],
137
+ `Guardrail execution failed: ${error.message}`,
138
+ Date.now() - startTime
139
+ );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Create a standardized guardrail result
145
+ * @protected
146
+ * @param {boolean} passed - Whether the check passed
147
+ * @param {Array<GuardrailViolation>} [violations=[]] - List of violations
148
+ * @param {string} [message] - Optional message
149
+ * @param {number} [executionTimeMs=0] - Execution time
150
+ * @param {Object} [metadata] - Additional metadata
151
+ * @returns {GuardrailResult}
152
+ */
153
+ createResult(passed, violations = [], message = undefined, executionTimeMs = 0, metadata = {}) {
154
+ return {
155
+ passed,
156
+ guardrailName: this.name,
157
+ message,
158
+ violations,
159
+ metadata,
160
+ executionTimeMs
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Create a violation object
166
+ * @protected
167
+ * @param {string} code - Violation code
168
+ * @param {string} message - Human-readable message
169
+ * @param {string} [severity] - Severity level
170
+ * @param {Object} [context] - Additional context
171
+ * @returns {GuardrailViolation}
172
+ */
173
+ createViolation(code, message, severity = null, context = {}) {
174
+ return {
175
+ code,
176
+ message,
177
+ severity: severity || this.defaultSeverity,
178
+ context
179
+ };
180
+ }
181
+
182
+ /**
183
+ * Enable the guardrail
184
+ */
185
+ enable() {
186
+ this.enabled = true;
187
+ }
188
+
189
+ /**
190
+ * Disable the guardrail
191
+ */
192
+ disable() {
193
+ this.enabled = false;
194
+ }
195
+
196
+ /**
197
+ * Enable tripwire mode
198
+ */
199
+ enableTripwire() {
200
+ this.tripwireEnabled = true;
201
+ }
202
+
203
+ /**
204
+ * Disable tripwire mode
205
+ */
206
+ disableTripwire() {
207
+ this.tripwireEnabled = false;
208
+ }
209
+
210
+ /**
211
+ * Get guardrail info
212
+ * @returns {Object}
213
+ */
214
+ getInfo() {
215
+ return {
216
+ name: this.name,
217
+ description: this.description,
218
+ enabled: this.enabled,
219
+ failFast: this.failFast,
220
+ tripwireEnabled: this.tripwireEnabled,
221
+ defaultSeverity: this.defaultSeverity
222
+ };
223
+ }
224
+ }
225
+
226
+ /**
227
+ * Guardrail chain for running multiple guardrails
228
+ */
229
+ class GuardrailChain {
230
+ /**
231
+ * @param {Object} [options] - Chain options
232
+ * @param {boolean} [options.parallel=false] - Run guardrails in parallel
233
+ * @param {boolean} [options.stopOnFirstFailure=false] - Stop on first failure
234
+ * @param {string} [options.name='GuardrailChain'] - Chain name
235
+ */
236
+ constructor(options = {}) {
237
+ this.name = options.name || 'GuardrailChain';
238
+ this.parallel = options.parallel || false;
239
+ this.stopOnFirstFailure = options.stopOnFirstFailure || false;
240
+ this.guardrails = [];
241
+ }
242
+
243
+ /**
244
+ * Add a guardrail to the chain
245
+ * @param {BaseGuardrail} guardrail - Guardrail to add
246
+ * @returns {GuardrailChain} this for chaining
247
+ */
248
+ add(guardrail) {
249
+ if (!(guardrail instanceof BaseGuardrail)) {
250
+ throw new Error('Can only add BaseGuardrail instances to the chain');
251
+ }
252
+ this.guardrails.push(guardrail);
253
+ return this;
254
+ }
255
+
256
+ /**
257
+ * Add multiple guardrails to the chain
258
+ * @param {Array<BaseGuardrail>} guardrails - Guardrails to add
259
+ * @returns {GuardrailChain} this for chaining
260
+ */
261
+ addAll(guardrails) {
262
+ guardrails.forEach(g => this.add(g));
263
+ return this;
264
+ }
265
+
266
+ /**
267
+ * Run all guardrails in the chain
268
+ * @param {*} input - Input to validate
269
+ * @param {Object} [context] - Execution context
270
+ * @returns {Promise<Object>} Combined result
271
+ */
272
+ async run(input, context = {}) {
273
+ const startTime = Date.now();
274
+ const results = [];
275
+ const allViolations = [];
276
+ let overallPassed = true;
277
+
278
+ if (this.parallel) {
279
+ // Parallel execution with optional early termination
280
+ const promises = this.guardrails.map(g => g.run(input, context));
281
+
282
+ if (this.stopOnFirstFailure) {
283
+ // Use Promise.race pattern for early termination
284
+ const settledResults = await Promise.allSettled(promises);
285
+
286
+ for (const settled of settledResults) {
287
+ if (settled.status === 'fulfilled') {
288
+ const result = settled.value;
289
+ results.push(result);
290
+
291
+ if (!result.passed) {
292
+ overallPassed = false;
293
+ allViolations.push(...result.violations);
294
+ }
295
+ } else {
296
+ // Handle rejected promise (tripwire or error)
297
+ throw settled.reason;
298
+ }
299
+ }
300
+ } else {
301
+ const resolvedResults = await Promise.all(promises);
302
+ for (const result of resolvedResults) {
303
+ results.push(result);
304
+ if (!result.passed) {
305
+ overallPassed = false;
306
+ allViolations.push(...result.violations);
307
+ }
308
+ }
309
+ }
310
+ } else {
311
+ // Sequential execution
312
+ for (const guardrail of this.guardrails) {
313
+ const result = await guardrail.run(input, context);
314
+ results.push(result);
315
+
316
+ if (!result.passed) {
317
+ overallPassed = false;
318
+ allViolations.push(...result.violations);
319
+
320
+ if (this.stopOnFirstFailure) {
321
+ break;
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ return {
328
+ passed: overallPassed,
329
+ chainName: this.name,
330
+ results,
331
+ violations: allViolations,
332
+ guardrailCount: this.guardrails.length,
333
+ executedCount: results.length,
334
+ executionTimeMs: Date.now() - startTime
335
+ };
336
+ }
337
+
338
+ /**
339
+ * Get list of guardrails in the chain
340
+ * @returns {Array<Object>}
341
+ */
342
+ getGuardrails() {
343
+ return this.guardrails.map(g => g.getInfo());
344
+ }
345
+
346
+ /**
347
+ * Clear all guardrails from the chain
348
+ */
349
+ clear() {
350
+ this.guardrails = [];
351
+ }
352
+ }
353
+
354
+ module.exports = {
355
+ BaseGuardrail,
356
+ GuardrailChain,
357
+ GuardrailTripwireException
358
+ };