agentic-qe 2.5.5 → 2.5.7

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 (168) hide show
  1. package/.claude/agents/n8n/n8n-base-agent.md +376 -0
  2. package/.claude/agents/n8n/n8n-bdd-scenario-tester.md +613 -0
  3. package/.claude/agents/n8n/n8n-chaos-tester.md +654 -0
  4. package/.claude/agents/n8n/n8n-ci-orchestrator.md +850 -0
  5. package/.claude/agents/n8n/n8n-compliance-validator.md +685 -0
  6. package/.claude/agents/n8n/n8n-expression-validator.md +560 -0
  7. package/.claude/agents/n8n/n8n-integration-test.md +602 -0
  8. package/.claude/agents/n8n/n8n-monitoring-validator.md +589 -0
  9. package/.claude/agents/n8n/n8n-node-validator.md +455 -0
  10. package/.claude/agents/n8n/n8n-performance-tester.md +630 -0
  11. package/.claude/agents/n8n/n8n-security-auditor.md +786 -0
  12. package/.claude/agents/n8n/n8n-trigger-test.md +500 -0
  13. package/.claude/agents/n8n/n8n-unit-tester.md +633 -0
  14. package/.claude/agents/n8n/n8n-version-comparator.md +567 -0
  15. package/.claude/agents/n8n/n8n-workflow-executor.md +392 -0
  16. package/.claude/skills/n8n-expression-testing/SKILL.md +434 -0
  17. package/.claude/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
  18. package/.claude/skills/n8n-security-testing/SKILL.md +599 -0
  19. package/.claude/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
  20. package/.claude/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
  21. package/CHANGELOG.md +111 -0
  22. package/README.md +7 -4
  23. package/dist/adapters/MemoryStoreAdapter.d.ts +75 -123
  24. package/dist/adapters/MemoryStoreAdapter.d.ts.map +1 -1
  25. package/dist/adapters/MemoryStoreAdapter.js +204 -219
  26. package/dist/adapters/MemoryStoreAdapter.js.map +1 -1
  27. package/dist/agents/AccessibilityAllyAgent.d.ts.map +1 -1
  28. package/dist/agents/AccessibilityAllyAgent.js +17 -1
  29. package/dist/agents/AccessibilityAllyAgent.js.map +1 -1
  30. package/dist/agents/BaseAgent.d.ts +18 -250
  31. package/dist/agents/BaseAgent.d.ts.map +1 -1
  32. package/dist/agents/BaseAgent.js +122 -520
  33. package/dist/agents/BaseAgent.js.map +1 -1
  34. package/dist/agents/n8n/N8nAPIClient.d.ts +121 -0
  35. package/dist/agents/n8n/N8nAPIClient.d.ts.map +1 -0
  36. package/dist/agents/n8n/N8nAPIClient.js +367 -0
  37. package/dist/agents/n8n/N8nAPIClient.js.map +1 -0
  38. package/dist/agents/n8n/N8nAuditPersistence.d.ts +120 -0
  39. package/dist/agents/n8n/N8nAuditPersistence.d.ts.map +1 -0
  40. package/dist/agents/n8n/N8nAuditPersistence.js +473 -0
  41. package/dist/agents/n8n/N8nAuditPersistence.js.map +1 -0
  42. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts +159 -0
  43. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.d.ts.map +1 -0
  44. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js +697 -0
  45. package/dist/agents/n8n/N8nBDDScenarioTesterAgent.js.map +1 -0
  46. package/dist/agents/n8n/N8nBaseAgent.d.ts +126 -0
  47. package/dist/agents/n8n/N8nBaseAgent.d.ts.map +1 -0
  48. package/dist/agents/n8n/N8nBaseAgent.js +446 -0
  49. package/dist/agents/n8n/N8nBaseAgent.js.map +1 -0
  50. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts +164 -0
  51. package/dist/agents/n8n/N8nCIOrchestratorAgent.d.ts.map +1 -0
  52. package/dist/agents/n8n/N8nCIOrchestratorAgent.js +610 -0
  53. package/dist/agents/n8n/N8nCIOrchestratorAgent.js.map +1 -0
  54. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts +205 -0
  55. package/dist/agents/n8n/N8nChaosTesterAgent.d.ts.map +1 -0
  56. package/dist/agents/n8n/N8nChaosTesterAgent.js +729 -0
  57. package/dist/agents/n8n/N8nChaosTesterAgent.js.map +1 -0
  58. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts +228 -0
  59. package/dist/agents/n8n/N8nComplianceValidatorAgent.d.ts.map +1 -0
  60. package/dist/agents/n8n/N8nComplianceValidatorAgent.js +986 -0
  61. package/dist/agents/n8n/N8nComplianceValidatorAgent.js.map +1 -0
  62. package/dist/agents/n8n/N8nContractTesterAgent.d.ts +213 -0
  63. package/dist/agents/n8n/N8nContractTesterAgent.d.ts.map +1 -0
  64. package/dist/agents/n8n/N8nContractTesterAgent.js +989 -0
  65. package/dist/agents/n8n/N8nContractTesterAgent.js.map +1 -0
  66. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts +99 -0
  67. package/dist/agents/n8n/N8nExpressionValidatorAgent.d.ts.map +1 -0
  68. package/dist/agents/n8n/N8nExpressionValidatorAgent.js +632 -0
  69. package/dist/agents/n8n/N8nExpressionValidatorAgent.js.map +1 -0
  70. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts +238 -0
  71. package/dist/agents/n8n/N8nFailureModeTesterAgent.d.ts.map +1 -0
  72. package/dist/agents/n8n/N8nFailureModeTesterAgent.js +956 -0
  73. package/dist/agents/n8n/N8nFailureModeTesterAgent.js.map +1 -0
  74. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts +242 -0
  75. package/dist/agents/n8n/N8nIdempotencyTesterAgent.d.ts.map +1 -0
  76. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js +992 -0
  77. package/dist/agents/n8n/N8nIdempotencyTesterAgent.js.map +1 -0
  78. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts +104 -0
  79. package/dist/agents/n8n/N8nIntegrationTestAgent.d.ts.map +1 -0
  80. package/dist/agents/n8n/N8nIntegrationTestAgent.js +653 -0
  81. package/dist/agents/n8n/N8nIntegrationTestAgent.js.map +1 -0
  82. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts +210 -0
  83. package/dist/agents/n8n/N8nMonitoringValidatorAgent.d.ts.map +1 -0
  84. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js +669 -0
  85. package/dist/agents/n8n/N8nMonitoringValidatorAgent.js.map +1 -0
  86. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts +142 -0
  87. package/dist/agents/n8n/N8nNodeValidatorAgent.d.ts.map +1 -0
  88. package/dist/agents/n8n/N8nNodeValidatorAgent.js +1090 -0
  89. package/dist/agents/n8n/N8nNodeValidatorAgent.js.map +1 -0
  90. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts +198 -0
  91. package/dist/agents/n8n/N8nPerformanceTesterAgent.d.ts.map +1 -0
  92. package/dist/agents/n8n/N8nPerformanceTesterAgent.js +653 -0
  93. package/dist/agents/n8n/N8nPerformanceTesterAgent.js.map +1 -0
  94. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts +245 -0
  95. package/dist/agents/n8n/N8nReplayabilityTesterAgent.d.ts.map +1 -0
  96. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js +952 -0
  97. package/dist/agents/n8n/N8nReplayabilityTesterAgent.js.map +1 -0
  98. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts +325 -0
  99. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.d.ts.map +1 -0
  100. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js +1187 -0
  101. package/dist/agents/n8n/N8nSecretsHygieneAuditorAgent.js.map +1 -0
  102. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts +91 -0
  103. package/dist/agents/n8n/N8nSecurityAuditorAgent.d.ts.map +1 -0
  104. package/dist/agents/n8n/N8nSecurityAuditorAgent.js +825 -0
  105. package/dist/agents/n8n/N8nSecurityAuditorAgent.js.map +1 -0
  106. package/dist/agents/n8n/N8nTestHarness.d.ts +131 -0
  107. package/dist/agents/n8n/N8nTestHarness.d.ts.map +1 -0
  108. package/dist/agents/n8n/N8nTestHarness.js +456 -0
  109. package/dist/agents/n8n/N8nTestHarness.js.map +1 -0
  110. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts +119 -0
  111. package/dist/agents/n8n/N8nTriggerTestAgent.d.ts.map +1 -0
  112. package/dist/agents/n8n/N8nTriggerTestAgent.js +652 -0
  113. package/dist/agents/n8n/N8nTriggerTestAgent.js.map +1 -0
  114. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts +130 -0
  115. package/dist/agents/n8n/N8nUnitTesterAgent.d.ts.map +1 -0
  116. package/dist/agents/n8n/N8nUnitTesterAgent.js +522 -0
  117. package/dist/agents/n8n/N8nUnitTesterAgent.js.map +1 -0
  118. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts +201 -0
  119. package/dist/agents/n8n/N8nVersionComparatorAgent.d.ts.map +1 -0
  120. package/dist/agents/n8n/N8nVersionComparatorAgent.js +645 -0
  121. package/dist/agents/n8n/N8nVersionComparatorAgent.js.map +1 -0
  122. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts +120 -0
  123. package/dist/agents/n8n/N8nWorkflowExecutorAgent.d.ts.map +1 -0
  124. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js +347 -0
  125. package/dist/agents/n8n/N8nWorkflowExecutorAgent.js.map +1 -0
  126. package/dist/agents/n8n/index.d.ts +119 -0
  127. package/dist/agents/n8n/index.d.ts.map +1 -0
  128. package/dist/agents/n8n/index.js +298 -0
  129. package/dist/agents/n8n/index.js.map +1 -0
  130. package/dist/agents/n8n/types.d.ts +486 -0
  131. package/dist/agents/n8n/types.d.ts.map +1 -0
  132. package/dist/agents/n8n/types.js +8 -0
  133. package/dist/agents/n8n/types.js.map +1 -0
  134. package/dist/agents/utils/generators.d.ts +30 -0
  135. package/dist/agents/utils/generators.d.ts.map +1 -0
  136. package/dist/agents/utils/generators.js +44 -0
  137. package/dist/agents/utils/generators.js.map +1 -0
  138. package/dist/agents/utils/index.d.ts +10 -0
  139. package/dist/agents/utils/index.d.ts.map +1 -0
  140. package/dist/agents/utils/index.js +19 -0
  141. package/dist/agents/utils/index.js.map +1 -0
  142. package/dist/agents/utils/validation.d.ts +72 -0
  143. package/dist/agents/utils/validation.d.ts.map +1 -0
  144. package/dist/agents/utils/validation.js +75 -0
  145. package/dist/agents/utils/validation.js.map +1 -0
  146. package/dist/cli/init/agents.d.ts.map +1 -1
  147. package/dist/cli/init/agents.js +29 -0
  148. package/dist/cli/init/agents.js.map +1 -1
  149. package/dist/cli/init/skills.d.ts.map +1 -1
  150. package/dist/cli/init/skills.js +7 -1
  151. package/dist/cli/init/skills.js.map +1 -1
  152. package/dist/core/memory/HNSWVectorMemory.js +1 -1
  153. package/dist/core/memory/SwarmMemoryManager.d.ts +114 -90
  154. package/dist/core/memory/SwarmMemoryManager.d.ts.map +1 -1
  155. package/dist/core/memory/SwarmMemoryManager.js +277 -235
  156. package/dist/core/memory/SwarmMemoryManager.js.map +1 -1
  157. package/dist/learning/baselines/StandardTaskSuite.d.ts.map +1 -1
  158. package/dist/learning/baselines/StandardTaskSuite.js +38 -0
  159. package/dist/learning/baselines/StandardTaskSuite.js.map +1 -1
  160. package/dist/mcp/server-instructions.d.ts +1 -1
  161. package/dist/mcp/server-instructions.js +1 -1
  162. package/dist/types/memory-interfaces.d.ts +76 -68
  163. package/dist/types/memory-interfaces.d.ts.map +1 -1
  164. package/dist/types/memory-interfaces.js +3 -0
  165. package/dist/types/memory-interfaces.js.map +1 -1
  166. package/docs/reference/agents.md +91 -2
  167. package/docs/reference/skills.md +97 -2
  168. package/package.json +2 -2
