@yasserkhanorg/e2e-agents 1.7.6 → 1.8.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/dist/agent/git.d.ts +5 -0
- package/dist/agent/git.d.ts.map +1 -1
- package/dist/agent/git.js +49 -0
- package/dist/agents/coverage-evaluator.d.ts +8 -0
- package/dist/agents/coverage-evaluator.d.ts.map +1 -0
- package/dist/agents/coverage-evaluator.js +41 -0
- package/dist/agents/cross-impact.d.ts +13 -0
- package/dist/agents/cross-impact.d.ts.map +1 -0
- package/dist/agents/cross-impact.js +135 -0
- package/dist/agents/executor.d.ts +8 -0
- package/dist/agents/executor.d.ts.map +1 -0
- package/dist/agents/executor.js +70 -0
- package/dist/agents/explorer.d.ts +12 -0
- package/dist/agents/explorer.d.ts.map +1 -0
- package/dist/agents/explorer.js +43 -0
- package/dist/agents/generator.d.ts +8 -0
- package/dist/agents/generator.d.ts.map +1 -0
- package/dist/agents/generator.js +77 -0
- package/dist/agents/healer.d.ts +8 -0
- package/dist/agents/healer.d.ts.map +1 -0
- package/dist/agents/healer.js +31 -0
- package/dist/agents/impact-analyst.d.ts +8 -0
- package/dist/agents/impact-analyst.d.ts.map +1 -0
- package/dist/agents/impact-analyst.js +38 -0
- package/dist/agents/regression-advisor.d.ts +8 -0
- package/dist/agents/regression-advisor.d.ts.map +1 -0
- package/dist/agents/regression-advisor.js +116 -0
- package/dist/agents/strategist.d.ts +9 -0
- package/dist/agents/strategist.d.ts.map +1 -0
- package/dist/agents/strategist.js +87 -0
- package/dist/agents/test-designer.d.ts +8 -0
- package/dist/agents/test-designer.d.ts.map +1 -0
- package/dist/agents/test-designer.js +106 -0
- package/dist/cli/commands/crew.d.ts +3 -0
- package/dist/cli/commands/crew.d.ts.map +1 -0
- package/dist/cli/commands/crew.js +137 -0
- package/dist/cli/parse_args.d.ts.map +1 -1
- package/dist/cli/parse_args.js +2 -1
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli.js +5 -0
- package/dist/crew/context.d.ts +40 -0
- package/dist/crew/context.d.ts.map +1 -0
- package/dist/crew/context.js +36 -0
- package/dist/crew/orchestrator.d.ts +36 -0
- package/dist/crew/orchestrator.d.ts.map +1 -0
- package/dist/crew/orchestrator.js +171 -0
- package/dist/crew/protocol.d.ts +33 -0
- package/dist/crew/protocol.d.ts.map +1 -0
- package/dist/crew/protocol.js +4 -0
- package/dist/crew/provider.d.ts +3 -0
- package/dist/crew/provider.d.ts.map +1 -0
- package/dist/crew/provider.js +16 -0
- package/dist/crew/sanitize.d.ts +3 -0
- package/dist/crew/sanitize.d.ts.map +1 -0
- package/dist/crew/sanitize.js +31 -0
- package/dist/crew/types.d.ts +52 -0
- package/dist/crew/types.d.ts.map +1 -0
- package/dist/crew/types.js +4 -0
- package/dist/crew/workflows.d.ts +52 -0
- package/dist/crew/workflows.d.ts.map +1 -0
- package/dist/crew/workflows.js +36 -0
- package/dist/esm/agent/git.js +48 -0
- package/dist/esm/agents/coverage-evaluator.js +37 -0
- package/dist/esm/agents/cross-impact.js +131 -0
- package/dist/esm/agents/executor.js +66 -0
- package/dist/esm/agents/explorer.js +39 -0
- package/dist/esm/agents/generator.js +73 -0
- package/dist/esm/agents/healer.js +27 -0
- package/dist/esm/agents/impact-analyst.js +34 -0
- package/dist/esm/agents/regression-advisor.js +112 -0
- package/dist/esm/agents/strategist.js +83 -0
- package/dist/esm/agents/test-designer.js +102 -0
- package/dist/esm/cli/commands/crew.js +134 -0
- package/dist/esm/cli/parse_args.js +2 -1
- package/dist/esm/cli.js +5 -0
- package/dist/esm/crew/context.js +32 -0
- package/dist/esm/crew/orchestrator.js +167 -0
- package/dist/esm/crew/protocol.js +3 -0
- package/dist/esm/crew/provider.js +13 -0
- package/dist/esm/crew/sanitize.js +27 -0
- package/dist/esm/crew/types.js +3 -0
- package/dist/esm/crew/workflows.js +33 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/prompts/cross-impact.js +71 -0
- package/dist/esm/prompts/strategist.js +79 -0
- package/dist/esm/prompts/test-designer.js +107 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/prompts/cross-impact.d.ts +22 -0
- package/dist/prompts/cross-impact.d.ts.map +1 -0
- package/dist/prompts/cross-impact.js +75 -0
- package/dist/prompts/strategist.d.ts +25 -0
- package/dist/prompts/strategist.d.ts.map +1 -0
- package/dist/prompts/strategist.js +83 -0
- package/dist/prompts/test-designer.d.ts +33 -0
- package/dist/prompts/test-designer.d.ts.map +1 -0
- package/dist/prompts/test-designer.js +111 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { RouteFamilyConfig } from '../knowledge/route_families.js';
|
|
2
|
+
import type { ApiSurfaceConfig } from '../knowledge/api_surface.js';
|
|
3
|
+
import type { Agent, AgentMessage, AgentResult } from './protocol.js';
|
|
4
|
+
import type { CrewContext } from './context.js';
|
|
5
|
+
import type { AgentRole } from './types.js';
|
|
6
|
+
import type { WorkflowName } from './workflows.js';
|
|
7
|
+
export interface CrewConfig {
|
|
8
|
+
appPath: string;
|
|
9
|
+
testsRoot: string;
|
|
10
|
+
gitSince: string;
|
|
11
|
+
gitIncludeUncommitted?: boolean;
|
|
12
|
+
routeFamilies?: RouteFamilyConfig;
|
|
13
|
+
apiSurface?: ApiSurfaceConfig;
|
|
14
|
+
workflow?: WorkflowName;
|
|
15
|
+
providerOverride?: string;
|
|
16
|
+
budgetUSD?: number;
|
|
17
|
+
dryRun?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface CrewResult {
|
|
20
|
+
context: CrewContext;
|
|
21
|
+
warnings: string[];
|
|
22
|
+
timings: Record<string, number>;
|
|
23
|
+
}
|
|
24
|
+
export declare class CrewOrchestrator {
|
|
25
|
+
private agents;
|
|
26
|
+
registerAgent(agent: Agent): void;
|
|
27
|
+
run(config: CrewConfig): Promise<CrewResult>;
|
|
28
|
+
dispatch(role: AgentRole, action: string, ctx: CrewContext): Promise<AgentResult>;
|
|
29
|
+
parallel(roles: AgentRole[], action: string, ctx: CrewContext): Promise<AgentResult[]>;
|
|
30
|
+
broadcast(msg: AgentMessage, ctx: CrewContext): Promise<void>;
|
|
31
|
+
private runBuiltInPhase;
|
|
32
|
+
private runParallel;
|
|
33
|
+
private runSequential;
|
|
34
|
+
private checkPhaseResults;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=orchestrator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/crew/orchestrator.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAC,KAAK,EAAE,YAAY,EAAE,WAAW,EAAY,MAAM,eAAe,CAAC;AAC/E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAE9C,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAA6B,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAG7E,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACvB,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC;AAED,qBAAa,gBAAgB;IACzB,OAAO,CAAC,MAAM,CAA+B;IAE7C,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAI3B,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAwE5C,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BjF,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAKtF,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;YAerD,eAAe;YAwBf,WAAW;YAMX,aAAa;IAS3B,OAAO,CAAC,iBAAiB;CAM5B"}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.CrewOrchestrator = void 0;
|
|
6
|
+
/**
|
|
7
|
+
* Crew Orchestrator — executes workflow definitions by dispatching to agents.
|
|
8
|
+
*/
|
|
9
|
+
const git_js_1 = require("../agent/git.js");
|
|
10
|
+
const stage0_preprocess_js_1 = require("../pipeline/stage0_preprocess.js");
|
|
11
|
+
const logger_js_1 = require("../logger.js");
|
|
12
|
+
const context_js_1 = require("./context.js");
|
|
13
|
+
const workflows_js_1 = require("./workflows.js");
|
|
14
|
+
class CrewOrchestrator {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.agents = new Map();
|
|
17
|
+
}
|
|
18
|
+
registerAgent(agent) {
|
|
19
|
+
this.agents.set(agent.role, agent);
|
|
20
|
+
}
|
|
21
|
+
async run(config) {
|
|
22
|
+
const workflow = workflows_js_1.WORKFLOWS[config.workflow || 'full-qa'];
|
|
23
|
+
const timings = {};
|
|
24
|
+
const warnings = [];
|
|
25
|
+
// Step 1: Get changed files
|
|
26
|
+
const gitResult = (0, git_js_1.getChangedFiles)(config.appPath, config.gitSince, {
|
|
27
|
+
includeUncommitted: config.gitIncludeUncommitted,
|
|
28
|
+
});
|
|
29
|
+
if (gitResult.error) {
|
|
30
|
+
warnings.push(`Git diff warning: ${gitResult.error}`);
|
|
31
|
+
}
|
|
32
|
+
const changedFiles = gitResult.files
|
|
33
|
+
.map((f) => f.replace(/\\/g, '/'))
|
|
34
|
+
.filter((f) => !(0, git_js_1.isTestFile)(f));
|
|
35
|
+
if (changedFiles.length === 0) {
|
|
36
|
+
warnings.push('No changed application files detected.');
|
|
37
|
+
}
|
|
38
|
+
// Initialize context (will be populated during preprocess phase)
|
|
39
|
+
const ctx = {
|
|
40
|
+
changedFiles,
|
|
41
|
+
routeFamilies: [],
|
|
42
|
+
manifest: null,
|
|
43
|
+
apiSurface: { pageObjects: [], generatedAt: '' },
|
|
44
|
+
specIndex: { specs: [], indexedAt: '' },
|
|
45
|
+
context: { documents: [], warnings: [] },
|
|
46
|
+
familyGroups: [],
|
|
47
|
+
preprocessResult: null,
|
|
48
|
+
appPath: config.appPath,
|
|
49
|
+
testsRoot: config.testsRoot,
|
|
50
|
+
gitSince: config.gitSince,
|
|
51
|
+
providerOverride: config.providerOverride,
|
|
52
|
+
impactedFlows: [],
|
|
53
|
+
strategyEntries: [],
|
|
54
|
+
testDesigns: [],
|
|
55
|
+
crossImpacts: [],
|
|
56
|
+
regressionRisks: [],
|
|
57
|
+
findings: [],
|
|
58
|
+
generatedSpecs: [],
|
|
59
|
+
usage: (0, context_js_1.createEmptyUsageStats)(),
|
|
60
|
+
messages: [],
|
|
61
|
+
warnings,
|
|
62
|
+
};
|
|
63
|
+
// Execute each phase
|
|
64
|
+
for (const phase of workflow.phases) {
|
|
65
|
+
const timer = logger_js_1.logger.timer(`crew:${phase.name}`);
|
|
66
|
+
if (phase.handler === 'built-in') {
|
|
67
|
+
await this.runBuiltInPhase(phase.name, ctx, config);
|
|
68
|
+
}
|
|
69
|
+
else if (phase.parallel && phase.parallel.length > 0) {
|
|
70
|
+
await this.runParallel(phase.parallel, phase.name, ctx);
|
|
71
|
+
}
|
|
72
|
+
else if (phase.sequential && phase.sequential.length > 0) {
|
|
73
|
+
await this.runSequential(phase.sequential, phase.name, ctx);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
warnings.push(`Phase '${phase.name}' has no handler, parallel, or sequential agents — skipped.`);
|
|
77
|
+
}
|
|
78
|
+
timings[phase.name] = timer.end();
|
|
79
|
+
// Budget check
|
|
80
|
+
if (config.budgetUSD && ctx.usage.totalCost >= config.budgetUSD) {
|
|
81
|
+
warnings.push(`Budget limit reached ($${ctx.usage.totalCost.toFixed(4)} >= $${config.budgetUSD}). Stopping workflow.`);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { context: ctx, warnings, timings };
|
|
86
|
+
}
|
|
87
|
+
async dispatch(role, action, ctx) {
|
|
88
|
+
const agent = this.agents.get(role);
|
|
89
|
+
if (!agent) {
|
|
90
|
+
return {
|
|
91
|
+
role,
|
|
92
|
+
status: 'failed',
|
|
93
|
+
output: null,
|
|
94
|
+
warnings: [`Agent '${role}' is not registered.`],
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const task = { role, action, input: null };
|
|
98
|
+
try {
|
|
99
|
+
const result = await agent.execute(task, ctx);
|
|
100
|
+
if (result.usage) {
|
|
101
|
+
(0, context_js_1.mergeUsageStats)(ctx.usage, result.usage);
|
|
102
|
+
}
|
|
103
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
104
|
+
ctx.warnings.push(...result.warnings);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
ctx.warnings.push(`Agent '${role}' failed: ${message}`);
|
|
111
|
+
return { role, status: 'failed', output: null, warnings: [message] };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async parallel(roles, action, ctx) {
|
|
115
|
+
const promises = roles.map((role) => this.dispatch(role, action, ctx));
|
|
116
|
+
return Promise.all(promises);
|
|
117
|
+
}
|
|
118
|
+
async broadcast(msg, ctx) {
|
|
119
|
+
ctx.messages.push(msg);
|
|
120
|
+
const promises = [];
|
|
121
|
+
for (const agent of this.agents.values()) {
|
|
122
|
+
if (agent.onMessage && agent.role !== msg.from) {
|
|
123
|
+
promises.push(agent.onMessage(msg).catch((err) => {
|
|
124
|
+
ctx.warnings.push(`Broadcast to ${agent.role} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
await Promise.all(promises);
|
|
129
|
+
}
|
|
130
|
+
async runBuiltInPhase(name, ctx, config) {
|
|
131
|
+
if (name === 'preprocess') {
|
|
132
|
+
if (ctx.changedFiles.length === 0) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const result = (0, stage0_preprocess_js_1.preprocess)(ctx.changedFiles, {
|
|
136
|
+
appPath: config.appPath,
|
|
137
|
+
testsRoot: config.testsRoot,
|
|
138
|
+
routeFamilies: config.routeFamilies,
|
|
139
|
+
apiSurface: config.apiSurface,
|
|
140
|
+
});
|
|
141
|
+
ctx.preprocessResult = result;
|
|
142
|
+
ctx.manifest = result.manifest;
|
|
143
|
+
ctx.routeFamilies = result.manifest?.families || [];
|
|
144
|
+
ctx.apiSurface = result.apiSurface;
|
|
145
|
+
ctx.specIndex = result.specIndex;
|
|
146
|
+
ctx.context = result.context;
|
|
147
|
+
ctx.familyGroups = result.familyGroups;
|
|
148
|
+
ctx.warnings.push(...result.warnings);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async runParallel(roles, phaseName, ctx) {
|
|
152
|
+
logger_js_1.logger.info(`Crew phase '${phaseName}': running ${roles.join(', ')} in parallel`);
|
|
153
|
+
const results = await this.parallel(roles, phaseName, ctx);
|
|
154
|
+
this.checkPhaseResults(phaseName, results, ctx);
|
|
155
|
+
}
|
|
156
|
+
async runSequential(roles, phaseName, ctx) {
|
|
157
|
+
logger_js_1.logger.info(`Crew phase '${phaseName}': running ${roles.join(' → ')} sequentially`);
|
|
158
|
+
const results = [];
|
|
159
|
+
for (const role of roles) {
|
|
160
|
+
results.push(await this.dispatch(role, phaseName, ctx));
|
|
161
|
+
}
|
|
162
|
+
this.checkPhaseResults(phaseName, results, ctx);
|
|
163
|
+
}
|
|
164
|
+
checkPhaseResults(phaseName, results, ctx) {
|
|
165
|
+
const allFailed = results.length > 0 && results.every((r) => r.status === 'failed');
|
|
166
|
+
if (allFailed) {
|
|
167
|
+
ctx.warnings.push(`Phase '${phaseName}': all ${results.length} agent(s) failed. Downstream phases may produce empty results.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
exports.CrewOrchestrator = CrewOrchestrator;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Crew Protocol — core interfaces for inter-agent communication and execution.
|
|
3
|
+
*/
|
|
4
|
+
import type { ProviderUsageStats } from '../provider_interface.js';
|
|
5
|
+
import type { AgentRole } from './types.js';
|
|
6
|
+
import type { CrewContext } from './context.js';
|
|
7
|
+
export interface AgentMessage {
|
|
8
|
+
id: string;
|
|
9
|
+
from: AgentRole;
|
|
10
|
+
to: AgentRole | 'broadcast';
|
|
11
|
+
type: 'task' | 'result' | 'escalation' | 'finding';
|
|
12
|
+
payload: unknown;
|
|
13
|
+
correlationId: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
export interface AgentTask {
|
|
17
|
+
role: AgentRole;
|
|
18
|
+
action: string;
|
|
19
|
+
input: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentResult {
|
|
22
|
+
role: AgentRole;
|
|
23
|
+
status: 'success' | 'partial' | 'failed';
|
|
24
|
+
output: unknown;
|
|
25
|
+
usage?: ProviderUsageStats;
|
|
26
|
+
warnings: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface Agent {
|
|
29
|
+
role: AgentRole;
|
|
30
|
+
execute(task: AgentTask, ctx: CrewContext): Promise<AgentResult>;
|
|
31
|
+
onMessage?(msg: AgentMessage): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/crew/protocol.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,SAAS,GAAG,WAAW,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAC;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACzC,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACjE,SAAS,CAAC,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/crew/provider.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAE1D,wBAAsB,eAAe,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAKrF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.getCrewProvider = getCrewProvider;
|
|
6
|
+
/**
|
|
7
|
+
* Shared provider creation for crew agents — ensures consistent provider
|
|
8
|
+
* instantiation and prevents usage stats fragmentation.
|
|
9
|
+
*/
|
|
10
|
+
const provider_factory_js_1 = require("../provider_factory.js");
|
|
11
|
+
async function getCrewProvider(providerOverride) {
|
|
12
|
+
if (providerOverride) {
|
|
13
|
+
return provider_factory_js_1.LLMProviderFactory.createFromString(providerOverride);
|
|
14
|
+
}
|
|
15
|
+
return provider_factory_js_1.LLMProviderFactory.createFromEnv();
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitize.d.ts","sourceRoot":"","sources":["../../src/crew/sanitize.ts"],"names":[],"mappings":"AAqBA,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAExD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.sanitizeForPrompt = sanitizeForPrompt;
|
|
6
|
+
exports.sanitizeArray = sanitizeArray;
|
|
7
|
+
/**
|
|
8
|
+
* Sanitize strings before interpolating into LLM prompts.
|
|
9
|
+
* Strips common prompt injection patterns while preserving useful content.
|
|
10
|
+
*/
|
|
11
|
+
const INJECTION_PATTERNS = [
|
|
12
|
+
/ignore\s+(all\s+)?previous\s+instructions/gi,
|
|
13
|
+
/disregard\s+(all\s+)?(above|prior|previous)/gi,
|
|
14
|
+
/system\s*:\s*/gi,
|
|
15
|
+
/\[INST\]/gi,
|
|
16
|
+
/<<SYS>>/gi,
|
|
17
|
+
/<\|im_start\|>/gi,
|
|
18
|
+
/\bHuman\s*:\s*/gi,
|
|
19
|
+
/\bAssistant\s*:\s*/gi,
|
|
20
|
+
];
|
|
21
|
+
const MAX_FIELD_LENGTH = 2000;
|
|
22
|
+
function sanitizeForPrompt(value) {
|
|
23
|
+
let sanitized = value.slice(0, MAX_FIELD_LENGTH);
|
|
24
|
+
for (const pattern of INJECTION_PATTERNS) {
|
|
25
|
+
sanitized = sanitized.replace(pattern, '[filtered]');
|
|
26
|
+
}
|
|
27
|
+
return sanitized;
|
|
28
|
+
}
|
|
29
|
+
function sanitizeArray(values) {
|
|
30
|
+
return values.map((v) => sanitizeForPrompt(v));
|
|
31
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crew data types — structured test design, cross-impact analysis, and findings.
|
|
3
|
+
*/
|
|
4
|
+
export type TestCaseType = 'happy-path' | 'edge-case' | 'boundary' | 'negative' | 'state-transition' | 'race-condition' | 'permission' | 'accessibility' | 'performance';
|
|
5
|
+
export interface TestCase {
|
|
6
|
+
name: string;
|
|
7
|
+
type: TestCaseType;
|
|
8
|
+
preconditions: string[];
|
|
9
|
+
steps: string[];
|
|
10
|
+
expectedOutcome: string;
|
|
11
|
+
priority: 'P0' | 'P1' | 'P2';
|
|
12
|
+
rationale: string;
|
|
13
|
+
}
|
|
14
|
+
export interface TestDesign {
|
|
15
|
+
flowId: string;
|
|
16
|
+
flowName: string;
|
|
17
|
+
testCases: TestCase[];
|
|
18
|
+
}
|
|
19
|
+
export interface CrossImpact {
|
|
20
|
+
sourceFamily: string;
|
|
21
|
+
affectedFamily: string;
|
|
22
|
+
sharedDependency: string;
|
|
23
|
+
riskLevel: 'high' | 'medium' | 'low';
|
|
24
|
+
evidence: string;
|
|
25
|
+
}
|
|
26
|
+
export interface Finding {
|
|
27
|
+
id: string;
|
|
28
|
+
type: 'bug' | 'gap' | 'risk' | 'flaky';
|
|
29
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
30
|
+
source: AgentRole;
|
|
31
|
+
summary: string;
|
|
32
|
+
details: string;
|
|
33
|
+
relatedFlows: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface RegressionRisk {
|
|
36
|
+
familyId: string;
|
|
37
|
+
filePattern: string;
|
|
38
|
+
riskScore: number;
|
|
39
|
+
reason: string;
|
|
40
|
+
historicalFailures: number;
|
|
41
|
+
}
|
|
42
|
+
export interface StrategyEntry {
|
|
43
|
+
flowId: string;
|
|
44
|
+
flowName: string;
|
|
45
|
+
priority: 'P0' | 'P1' | 'P2';
|
|
46
|
+
approach: 'full-test' | 'smoke-test' | 'skip' | 'manual-review';
|
|
47
|
+
rationale: string;
|
|
48
|
+
testCategories: TestCaseType[];
|
|
49
|
+
crossImpactRisk: 'high' | 'medium' | 'low' | 'none';
|
|
50
|
+
}
|
|
51
|
+
export type AgentRole = 'strategist' | 'test-designer' | 'cross-impact' | 'regression-advisor' | 'impact-analyst' | 'coverage-evaluator' | 'generator' | 'executor' | 'healer' | 'explorer';
|
|
52
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/crew/types.ts"],"names":[],"mappings":"AAGA;;GAEG;AAEH,MAAM,MAAM,YAAY,GAClB,YAAY,GACZ,WAAW,GACX,UAAU,GACV,UAAU,GACV,kBAAkB,GAClB,gBAAgB,GAChB,YAAY,GACZ,eAAe,GACf,aAAa,CAAC;AAEpB,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACrC,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,QAAQ,EAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACjD,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,WAAW,GAAG,YAAY,GAAG,MAAM,GAAG,eAAe,CAAC;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACvD;AAED,MAAM,MAAM,SAAS,GACf,YAAY,GACZ,eAAe,GACf,cAAc,GACd,oBAAoB,GACpB,gBAAgB,GAChB,oBAAoB,GACpB,WAAW,GACX,UAAU,GACV,QAAQ,GACR,UAAU,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predefined workflow definitions — playbooks that compose agents into phases.
|
|
3
|
+
*
|
|
4
|
+
* Data flow through full-qa workflow:
|
|
5
|
+
*
|
|
6
|
+
* preprocess (built-in)
|
|
7
|
+
* → populates: familyGroups, routeFamilies, manifest, apiSurface, specIndex, context
|
|
8
|
+
*
|
|
9
|
+
* understand (parallel):
|
|
10
|
+
* impact-analyst → writes: impactedFlows
|
|
11
|
+
* cross-impact → writes: crossImpacts
|
|
12
|
+
* regression-advisor → writes: regressionRisks
|
|
13
|
+
*
|
|
14
|
+
* strategize (sequential):
|
|
15
|
+
* strategist → reads: impactedFlows, crossImpacts, regressionRisks
|
|
16
|
+
* → writes: strategyEntries
|
|
17
|
+
* test-designer → reads: strategyEntries, impactedFlows, crossImpacts, apiSurface, specIndex
|
|
18
|
+
* → writes: testDesigns
|
|
19
|
+
*
|
|
20
|
+
* execute (parallel):
|
|
21
|
+
* generator → reads: impactedFlows, testDesigns, apiSurface
|
|
22
|
+
* → writes: generatedSpecs
|
|
23
|
+
*
|
|
24
|
+
* validate (sequential):
|
|
25
|
+
* executor → reads: generatedSpecs, impactedFlows
|
|
26
|
+
* healer → reads: generatedSpecs, impactedFlows
|
|
27
|
+
*/
|
|
28
|
+
import type { AgentRole } from './types.js';
|
|
29
|
+
export type WorkflowPhase = {
|
|
30
|
+
name: string;
|
|
31
|
+
handler: 'built-in';
|
|
32
|
+
parallel?: never;
|
|
33
|
+
sequential?: never;
|
|
34
|
+
} | {
|
|
35
|
+
name: string;
|
|
36
|
+
handler?: never;
|
|
37
|
+
parallel: AgentRole[];
|
|
38
|
+
sequential?: never;
|
|
39
|
+
} | {
|
|
40
|
+
name: string;
|
|
41
|
+
handler?: never;
|
|
42
|
+
parallel?: never;
|
|
43
|
+
sequential: AgentRole[];
|
|
44
|
+
};
|
|
45
|
+
export interface WorkflowDef {
|
|
46
|
+
name: string;
|
|
47
|
+
description: string;
|
|
48
|
+
phases: WorkflowPhase[];
|
|
49
|
+
}
|
|
50
|
+
export type WorkflowName = 'full-qa' | 'quick-check' | 'design-only';
|
|
51
|
+
export declare const WORKFLOWS: Record<WorkflowName, WorkflowDef>;
|
|
52
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/crew/workflows.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAE1C,MAAM,MAAM,aAAa,GACnB;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAC,GACzE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE,SAAS,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,KAAK,CAAA;CAAC,GAC1E;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAC,CAAC;AAEjF,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,aAAa,GAAG,aAAa,CAAC;AAErE,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,WAAW,CA8BvD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.WORKFLOWS = void 0;
|
|
6
|
+
exports.WORKFLOWS = {
|
|
7
|
+
'full-qa': {
|
|
8
|
+
name: 'full-qa',
|
|
9
|
+
description: 'Full multi-agent QA analysis: understand → strategize → execute → validate',
|
|
10
|
+
phases: [
|
|
11
|
+
{ name: 'preprocess', handler: 'built-in' },
|
|
12
|
+
{ name: 'understand', parallel: ['impact-analyst', 'cross-impact', 'regression-advisor'] },
|
|
13
|
+
{ name: 'strategize', sequential: ['strategist', 'test-designer'] },
|
|
14
|
+
{ name: 'execute', parallel: ['generator'] },
|
|
15
|
+
{ name: 'validate', sequential: ['executor', 'healer'] },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
'quick-check': {
|
|
19
|
+
name: 'quick-check',
|
|
20
|
+
description: 'Quick impact analysis with strategy recommendations',
|
|
21
|
+
phases: [
|
|
22
|
+
{ name: 'preprocess', handler: 'built-in' },
|
|
23
|
+
{ name: 'understand', parallel: ['impact-analyst'] },
|
|
24
|
+
{ name: 'strategize', sequential: ['strategist'] },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
'design-only': {
|
|
28
|
+
name: 'design-only',
|
|
29
|
+
description: 'Impact analysis through test design — no generation or execution',
|
|
30
|
+
phases: [
|
|
31
|
+
{ name: 'preprocess', handler: 'built-in' },
|
|
32
|
+
{ name: 'understand', parallel: ['impact-analyst', 'cross-impact'] },
|
|
33
|
+
{ name: 'strategize', sequential: ['strategist', 'test-designer'] },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
};
|
package/dist/esm/agent/git.js
CHANGED
|
@@ -143,6 +143,50 @@ function parseStatusLines(lines) {
|
|
|
143
143
|
}
|
|
144
144
|
return files;
|
|
145
145
|
}
|
|
146
|
+
// Comment-line patterns by file extension.
|
|
147
|
+
// A diff that ONLY touches these lines is a comment-only change (typo fix, doc update).
|
|
148
|
+
const COMMENT_PATTERNS = [
|
|
149
|
+
{ extensions: ['.go'], pattern: /^\s*(\/\/|\/\*|\*)/ },
|
|
150
|
+
{ extensions: ['.ts', '.tsx', '.js', '.jsx'], pattern: /^\s*(\/\/|\/\*|\*|\*\/)/ },
|
|
151
|
+
{ extensions: ['.py'], pattern: /^\s*#/ },
|
|
152
|
+
{ extensions: ['.css', '.scss'], pattern: /^\s*(\/\*|\*|\*\/)/ },
|
|
153
|
+
];
|
|
154
|
+
/**
|
|
155
|
+
* Check if a file's diff only changes comment lines (no code changes).
|
|
156
|
+
* Returns true if the diff is comment-only and can be safely excluded.
|
|
157
|
+
*/
|
|
158
|
+
function isCommentOnlyDiff(file, repoRoot, baseRef) {
|
|
159
|
+
const diff = runGitRaw(['diff', `${baseRef}..HEAD`, '-U0', '--', file], repoRoot);
|
|
160
|
+
if (!diff)
|
|
161
|
+
return false;
|
|
162
|
+
const ext = file.slice(file.lastIndexOf('.'));
|
|
163
|
+
const commentEntry = COMMENT_PATTERNS.find((cp) => cp.extensions.includes(ext));
|
|
164
|
+
if (!commentEntry)
|
|
165
|
+
return false;
|
|
166
|
+
// Extract only added/removed content lines (skip diff headers)
|
|
167
|
+
const contentLines = diff
|
|
168
|
+
.split('\n')
|
|
169
|
+
.filter((line) => (line.startsWith('+') || line.startsWith('-')) && !line.startsWith('+++') && !line.startsWith('---'));
|
|
170
|
+
if (contentLines.length === 0)
|
|
171
|
+
return false;
|
|
172
|
+
// Every changed line must be a comment line
|
|
173
|
+
return contentLines.every((line) => {
|
|
174
|
+
const content = line.slice(1).trim(); // Remove +/- prefix
|
|
175
|
+
return content === '' || commentEntry.pattern.test(content);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if a file path is a test file (spec, test, or in test directories).
|
|
180
|
+
* Shared across pipeline and crew orchestrators.
|
|
181
|
+
*/
|
|
182
|
+
export function isTestFile(file) {
|
|
183
|
+
const normalized = file.replace(/\\/g, '/');
|
|
184
|
+
return /\.(spec|test)\.(ts|tsx|js|jsx)$/.test(normalized) ||
|
|
185
|
+
/_test\.go$/.test(normalized) ||
|
|
186
|
+
normalized.includes('__tests__/') ||
|
|
187
|
+
normalized.includes('/tests/') ||
|
|
188
|
+
normalized.includes('/test/');
|
|
189
|
+
}
|
|
146
190
|
export function getChangedFiles(appRoot, since, options) {
|
|
147
191
|
try {
|
|
148
192
|
const files = new Set();
|
|
@@ -183,6 +227,10 @@ export function getChangedFiles(appRoot, since, options) {
|
|
|
183
227
|
const filteredTestFiles = [];
|
|
184
228
|
for (const f of allFiles) {
|
|
185
229
|
if (isRelevantFile(f)) {
|
|
230
|
+
// Skip files where the diff only touches comments (typo fixes, doc updates)
|
|
231
|
+
if (isCommentOnlyDiff(f, repoRoot, baseRef)) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
186
234
|
relevant.push(f);
|
|
187
235
|
}
|
|
188
236
|
else {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
2
|
+
// See LICENSE.txt for license information.
|
|
3
|
+
/**
|
|
4
|
+
* Coverage Evaluator Agent — wraps pipeline stage2 (coverage evaluation) in the Agent interface.
|
|
5
|
+
*/
|
|
6
|
+
import { runCoverageStage } from '../pipeline/stage2_coverage.js';
|
|
7
|
+
export class CoverageEvaluatorAgent {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.role = 'coverage-evaluator';
|
|
10
|
+
}
|
|
11
|
+
async execute(_task, ctx) {
|
|
12
|
+
const warnings = [];
|
|
13
|
+
if (ctx.impactedFlows.length === 0) {
|
|
14
|
+
warnings.push('Coverage evaluator: no impacted flows to evaluate.');
|
|
15
|
+
return { role: this.role, status: 'partial', output: [], warnings };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const result = await runCoverageStage(ctx.impactedFlows, ctx.specIndex, ctx.context, ctx.testsRoot, { provider: ctx.providerOverride });
|
|
19
|
+
// Replace impacted flows with coverage-enriched versions.
|
|
20
|
+
// This is intentionally a full replace (not push) because coverage evaluation
|
|
21
|
+
// returns the same flow IDs with updated coverage fields.
|
|
22
|
+
ctx.impactedFlows = result.decisions;
|
|
23
|
+
warnings.push(...result.warnings);
|
|
24
|
+
return {
|
|
25
|
+
role: this.role,
|
|
26
|
+
status: 'success',
|
|
27
|
+
output: result.decisions,
|
|
28
|
+
warnings,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
33
|
+
warnings.push(`Coverage evaluator failed: ${message}`);
|
|
34
|
+
return { role: this.role, status: 'failed', output: null, warnings };
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|