mcp-rubber-duck 1.8.0 → 1.9.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 (118) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +158 -1
  3. package/audit-ci.json +2 -1
  4. package/dist/config/config.d.ts +2 -0
  5. package/dist/config/config.d.ts.map +1 -1
  6. package/dist/config/config.js +144 -1
  7. package/dist/config/config.js.map +1 -1
  8. package/dist/config/types.d.ts +1084 -2
  9. package/dist/config/types.d.ts.map +1 -1
  10. package/dist/config/types.js +59 -0
  11. package/dist/config/types.js.map +1 -1
  12. package/dist/guardrails/context.d.ts +10 -0
  13. package/dist/guardrails/context.d.ts.map +1 -0
  14. package/dist/guardrails/context.js +35 -0
  15. package/dist/guardrails/context.js.map +1 -0
  16. package/dist/guardrails/errors.d.ts +26 -0
  17. package/dist/guardrails/errors.d.ts.map +1 -0
  18. package/dist/guardrails/errors.js +42 -0
  19. package/dist/guardrails/errors.js.map +1 -0
  20. package/dist/guardrails/index.d.ts +6 -0
  21. package/dist/guardrails/index.d.ts.map +1 -0
  22. package/dist/guardrails/index.js +11 -0
  23. package/dist/guardrails/index.js.map +1 -0
  24. package/dist/guardrails/plugins/base-plugin.d.ts +35 -0
  25. package/dist/guardrails/plugins/base-plugin.d.ts.map +1 -0
  26. package/dist/guardrails/plugins/base-plugin.js +70 -0
  27. package/dist/guardrails/plugins/base-plugin.js.map +1 -0
  28. package/dist/guardrails/plugins/index.d.ts +6 -0
  29. package/dist/guardrails/plugins/index.d.ts.map +1 -0
  30. package/dist/guardrails/plugins/index.js +6 -0
  31. package/dist/guardrails/plugins/index.js.map +1 -0
  32. package/dist/guardrails/plugins/pattern-blocker.d.ts +27 -0
  33. package/dist/guardrails/plugins/pattern-blocker.d.ts.map +1 -0
  34. package/dist/guardrails/plugins/pattern-blocker.js +140 -0
  35. package/dist/guardrails/plugins/pattern-blocker.js.map +1 -0
  36. package/dist/guardrails/plugins/pii-redactor/detectors.d.ts +40 -0
  37. package/dist/guardrails/plugins/pii-redactor/detectors.d.ts.map +1 -0
  38. package/dist/guardrails/plugins/pii-redactor/detectors.js +134 -0
  39. package/dist/guardrails/plugins/pii-redactor/detectors.js.map +1 -0
  40. package/dist/guardrails/plugins/pii-redactor/index.d.ts +28 -0
  41. package/dist/guardrails/plugins/pii-redactor/index.d.ts.map +1 -0
  42. package/dist/guardrails/plugins/pii-redactor/index.js +157 -0
  43. package/dist/guardrails/plugins/pii-redactor/index.js.map +1 -0
  44. package/dist/guardrails/plugins/pii-redactor/pseudonymizer.d.ts +33 -0
  45. package/dist/guardrails/plugins/pii-redactor/pseudonymizer.d.ts.map +1 -0
  46. package/dist/guardrails/plugins/pii-redactor/pseudonymizer.js +70 -0
  47. package/dist/guardrails/plugins/pii-redactor/pseudonymizer.js.map +1 -0
  48. package/dist/guardrails/plugins/rate-limiter.d.ts +28 -0
  49. package/dist/guardrails/plugins/rate-limiter.d.ts.map +1 -0
  50. package/dist/guardrails/plugins/rate-limiter.js +91 -0
  51. package/dist/guardrails/plugins/rate-limiter.js.map +1 -0
  52. package/dist/guardrails/plugins/token-limiter.d.ts +30 -0
  53. package/dist/guardrails/plugins/token-limiter.d.ts.map +1 -0
  54. package/dist/guardrails/plugins/token-limiter.js +98 -0
  55. package/dist/guardrails/plugins/token-limiter.js.map +1 -0
  56. package/dist/guardrails/service.d.ts +38 -0
  57. package/dist/guardrails/service.d.ts.map +1 -0
  58. package/dist/guardrails/service.js +183 -0
  59. package/dist/guardrails/service.js.map +1 -0
  60. package/dist/guardrails/types.d.ts +96 -0
  61. package/dist/guardrails/types.d.ts.map +1 -0
  62. package/dist/guardrails/types.js +2 -0
  63. package/dist/guardrails/types.js.map +1 -0
  64. package/dist/providers/duck-provider-enhanced.d.ts +2 -1
  65. package/dist/providers/duck-provider-enhanced.d.ts.map +1 -1
  66. package/dist/providers/duck-provider-enhanced.js +55 -6
  67. package/dist/providers/duck-provider-enhanced.js.map +1 -1
  68. package/dist/providers/enhanced-manager.d.ts +2 -1
  69. package/dist/providers/enhanced-manager.d.ts.map +1 -1
  70. package/dist/providers/enhanced-manager.js +3 -3
  71. package/dist/providers/enhanced-manager.js.map +1 -1
  72. package/dist/providers/manager.d.ts +3 -1
  73. package/dist/providers/manager.d.ts.map +1 -1
  74. package/dist/providers/manager.js +4 -2
  75. package/dist/providers/manager.js.map +1 -1
  76. package/dist/providers/provider.d.ts +3 -1
  77. package/dist/providers/provider.d.ts.map +1 -1
  78. package/dist/providers/provider.js +43 -3
  79. package/dist/providers/provider.js.map +1 -1
  80. package/dist/server.d.ts +1 -0
  81. package/dist/server.d.ts.map +1 -1
  82. package/dist/server.js +28 -6
  83. package/dist/server.js.map +1 -1
  84. package/dist/services/function-bridge.d.ts +3 -1
  85. package/dist/services/function-bridge.d.ts.map +1 -1
  86. package/dist/services/function-bridge.js +40 -1
  87. package/dist/services/function-bridge.js.map +1 -1
  88. package/package.json +1 -1
  89. package/src/config/config.ts +187 -1
  90. package/src/config/types.ts +73 -0
  91. package/src/guardrails/context.ts +37 -0
  92. package/src/guardrails/errors.ts +46 -0
  93. package/src/guardrails/index.ts +20 -0
  94. package/src/guardrails/plugins/base-plugin.ts +103 -0
  95. package/src/guardrails/plugins/index.ts +5 -0
  96. package/src/guardrails/plugins/pattern-blocker.ts +190 -0
  97. package/src/guardrails/plugins/pii-redactor/detectors.ts +200 -0
  98. package/src/guardrails/plugins/pii-redactor/index.ts +203 -0
  99. package/src/guardrails/plugins/pii-redactor/pseudonymizer.ts +91 -0
  100. package/src/guardrails/plugins/rate-limiter.ts +142 -0
  101. package/src/guardrails/plugins/token-limiter.ts +155 -0
  102. package/src/guardrails/service.ts +209 -0
  103. package/src/guardrails/types.ts +120 -0
  104. package/src/providers/duck-provider-enhanced.ts +76 -7
  105. package/src/providers/enhanced-manager.ts +5 -3
  106. package/src/providers/manager.ts +6 -3
  107. package/src/providers/provider.ts +57 -6
  108. package/src/server.ts +32 -6
  109. package/src/services/function-bridge.ts +53 -2
  110. package/tests/guardrails/config.test.ts +267 -0
  111. package/tests/guardrails/errors.test.ts +109 -0
  112. package/tests/guardrails/plugins/pattern-blocker.test.ts +309 -0
  113. package/tests/guardrails/plugins/pii-redactor.test.ts +1004 -0
  114. package/tests/guardrails/plugins/rate-limiter.test.ts +310 -0
  115. package/tests/guardrails/plugins/token-limiter.test.ts +216 -0
  116. package/tests/guardrails/service.test.ts +911 -0
  117. package/tests/mcp-bridge.test.ts +248 -0
  118. package/tests/providers.test.ts +739 -0