@@ -0,0 +1,729 @@
1
+ "use strict";
2
+ /**
3
+ * N8nChaosTesterAgent
4
+ *
5
+ * REAL Chaos engineering for n8n workflows:
6
+ * - ACTUAL fault injection via modified workflow copies
7
+ * - REAL workflow execution to test resilience
8
+ * - ACTUAL recovery testing with live execution
9
+ * - TRUE failure scenario validation
10
+ *
11
+ * This agent creates fault-injected workflow copies, executes them against
12
+ * the n8n instance, and measures real behavior - NOT just static analysis.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.N8nChaosTesterAgent = void 0;
16
+ const N8nBaseAgent_1 = require("./N8nBaseAgent");
17
+ const N8nTestHarness_1 = require("./N8nTestHarness");
18
+ // Pre-defined chaos experiments with REAL fault injection
19
+ const STANDARD_EXPERIMENTS = [
20
+ {
21
+ name: 'HTTP 500 Error Injection',
22
+ type: 'node-failure',
23
+ target: 'httpRequest',
24
+ parameters: { errorCode: 500, errorMessage: 'Internal Server Error' },
25
+ expectedBehavior: 'Workflow handles 500 error gracefully with retry or error branch',
26
+ },
27
+ {
28
+ name: 'Network Timeout',
29
+ type: 'timeout',
30
+ target: 'httpRequest',
31
+ parameters: { timeoutMs: 30000 },
32
+ expectedBehavior: 'Workflow times out gracefully and triggers error handling',
33
+ },
34
+ {
35
+ name: 'Empty Response',
36
+ type: 'invalid-response',
37
+ target: 'httpRequest',
38
+ parameters: { responseType: 'empty', response: {} },
39
+ expectedBehavior: 'Workflow handles empty/null responses without crashing',
40
+ },
41
+ {
42
+ name: 'Malformed JSON Response',
43
+ type: 'data-corruption',
44
+ target: 'httpRequest',
45
+ parameters: { responseType: 'malformed', response: 'not-json' },
46
+ expectedBehavior: 'Workflow validates data and handles parse errors',
47
+ },
48
+ {
49
+ name: 'Rate Limit (429)',
50
+ type: 'rate-limit',
51
+ target: 'httpRequest',
52
+ parameters: { errorCode: 429, retryAfter: 60 },
53
+ expectedBehavior: 'Workflow implements retry with backoff or handles rate limit',
54
+ },
55
+ {
56
+ name: 'Authentication Failure (401)',
57
+ type: 'credential-failure',
58
+ target: 'httpRequest',
59
+ parameters: { errorCode: 401, errorMessage: 'Unauthorized' },
60
+ expectedBehavior: 'Workflow reports authentication failure clearly',
61
+ },
62
+ ];
63
+ class N8nChaosTesterAgent extends N8nBaseAgent_1.N8nBaseAgent {
64
+ constructor(config) {
65
+ const capabilities = [
66
+ {
67
+ name: 'real-fault-injection',
68
+ version: '2.0.0',
69
+ description: 'Inject REAL faults into workflow execution via modified copies',
70
+ parameters: {},
71
+ },
72
+ {
73
+ name: 'live-resilience-testing',
74
+ version: '2.0.0',
75
+ description: 'Test workflow resilience with ACTUAL execution',
76
+ parameters: {},
77
+ },
78
+ {
79
+ name: 'recovery-validation',
80
+ version: '2.0.0',
81
+ description: 'Validate recovery mechanisms with live retry testing',
82
+ parameters: {},
83
+ },
84
+ {
85
+ name: 'chaos-orchestration',
86
+ version: '2.0.0',
87
+ description: 'Orchestrate comprehensive chaos experiments',
88
+ parameters: {},
89
+ },
90
+ ];
91
+ super({
92
+ ...config,
93
+ type: 'n8n-chaos-tester',
94
+ capabilities: [...capabilities, ...(config.capabilities || [])],
95
+ });
96
+ this.experimentHistory = new Map();
97
+ this.testHarness = null;
98
+ }
99
+ async performTask(task) {
100
+ const chaosTask = task;
101
+ if (chaosTask.type !== 'chaos-test') {
102
+ throw new Error(`Unsupported task type: ${chaosTask.type}`);
103
+ }
104
+ return this.runChaosTests(chaosTask.target, chaosTask.options);
105
+ }
106
+ /**
107
+ * Run REAL chaos tests on workflow with actual fault injection and execution
108
+ */
109
+ async runChaosTests(workflowId, options) {
110
+ const workflow = await this.getWorkflow(workflowId);
111
+ const startTime = Date.now();
112
+ // Initialize test harness for real fault injection
113
+ this.testHarness = new N8nTestHarness_1.N8nTestHarness(this.n8nConfig);
114
+ try {
115
+ // Get experiments to run
116
+ const experiments = options?.experiments ||
117
+ this.selectExperiments(workflow, options?.intensity || 'moderate');
118
+ // Run experiments with REAL fault injection
119
+ const experimentResults = [];
120
+ let actualExecutions = 0;
121
+ for (const experiment of experiments) {
122
+ if (options?.safeMode && this.isDestructive(experiment)) {
123
+ experimentResults.push(this.createSkippedResult(experiment, 'Safe mode enabled'));
124
+ continue;
125
+ }
126
+ const result = await this.runRealExperiment(workflow, experiment, options?.testInput || {});
127
+ experimentResults.push(result);
128
+ if (result.execution) {
129
+ actualExecutions++;
130
+ }
131
+ // Store for history
132
+ const history = this.experimentHistory.get(workflowId) || [];
133
+ history.push(result);
134
+ this.experimentHistory.set(workflowId, history);
135
+ }
136
+ // Calculate resilience score based on REAL execution results
137
+ const resilience = this.calculateResilienceScore(workflow, experimentResults);
138
+ // Identify vulnerabilities from ACTUAL test failures
139
+ const vulnerabilities = this.identifyVulnerabilities(experimentResults);
140
+ // Generate recommendations based on REAL test data
141
+ const recommendations = this.generateRecommendations(workflow, experimentResults, vulnerabilities);
142
+ // Create summary
143
+ const summary = {
144
+ totalExperiments: experimentResults.length,
145
+ passed: experimentResults.filter(r => r.status === 'passed').length,
146
+ failed: experimentResults.filter(r => r.status === 'failed').length,
147
+ errors: experimentResults.filter(r => r.status === 'error').length,
148
+ skipped: experimentResults.filter(r => r.status === 'skipped').length,
149
+ duration: Date.now() - startTime,
150
+ vulnerabilitiesFound: vulnerabilities.length,
151
+ actualExecutions,
152
+ };
153
+ const result = {
154
+ workflowId,
155
+ experiments: experimentResults,
156
+ resilience,
157
+ vulnerabilities,
158
+ recommendations,
159
+ summary,
160
+ };
161
+ // Store result
162
+ await this.storeTestResult(`chaos-test:${workflowId}`, result);
163
+ // Emit event
164
+ this.emitEvent('chaos.test.completed', {
165
+ workflowId,
166
+ passed: summary.passed,
167
+ failed: summary.failed,
168
+ resilienceScore: resilience.overall,
169
+ vulnerabilities: vulnerabilities.length,
170
+ actualExecutions,
171
+ });
172
+ return result;
173
+ }
174
+ finally {
175
+ // Always cleanup test workflows
176
+ if (this.testHarness) {
177
+ await this.testHarness.cleanup();
178
+ }
179
+ }
180
+ }
181
+ /**
182
+ * Run a REAL experiment with actual fault injection and workflow execution
183
+ */
184
+ async runRealExperiment(workflow, experiment, testInput) {
185
+ const startTime = Date.now();
186
+ const observations = [];
187
+ observations.push({
188
+ timestamp: new Date(),
189
+ type: 'info',
190
+ message: `Starting REAL experiment: ${experiment.name}`,
191
+ details: { experimentType: experiment.type, target: experiment.target },
192
+ });
193
+ try {
194
+ // Select target node
195
+ const targetNode = this.selectTargetNode(workflow, experiment.target);
196
+ if (!targetNode) {
197
+ observations.push({
198
+ timestamp: new Date(),
199
+ type: 'warning',
200
+ message: `No suitable target node found for experiment: ${experiment.name}`,
201
+ });
202
+ return this.createSkippedResult(experiment, 'No suitable target node');
203
+ }
204
+ // Convert experiment to fault injection config
205
+ const faultConfig = this.experimentToFaultConfig(experiment, targetNode);
206
+ observations.push({
207
+ timestamp: new Date(),
208
+ type: 'info',
209
+ message: `Creating fault-injected workflow copy targeting node: ${targetNode.name}`,
210
+ details: { faultConfig: JSON.parse(JSON.stringify(faultConfig)) },
211
+ });
212
+ // Create fault-injected workflow via test harness
213
+ const { workflow: testWorkflow, cleanup } = await this.testHarness.createFaultInjectedWorkflow(workflow.id, [faultConfig]);
214
+ observations.push({
215
+ timestamp: new Date(),
216
+ type: 'execution',
217
+ message: `Created test workflow: ${testWorkflow.id}`,
218
+ details: { testWorkflowId: testWorkflow.id, testWorkflowName: testWorkflow.name },
219
+ });
220
+ try {
221
+ // Execute the fault-injected workflow
222
+ observations.push({
223
+ timestamp: new Date(),
224
+ type: 'execution',
225
+ message: 'Executing fault-injected workflow...',
226
+ });
227
+ const execution = await this.executeWorkflow(testWorkflow.id, testInput);
228
+ const executionResult = await this.waitForExecutionWithTimeout(execution.id, 60000);
229
+ observations.push({
230
+ timestamp: new Date(),
231
+ type: 'execution',
232
+ message: `Execution completed: ${executionResult.status}`,
233
+ details: {
234
+ executionId: executionResult.id,
235
+ status: executionResult.status,
236
+ finished: executionResult.finished,
237
+ },
238
+ });
239
+ // Analyze the REAL execution result
240
+ const impact = this.assessRealImpact(workflow, executionResult, experiment);
241
+ const recovery = await this.assessRealRecovery(workflow, executionResult, experiment);
242
+ // Determine status based on ACTUAL behavior
243
+ const status = this.determineRealStatus(experiment, executionResult, impact, recovery);
244
+ // Extract executed nodes
245
+ const nodesExecuted = this.extractExecutedNodes(executionResult);
246
+ const errorHandlingTriggered = this.checkErrorHandlingTriggered(executionResult, workflow);
247
+ return {
248
+ experiment,
249
+ status,
250
+ duration: Date.now() - startTime,
251
+ observations,
252
+ impact,
253
+ recovery,
254
+ execution: {
255
+ executionId: executionResult.id,
256
+ workflowStatus: executionResult.status,
257
+ errorMessage: this.extractErrorMessage(executionResult),
258
+ nodesExecuted,
259
+ errorHandlingTriggered,
260
+ },
261
+ };
262
+ }
263
+ finally {
264
+ // Always cleanup the test workflow
265
+ await cleanup();
266
+ observations.push({
267
+ timestamp: new Date(),
268
+ type: 'info',
269
+ message: 'Test workflow cleaned up',
270
+ });
271
+ }
272
+ }
273
+ catch (error) {
274
+ observations.push({
275
+ timestamp: new Date(),
276
+ type: 'error',
277
+ message: error instanceof Error ? error.message : String(error),
278
+ });
279
+ return {
280
+ experiment,
281
+ status: 'error',
282
+ duration: Date.now() - startTime,
283
+ observations,
284
+ impact: {
285
+ severity: 'critical',
286
+ affectedNodes: [],
287
+ dataLoss: false,
288
+ serviceDisruption: true,
289
+ cascadeFailure: false,
290
+ },
291
+ };
292
+ }
293
+ }
294
+ /**
295
+ * Convert experiment to fault injection config for TestHarness
296
+ */
297
+ experimentToFaultConfig(experiment, targetNode) {
298
+ const params = experiment.parameters;
299
+ switch (experiment.type) {
300
+ case 'node-failure':
301
+ return {
302
+ targetNode: targetNode.name,
303
+ faultType: 'error',
304
+ errorCode: params.errorCode || 500,
305
+ errorMessage: params.errorMessage || 'Injected fault: node failure',
306
+ };
307
+ case 'timeout':
308
+ return {
309
+ targetNode: targetNode.name,
310
+ faultType: 'timeout',
311
+ delay: params.timeoutMs || 30000,
312
+ };
313
+ case 'invalid-response':
314
+ case 'data-corruption':
315
+ return {
316
+ targetNode: targetNode.name,
317
+ faultType: 'empty-response',
318
+ };
319
+ case 'rate-limit':
320
+ return {
321
+ targetNode: targetNode.name,
322
+ faultType: 'error',
323
+ errorCode: 429,
324
+ errorMessage: 'Rate limit exceeded',
325
+ };
326
+ case 'credential-failure':
327
+ return {
328
+ targetNode: targetNode.name,
329
+ faultType: 'error',
330
+ errorCode: 401,
331
+ errorMessage: 'Authentication failed',
332
+ };
333
+ case 'network-delay':
334
+ return {
335
+ targetNode: targetNode.name,
336
+ faultType: 'timeout',
337
+ delay: params.delayMs || 5000,
338
+ };
339
+ case 'network-partition':
340
+ return {
341
+ targetNode: targetNode.name,
342
+ faultType: 'error',
343
+ errorCode: 503,
344
+ errorMessage: 'Service unavailable - network partition',
345
+ };
346
+ case 'resource-exhaustion':
347
+ return {
348
+ targetNode: targetNode.name,
349
+ faultType: 'error',
350
+ errorCode: 503,
351
+ errorMessage: 'Resource exhaustion',
352
+ };
353
+ default:
354
+ return {
355
+ targetNode: targetNode.name,
356
+ faultType: 'error',
357
+ errorCode: 500,
358
+ errorMessage: `Injected fault: ${experiment.type}`,
359
+ };
360
+ }
361
+ }
362
+ /**
363
+ * Wait for execution with timeout
364
+ */
365
+ async waitForExecutionWithTimeout(executionId, timeout) {
366
+ const startTime = Date.now();
367
+ while (Date.now() - startTime < timeout) {
368
+ const execution = await this.n8nClient.getExecution(executionId);
369
+ if (execution.finished) {
370
+ return execution;
371
+ }
372
+ // Poll every 500ms
373
+ await new Promise(resolve => setTimeout(resolve, 500));
374
+ }
375
+ // Return the current state even if not finished
376
+ return this.n8nClient.getExecution(executionId);
377
+ }
378
+ /**
379
+ * Assess REAL impact from actual execution
380
+ */
381
+ assessRealImpact(workflow, execution, experiment) {
382
+ const failedNodes = this.extractFailedNodes(execution);
383
+ const executedNodes = this.extractExecutedNodes(execution);
384
+ // Determine severity based on actual execution outcome
385
+ let severity;
386
+ if (execution.status === 'success') {
387
+ severity = 'none';
388
+ }
389
+ else if (execution.status === 'failed' || execution.status === 'crashed') {
390
+ if (failedNodes.length === 0) {
391
+ severity = 'minor';
392
+ }
393
+ else if (failedNodes.length === 1) {
394
+ severity = 'moderate';
395
+ }
396
+ else if (failedNodes.length > workflow.nodes.length / 2) {
397
+ severity = 'critical';
398
+ }
399
+ else {
400
+ severity = 'severe';
401
+ }
402
+ }
403
+ else {
404
+ severity = 'moderate';
405
+ }
406
+ // Check for cascade failure - did error propagate beyond target?
407
+ const cascadeFailure = failedNodes.length > 1;
408
+ return {
409
+ severity,
410
+ affectedNodes: failedNodes,
411
+ dataLoss: false, // Would need to check output data
412
+ serviceDisruption: execution.status === 'failed' || execution.status === 'crashed',
413
+ cascadeFailure,
414
+ };
415
+ }
416
+ /**
417
+ * Assess REAL recovery from actual execution
418
+ */
419
+ async assessRealRecovery(workflow, execution, experiment) {
420
+ const startTime = Date.now();
421
+ // Check if error handling was triggered
422
+ const errorHandlingTriggered = this.checkErrorHandlingTriggered(execution, workflow);
423
+ // Check if retry was attempted (look for multiple executions of same node)
424
+ const retryAttempted = this.checkRetryAttempted(execution);
425
+ // Check if workflow completed despite the fault
426
+ const recovered = execution.status === 'success' || errorHandlingTriggered;
427
+ return {
428
+ recovered,
429
+ timeToRecovery: Date.now() - startTime,
430
+ manualIntervention: !recovered,
431
+ dataIntegrity: execution.status === 'success',
432
+ retrySucceeded: retryAttempted && execution.status === 'success',
433
+ };
434
+ }
435
+ /**
436
+ * Determine status based on REAL execution results
437
+ */
438
+ determineRealStatus(experiment, execution, impact, recovery) {
439
+ // If workflow succeeded despite fault, that's a PASS
440
+ if (execution.status === 'success') {
441
+ return 'passed';
442
+ }
443
+ // If error was handled gracefully (error handling triggered), that's a PASS
444
+ if (recovery.recovered) {
445
+ return 'passed';
446
+ }
447
+ // If we expected the workflow to fail and it did, check if it failed gracefully
448
+ if (impact.severity === 'critical' || impact.cascadeFailure) {
449
+ return 'failed';
450
+ }
451
+ // Default to failed if error wasn't handled
452
+ return 'failed';
453
+ }
454
+ /**
455
+ * Extract list of nodes that executed
456
+ */
457
+ extractExecutedNodes(execution) {
458
+ if (!execution.data?.resultData?.runData) {
459
+ return [];
460
+ }
461
+ return Object.keys(execution.data.resultData.runData);
462
+ }
463
+ /**
464
+ * Extract list of nodes that failed
465
+ */
466
+ extractFailedNodes(execution) {
467
+ if (!execution.data?.resultData?.runData) {
468
+ return [];
469
+ }
470
+ const failedNodes = [];
471
+ const runData = execution.data.resultData.runData;
472
+ for (const [nodeName, nodeRuns] of Object.entries(runData)) {
473
+ if (Array.isArray(nodeRuns)) {
474
+ for (const run of nodeRuns) {
475
+ if (run.error) {
476
+ failedNodes.push(nodeName);
477
+ break;
478
+ }
479
+ }
480
+ }
481
+ }
482
+ return failedNodes;
483
+ }
484
+ /**
485
+ * Extract error message from execution
486
+ */
487
+ extractErrorMessage(execution) {
488
+ if (!execution.data?.resultData?.error) {
489
+ return undefined;
490
+ }
491
+ const error = execution.data.resultData.error;
492
+ return typeof error === 'string' ? error : error.message;
493
+ }
494
+ /**
495
+ * Check if error handling was triggered
496
+ */
497
+ checkErrorHandlingTriggered(execution, workflow) {
498
+ const executedNodes = this.extractExecutedNodes(execution);
499
+ // Check if error workflow was executed
500
+ if (workflow.settings?.errorWorkflow) {
501
+ // Would need to check if error workflow was triggered
502
+ return execution.status === 'failed' || execution.status === 'crashed';
503
+ }
504
+ // Check if any error handling nodes were executed
505
+ const errorHandlingNodes = workflow.nodes.filter(n => n.type.includes('errorTrigger') ||
506
+ n.type.includes('Error') ||
507
+ (n.parameters.onError && n.parameters.onError !== 'stopWorkflow'));
508
+ return errorHandlingNodes.some(n => executedNodes.includes(n.name));
509
+ }
510
+ /**
511
+ * Check if retry was attempted
512
+ */
513
+ checkRetryAttempted(execution) {
514
+ if (!execution.data?.resultData?.runData) {
515
+ return false;
516
+ }
517
+ // Check if any node was executed multiple times
518
+ for (const nodeRuns of Object.values(execution.data.resultData.runData)) {
519
+ if (Array.isArray(nodeRuns) && nodeRuns.length > 1) {
520
+ return true;
521
+ }
522
+ }
523
+ return false;
524
+ }
525
+ /**
526
+ * Select experiments based on workflow and intensity
527
+ */
528
+ selectExperiments(workflow, intensity) {
529
+ const experiments = [];
530
+ // Add relevant standard experiments
531
+ for (const experiment of STANDARD_EXPERIMENTS) {
532
+ if (this.isExperimentApplicable(workflow, experiment)) {
533
+ experiments.push(experiment);
534
+ }
535
+ }
536
+ // Adjust based on intensity
537
+ const countMap = { light: 2, moderate: 4, heavy: experiments.length };
538
+ return experiments.slice(0, countMap[intensity]);
539
+ }
540
+ /**
541
+ * Check if experiment is applicable to workflow
542
+ */
543
+ isExperimentApplicable(workflow, experiment) {
544
+ switch (experiment.target) {
545
+ case 'httpRequest':
546
+ return workflow.nodes.some(n => n.type.includes('httpRequest'));
547
+ case 'code':
548
+ return workflow.nodes.some(n => n.type.includes('code'));
549
+ case 'random':
550
+ return workflow.nodes.filter(n => !n.type.includes('trigger')).length > 0;
551
+ default:
552
+ return workflow.nodes.some(n => n.name === experiment.target);
553
+ }
554
+ }
555
+ /**
556
+ * Check if experiment is destructive
557
+ */
558
+ isDestructive(experiment) {
559
+ const destructiveTypes = [
560
+ 'data-corruption',
561
+ 'resource-exhaustion',
562
+ ];
563
+ return destructiveTypes.includes(experiment.type);
564
+ }
565
+ /**
566
+ * Select target node for chaos
567
+ */
568
+ selectTargetNode(workflow, target) {
569
+ if (!target || target === 'random') {
570
+ const nonTriggerNodes = workflow.nodes.filter(n => !n.type.includes('trigger') && !n.type.includes('Trigger'));
571
+ return nonTriggerNodes[Math.floor(Math.random() * nonTriggerNodes.length)];
572
+ }
573
+ if (target.includes('Request') || target.includes('http') || target === 'httpRequest') {
574
+ return workflow.nodes.find(n => n.type.includes('httpRequest'));
575
+ }
576
+ if (target === 'code') {
577
+ return workflow.nodes.find(n => n.type.includes('code'));
578
+ }
579
+ return workflow.nodes.find(n => n.name === target || n.id === target);
580
+ }
581
+ /**
582
+ * Calculate resilience score based on REAL test results
583
+ */
584
+ calculateResilienceScore(workflow, results) {
585
+ // Calculate based on ACTUAL execution results
586
+ const passedExperiments = results.filter(r => r.status === 'passed');
587
+ const executedExperiments = results.filter(r => r.execution);
588
+ // Fault tolerance: Did the workflow handle faults gracefully?
589
+ const faultTolerance = executedExperiments.length > 0
590
+ ? (passedExperiments.length / executedExperiments.length) * 100
591
+ : 50;
592
+ // Recovery: Did error handling trigger when needed?
593
+ const recoveryResults = results.filter(r => r.recovery);
594
+ const recovery = recoveryResults.length > 0
595
+ ? (recoveryResults.filter(r => r.recovery?.recovered).length / recoveryResults.length) * 100
596
+ : 50;
597
+ // Graceful degradation: Did failures cascade?
598
+ const cascadeFailures = results.filter(r => r.impact.cascadeFailure).length;
599
+ const gracefulDegradation = executedExperiments.length > 0
600
+ ? Math.max(0, 100 - (cascadeFailures / executedExperiments.length) * 100)
601
+ : 50;
602
+ // Monitoring: Check workflow configuration
603
+ const monitoring = this.calculateMonitoringScore(workflow);
604
+ const overall = (faultTolerance + recovery + gracefulDegradation + monitoring) / 4;
605
+ let grade;
606
+ if (overall >= 90)
607
+ grade = 'A';
608
+ else if (overall >= 80)
609
+ grade = 'B';
610
+ else if (overall >= 70)
611
+ grade = 'C';
612
+ else if (overall >= 60)
613
+ grade = 'D';
614
+ else
615
+ grade = 'F';
616
+ return {
617
+ overall: Math.round(overall),
618
+ categories: {
619
+ faultTolerance: Math.round(faultTolerance),
620
+ recovery: Math.round(recovery),
621
+ gracefulDegradation: Math.round(gracefulDegradation),
622
+ monitoring: Math.round(monitoring),
623
+ },
624
+ grade,
625
+ };
626
+ }
627
+ calculateMonitoringScore(workflow) {
628
+ let score = 50;
629
+ if (workflow.settings?.saveExecutionProgress)
630
+ score += 25;
631
+ if (workflow.settings?.errorWorkflow)
632
+ score += 15;
633
+ if (workflow.settings?.saveDataErrorExecution !== 'none')
634
+ score += 10;
635
+ return Math.min(100, score);
636
+ }
637
+ /**
638
+ * Identify vulnerabilities from ACTUAL test failures
639
+ */
640
+ identifyVulnerabilities(results) {
641
+ const vulnerabilities = [];
642
+ for (const result of results) {
643
+ if (result.status === 'failed' && result.execution) {
644
+ vulnerabilities.push({
645
+ id: `vuln-${result.experiment.type}-${Date.now()}`,
646
+ type: result.experiment.type,
647
+ severity: result.impact.severity === 'critical' ? 'critical' :
648
+ result.impact.severity === 'severe' ? 'high' : 'medium',
649
+ description: `Workflow failed to handle ${result.experiment.name}: ${result.execution.errorMessage || 'No error handling triggered'}`,
650
+ location: result.impact.affectedNodes.join(', ') || 'workflow',
651
+ exploitedBy: result.experiment.type,
652
+ mitigation: result.experiment.expectedBehavior,
653
+ });
654
+ }
655
+ }
656
+ return vulnerabilities;
657
+ }
658
+ /**
659
+ * Generate recommendations based on REAL test data
660
+ */
661
+ generateRecommendations(workflow, results, vulnerabilities) {
662
+ const recommendations = [];
663
+ // Based on ACTUAL failed experiments
664
+ for (const result of results.filter(r => r.status === 'failed')) {
665
+ recommendations.push({
666
+ priority: result.impact.severity === 'critical' ? 'critical' : 'high',
667
+ category: result.experiment.type,
668
+ issue: `Failed ${result.experiment.name} - ${result.execution?.errorMessage || 'Unhandled error'}`,
669
+ recommendation: result.experiment.expectedBehavior,
670
+ effort: 'medium',
671
+ });
672
+ }
673
+ // Check for missing error handling
674
+ if (!workflow.settings?.errorWorkflow) {
675
+ const failedCount = results.filter(r => r.status === 'failed').length;
676
+ if (failedCount > 0) {
677
+ recommendations.push({
678
+ priority: 'critical',
679
+ category: 'error-handling',
680
+ issue: `No error workflow configured - ${failedCount} experiments failed`,
681
+ recommendation: 'Configure an error workflow to catch and handle failures',
682
+ effort: 'low',
683
+ });
684
+ }
685
+ }
686
+ // Check for cascade failures
687
+ const cascadeFailures = results.filter(r => r.impact.cascadeFailure);
688
+ if (cascadeFailures.length > 0) {
689
+ recommendations.push({
690
+ priority: 'high',
691
+ category: 'isolation',
692
+ issue: `${cascadeFailures.length} experiments caused cascade failures`,
693
+ recommendation: 'Add error isolation with continueOnFail or error branches to prevent cascade',
694
+ effort: 'medium',
695
+ });
696
+ }
697
+ return recommendations;
698
+ }
699
+ /**
700
+ * Create skipped result
701
+ */
702
+ createSkippedResult(experiment, reason) {
703
+ return {
704
+ experiment,
705
+ status: 'skipped',
706
+ duration: 0,
707
+ observations: [{
708
+ timestamp: new Date(),
709
+ type: 'info',
710
+ message: `Skipped: ${reason}`,
711
+ }],
712
+ impact: {
713
+ severity: 'none',
714
+ affectedNodes: [],
715
+ dataLoss: false,
716
+ serviceDisruption: false,
717
+ cascadeFailure: false,
718
+ },
719
+ };
720
+ }
721
+ /**
722
+ * Get experiment history
723
+ */
724
+ getExperimentHistory(workflowId) {
725
+ return this.experimentHistory.get(workflowId) || [];
726
+ }
727
+ }
728
+ exports.N8nChaosTesterAgent = N8nChaosTesterAgent;
729
+ //# sourceMappingURL=N8nChaosTesterAgent.js.map