@@ -0,0 +1,30 @@
1
+ import { BaseGuardrailPlugin } from './base-plugin.js';
2
+ import { GuardrailPhase, GuardrailContext, GuardrailResult } from '../types.js';
3
+ /**
4
+ * Token limiter plugin - limits input/output token counts
5
+ */
6
+ export declare class TokenLimiterPlugin extends BaseGuardrailPlugin {
7
+ name: string;
8
+ phases: GuardrailPhase[];
9
+ private maxInputTokens;
10
+ private maxOutputTokens;
11
+ private warnAtPercentage;
12
+ initialize(config: Record<string, unknown>): Promise<void>;
13
+ execute(phase: GuardrailPhase, context: GuardrailContext): Promise<GuardrailResult>;
14
+ private checkInputTokens;
15
+ private checkOutputTokens;
16
+ /**
17
+ * Estimate token count from text
18
+ * Uses a simple heuristic: ~4 characters per token for English text
19
+ * This is a rough approximation - for accuracy, use tiktoken
20
+ */
21
+ estimateTokenCount(text: string): number;
22
+ /**
23
+ * Get configured limits (for testing/monitoring)
24
+ */
25
+ getLimits(): {
26
+ maxInputTokens: number;
27
+ maxOutputTokens: number | undefined;
28
+ };
29
+ }
30
+ //# sourceMappingURL=token-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-limiter.d.ts","sourceRoot":"","sources":["../../../src/guardrails/plugins/token-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGhF;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,mBAAmB;IACzD,IAAI,SAAmB;IACvB,MAAM,EAAE,cAAc,EAAE,CAAoC;IAE5D,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,gBAAgB,CAAc;IAEhC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUhE,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IASnF,OAAO,CAAC,gBAAgB;IAiDxB,OAAO,CAAC,iBAAiB;IAkDzB;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAOxC;;OAEG;IACH,SAAS,IAAI;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;CAM7E"}
@@ -0,0 +1,98 @@
1
+ import { BaseGuardrailPlugin } from './base-plugin.js';
2
+ /**
3
+ * Token limiter plugin - limits input/output token counts
4
+ */
5
+ export class TokenLimiterPlugin extends BaseGuardrailPlugin {
6
+ name = 'token_limiter';
7
+ phases = ['pre_request', 'post_response'];
8
+ maxInputTokens = 8192;
9
+ maxOutputTokens;
10
+ warnAtPercentage = 80;
11
+ async initialize(config) {
12
+ await super.initialize(config);
13
+ const typedConfig = config;
14
+ this.maxInputTokens = typedConfig.max_input_tokens ?? 8192;
15
+ this.maxOutputTokens = typedConfig.max_output_tokens;
16
+ this.warnAtPercentage = typedConfig.warn_at_percentage ?? 80;
17
+ this.priority = typedConfig.priority ?? 20;
18
+ }
19
+ execute(phase, context) {
20
+ if (phase === 'pre_request') {
21
+ return this.checkInputTokens(context, phase);
22
+ }
23
+ else if (phase === 'post_response') {
24
+ return this.checkOutputTokens(context, phase);
25
+ }
26
+ return Promise.resolve(this.allow(context));
27
+ }
28
+ checkInputTokens(context, phase) {
29
+ // Estimate token count from prompt
30
+ const prompt = context.prompt || '';
31
+ const estimatedTokens = this.estimateTokenCount(prompt);
32
+ // Also count messages if present
33
+ let totalTokens = estimatedTokens;
34
+ for (const msg of context.messages) {
35
+ totalTokens += this.estimateTokenCount(msg.content);
36
+ }
37
+ // Check if over limit
38
+ if (totalTokens > this.maxInputTokens) {
39
+ this.addViolation(context, phase, 'max_input_tokens', 'error', `Token limit exceeded: estimated ${totalTokens} tokens (limit: ${this.maxInputTokens})`, { estimatedTokens: totalTokens, limit: this.maxInputTokens });
40
+ return Promise.resolve(this.block(context, `Token limit exceeded: ~${totalTokens}/${this.maxInputTokens} tokens`));
41
+ }
42
+ // Warn if approaching limit
43
+ const warnThreshold = this.maxInputTokens * (this.warnAtPercentage / 100);
44
+ if (totalTokens >= warnThreshold) {
45
+ this.addViolation(context, phase, 'max_input_tokens_warning', 'warning', `Approaching token limit: estimated ${totalTokens}/${this.maxInputTokens} tokens (${Math.round((totalTokens / this.maxInputTokens) * 100)}%)`, {
46
+ estimatedTokens: totalTokens,
47
+ limit: this.maxInputTokens,
48
+ percentage: Math.round((totalTokens / this.maxInputTokens) * 100),
49
+ });
50
+ }
51
+ return Promise.resolve(this.allow(context));
52
+ }
53
+ checkOutputTokens(context, phase) {
54
+ // Skip if no output limit configured
55
+ if (!this.maxOutputTokens) {
56
+ return Promise.resolve(this.allow(context));
57
+ }
58
+ const response = context.response || '';
59
+ const estimatedTokens = this.estimateTokenCount(response);
60
+ // Check if over limit
61
+ if (estimatedTokens > this.maxOutputTokens) {
62
+ this.addViolation(context, phase, 'max_output_tokens', 'error', `Output token limit exceeded: estimated ${estimatedTokens} tokens (limit: ${this.maxOutputTokens})`, { estimatedTokens, limit: this.maxOutputTokens });
63
+ return Promise.resolve(this.block(context, `Output token limit exceeded: ~${estimatedTokens}/${this.maxOutputTokens} tokens`));
64
+ }
65
+ // Warn if approaching limit
66
+ const warnThreshold = this.maxOutputTokens * (this.warnAtPercentage / 100);
67
+ if (estimatedTokens >= warnThreshold) {
68
+ this.addViolation(context, phase, 'max_output_tokens_warning', 'warning', `Approaching output token limit: estimated ${estimatedTokens}/${this.maxOutputTokens} tokens (${Math.round((estimatedTokens / this.maxOutputTokens) * 100)}%)`, {
69
+ estimatedTokens,
70
+ limit: this.maxOutputTokens,
71
+ percentage: Math.round((estimatedTokens / this.maxOutputTokens) * 100),
72
+ });
73
+ }
74
+ return Promise.resolve(this.allow(context));
75
+ }
76
+ /**
77
+ * Estimate token count from text
78
+ * Uses a simple heuristic: ~4 characters per token for English text
79
+ * This is a rough approximation - for accuracy, use tiktoken
80
+ */
81
+ estimateTokenCount(text) {
82
+ if (!text)
83
+ return 0;
84
+ // Rough approximation: 1 token ≈ 4 characters for English
85
+ // Add some overhead for special tokens
86
+ return Math.ceil(text.length / 4) + 4;
87
+ }
88
+ /**
89
+ * Get configured limits (for testing/monitoring)
90
+ */
91
+ getLimits() {
92
+ return {
93
+ maxInputTokens: this.maxInputTokens,
94
+ maxOutputTokens: this.maxOutputTokens,
95
+ };
96
+ }
97
+ }
98
+ //# sourceMappingURL=token-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-limiter.js","sourceRoot":"","sources":["../../../src/guardrails/plugins/token-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { GuardrailPlugin, GuardrailPhase, GuardrailContext, GuardrailResult, CreateContextOptions } from './types.js';
2
+ import { GuardrailsConfig } from '../config/types.js';
3
+ /**
4
+ * Main service that orchestrates guardrail plugins
5
+ */
6
+ export declare class GuardrailsService {
7
+ private plugins;
8
+ private config;
9
+ private enabled;
10
+ constructor(config?: Partial<GuardrailsConfig>);
11
+ /**
12
+ * Initialize the service and all configured plugins
13
+ */
14
+ initialize(): Promise<void>;
15
+ private loadPluginsFromConfig;
16
+ private loadPlugin;
17
+ /**
18
+ * Check if guardrails are enabled
19
+ */
20
+ isEnabled(): boolean;
21
+ /**
22
+ * Create a new context for guardrail execution
23
+ */
24
+ createContext(options: CreateContextOptions): GuardrailContext;
25
+ /**
26
+ * Execute all relevant plugins for a given phase
27
+ */
28
+ execute(phase: GuardrailPhase, context: GuardrailContext): Promise<GuardrailResult>;
29
+ /**
30
+ * Shutdown the service and all plugins
31
+ */
32
+ shutdown(): Promise<void>;
33
+ /**
34
+ * Get list of loaded plugins
35
+ */
36
+ getPlugins(): GuardrailPlugin[];
37
+ }
38
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/guardrails/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEtH,OAAO,EAAE,gBAAgB,EAA2B,MAAM,oBAAoB,CAAC;AAG/E;;GAEG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,OAAO,CAAkB;gBAErB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC;IAY9C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAkBnB,qBAAqB;YA2BrB,UAAU;IAwBxB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,gBAAgB;IAI9D;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAyEzF;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAY/B;;OAEG;IACH,UAAU,IAAI,eAAe,EAAE;CAGhC"}
@@ -0,0 +1,183 @@
1
+ import { createGuardrailContext } from './context.js';
2
+ import { logger } from '../utils/logger.js';
3
+ /**
4
+ * Main service that orchestrates guardrail plugins
5
+ */
6
+ export class GuardrailsService {
7
+ plugins = [];
8
+ config;
9
+ enabled = false;
10
+ constructor(config) {
11
+ this.config = {
12
+ enabled: config?.enabled ?? false,
13
+ log_violations: config?.log_violations ?? true,
14
+ log_modifications: config?.log_modifications ?? false,
15
+ fail_open: config?.fail_open ?? false,
16
+ plugins: config?.plugins,
17
+ };
18
+ // Start disabled - will be enabled after successful initialization with plugins
19
+ this.enabled = false;
20
+ }
21
+ /**
22
+ * Initialize the service and all configured plugins
23
+ */
24
+ async initialize() {
25
+ if (!this.config.enabled) {
26
+ logger.info('Guardrails disabled in configuration');
27
+ return;
28
+ }
29
+ const pluginConfigs = this.config.plugins || {};
30
+ // Load plugins in order
31
+ await this.loadPluginsFromConfig(pluginConfigs);
32
+ // Sort by priority (lower = runs first)
33
+ this.plugins.sort((a, b) => a.priority - b.priority);
34
+ this.enabled = this.plugins.length > 0;
35
+ logger.info(`Guardrails initialized with ${this.plugins.length} plugins`);
36
+ }
37
+ async loadPluginsFromConfig(pluginConfigs) {
38
+ const pluginOrder = [
39
+ ['rate_limiter', pluginConfigs.rate_limiter],
40
+ ['token_limiter', pluginConfigs.token_limiter],
41
+ ['pii_redactor', pluginConfigs.pii_redactor],
42
+ ['pattern_blocker', pluginConfigs.pattern_blocker],
43
+ ];
44
+ for (const [pluginName, pluginConfig] of pluginOrder) {
45
+ if (!pluginConfig || !pluginConfig.enabled) {
46
+ continue;
47
+ }
48
+ try {
49
+ const plugin = await this.loadPlugin(pluginName);
50
+ await plugin.initialize(pluginConfig);
51
+ if (pluginConfig.priority !== undefined) {
52
+ plugin.priority = pluginConfig.priority;
53
+ }
54
+ this.plugins.push(plugin);
55
+ logger.info(`Guardrail plugin '${pluginName}' initialized`);
56
+ }
57
+ catch (error) {
58
+ logger.error(`Failed to initialize guardrail plugin '${pluginName}':`, error);
59
+ }
60
+ }
61
+ }
62
+ async loadPlugin(name) {
63
+ // Dynamic plugin loading
64
+ switch (name) {
65
+ case 'rate_limiter': {
66
+ const { RateLimiterPlugin } = await import('./plugins/rate-limiter.js');
67
+ return new RateLimiterPlugin();
68
+ }
69
+ case 'token_limiter': {
70
+ const { TokenLimiterPlugin } = await import('./plugins/token-limiter.js');
71
+ return new TokenLimiterPlugin();
72
+ }
73
+ case 'pattern_blocker': {
74
+ const { PatternBlockerPlugin } = await import('./plugins/pattern-blocker.js');
75
+ return new PatternBlockerPlugin();
76
+ }
77
+ case 'pii_redactor': {
78
+ const { PIIRedactorPlugin } = await import('./plugins/pii-redactor/index.js');
79
+ return new PIIRedactorPlugin();
80
+ }
81
+ default:
82
+ throw new Error(`Unknown guardrail plugin: ${name}`);
83
+ }
84
+ }
85
+ /**
86
+ * Check if guardrails are enabled
87
+ */
88
+ isEnabled() {
89
+ return this.enabled;
90
+ }
91
+ /**
92
+ * Create a new context for guardrail execution
93
+ */
94
+ createContext(options) {
95
+ return createGuardrailContext(options);
96
+ }
97
+ /**
98
+ * Execute all relevant plugins for a given phase
99
+ */
100
+ async execute(phase, context) {
101
+ if (!this.enabled) {
102
+ return { action: 'allow', context };
103
+ }
104
+ const relevantPlugins = this.plugins.filter((p) => p.enabled && p.phases.includes(phase));
105
+ // Track logged items to avoid duplicates
106
+ let lastViolationCount = 0;
107
+ let lastModificationCount = 0;
108
+ let wasModified = false;
109
+ for (const plugin of relevantPlugins) {
110
+ try {
111
+ const result = await plugin.execute(phase, context);
112
+ // Log only NEW violations if configured
113
+ if (this.config.log_violations && context.violations.length > lastViolationCount) {
114
+ for (let i = lastViolationCount; i < context.violations.length; i++) {
115
+ const violation = context.violations[i];
116
+ logger.warn(`Guardrail violation: ${violation.pluginName} - ${violation.message}`, {
117
+ rule: violation.rule,
118
+ severity: violation.severity,
119
+ details: violation.details,
120
+ });
121
+ }
122
+ lastViolationCount = context.violations.length;
123
+ }
124
+ // Log only NEW modifications if configured
125
+ if (this.config.log_modifications && context.modifications.length > lastModificationCount) {
126
+ for (let i = lastModificationCount; i < context.modifications.length; i++) {
127
+ const mod = context.modifications[i];
128
+ logger.info(`Guardrail modification: ${mod.pluginName} - ${mod.reason}`, {
129
+ field: mod.field,
130
+ });
131
+ }
132
+ lastModificationCount = context.modifications.length;
133
+ }
134
+ if (result.action === 'block') {
135
+ logger.warn(`Request blocked by guardrail '${plugin.name}': ${result.blockReason}`);
136
+ return result;
137
+ }
138
+ // Track if any plugin modified the context
139
+ if (result.action === 'modify') {
140
+ wasModified = true;
141
+ }
142
+ // Update context for next plugin
143
+ context = result.context;
144
+ }
145
+ catch (error) {
146
+ const errorMessage = error instanceof Error ? error.message : String(error);
147
+ logger.error(`Guardrail plugin '${plugin.name}' error:`, error);
148
+ if (!this.config.fail_open) {
149
+ return {
150
+ action: 'block',
151
+ context,
152
+ blockedBy: plugin.name,
153
+ blockReason: `Plugin error: ${errorMessage}`,
154
+ };
155
+ }
156
+ // fail_open: continue to next plugin
157
+ }
158
+ }
159
+ return { action: wasModified ? 'modify' : 'allow', context };
160
+ }
161
+ /**
162
+ * Shutdown the service and all plugins
163
+ */
164
+ async shutdown() {
165
+ for (const plugin of this.plugins) {
166
+ try {
167
+ await plugin.shutdown();
168
+ }
169
+ catch (error) {
170
+ logger.error(`Error shutting down plugin '${plugin.name}':`, error);
171
+ }
172
+ }
173
+ this.plugins = [];
174
+ this.enabled = false;
175
+ }
176
+ /**
177
+ * Get list of loaded plugins
178
+ */
179
+ getPlugins() {
180
+ return [...this.plugins];
181
+ }
182
+ }
183
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/guardrails/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C;;GAEG;AACH,MAAM,OAAO,iBAAiB;IACpB,OAAO,GAAsB,EAAE,CAAC;IAChC,MAAM,CAAmB;IACzB,OAAO,GAAY,KAAK,CAAC;IAEjC,YAAY,MAAkC;QAC5C,IAAI,CAAC,MAAM,GAAG;YACZ,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,KAAK;YACjC,cAAc,EAAE,MAAM,EAAE,cAAc,IAAI,IAAI;YAC9C,iBAAiB,EAAE,MAAM,EAAE,iBAAiB,IAAI,KAAK;YACrD,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,KAAK;YACrC,OAAO,EAAE,MAAM,EAAE,OAAO;SACzB,CAAC;QACF,gFAAgF;QAChF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACzB,MAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QAEhD,wBAAwB;QACxB,MAAM,IAAI,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAC;QAEhD,wCAAwC;QACxC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAErD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,+BAA+B,IAAI,CAAC,OAAO,CAAC,MAAM,UAAU,CAAC,CAAC;IAC5E,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,aAA+C;QACjF,MAAM,WAAW,GAA6B;YAC5C,CAAC,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC;YAC5C,CAAC,eAAe,EAAE,aAAa,CAAC,aAAa,CAAC;YAC9C,CAAC,cAAc,EAAE,aAAa,CAAC,YAAY,CAAC;YAC5C,CAAC,iBAAiB,EAAE,aAAa,CAAC,eAAe,CAAC;SACnD,CAAC;QAEF,KAAK,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC,IAAI,WAAW,EAAE,CAAC;YACrD,IAAI,CAAC,YAAY,IAAI,CAAE,YAAsC,CAAC,OAAO,EAAE,CAAC;gBACtE,SAAS;YACX,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;gBACjD,MAAM,MAAM,CAAC,UAAU,CAAC,YAAuC,CAAC,CAAC;gBACjE,IAAK,YAAsC,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBACnE,MAAM,CAAC,QAAQ,GAAI,YAAqC,CAAC,QAAQ,CAAC;gBACpE,CAAC;gBACD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC,qBAAqB,UAAU,eAAe,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,0CAA0C,UAAU,IAAI,EAAE,KAAK,CAAC,CAAC;YAChF,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,IAAY;QACnC,yBAAyB;QACzB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;gBACxE,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD,KAAK,eAAe,CAAC,CAAC,CAAC;gBACrB,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;gBAC1E,OAAO,IAAI,kBAAkB,EAAE,CAAC;YAClC,CAAC;YACD,KAAK,iBAAiB,CAAC,CAAC,CAAC;gBACvB,MAAM,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;gBAC9E,OAAO,IAAI,oBAAoB,EAAE,CAAC;YACpC,CAAC;YACD,KAAK,cAAc,CAAC,CAAC,CAAC;gBACpB,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,iCAAiC,CAAC,CAAC;gBAC9E,OAAO,IAAI,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CAAC,6BAA6B,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,OAA6B;QACzC,OAAO,sBAAsB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,KAAqB,EAAE,OAAyB;QAC5D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACtC,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CACzC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAC7C,CAAC;QAEF,yCAAyC;QACzC,IAAI,kBAAkB,GAAG,CAAC,CAAC;QAC3B,IAAI,qBAAqB,GAAG,CAAC,CAAC;QAC9B,IAAI,WAAW,GAAG,KAAK,CAAC;QAExB,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;YACrC,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;gBAEpD,wCAAwC;gBACxC,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,kBAAkB,EAAE,CAAC;oBACjF,KAAK,IAAI,CAAC,GAAG,kBAAkB,EAAE,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBACpE,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;wBACxC,MAAM,CAAC,IAAI,CAAC,wBAAwB,SAAS,CAAC,UAAU,MAAM,SAAS,CAAC,OAAO,EAAE,EAAE;4BACjF,IAAI,EAAE,SAAS,CAAC,IAAI;4BACpB,QAAQ,EAAE,SAAS,CAAC,QAAQ;4BAC5B,OAAO,EAAE,SAAS,CAAC,OAAO;yBAC3B,CAAC,CAAC;oBACL,CAAC;oBACD,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC;gBACjD,CAAC;gBAED,2CAA2C;gBAC3C,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,qBAAqB,EAAE,CAAC;oBAC1F,KAAK,IAAI,CAAC,GAAG,qBAAqB,EAAE,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;wBAC1E,MAAM,GAAG,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;wBACrC,MAAM,CAAC,IAAI,CAAC,2BAA2B,GAAG,CAAC,UAAU,MAAM,GAAG,CAAC,MAAM,EAAE,EAAE;4BACvE,KAAK,EAAE,GAAG,CAAC,KAAK;yBACjB,CAAC,CAAC;oBACL,CAAC;oBACD,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC;gBACvD,CAAC;gBAED,IAAI,MAAM,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;oBAC9B,MAAM,CAAC,IAAI,CAAC,iCAAiC,MAAM,CAAC,IAAI,MAAM,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;oBACpF,OAAO,MAAM,CAAC;gBAChB,CAAC;gBAED,2CAA2C;gBAC3C,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;oBAC/B,WAAW,GAAG,IAAI,CAAC;gBACrB,CAAC;gBAED,iCAAiC;gBACjC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YAC3B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBAC5E,MAAM,CAAC,KAAK,CAAC,qBAAqB,MAAM,CAAC,IAAI,UAAU,EAAE,KAAK,CAAC,CAAC;gBAEhE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;oBAC3B,OAAO;wBACL,MAAM,EAAE,OAAO;wBACf,OAAO;wBACP,SAAS,EAAE,MAAM,CAAC,IAAI;wBACtB,WAAW,EAAE,iBAAiB,YAAY,EAAE;qBAC7C,CAAC;gBACJ,CAAC;gBACD,qCAAqC;YACvC,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAClC,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,KAAK,CAAC,+BAA+B,MAAM,CAAC,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;OAEG;IACH,UAAU;QACR,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC;CACF"}
@@ -0,0 +1,96 @@
1
+ import { ConversationMessage } from '../config/types.js';
2
+ /**
3
+ * Phases in the guardrail pipeline where plugins can intercept
4
+ */
5
+ export type GuardrailPhase = 'pre_request' | 'post_response' | 'pre_tool_input' | 'post_tool_output' | 'pre_cache';
6
+ /**
7
+ * Action to take after guardrail evaluation
8
+ */
9
+ export type GuardrailAction = 'allow' | 'block' | 'modify';
10
+ /**
11
+ * Severity levels for violations
12
+ */
13
+ export type ViolationSeverity = 'info' | 'warning' | 'error' | 'critical';
14
+ /**
15
+ * A violation detected by a guardrail plugin
16
+ */
17
+ export interface GuardrailViolation {
18
+ pluginName: string;
19
+ phase: GuardrailPhase;
20
+ rule: string;
21
+ severity: ViolationSeverity;
22
+ message: string;
23
+ details?: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * A modification made by a guardrail plugin
27
+ */
28
+ export interface GuardrailModification {
29
+ pluginName: string;
30
+ phase: GuardrailPhase;
31
+ field: string;
32
+ originalValue?: unknown;
33
+ newValue?: unknown;
34
+ reason: string;
35
+ }
36
+ /**
37
+ * Context passed through the guardrail pipeline
38
+ */
39
+ export interface GuardrailContext {
40
+ requestId: string;
41
+ provider: string;
42
+ model: string;
43
+ timestamp: Date;
44
+ messages: ConversationMessage[];
45
+ prompt?: string;
46
+ response?: string;
47
+ toolName?: string;
48
+ toolArgs?: Record<string, unknown>;
49
+ toolResult?: unknown;
50
+ metadata: Map<string, unknown>;
51
+ violations: GuardrailViolation[];
52
+ modifications: GuardrailModification[];
53
+ }
54
+ /**
55
+ * Result from a guardrail plugin execution
56
+ */
57
+ export interface GuardrailResult {
58
+ action: GuardrailAction;
59
+ context: GuardrailContext;
60
+ blockedBy?: string;
61
+ blockReason?: string;
62
+ }
63
+ /**
64
+ * Base interface for guardrail plugins
65
+ */
66
+ export interface GuardrailPlugin {
67
+ /** Unique plugin name */
68
+ name: string;
69
+ /** Whether the plugin is currently enabled */
70
+ enabled: boolean;
71
+ /** Execution priority (lower = runs first) */
72
+ priority: number;
73
+ /** Which phases this plugin handles */
74
+ phases: GuardrailPhase[];
75
+ /** Initialize the plugin with its configuration */
76
+ initialize(config: Record<string, unknown>): Promise<void>;
77
+ /** Execute the plugin for a specific phase */
78
+ execute(phase: GuardrailPhase, context: GuardrailContext): Promise<GuardrailResult>;
79
+ /** Cleanup plugin resources */
80
+ shutdown(): Promise<void>;
81
+ }
82
+ /**
83
+ * Options for creating a guardrail context
84
+ */
85
+ export interface CreateContextOptions {
86
+ requestId?: string;
87
+ provider?: string;
88
+ model?: string;
89
+ messages?: ConversationMessage[];
90
+ prompt?: string;
91
+ response?: string;
92
+ toolName?: string;
93
+ toolArgs?: Record<string, unknown>;
94
+ toolResult?: unknown;
95
+ }
96
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/guardrails/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/guardrails/types.ts"],"names":[],"mappings":""}
@@ -2,6 +2,7 @@ import { DuckProvider } from './provider.js';
2
2
  import { ChatOptions, ChatResponse, ProviderOptions, MCPResult } from './types.js';
3
3
  import { FunctionBridge } from '../services/function-bridge.js';
4
4
  import { ConversationMessage } from '../config/types.js';
5
+ import { GuardrailsService } from '../guardrails/service.js';
5
6
  export interface EnhancedChatResponse extends ChatResponse {
6
7
  pendingApprovals?: {
7
8
  id: string;
@@ -12,7 +13,7 @@ export interface EnhancedChatResponse extends ChatResponse {
12
13
  export declare class EnhancedDuckProvider extends DuckProvider {
13
14
  private functionBridge;
14
15
  private mcpEnabled;
15
- constructor(name: string, nickname: string, options: ProviderOptions, functionBridge: FunctionBridge, mcpEnabled?: boolean);
16
+ constructor(name: string, nickname: string, options: ProviderOptions, functionBridge: FunctionBridge, mcpEnabled?: boolean, guardrailsService?: GuardrailsService);
16
17
  chat(options: ChatOptions): Promise<EnhancedChatResponse>;
17
18
  private handleToolCalls;
18
19
  retryWithApproval(approvalId: string, originalMessages: ConversationMessage[], options: ChatOptions): Promise<EnhancedChatResponse>;
@@ -1 +1 @@
1
- {"version":3,"file":"duck-provider-enhanced.d.ts","sourceRoot":"","sources":["../../src/providers/duck-provider-enhanced.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAmC,SAAS,EAAkB,MAAM,YAAY,CAAC;AACpI,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAIzD,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,gBAAgB,CAAC,EAAE;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,UAAU,CAAU;gBAG1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,cAAc,EAC9B,UAAU,GAAE,OAAc;IAOtB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAyEjD,eAAe;IAgIvB,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,mBAAmB,EAAE,EACvC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IA0B1B,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAc5C,WAAW;;;;;;IASX,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKrC,YAAY,IAAI,OAAO;CAGxB"}
1
+ {"version":3,"file":"duck-provider-enhanced.d.ts","sourceRoot":"","sources":["../../src/providers/duck-provider-enhanced.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,eAAe,EAAmC,SAAS,EAAkB,MAAM,YAAY,CAAC;AACpI,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAG7D,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,gBAAgB,CAAC,EAAE;QACjB,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,EAAE,CAAC;IACJ,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED,qBAAa,oBAAqB,SAAQ,YAAY;IACpD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,UAAU,CAAU;gBAG1B,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,cAAc,EAAE,cAAc,EAC9B,UAAU,GAAE,OAAc,EAC1B,iBAAiB,CAAC,EAAE,iBAAiB;IAOjC,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,oBAAoB,CAAC;YA0IjD,eAAe;IAiIvB,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,mBAAmB,EAAE,EACvC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,oBAAoB,CAAC;IA0B1B,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAc5C,WAAW;;;;;;IASX,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKrC,YAAY,IAAI,OAAO;CAGxB"}
@@ -1,16 +1,38 @@
1
1
  import { DuckProvider } from './provider.js';
2
2
  import { logger } from '../utils/logger.js';
3
3
  import { SafeLogger } from '../utils/safe-logger.js';
4
+ import { GuardrailBlockError } from '../guardrails/errors.js';
4
5
  export class EnhancedDuckProvider extends DuckProvider {
5
6
  functionBridge;
6
7
  mcpEnabled;
7
- constructor(name, nickname, options, functionBridge, mcpEnabled = true) {
8
- super(name, nickname, options);
8
+ constructor(name, nickname, options, functionBridge, mcpEnabled = true, guardrailsService) {
9
+ super(name, nickname, options, guardrailsService);
9
10
  this.functionBridge = functionBridge;
10
11
  this.mcpEnabled = mcpEnabled;
11
12
  }
12
13
  async chat(options) {
13
14
  try {
15
+ const modelToUse = options.model || this.options.model;
16
+ // Create guardrail context if service is enabled
17
+ const guardrailContext = this.guardrailsService?.isEnabled()
18
+ ? this.guardrailsService.createContext({
19
+ provider: this.name,
20
+ model: modelToUse,
21
+ messages: options.messages,
22
+ prompt: options.messages[options.messages.length - 1]?.content,
23
+ })
24
+ : undefined;
25
+ // Execute pre_request guardrails
26
+ if (guardrailContext && this.guardrailsService?.isEnabled()) {
27
+ const preResult = await this.guardrailsService.execute('pre_request', guardrailContext);
28
+ if (preResult.action === 'block') {
29
+ throw new GuardrailBlockError(preResult.blockedBy || 'unknown', preResult.blockReason || 'Request blocked by guardrails');
30
+ }
31
+ // Update messages if modified by guardrails (e.g., PII redaction)
32
+ if (preResult.action === 'modify' && guardrailContext.messages.length > 0) {
33
+ options = { ...options, messages: guardrailContext.messages };
34
+ }
35
+ }
14
36
  // If MCP is enabled, add function definitions
15
37
  if (this.mcpEnabled) {
16
38
  const functions = await this.functionBridge.getFunctionDefinitions();
@@ -22,7 +44,6 @@ export class EnhancedDuckProvider extends DuckProvider {
22
44
  }
23
45
  // Prepare messages for function calling
24
46
  const messages = this.prepareMessages(options.messages, options.systemPrompt);
25
- const modelToUse = options.model || this.options.model;
26
47
  const baseParams = {
27
48
  model: modelToUse,
28
49
  messages: messages,
@@ -49,11 +70,35 @@ export class EnhancedDuckProvider extends DuckProvider {
49
70
  const choice = response.choices[0];
50
71
  // Check if the model wants to call functions
51
72
  if (choice.message?.tool_calls && choice.message.tool_calls.length > 0) {
52
- return await this.handleToolCalls(choice.message.tool_calls, messages, baseParams, modelToUse);
73
+ const toolResult = await this.handleToolCalls(choice.message.tool_calls, messages, baseParams, modelToUse, guardrailContext);
74
+ // Execute post_response guardrails on final result
75
+ if (guardrailContext && this.guardrailsService?.isEnabled()) {
76
+ guardrailContext.response = toolResult.content;
77
+ const postResult = await this.guardrailsService.execute('post_response', guardrailContext);
78
+ if (postResult.action === 'block') {
79
+ throw new GuardrailBlockError(postResult.blockedBy || 'unknown', postResult.blockReason || 'Response blocked by guardrails');
80
+ }
81
+ if (postResult.action === 'modify' && guardrailContext.response) {
82
+ toolResult.content = guardrailContext.response;
83
+ }
84
+ }
85
+ return toolResult;
86
+ }
87
+ let content = choice.message?.content || '';
88
+ // Execute post_response guardrails
89
+ if (guardrailContext && this.guardrailsService?.isEnabled()) {
90
+ guardrailContext.response = content;
91
+ const postResult = await this.guardrailsService.execute('post_response', guardrailContext);
92
+ if (postResult.action === 'block') {
93
+ throw new GuardrailBlockError(postResult.blockedBy || 'unknown', postResult.blockReason || 'Response blocked by guardrails');
94
+ }
95
+ if (postResult.action === 'modify' && guardrailContext.response) {
96
+ content = guardrailContext.response;
97
+ }
53
98
  }
54
99
  // No tool calls, return regular response
55
100
  return {
56
- content: choice.message?.content || '',
101
+ content,
57
102
  usage: response.usage ? {
58
103
  promptTokens: response.usage.prompt_tokens,
59
104
  completionTokens: response.usage.completion_tokens,
@@ -64,12 +109,16 @@ export class EnhancedDuckProvider extends DuckProvider {
64
109
  };
65
110
  }
66
111
  catch (error) {
112
+ // Re-throw GuardrailBlockError as-is
113
+ if (error instanceof GuardrailBlockError) {
114
+ throw error;
115
+ }
67
116
  logger.error(`Enhanced provider ${this.name} chat error:`, error);
68
117
  const errorMessage = error instanceof Error ? error.message : String(error);
69
118
  throw new Error(`Duck ${this.nickname} couldn't respond: ${errorMessage}`);
70
119
  }
71
120
  }
72
- async handleToolCalls(toolCalls, messages, baseParams, modelToUse) {
121
+ async handleToolCalls(toolCalls, messages, baseParams, modelToUse, _guardrailContext) {
73
122
  const pendingApprovals = [];
74
123
  const toolMessages = [];
75
124
  let hasExecutedTools = false;