@h9-foundry/agentforge-cli 0.6.0 → 0.7.1
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/.tsbuildinfo +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +536 -11
- package/dist/index.js.map +1 -1
- package/dist/internal/builtin-agents.d.ts.map +1 -1
- package/dist/internal/builtin-agents.js +1866 -67
- package/dist/internal/builtin-agents.js.map +1 -1
- package/package.json +8 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { agentManifestSchema, agentOutputSchema, designArtifactSchema, implementationArtifactSchema, implementationInventorySchema, qaRequestSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
|
|
3
|
+
import { agentManifestSchema, agentOutputSchema, designArtifactSchema, githubActionsEvidenceSchema, implementationArtifactSchema, implementationInventorySchema, incidentArtifactSchema, incidentEvidenceNormalizationSchema, incidentRequestSchema, maintenanceArtifactSchema, maintenanceEvidenceNormalizationSchema, maintenanceRequestSchema, qaArtifactSchema, qaEvidenceNormalizationSchema, qaRequestSchema, releaseApprovalRecommendationSchema, releaseArtifactSchema, releaseEvidenceNormalizationSchema, releaseRequestSchema, securityArtifactSchema, securityEvidenceNormalizationSchema, securityRequestSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
|
|
4
4
|
const contextCollectorAgent = {
|
|
5
5
|
manifest: agentManifestSchema.parse({
|
|
6
6
|
version: 1,
|
|
@@ -73,6 +73,32 @@ function parsePackageScripts(packageJsonPath) {
|
|
|
73
73
|
}
|
|
74
74
|
return Object.fromEntries(Object.entries(parsed.scripts).filter((entry) => typeof entry[1] === "string"));
|
|
75
75
|
}
|
|
76
|
+
function resolveWorkspacePackage(root, packageName) {
|
|
77
|
+
if (!root) {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
for (const topLevel of ["packages", "agents", "adapters"]) {
|
|
81
|
+
const scopeRoot = join(root, topLevel);
|
|
82
|
+
if (!existsSync(scopeRoot)) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const entry of readdirSync(scopeRoot)) {
|
|
86
|
+
const manifestPath = join(scopeRoot, entry, "package.json");
|
|
87
|
+
if (!existsSync(manifestPath)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const parsed = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
91
|
+
if (!isRecord(parsed) || parsed.name !== packageName || typeof parsed.version !== "string") {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
currentVersion: parsed.version,
|
|
96
|
+
manifestPath
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
76
102
|
const allowedValidationScriptNames = new Set(["test", "lint", "typecheck", "build", "build:packages", "release:verify"]);
|
|
77
103
|
function normalizeRequestedCommand(command) {
|
|
78
104
|
return command.trim().replace(/\s+/g, " ");
|
|
@@ -83,6 +109,144 @@ function buildValidationCommand(packageManager, scriptName, packageName) {
|
|
|
83
109
|
}
|
|
84
110
|
return `${packageManager} ${scriptName}`;
|
|
85
111
|
}
|
|
112
|
+
function collectValidationCommands(repoRoot, packageManager, packageScopes) {
|
|
113
|
+
const discoveredValidationCommands = [];
|
|
114
|
+
const registerScripts = (packageJsonPath, source, packageName) => {
|
|
115
|
+
const scripts = parsePackageScripts(packageJsonPath);
|
|
116
|
+
for (const scriptName of Object.keys(scripts)) {
|
|
117
|
+
const command = buildValidationCommand(packageManager, scriptName, packageName);
|
|
118
|
+
discoveredValidationCommands.push({
|
|
119
|
+
command,
|
|
120
|
+
source,
|
|
121
|
+
classification: allowedValidationScriptNames.has(scriptName) ? "approval_required" : "deny",
|
|
122
|
+
reason: allowedValidationScriptNames.has(scriptName)
|
|
123
|
+
? "Discovered from a bounded repository script; execution would still require approval."
|
|
124
|
+
: "Command is not in the bounded allowlist for workflow validation."
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
if (!repoRoot) {
|
|
129
|
+
return discoveredValidationCommands;
|
|
130
|
+
}
|
|
131
|
+
registerScripts(join(repoRoot, "package.json"), "package-script");
|
|
132
|
+
for (const packageScope of packageScopes) {
|
|
133
|
+
const packageJsonPath = join(repoRoot, packageScope, "package.json");
|
|
134
|
+
if (!existsSync(packageJsonPath)) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
138
|
+
const packageName = isRecord(parsed) && typeof parsed.name === "string" ? parsed.name : packageScope;
|
|
139
|
+
registerScripts(packageJsonPath, "workspace-script", packageName);
|
|
140
|
+
}
|
|
141
|
+
return discoveredValidationCommands;
|
|
142
|
+
}
|
|
143
|
+
function loadBundleArtifactKinds(bundlePath) {
|
|
144
|
+
if (!existsSync(bundlePath)) {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
|
|
148
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
return parsed.lifecycleArtifacts
|
|
152
|
+
.map((artifact) => (isRecord(artifact) && typeof artifact.artifactKind === "string" ? artifact.artifactKind : undefined))
|
|
153
|
+
.filter((artifactKind) => Boolean(artifactKind));
|
|
154
|
+
}
|
|
155
|
+
function loadBundleArtifactPayloadPaths(bundlePath) {
|
|
156
|
+
if (!existsSync(bundlePath)) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
|
|
160
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
return parsed.lifecycleArtifacts.flatMap((artifact) => {
|
|
164
|
+
if (!isRecord(artifact) || !isRecord(artifact.payload)) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
const payload = artifact.payload;
|
|
168
|
+
if (Array.isArray(payload.affectedPaths)) {
|
|
169
|
+
return asStringArray(payload.affectedPaths);
|
|
170
|
+
}
|
|
171
|
+
if (Array.isArray(payload.evidenceSources)) {
|
|
172
|
+
return asStringArray(payload.evidenceSources);
|
|
173
|
+
}
|
|
174
|
+
return [];
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function loadBundleFinishedAt(bundlePath) {
|
|
178
|
+
if (!existsSync(bundlePath)) {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
|
|
182
|
+
return isRecord(parsed) && typeof parsed.finishedAt === "string" ? parsed.finishedAt : undefined;
|
|
183
|
+
}
|
|
184
|
+
function describeEvidenceObservation(repoRoot, pathValue) {
|
|
185
|
+
if (!repoRoot) {
|
|
186
|
+
return `Observed source ${pathValue} during deterministic intake.`;
|
|
187
|
+
}
|
|
188
|
+
const absolutePath = join(repoRoot, pathValue);
|
|
189
|
+
if (!existsSync(absolutePath)) {
|
|
190
|
+
return `Observed source ${pathValue} during deterministic intake.`;
|
|
191
|
+
}
|
|
192
|
+
const observedAt = pathValue.endsWith(".json") && pathValue.includes(".agentops/runs/")
|
|
193
|
+
? loadBundleFinishedAt(absolutePath)
|
|
194
|
+
: undefined;
|
|
195
|
+
const fallbackObservedAt = observedAt ?? statSync(absolutePath).mtime.toISOString();
|
|
196
|
+
return `Observed source ${pathValue} at ${fallbackObservedAt}.`;
|
|
197
|
+
}
|
|
198
|
+
function loadGitHubActionsEvidence(bundlePath) {
|
|
199
|
+
if (!existsSync(bundlePath) || !bundlePath.endsWith(".json")) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
|
|
203
|
+
const candidate = isRecord(parsed) ? { ...parsed, sourcePath: parsed.sourcePath ?? bundlePath } : parsed;
|
|
204
|
+
const result = githubActionsEvidenceSchema.safeParse(candidate);
|
|
205
|
+
return result.success ? result.data : undefined;
|
|
206
|
+
}
|
|
207
|
+
function isFailingGitHubActionsConclusion(conclusion) {
|
|
208
|
+
return Boolean(conclusion && !["success", "neutral", "skipped"].includes(conclusion));
|
|
209
|
+
}
|
|
210
|
+
function summarizeGitHubActionsFailures(evidence) {
|
|
211
|
+
const failedJobs = evidence.jobs
|
|
212
|
+
.filter((job) => job.status === "completed" && isFailingGitHubActionsConclusion(job.conclusion))
|
|
213
|
+
.map((job) => `${evidence.workflowName} / ${job.name}`);
|
|
214
|
+
const failedCheckRuns = evidence.checkRuns
|
|
215
|
+
.filter((checkRun) => checkRun.status === "completed" && isFailingGitHubActionsConclusion(checkRun.conclusion))
|
|
216
|
+
.map((checkRun) => `${evidence.workflowName} / ${checkRun.name}`);
|
|
217
|
+
const runLevelFailure = failedJobs.length === 0 &&
|
|
218
|
+
failedCheckRuns.length === 0 &&
|
|
219
|
+
evidence.status === "completed" &&
|
|
220
|
+
isFailingGitHubActionsConclusion(evidence.conclusion)
|
|
221
|
+
? [`${evidence.workflowName} / workflow-run`]
|
|
222
|
+
: [];
|
|
223
|
+
return [...failedJobs, ...failedCheckRuns, ...runLevelFailure];
|
|
224
|
+
}
|
|
225
|
+
function normalizeGitHubActionsEvidence(repoRoot, evidenceSources) {
|
|
226
|
+
const evidence = evidenceSources.flatMap((pathValue) => {
|
|
227
|
+
if (!repoRoot) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
const normalized = loadGitHubActionsEvidence(join(repoRoot, pathValue));
|
|
231
|
+
return normalized ? [normalized] : [];
|
|
232
|
+
});
|
|
233
|
+
const workflowNames = [...new Set(evidence.map((entry) => entry.workflowName))];
|
|
234
|
+
const failingChecks = [...new Set(evidence.flatMap((entry) => summarizeGitHubActionsFailures(entry)))];
|
|
235
|
+
const provenanceRefs = [
|
|
236
|
+
...new Set(evidence.flatMap((entry) => [
|
|
237
|
+
entry.sourcePath,
|
|
238
|
+
entry.htmlUrl,
|
|
239
|
+
...entry.jobs.map((job) => job.htmlUrl),
|
|
240
|
+
...entry.checkRuns.map((checkRun) => checkRun.detailsUrl)
|
|
241
|
+
].filter((value) => Boolean(value))))
|
|
242
|
+
];
|
|
243
|
+
return {
|
|
244
|
+
evidence,
|
|
245
|
+
workflowNames,
|
|
246
|
+
failingChecks,
|
|
247
|
+
provenanceRefs
|
|
248
|
+
};
|
|
249
|
+
}
|
|
86
250
|
function derivePackageScope(pathValue) {
|
|
87
251
|
const segments = pathValue.split("/").filter(Boolean);
|
|
88
252
|
if (segments.length < 2) {
|
|
@@ -94,13 +258,57 @@ function derivePackageScope(pathValue) {
|
|
|
94
258
|
}
|
|
95
259
|
return undefined;
|
|
96
260
|
}
|
|
261
|
+
function includesAnyKeyword(values, keywords) {
|
|
262
|
+
const haystack = values.join(" ").toLowerCase();
|
|
263
|
+
return keywords.some((keyword) => haystack.includes(keyword));
|
|
264
|
+
}
|
|
97
265
|
function getWorkflowInput(stateSlice, key) {
|
|
98
266
|
if (!isRecord(stateSlice.workflowInputs)) {
|
|
99
267
|
return undefined;
|
|
100
268
|
}
|
|
101
269
|
return stateSlice.workflowInputs[key];
|
|
102
270
|
}
|
|
103
|
-
function
|
|
271
|
+
function buildLifecycleArtifactEnvelopeBase(state, displayName, summary, inputRefs, issueRefs = [], githubRefs = []) {
|
|
272
|
+
return {
|
|
273
|
+
schemaVersion: state.version,
|
|
274
|
+
workflow: {
|
|
275
|
+
name: state.workflow,
|
|
276
|
+
displayName
|
|
277
|
+
},
|
|
278
|
+
source: {
|
|
279
|
+
sourceType: "workflow-run",
|
|
280
|
+
runId: state.runId,
|
|
281
|
+
inputRefs: [...inputRefs],
|
|
282
|
+
issueRefs: [...issueRefs],
|
|
283
|
+
githubRefs: [...githubRefs]
|
|
284
|
+
},
|
|
285
|
+
status: "complete",
|
|
286
|
+
generatedAt: new Date().toISOString(),
|
|
287
|
+
repo: {
|
|
288
|
+
root: state.repo.root,
|
|
289
|
+
name: state.repo.name,
|
|
290
|
+
branch: state.repo.branch
|
|
291
|
+
},
|
|
292
|
+
provenance: {
|
|
293
|
+
generatedBy: "agentforge-runtime",
|
|
294
|
+
schemaVersion: state.version,
|
|
295
|
+
executionEnvironment: state.context.ciExecution ? "ci" : "local",
|
|
296
|
+
repoRoot: state.repo.root
|
|
297
|
+
},
|
|
298
|
+
redaction: {
|
|
299
|
+
applied: true,
|
|
300
|
+
strategyVersion: "1.0.0",
|
|
301
|
+
categories: ["github-token", "api-key", "aws-key", "bearer-token", "password", "private-key"]
|
|
302
|
+
},
|
|
303
|
+
auditLink: {
|
|
304
|
+
entryIds: [],
|
|
305
|
+
findingIds: [],
|
|
306
|
+
proposedActionIds: []
|
|
307
|
+
},
|
|
308
|
+
summary
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function buildArtifactEnvelopeBase(state, summary, inputRefs, issueRefs, githubRefs = []) {
|
|
104
312
|
return {
|
|
105
313
|
schemaVersion: state.version,
|
|
106
314
|
workflow: {
|
|
@@ -110,7 +318,8 @@ function buildArtifactEnvelopeBase(state, summary, inputRefs, issueRefs) {
|
|
|
110
318
|
sourceType: "workflow-run",
|
|
111
319
|
runId: state.runId,
|
|
112
320
|
inputRefs: [...inputRefs],
|
|
113
|
-
issueRefs: [...issueRefs]
|
|
321
|
+
issueRefs: [...issueRefs],
|
|
322
|
+
githubRefs: [...githubRefs]
|
|
114
323
|
},
|
|
115
324
|
status: "complete",
|
|
116
325
|
generatedAt: new Date().toISOString(),
|
|
@@ -236,6 +445,7 @@ const planningAnalystAgent = {
|
|
|
236
445
|
outputSchema: agentOutputSchema,
|
|
237
446
|
async execute({ state, stateSlice }) {
|
|
238
447
|
const planningRequest = getWorkflowInput(stateSlice, "planningRequest");
|
|
448
|
+
const planningGithubRefs = getWorkflowInput(stateSlice, "planningGithubRefs") ?? [];
|
|
239
449
|
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
240
450
|
if (!planningRequest) {
|
|
241
451
|
throw new Error("planning-discovery requires a validated planning request before planning analysis.");
|
|
@@ -259,7 +469,7 @@ const planningAnalystAgent = {
|
|
|
259
469
|
];
|
|
260
470
|
const summary = `Planning brief scoped ${objectives.length} objective(s) for ${state.repo.name}.`;
|
|
261
471
|
const planningBrief = planningArtifactSchema.parse({
|
|
262
|
-
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/planning.yaml"], planningRequest.issueRefs),
|
|
472
|
+
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/planning.yaml"], planningRequest.issueRefs, planningGithubRefs),
|
|
263
473
|
artifactKind: "planning-brief",
|
|
264
474
|
lifecycleDomain: "plan",
|
|
265
475
|
workflow: {
|
|
@@ -593,12 +803,12 @@ const qaIntakeAgent = {
|
|
|
593
803
|
});
|
|
594
804
|
}
|
|
595
805
|
};
|
|
596
|
-
const
|
|
806
|
+
const securityIntakeAgent = {
|
|
597
807
|
manifest: agentManifestSchema.parse({
|
|
598
808
|
version: 1,
|
|
599
|
-
name: "
|
|
600
|
-
displayName: "
|
|
601
|
-
category: "
|
|
809
|
+
name: "security-intake",
|
|
810
|
+
displayName: "Security Intake",
|
|
811
|
+
category: "security",
|
|
602
812
|
runtime: {
|
|
603
813
|
minVersion: "0.1.0",
|
|
604
814
|
kind: "deterministic"
|
|
@@ -607,17 +817,17 @@ const implementationInventoryAgent = {
|
|
|
607
817
|
model: false,
|
|
608
818
|
network: false,
|
|
609
819
|
tools: [],
|
|
610
|
-
readPaths: ["
|
|
820
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**"],
|
|
611
821
|
writePaths: []
|
|
612
822
|
},
|
|
613
|
-
inputs: ["workflowInputs", "repo"
|
|
823
|
+
inputs: ["workflowInputs", "repo"],
|
|
614
824
|
outputs: ["summary", "metadata"],
|
|
615
825
|
contextPolicy: {
|
|
616
|
-
sections: ["workflowInputs", "repo", "
|
|
826
|
+
sections: ["workflowInputs", "repo", "context"],
|
|
617
827
|
minimalContext: true
|
|
618
828
|
},
|
|
619
829
|
catalog: {
|
|
620
|
-
domain: "
|
|
830
|
+
domain: "security",
|
|
621
831
|
supportLevel: "internal",
|
|
622
832
|
maturity: "mvp",
|
|
623
833
|
trustScope: "official-core-only"
|
|
@@ -630,65 +840,1640 @@ const implementationInventoryAgent = {
|
|
|
630
840
|
}),
|
|
631
841
|
outputSchema: agentOutputSchema,
|
|
632
842
|
async execute({ stateSlice }) {
|
|
633
|
-
const
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
843
|
+
const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
|
|
844
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
845
|
+
const referencedArtifactKinds = getWorkflowInput(stateSlice, "securityTargetArtifactKinds") ?? [];
|
|
846
|
+
if (!securityRequest) {
|
|
847
|
+
throw new Error("security-review requires a validated security request before runtime execution.");
|
|
637
848
|
}
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
849
|
+
const targetType = securityRequest.targetRef.endsWith("bundle.json") ? "artifact-bundle" : "local-reference";
|
|
850
|
+
return agentOutputSchema.parse({
|
|
851
|
+
summary: `Loaded security request from ${requestFile ?? ".agentops/requests/security.yaml"} targeting ${securityRequest.targetRef}.`,
|
|
852
|
+
findings: [],
|
|
853
|
+
proposedActions: [],
|
|
854
|
+
lifecycleArtifacts: [],
|
|
855
|
+
requestedTools: [],
|
|
856
|
+
blockedActionFlags: [],
|
|
857
|
+
metadata: {
|
|
858
|
+
...securityRequestSchema.parse({
|
|
859
|
+
...securityRequest,
|
|
860
|
+
evidenceSources: [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])]
|
|
861
|
+
}),
|
|
862
|
+
targetType,
|
|
863
|
+
referencedArtifactKinds
|
|
651
864
|
}
|
|
652
|
-
|
|
653
|
-
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
const incidentIntakeAgent = {
|
|
869
|
+
manifest: agentManifestSchema.parse({
|
|
870
|
+
version: 1,
|
|
871
|
+
name: "incident-intake",
|
|
872
|
+
displayName: "Incident Intake",
|
|
873
|
+
category: "operate",
|
|
874
|
+
runtime: {
|
|
875
|
+
minVersion: "0.1.0",
|
|
876
|
+
kind: "deterministic"
|
|
877
|
+
},
|
|
878
|
+
permissions: {
|
|
879
|
+
model: false,
|
|
880
|
+
network: false,
|
|
881
|
+
tools: [],
|
|
882
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.log", "**/*.md"],
|
|
883
|
+
writePaths: []
|
|
884
|
+
},
|
|
885
|
+
inputs: ["workflowInputs", "repo"],
|
|
886
|
+
outputs: ["summary", "metadata"],
|
|
887
|
+
contextPolicy: {
|
|
888
|
+
sections: ["workflowInputs", "repo", "context"],
|
|
889
|
+
minimalContext: true
|
|
890
|
+
},
|
|
891
|
+
catalog: {
|
|
892
|
+
domain: "operate",
|
|
893
|
+
supportLevel: "internal",
|
|
894
|
+
maturity: "mvp",
|
|
895
|
+
trustScope: "official-core-only"
|
|
896
|
+
},
|
|
897
|
+
trust: {
|
|
898
|
+
tier: "core",
|
|
899
|
+
source: "official",
|
|
900
|
+
reviewed: true
|
|
901
|
+
}
|
|
902
|
+
}),
|
|
903
|
+
outputSchema: agentOutputSchema,
|
|
904
|
+
async execute({ stateSlice }) {
|
|
905
|
+
const incidentRequest = getWorkflowInput(stateSlice, "incidentRequest");
|
|
906
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
907
|
+
if (!incidentRequest) {
|
|
908
|
+
throw new Error("incident-handoff requires a validated incident request before runtime execution.");
|
|
909
|
+
}
|
|
910
|
+
return agentOutputSchema.parse({
|
|
911
|
+
summary: `Loaded incident request from ${requestFile ?? ".agentops/requests/incident.yaml"} for ${incidentRequest.incidentSummary}.`,
|
|
912
|
+
findings: [],
|
|
913
|
+
proposedActions: [],
|
|
914
|
+
lifecycleArtifacts: [],
|
|
915
|
+
requestedTools: [],
|
|
916
|
+
blockedActionFlags: [],
|
|
917
|
+
metadata: {
|
|
918
|
+
...incidentRequestSchema.parse({
|
|
919
|
+
...incidentRequest,
|
|
920
|
+
evidenceSources: [...new Set(incidentRequest.evidenceSources)]
|
|
921
|
+
}),
|
|
922
|
+
evidenceSourceCount: incidentRequest.evidenceSources.length + incidentRequest.releaseReportRefs.length
|
|
654
923
|
}
|
|
655
|
-
return existsSync(join(repoRoot, pathValue));
|
|
656
924
|
});
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
const incidentEvidenceNormalizationAgent = {
|
|
928
|
+
manifest: agentManifestSchema.parse({
|
|
929
|
+
version: 1,
|
|
930
|
+
name: "incident-evidence-normalizer",
|
|
931
|
+
displayName: "Incident Evidence Normalizer",
|
|
932
|
+
category: "operate",
|
|
933
|
+
runtime: {
|
|
934
|
+
minVersion: "0.1.0",
|
|
935
|
+
kind: "deterministic"
|
|
936
|
+
},
|
|
937
|
+
permissions: {
|
|
938
|
+
model: false,
|
|
939
|
+
network: false,
|
|
940
|
+
tools: [],
|
|
941
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.log", "**/*.md", "**/*.txt"],
|
|
942
|
+
writePaths: []
|
|
943
|
+
},
|
|
944
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
945
|
+
outputs: ["summary", "metadata"],
|
|
946
|
+
contextPolicy: {
|
|
947
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
948
|
+
minimalContext: true
|
|
949
|
+
},
|
|
950
|
+
catalog: {
|
|
951
|
+
domain: "operate",
|
|
952
|
+
supportLevel: "internal",
|
|
953
|
+
maturity: "mvp",
|
|
954
|
+
trustScope: "official-core-only"
|
|
955
|
+
},
|
|
956
|
+
trust: {
|
|
957
|
+
tier: "core",
|
|
958
|
+
source: "official",
|
|
959
|
+
reviewed: true
|
|
960
|
+
}
|
|
961
|
+
}),
|
|
962
|
+
outputSchema: agentOutputSchema,
|
|
963
|
+
async execute({ stateSlice }) {
|
|
964
|
+
const incidentRequest = getWorkflowInput(stateSlice, "incidentRequest");
|
|
965
|
+
if (!incidentRequest) {
|
|
966
|
+
throw new Error("incident-handoff requires validated incident request inputs before evidence normalization.");
|
|
967
|
+
}
|
|
968
|
+
const repoRoot = stateSlice.repo?.root;
|
|
969
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
970
|
+
const releaseReportRefs = asStringArray(intakeMetadata.releaseReportRefs).length > 0
|
|
971
|
+
? asStringArray(intakeMetadata.releaseReportRefs)
|
|
972
|
+
: incidentRequest.releaseReportRefs;
|
|
973
|
+
const evidenceSources = asStringArray(intakeMetadata.evidenceSources).length > 0
|
|
974
|
+
? asStringArray(intakeMetadata.evidenceSources)
|
|
975
|
+
: incidentRequest.evidenceSources;
|
|
976
|
+
const severityHint = typeof intakeMetadata.severityHint === "string" ? intakeMetadata.severityHint : incidentRequest.severityHint;
|
|
977
|
+
const normalizedEvidenceSources = [...new Set([...evidenceSources, ...releaseReportRefs])];
|
|
978
|
+
const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
|
|
979
|
+
if (missingEvidenceSources.length > 0) {
|
|
980
|
+
throw new Error(`Incident evidence source not found: ${missingEvidenceSources[0]}`);
|
|
981
|
+
}
|
|
982
|
+
const referencedArtifactKinds = [...new Set(releaseReportRefs.flatMap((pathValue) => (repoRoot ? loadBundleArtifactKinds(join(repoRoot, pathValue)) : [])))];
|
|
983
|
+
const timelineSummary = [
|
|
984
|
+
`Severity hint: ${severityHint}.`,
|
|
985
|
+
"Normalized staged incident evidence and release-report references before reasoning.",
|
|
986
|
+
...normalizedEvidenceSources.map((pathValue) => describeEvidenceObservation(repoRoot, pathValue))
|
|
660
987
|
];
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
...
|
|
988
|
+
const likelyImpactedAreas = [
|
|
989
|
+
...(releaseReportRefs.length > 0 ? ["release-readiness"] : []),
|
|
990
|
+
...(evidenceSources.length > 0 ? ["staged-operational-evidence"] : []),
|
|
991
|
+
...(severityHint === "high" || severityHint === "critical" ? ["security-follow-up"] : [])
|
|
664
992
|
];
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
993
|
+
const followUpWorkflowRefs = [
|
|
994
|
+
"maintenance-triage",
|
|
995
|
+
...(releaseReportRefs.length > 0 ? ["release-readiness"] : []),
|
|
996
|
+
...(severityHint === "high" || severityHint === "critical" ? ["security-review"] : [])
|
|
997
|
+
];
|
|
998
|
+
const normalization = incidentEvidenceNormalizationSchema.parse({
|
|
999
|
+
incidentSummary: incidentRequest.incidentSummary,
|
|
1000
|
+
severityHint,
|
|
1001
|
+
normalizedEvidenceSources,
|
|
1002
|
+
missingEvidenceSources: [],
|
|
1003
|
+
releaseReportRefs,
|
|
1004
|
+
timelineSummary,
|
|
1005
|
+
likelyImpactedAreas: [...new Set(likelyImpactedAreas)],
|
|
1006
|
+
followUpWorkflowRefs: [...new Set(followUpWorkflowRefs)],
|
|
1007
|
+
provenanceRefs: [
|
|
1008
|
+
...new Set([
|
|
1009
|
+
...evidenceSources,
|
|
1010
|
+
...releaseReportRefs.map((pathValue) => `${pathValue}#release-report`)
|
|
1011
|
+
])
|
|
1012
|
+
],
|
|
1013
|
+
redactionCategories: ["github-token", "api-key", "aws-key", "bearer-token", "password", "private-key", "operational-sensitive"],
|
|
1014
|
+
referencedArtifactKinds
|
|
1015
|
+
});
|
|
1016
|
+
return agentOutputSchema.parse({
|
|
1017
|
+
summary: `Normalized incident evidence across ${normalization.normalizedEvidenceSources.length} staged source(s).`,
|
|
1018
|
+
findings: [],
|
|
1019
|
+
proposedActions: [],
|
|
1020
|
+
lifecycleArtifacts: [],
|
|
1021
|
+
requestedTools: [],
|
|
1022
|
+
blockedActionFlags: [],
|
|
1023
|
+
metadata: normalization
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
const maintenanceIntakeAgent = {
|
|
1028
|
+
manifest: agentManifestSchema.parse({
|
|
1029
|
+
version: 1,
|
|
1030
|
+
name: "maintenance-intake",
|
|
1031
|
+
displayName: "Maintenance Intake",
|
|
1032
|
+
category: "maintain",
|
|
1033
|
+
runtime: {
|
|
1034
|
+
minVersion: "0.1.0",
|
|
1035
|
+
kind: "deterministic"
|
|
1036
|
+
},
|
|
1037
|
+
permissions: {
|
|
1038
|
+
model: false,
|
|
1039
|
+
network: false,
|
|
1040
|
+
tools: [],
|
|
1041
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md", "**/*.txt"],
|
|
1042
|
+
writePaths: []
|
|
1043
|
+
},
|
|
1044
|
+
inputs: ["workflowInputs", "repo"],
|
|
1045
|
+
outputs: ["summary", "metadata"],
|
|
1046
|
+
contextPolicy: {
|
|
1047
|
+
sections: ["workflowInputs", "repo", "context"],
|
|
1048
|
+
minimalContext: true
|
|
1049
|
+
},
|
|
1050
|
+
catalog: {
|
|
1051
|
+
domain: "maintain",
|
|
1052
|
+
supportLevel: "internal",
|
|
1053
|
+
maturity: "mvp",
|
|
1054
|
+
trustScope: "official-core-only"
|
|
1055
|
+
},
|
|
1056
|
+
trust: {
|
|
1057
|
+
tier: "core",
|
|
1058
|
+
source: "official",
|
|
1059
|
+
reviewed: true
|
|
1060
|
+
}
|
|
1061
|
+
}),
|
|
1062
|
+
outputSchema: agentOutputSchema,
|
|
1063
|
+
async execute({ stateSlice }) {
|
|
1064
|
+
const maintenanceRequest = getWorkflowInput(stateSlice, "maintenanceRequest");
|
|
1065
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
1066
|
+
const maintenanceIssueRefs = getWorkflowInput(stateSlice, "maintenanceIssueRefs") ?? [];
|
|
1067
|
+
const maintenanceGithubRefs = getWorkflowInput(stateSlice, "maintenanceGithubRefs") ?? [];
|
|
1068
|
+
if (!maintenanceRequest) {
|
|
1069
|
+
throw new Error("maintenance-triage requires a validated maintenance request before runtime execution.");
|
|
691
1070
|
}
|
|
1071
|
+
return agentOutputSchema.parse({
|
|
1072
|
+
summary: `Loaded maintenance request from ${requestFile ?? ".agentops/requests/maintenance.yaml"} for ${maintenanceRequest.maintenanceGoal}.`,
|
|
1073
|
+
findings: [],
|
|
1074
|
+
proposedActions: [],
|
|
1075
|
+
lifecycleArtifacts: [],
|
|
1076
|
+
requestedTools: [],
|
|
1077
|
+
blockedActionFlags: [],
|
|
1078
|
+
metadata: {
|
|
1079
|
+
...maintenanceRequestSchema.parse({
|
|
1080
|
+
...maintenanceRequest,
|
|
1081
|
+
dependencyAlertRefs: [...new Set(maintenanceRequest.dependencyAlertRefs)],
|
|
1082
|
+
docsTaskRefs: [...new Set(maintenanceRequest.docsTaskRefs)],
|
|
1083
|
+
releaseReportRefs: [...new Set(maintenanceRequest.releaseReportRefs)],
|
|
1084
|
+
issueRefs: [...new Set(maintenanceRequest.issueRefs)]
|
|
1085
|
+
}),
|
|
1086
|
+
maintenanceIssueRefs,
|
|
1087
|
+
maintenanceGithubRefs,
|
|
1088
|
+
evidenceSourceCount: maintenanceRequest.dependencyAlertRefs.length +
|
|
1089
|
+
maintenanceRequest.docsTaskRefs.length +
|
|
1090
|
+
maintenanceRequest.releaseReportRefs.length
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
};
|
|
1095
|
+
const maintenanceEvidenceNormalizerAgent = {
|
|
1096
|
+
manifest: agentManifestSchema.parse({
|
|
1097
|
+
version: 1,
|
|
1098
|
+
name: "maintenance-evidence-normalizer",
|
|
1099
|
+
displayName: "Maintenance Evidence Normalizer",
|
|
1100
|
+
category: "maintain",
|
|
1101
|
+
runtime: {
|
|
1102
|
+
minVersion: "0.1.0",
|
|
1103
|
+
kind: "deterministic"
|
|
1104
|
+
},
|
|
1105
|
+
permissions: {
|
|
1106
|
+
model: false,
|
|
1107
|
+
network: false,
|
|
1108
|
+
tools: [],
|
|
1109
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md", "**/*.txt", "**/package.json"],
|
|
1110
|
+
writePaths: []
|
|
1111
|
+
},
|
|
1112
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
1113
|
+
outputs: ["summary", "metadata"],
|
|
1114
|
+
contextPolicy: {
|
|
1115
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
1116
|
+
minimalContext: true
|
|
1117
|
+
},
|
|
1118
|
+
catalog: {
|
|
1119
|
+
domain: "maintain",
|
|
1120
|
+
supportLevel: "internal",
|
|
1121
|
+
maturity: "mvp",
|
|
1122
|
+
trustScope: "official-core-only"
|
|
1123
|
+
},
|
|
1124
|
+
trust: {
|
|
1125
|
+
tier: "core",
|
|
1126
|
+
source: "official",
|
|
1127
|
+
reviewed: true
|
|
1128
|
+
}
|
|
1129
|
+
}),
|
|
1130
|
+
outputSchema: agentOutputSchema,
|
|
1131
|
+
async execute({ stateSlice }) {
|
|
1132
|
+
const maintenanceRequest = getWorkflowInput(stateSlice, "maintenanceRequest");
|
|
1133
|
+
if (!maintenanceRequest) {
|
|
1134
|
+
throw new Error("maintenance-triage requires validated maintenance inputs before evidence normalization.");
|
|
1135
|
+
}
|
|
1136
|
+
const repoRoot = stateSlice.repo?.root;
|
|
1137
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1138
|
+
const dependencyAlertRefs = asStringArray(intakeMetadata.dependencyAlertRefs).length > 0
|
|
1139
|
+
? asStringArray(intakeMetadata.dependencyAlertRefs)
|
|
1140
|
+
: maintenanceRequest.dependencyAlertRefs;
|
|
1141
|
+
const docsTaskRefs = asStringArray(intakeMetadata.docsTaskRefs).length > 0
|
|
1142
|
+
? asStringArray(intakeMetadata.docsTaskRefs)
|
|
1143
|
+
: maintenanceRequest.docsTaskRefs;
|
|
1144
|
+
const releaseReportRefs = asStringArray(intakeMetadata.releaseReportRefs).length > 0
|
|
1145
|
+
? asStringArray(intakeMetadata.releaseReportRefs)
|
|
1146
|
+
: maintenanceRequest.releaseReportRefs;
|
|
1147
|
+
const normalizedEvidenceSources = [...new Set([...dependencyAlertRefs, ...docsTaskRefs, ...releaseReportRefs])];
|
|
1148
|
+
const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
|
|
1149
|
+
if (missingEvidenceSources.length > 0) {
|
|
1150
|
+
throw new Error(`Maintenance evidence source not found: ${missingEvidenceSources[0]}`);
|
|
1151
|
+
}
|
|
1152
|
+
const referencedArtifactKinds = [
|
|
1153
|
+
...new Set(releaseReportRefs.flatMap((pathValue) => (repoRoot ? loadBundleArtifactKinds(join(repoRoot, pathValue)) : [])))
|
|
1154
|
+
];
|
|
1155
|
+
const releasePayloadPaths = releaseReportRefs.flatMap((pathValue) => (repoRoot ? loadBundleArtifactPayloadPaths(join(repoRoot, pathValue)) : []));
|
|
1156
|
+
const affectedPackagesOrDocs = [
|
|
1157
|
+
...new Set([...docsTaskRefs, ...releasePayloadPaths]
|
|
1158
|
+
.map((pathValue) => derivePackageScope(pathValue) ?? pathValue)
|
|
1159
|
+
.filter((value) => Boolean(value)))
|
|
1160
|
+
];
|
|
1161
|
+
const maintenanceSignals = [
|
|
1162
|
+
...normalizedEvidenceSources.map((pathValue) => describeEvidenceObservation(repoRoot, pathValue)),
|
|
1163
|
+
...(dependencyAlertRefs.length > 0 ? ["Dependency alert references contribute bounded maintenance follow-up context."] : []),
|
|
1164
|
+
...(docsTaskRefs.length > 0 ? ["Documentation task references contribute bounded maintenance follow-up context."] : []),
|
|
1165
|
+
...(releaseReportRefs.length > 0 ? ["Release report references contribute bounded maintenance follow-up context."] : []),
|
|
1166
|
+
...(referencedArtifactKinds.length > 0 ? [`Referenced artifact kinds: ${referencedArtifactKinds.join(", ")}`] : [])
|
|
1167
|
+
];
|
|
1168
|
+
const routingInputs = [
|
|
1169
|
+
maintenanceRequest.maintenanceGoal,
|
|
1170
|
+
...dependencyAlertRefs,
|
|
1171
|
+
...docsTaskRefs,
|
|
1172
|
+
...releaseReportRefs,
|
|
1173
|
+
...maintenanceSignals
|
|
1174
|
+
];
|
|
1175
|
+
const securitySignal = referencedArtifactKinds.includes("security-report") ||
|
|
1176
|
+
includesAnyKeyword(routingInputs, ["security", "vulnerability", "vuln", "cve", "advisory"]);
|
|
1177
|
+
const qaSignal = referencedArtifactKinds.includes("qa-report") ||
|
|
1178
|
+
includesAnyKeyword(routingInputs, ["qa", "test", "coverage", "flaky"]);
|
|
1179
|
+
const followUpWorkflowRefs = [
|
|
1180
|
+
...new Set([
|
|
1181
|
+
...(securitySignal ? ["security-review"] : []),
|
|
1182
|
+
...(dependencyAlertRefs.length > 0 || docsTaskRefs.length > 0 ? ["implementation-proposal"] : []),
|
|
1183
|
+
...(releaseReportRefs.length > 0 ? ["release-readiness"] : []),
|
|
1184
|
+
...(qaSignal ? ["qa-review"] : [])
|
|
1185
|
+
])
|
|
1186
|
+
];
|
|
1187
|
+
const routingRecommendation = followUpWorkflowRefs[0] ?? "implementation-proposal";
|
|
1188
|
+
const provenanceRefs = [
|
|
1189
|
+
...new Set([
|
|
1190
|
+
...dependencyAlertRefs,
|
|
1191
|
+
...docsTaskRefs,
|
|
1192
|
+
...releaseReportRefs.map((pathValue) => `${pathValue}#release-report`)
|
|
1193
|
+
])
|
|
1194
|
+
];
|
|
1195
|
+
const normalization = maintenanceEvidenceNormalizationSchema.parse({
|
|
1196
|
+
maintenanceGoal: maintenanceRequest.maintenanceGoal,
|
|
1197
|
+
dependencyAlertRefs,
|
|
1198
|
+
docsTaskRefs,
|
|
1199
|
+
releaseReportRefs,
|
|
1200
|
+
normalizedEvidenceSources,
|
|
1201
|
+
missingEvidenceSources: [],
|
|
1202
|
+
referencedArtifactKinds,
|
|
1203
|
+
affectedPackagesOrDocs,
|
|
1204
|
+
maintenanceSignals,
|
|
1205
|
+
followUpWorkflowRefs,
|
|
1206
|
+
routingRecommendation,
|
|
1207
|
+
provenanceRefs
|
|
1208
|
+
});
|
|
1209
|
+
return agentOutputSchema.parse({
|
|
1210
|
+
summary: `Normalized maintenance evidence across ${normalization.normalizedEvidenceSources.length} source(s) for ${maintenanceRequest.maintenanceGoal}.`,
|
|
1211
|
+
findings: [],
|
|
1212
|
+
proposedActions: [],
|
|
1213
|
+
lifecycleArtifacts: [],
|
|
1214
|
+
requestedTools: [],
|
|
1215
|
+
blockedActionFlags: [],
|
|
1216
|
+
metadata: normalization
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
const maintenanceAnalystAgent = {
|
|
1221
|
+
manifest: agentManifestSchema.parse({
|
|
1222
|
+
version: 1,
|
|
1223
|
+
name: "maintenance-analyst",
|
|
1224
|
+
displayName: "Maintenance Analyst",
|
|
1225
|
+
category: "maintain",
|
|
1226
|
+
runtime: {
|
|
1227
|
+
minVersion: "0.1.0",
|
|
1228
|
+
kind: "reasoning"
|
|
1229
|
+
},
|
|
1230
|
+
permissions: {
|
|
1231
|
+
model: true,
|
|
1232
|
+
network: false,
|
|
1233
|
+
tools: [],
|
|
1234
|
+
readPaths: ["**/*"],
|
|
1235
|
+
writePaths: []
|
|
1236
|
+
},
|
|
1237
|
+
inputs: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
1238
|
+
outputs: ["lifecycleArtifacts"],
|
|
1239
|
+
contextPolicy: {
|
|
1240
|
+
sections: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
1241
|
+
minimalContext: true
|
|
1242
|
+
},
|
|
1243
|
+
catalog: {
|
|
1244
|
+
domain: "maintain",
|
|
1245
|
+
supportLevel: "internal",
|
|
1246
|
+
maturity: "mvp",
|
|
1247
|
+
trustScope: "official-core-only"
|
|
1248
|
+
},
|
|
1249
|
+
trust: {
|
|
1250
|
+
tier: "core",
|
|
1251
|
+
source: "official",
|
|
1252
|
+
reviewed: true
|
|
1253
|
+
}
|
|
1254
|
+
}),
|
|
1255
|
+
outputSchema: agentOutputSchema,
|
|
1256
|
+
async execute({ state, stateSlice }) {
|
|
1257
|
+
const maintenanceRequest = getWorkflowInput(stateSlice, "maintenanceRequest");
|
|
1258
|
+
const maintenanceIssueRefs = getWorkflowInput(stateSlice, "maintenanceIssueRefs") ?? [];
|
|
1259
|
+
const maintenanceGithubRefs = getWorkflowInput(stateSlice, "maintenanceGithubRefs") ?? [];
|
|
1260
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
1261
|
+
if (!maintenanceRequest) {
|
|
1262
|
+
throw new Error("maintenance-triage requires validated maintenance inputs before maintenance analysis.");
|
|
1263
|
+
}
|
|
1264
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1265
|
+
const evidenceMetadata = maintenanceEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
|
|
1266
|
+
const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
|
|
1267
|
+
const dependencyAlertRefs = normalizedEvidence?.dependencyAlertRefs ??
|
|
1268
|
+
(asStringArray(intakeMetadata.dependencyAlertRefs).length > 0
|
|
1269
|
+
? asStringArray(intakeMetadata.dependencyAlertRefs)
|
|
1270
|
+
: maintenanceRequest.dependencyAlertRefs);
|
|
1271
|
+
const docsTaskRefs = normalizedEvidence?.docsTaskRefs ??
|
|
1272
|
+
(asStringArray(intakeMetadata.docsTaskRefs).length > 0
|
|
1273
|
+
? asStringArray(intakeMetadata.docsTaskRefs)
|
|
1274
|
+
: maintenanceRequest.docsTaskRefs);
|
|
1275
|
+
const releaseReportRefs = normalizedEvidence?.releaseReportRefs ??
|
|
1276
|
+
(asStringArray(intakeMetadata.releaseReportRefs).length > 0
|
|
1277
|
+
? asStringArray(intakeMetadata.releaseReportRefs)
|
|
1278
|
+
: maintenanceRequest.releaseReportRefs);
|
|
1279
|
+
const normalizedConstraints = asStringArray(intakeMetadata.constraints);
|
|
1280
|
+
const evidenceSources = normalizedEvidence?.normalizedEvidenceSources ?? [...new Set([...dependencyAlertRefs, ...docsTaskRefs, ...releaseReportRefs])];
|
|
1281
|
+
const affectedPackagesOrDocs = normalizedEvidence?.affectedPackagesOrDocs ?? [];
|
|
1282
|
+
const followUpWorkflowRefs = normalizedEvidence?.followUpWorkflowRefs ?? [];
|
|
1283
|
+
const routingRecommendation = normalizedEvidence?.routingRecommendation ?? "implementation-proposal";
|
|
1284
|
+
const maintenanceSignals = normalizedEvidence?.maintenanceSignals ?? [];
|
|
1285
|
+
const referencedArtifactKinds = normalizedEvidence?.referencedArtifactKinds ?? [];
|
|
1286
|
+
const currentFindings = [
|
|
1287
|
+
...(maintenanceSignals.length > 0
|
|
1288
|
+
? maintenanceSignals
|
|
1289
|
+
: [
|
|
1290
|
+
...(dependencyAlertRefs.length > 0 ? [`${dependencyAlertRefs.length} dependency alert reference(s) require maintenance triage.`] : []),
|
|
1291
|
+
...(docsTaskRefs.length > 0 ? [`${docsTaskRefs.length} docs task reference(s) require maintenance triage.`] : []),
|
|
1292
|
+
...(releaseReportRefs.length > 0 ? [`${releaseReportRefs.length} release-report reference(s) contribute maintenance follow-up context.`] : [])
|
|
1293
|
+
])
|
|
1294
|
+
];
|
|
1295
|
+
const recommendedActions = [
|
|
1296
|
+
...dependencyAlertRefs.map((pathValue) => `Review dependency alert reference \`${pathValue}\` before choosing a follow-up workflow.`),
|
|
1297
|
+
...docsTaskRefs.map((pathValue) => `Review docs task reference \`${pathValue}\` before choosing a follow-up workflow.`),
|
|
1298
|
+
...releaseReportRefs.map((pathValue) => `Review release report reference \`${pathValue}\` for maintenance-linked follow-up work.`),
|
|
1299
|
+
...(affectedPackagesOrDocs.length > 0 ? [`Review the affected maintenance surfaces: ${affectedPackagesOrDocs.join(", ")}.`] : []),
|
|
1300
|
+
...(followUpWorkflowRefs.length > 0
|
|
1301
|
+
? [`Route the next bounded follow-up through ${routingRecommendation} (${followUpWorkflowRefs.join(", ")} considered).`]
|
|
1302
|
+
: []),
|
|
1303
|
+
...(normalizedConstraints.length > 0 ? [`Keep maintenance follow-up bounded by: ${normalizedConstraints.join("; ")}.`] : [])
|
|
1304
|
+
];
|
|
1305
|
+
const priorityAssessment = releaseReportRefs.length > 0 || dependencyAlertRefs.length > 1
|
|
1306
|
+
? "Elevated maintenance triage: release-linked or multi-alert follow-up should be prioritized before broader maintenance work."
|
|
1307
|
+
: "Routine maintenance triage: review bounded references and route follow-up deliberately.";
|
|
1308
|
+
const risks = [
|
|
1309
|
+
...(releaseReportRefs.length > 0 ? ["Release-linked maintenance follow-up can drift if release-readiness is deferred."] : []),
|
|
1310
|
+
...(dependencyAlertRefs.length > 0 ? ["Dependency alert follow-up can widen change scope once implementation work begins."] : []),
|
|
1311
|
+
...(docsTaskRefs.length > 0 ? ["Documentation debt can diverge from implemented behavior if maintenance triage is deferred."] : []),
|
|
1312
|
+
...(referencedArtifactKinds.includes("security-report")
|
|
1313
|
+
? ["Security-linked maintenance follow-up should remain prioritized until the linked evidence is resolved."]
|
|
1314
|
+
: [])
|
|
1315
|
+
];
|
|
1316
|
+
const stalenessSignals = [
|
|
1317
|
+
...(dependencyAlertRefs.length > 0 ? ["Dependency alert follow-up remains pending review."] : []),
|
|
1318
|
+
...(docsTaskRefs.length > 0 ? ["Documentation maintenance follow-up remains pending review."] : []),
|
|
1319
|
+
...(releaseReportRefs.length > 0 ? ["Release-linked maintenance follow-up remains pending review."] : [])
|
|
1320
|
+
];
|
|
1321
|
+
const summary = `Maintenance report prepared for ${maintenanceRequest.maintenanceGoal}.`;
|
|
1322
|
+
const maintenanceReport = maintenanceArtifactSchema.parse({
|
|
1323
|
+
...buildLifecycleArtifactEnvelopeBase(state, "Maintenance Triage", summary, [requestFile ?? ".agentops/requests/maintenance.yaml", ...evidenceSources], maintenanceIssueRefs, maintenanceGithubRefs),
|
|
1324
|
+
artifactKind: "maintenance-report",
|
|
1325
|
+
lifecycleDomain: "maintain",
|
|
1326
|
+
payload: {
|
|
1327
|
+
maintenanceScope: maintenanceRequest.maintenanceGoal,
|
|
1328
|
+
evidenceSources,
|
|
1329
|
+
affectedPackagesOrDocs,
|
|
1330
|
+
currentFindings: currentFindings.length > 0
|
|
1331
|
+
? currentFindings
|
|
1332
|
+
: ["Maintenance triage remained bounded to validated references; no additional findings were synthesized."],
|
|
1333
|
+
recommendedActions: recommendedActions.length > 0
|
|
1334
|
+
? recommendedActions
|
|
1335
|
+
: ["Add at least one bounded maintenance reference before broadening the workflow surface."],
|
|
1336
|
+
routingRecommendation,
|
|
1337
|
+
followUpWorkflowRefs,
|
|
1338
|
+
risks,
|
|
1339
|
+
priorityAssessment,
|
|
1340
|
+
dependencyUpdates: dependencyAlertRefs,
|
|
1341
|
+
docsUpdates: docsTaskRefs,
|
|
1342
|
+
stalenessSignals,
|
|
1343
|
+
followUpIssues: maintenanceIssueRefs
|
|
1344
|
+
}
|
|
1345
|
+
});
|
|
1346
|
+
return agentOutputSchema.parse({
|
|
1347
|
+
summary,
|
|
1348
|
+
findings: [],
|
|
1349
|
+
proposedActions: [],
|
|
1350
|
+
lifecycleArtifacts: [maintenanceReport],
|
|
1351
|
+
requestedTools: [],
|
|
1352
|
+
blockedActionFlags: [],
|
|
1353
|
+
confidence: 0.73,
|
|
1354
|
+
metadata: {
|
|
1355
|
+
deterministicInputs: {
|
|
1356
|
+
evidenceSources,
|
|
1357
|
+
dependencyAlertRefs,
|
|
1358
|
+
docsTaskRefs,
|
|
1359
|
+
releaseReportRefs,
|
|
1360
|
+
affectedPackagesOrDocs,
|
|
1361
|
+
maintenanceSignals,
|
|
1362
|
+
referencedArtifactKinds,
|
|
1363
|
+
issueRefs: maintenanceIssueRefs,
|
|
1364
|
+
constraints: normalizedConstraints
|
|
1365
|
+
},
|
|
1366
|
+
synthesizedAssessment: {
|
|
1367
|
+
priorityAssessment: maintenanceReport.payload.priorityAssessment,
|
|
1368
|
+
recommendedActions: maintenanceReport.payload.recommendedActions,
|
|
1369
|
+
routingRecommendation: maintenanceReport.payload.routingRecommendation,
|
|
1370
|
+
followUpWorkflowRefs: maintenanceReport.payload.followUpWorkflowRefs,
|
|
1371
|
+
risks: maintenanceReport.payload.risks,
|
|
1372
|
+
followUpIssues: maintenanceReport.payload.followUpIssues
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
const incidentAnalystAgent = {
|
|
1379
|
+
manifest: agentManifestSchema.parse({
|
|
1380
|
+
version: 1,
|
|
1381
|
+
name: "incident-analyst",
|
|
1382
|
+
displayName: "Incident Analyst",
|
|
1383
|
+
category: "operate",
|
|
1384
|
+
runtime: {
|
|
1385
|
+
minVersion: "0.1.0",
|
|
1386
|
+
kind: "reasoning"
|
|
1387
|
+
},
|
|
1388
|
+
permissions: {
|
|
1389
|
+
model: true,
|
|
1390
|
+
network: false,
|
|
1391
|
+
tools: [],
|
|
1392
|
+
readPaths: ["**/*"],
|
|
1393
|
+
writePaths: []
|
|
1394
|
+
},
|
|
1395
|
+
inputs: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
1396
|
+
outputs: ["lifecycleArtifacts"],
|
|
1397
|
+
contextPolicy: {
|
|
1398
|
+
sections: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
1399
|
+
minimalContext: true
|
|
1400
|
+
},
|
|
1401
|
+
catalog: {
|
|
1402
|
+
domain: "operate",
|
|
1403
|
+
supportLevel: "internal",
|
|
1404
|
+
maturity: "mvp",
|
|
1405
|
+
trustScope: "official-core-only"
|
|
1406
|
+
},
|
|
1407
|
+
trust: {
|
|
1408
|
+
tier: "core",
|
|
1409
|
+
source: "official",
|
|
1410
|
+
reviewed: true
|
|
1411
|
+
}
|
|
1412
|
+
}),
|
|
1413
|
+
outputSchema: agentOutputSchema,
|
|
1414
|
+
async execute({ state, stateSlice }) {
|
|
1415
|
+
const incidentRequest = getWorkflowInput(stateSlice, "incidentRequest");
|
|
1416
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
1417
|
+
const incidentIssueRefs = getWorkflowInput(stateSlice, "incidentIssueRefs") ?? [];
|
|
1418
|
+
const incidentGithubRefs = getWorkflowInput(stateSlice, "incidentGithubRefs") ?? [];
|
|
1419
|
+
if (!incidentRequest) {
|
|
1420
|
+
throw new Error("incident-handoff requires validated incident inputs before incident analysis.");
|
|
1421
|
+
}
|
|
1422
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1423
|
+
const evidenceMetadata = incidentEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
|
|
1424
|
+
const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
|
|
1425
|
+
const evidenceSources = normalizedEvidence?.normalizedEvidenceSources && normalizedEvidence.normalizedEvidenceSources.length > 0
|
|
1426
|
+
? normalizedEvidence.normalizedEvidenceSources
|
|
1427
|
+
: asStringArray(intakeMetadata.evidenceSources).length > 0
|
|
1428
|
+
? [
|
|
1429
|
+
...new Set([
|
|
1430
|
+
...asStringArray(intakeMetadata.evidenceSources),
|
|
1431
|
+
...asStringArray(intakeMetadata.releaseReportRefs)
|
|
1432
|
+
])
|
|
1433
|
+
]
|
|
1434
|
+
: [...new Set([...incidentRequest.evidenceSources, ...incidentRequest.releaseReportRefs])];
|
|
1435
|
+
const severityHint = normalizedEvidence?.severityHint ??
|
|
1436
|
+
(typeof intakeMetadata.severityHint === "string" ? intakeMetadata.severityHint : incidentRequest.severityHint);
|
|
1437
|
+
const constraints = asStringArray(intakeMetadata.constraints);
|
|
1438
|
+
const followUpWorkflowRefs = normalizedEvidence?.followUpWorkflowRefs && normalizedEvidence.followUpWorkflowRefs.length > 0
|
|
1439
|
+
? normalizedEvidence.followUpWorkflowRefs
|
|
1440
|
+
: [
|
|
1441
|
+
"maintenance-triage",
|
|
1442
|
+
...(incidentRequest.releaseReportRefs.length > 0 ? ["release-readiness"] : []),
|
|
1443
|
+
...(severityHint === "high" || severityHint === "critical" ? ["security-review"] : [])
|
|
1444
|
+
];
|
|
1445
|
+
const likelyImpactedAreas = normalizedEvidence?.likelyImpactedAreas && normalizedEvidence.likelyImpactedAreas.length > 0
|
|
1446
|
+
? normalizedEvidence.likelyImpactedAreas
|
|
1447
|
+
: [
|
|
1448
|
+
...(incidentRequest.releaseReportRefs.length > 0 ? ["release-readiness"] : []),
|
|
1449
|
+
...(incidentRequest.evidenceSources.length > 0 ? ["staged-operational-evidence"] : []),
|
|
1450
|
+
...(severityHint === "high" || severityHint === "critical" ? ["security-follow-up"] : [])
|
|
1451
|
+
];
|
|
1452
|
+
const openQuestions = [
|
|
1453
|
+
...(incidentRequest.issueRefs.length === 0 ? ["Should this incident be linked to a tracked issue before escalation?"] : []),
|
|
1454
|
+
...(incidentRequest.releaseReportRefs.length === 0
|
|
1455
|
+
? ["Is there a release-report bundle that should be attached for additional provenance?"]
|
|
1456
|
+
: [])
|
|
1457
|
+
];
|
|
1458
|
+
const summary = `Incident brief prepared for ${incidentRequest.incidentSummary}.`;
|
|
1459
|
+
const incidentBrief = incidentArtifactSchema.parse({
|
|
1460
|
+
...buildLifecycleArtifactEnvelopeBase(state, "Incident Handoff", summary, [requestFile ?? ".agentops/requests/incident.yaml", ...evidenceSources], incidentIssueRefs, incidentGithubRefs),
|
|
1461
|
+
artifactKind: "incident-brief",
|
|
1462
|
+
lifecycleDomain: "operate",
|
|
1463
|
+
redaction: {
|
|
1464
|
+
applied: true,
|
|
1465
|
+
strategyVersion: "1.0.0",
|
|
1466
|
+
categories: normalizedEvidence?.redactionCategories ?? [
|
|
1467
|
+
"github-token",
|
|
1468
|
+
"api-key",
|
|
1469
|
+
"aws-key",
|
|
1470
|
+
"bearer-token",
|
|
1471
|
+
"password",
|
|
1472
|
+
"private-key",
|
|
1473
|
+
"operational-sensitive"
|
|
1474
|
+
]
|
|
1475
|
+
},
|
|
1476
|
+
payload: {
|
|
1477
|
+
incidentSummary: incidentRequest.incidentSummary,
|
|
1478
|
+
evidenceSources,
|
|
1479
|
+
timelineSummary: normalizedEvidence?.timelineSummary ?? [
|
|
1480
|
+
`Severity hint: ${severityHint}.`,
|
|
1481
|
+
`Validated ${incidentRequest.evidenceSources.length} staged evidence source(s) and ${incidentRequest.releaseReportRefs.length} release-report reference(s) before reasoning.`
|
|
1482
|
+
],
|
|
1483
|
+
likelyImpactedAreas: likelyImpactedAreas.length > 0
|
|
1484
|
+
? likelyImpactedAreas
|
|
1485
|
+
: ["manual incident triage is still required to identify impacted repository areas."],
|
|
1486
|
+
followUpWorkflowRefs: [...new Set(followUpWorkflowRefs)],
|
|
1487
|
+
openQuestions
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
return agentOutputSchema.parse({
|
|
1491
|
+
summary,
|
|
1492
|
+
findings: [],
|
|
1493
|
+
proposedActions: [],
|
|
1494
|
+
lifecycleArtifacts: [incidentBrief],
|
|
1495
|
+
requestedTools: [],
|
|
1496
|
+
blockedActionFlags: [],
|
|
1497
|
+
confidence: severityHint === "critical" ? 0.7 : 0.74,
|
|
1498
|
+
metadata: {
|
|
1499
|
+
deterministicInputs: {
|
|
1500
|
+
severityHint,
|
|
1501
|
+
evidenceSources,
|
|
1502
|
+
issueRefs: incidentIssueRefs,
|
|
1503
|
+
constraints,
|
|
1504
|
+
normalizedEvidence: normalizedEvidence ?? null
|
|
1505
|
+
},
|
|
1506
|
+
synthesizedAssessment: {
|
|
1507
|
+
likelyImpactedAreas: incidentBrief.payload.likelyImpactedAreas,
|
|
1508
|
+
followUpWorkflowRefs: incidentBrief.payload.followUpWorkflowRefs,
|
|
1509
|
+
openQuestions: incidentBrief.payload.openQuestions
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
const releaseIntakeAgent = {
|
|
1516
|
+
manifest: agentManifestSchema.parse({
|
|
1517
|
+
version: 1,
|
|
1518
|
+
name: "release-intake",
|
|
1519
|
+
displayName: "Release Intake",
|
|
1520
|
+
category: "release",
|
|
1521
|
+
runtime: {
|
|
1522
|
+
minVersion: "0.1.0",
|
|
1523
|
+
kind: "deterministic"
|
|
1524
|
+
},
|
|
1525
|
+
permissions: {
|
|
1526
|
+
model: false,
|
|
1527
|
+
network: false,
|
|
1528
|
+
tools: [],
|
|
1529
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**"],
|
|
1530
|
+
writePaths: []
|
|
1531
|
+
},
|
|
1532
|
+
inputs: ["workflowInputs", "repo"],
|
|
1533
|
+
outputs: ["summary", "metadata"],
|
|
1534
|
+
contextPolicy: {
|
|
1535
|
+
sections: ["workflowInputs", "repo", "context"],
|
|
1536
|
+
minimalContext: true
|
|
1537
|
+
},
|
|
1538
|
+
catalog: {
|
|
1539
|
+
domain: "release",
|
|
1540
|
+
supportLevel: "internal",
|
|
1541
|
+
maturity: "mvp",
|
|
1542
|
+
trustScope: "official-core-only"
|
|
1543
|
+
},
|
|
1544
|
+
trust: {
|
|
1545
|
+
tier: "core",
|
|
1546
|
+
source: "official",
|
|
1547
|
+
reviewed: true
|
|
1548
|
+
}
|
|
1549
|
+
}),
|
|
1550
|
+
outputSchema: agentOutputSchema,
|
|
1551
|
+
async execute({ stateSlice }) {
|
|
1552
|
+
const releaseRequest = getWorkflowInput(stateSlice, "releaseRequest");
|
|
1553
|
+
const releaseIssueRefs = getWorkflowInput(stateSlice, "releaseIssueRefs") ?? [];
|
|
1554
|
+
const releaseGithubRefs = getWorkflowInput(stateSlice, "releaseGithubRefs") ?? [];
|
|
1555
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
1556
|
+
if (!releaseRequest) {
|
|
1557
|
+
throw new Error("release-readiness requires a validated release request before runtime execution.");
|
|
1558
|
+
}
|
|
1559
|
+
return agentOutputSchema.parse({
|
|
1560
|
+
summary: `Loaded release request from ${requestFile ?? ".agentops/requests/release.yaml"} for ${releaseRequest.releaseScope}.`,
|
|
1561
|
+
findings: [],
|
|
1562
|
+
proposedActions: [],
|
|
1563
|
+
lifecycleArtifacts: [],
|
|
1564
|
+
requestedTools: [],
|
|
1565
|
+
blockedActionFlags: [],
|
|
1566
|
+
metadata: {
|
|
1567
|
+
...releaseRequestSchema.parse(releaseRequest),
|
|
1568
|
+
releaseIssueRefs,
|
|
1569
|
+
releaseGithubRefs,
|
|
1570
|
+
evidenceSourceCount: releaseRequest.qaReportRefs.length + releaseRequest.securityReportRefs.length + releaseRequest.evidenceSources.length
|
|
1571
|
+
}
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
};
|
|
1575
|
+
const releaseEvidenceNormalizationAgent = {
|
|
1576
|
+
manifest: agentManifestSchema.parse({
|
|
1577
|
+
version: 1,
|
|
1578
|
+
name: "release-evidence-normalizer",
|
|
1579
|
+
displayName: "Release Evidence Normalizer",
|
|
1580
|
+
category: "release",
|
|
1581
|
+
runtime: {
|
|
1582
|
+
minVersion: "0.1.0",
|
|
1583
|
+
kind: "deterministic"
|
|
1584
|
+
},
|
|
1585
|
+
permissions: {
|
|
1586
|
+
model: false,
|
|
1587
|
+
network: false,
|
|
1588
|
+
tools: [],
|
|
1589
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md", "**/package.json"],
|
|
1590
|
+
writePaths: []
|
|
1591
|
+
},
|
|
1592
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
1593
|
+
outputs: ["summary", "metadata"],
|
|
1594
|
+
contextPolicy: {
|
|
1595
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
1596
|
+
minimalContext: true
|
|
1597
|
+
},
|
|
1598
|
+
catalog: {
|
|
1599
|
+
domain: "release",
|
|
1600
|
+
supportLevel: "internal",
|
|
1601
|
+
maturity: "mvp",
|
|
1602
|
+
trustScope: "official-core-only"
|
|
1603
|
+
},
|
|
1604
|
+
trust: {
|
|
1605
|
+
tier: "core",
|
|
1606
|
+
source: "official",
|
|
1607
|
+
reviewed: true
|
|
1608
|
+
}
|
|
1609
|
+
}),
|
|
1610
|
+
outputSchema: agentOutputSchema,
|
|
1611
|
+
async execute({ stateSlice }) {
|
|
1612
|
+
const releaseRequest = getWorkflowInput(stateSlice, "releaseRequest");
|
|
1613
|
+
if (!releaseRequest) {
|
|
1614
|
+
throw new Error("release-readiness requires validated release request inputs before evidence normalization.");
|
|
1615
|
+
}
|
|
1616
|
+
const repoRoot = stateSlice.repo?.root;
|
|
1617
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1618
|
+
const qaReportRefs = asStringArray(intakeMetadata.qaReportRefs).length > 0
|
|
1619
|
+
? asStringArray(intakeMetadata.qaReportRefs)
|
|
1620
|
+
: releaseRequest.qaReportRefs;
|
|
1621
|
+
const securityReportRefs = asStringArray(intakeMetadata.securityReportRefs).length > 0
|
|
1622
|
+
? asStringArray(intakeMetadata.securityReportRefs)
|
|
1623
|
+
: releaseRequest.securityReportRefs;
|
|
1624
|
+
const evidenceSources = asStringArray(intakeMetadata.evidenceSources).length > 0
|
|
1625
|
+
? asStringArray(intakeMetadata.evidenceSources)
|
|
1626
|
+
: releaseRequest.evidenceSources;
|
|
1627
|
+
const normalizedEvidenceSources = [...new Set([...qaReportRefs, ...securityReportRefs, ...evidenceSources])];
|
|
1628
|
+
const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
|
|
1629
|
+
if (missingEvidenceSources.length > 0) {
|
|
1630
|
+
throw new Error(`Release evidence source not found: ${missingEvidenceSources[0]}`);
|
|
1631
|
+
}
|
|
1632
|
+
const versionResolutions = releaseRequest.versionTargets.map((target) => {
|
|
1633
|
+
const resolved = resolveWorkspacePackage(repoRoot, target.name);
|
|
1634
|
+
return {
|
|
1635
|
+
name: target.name,
|
|
1636
|
+
targetVersion: target.version,
|
|
1637
|
+
currentVersion: resolved.currentVersion,
|
|
1638
|
+
status: !resolved.currentVersion
|
|
1639
|
+
? "package-missing"
|
|
1640
|
+
: resolved.currentVersion === target.version
|
|
1641
|
+
? "matches-target"
|
|
1642
|
+
: "pending-version-bump",
|
|
1643
|
+
manifestPath: resolved.manifestPath
|
|
1644
|
+
};
|
|
1645
|
+
});
|
|
1646
|
+
const missingPackages = versionResolutions.filter((entry) => entry.status === "package-missing").map((entry) => entry.name);
|
|
1647
|
+
const versionCheckStatus = missingPackages.length === 0 ? "passed" : "failed";
|
|
1648
|
+
const baseReadinessStatus = qaReportRefs.length > 0 && securityReportRefs.length > 0
|
|
1649
|
+
? "ready"
|
|
1650
|
+
: qaReportRefs.length > 0 || securityReportRefs.length > 0
|
|
1651
|
+
? "partial"
|
|
1652
|
+
: "blocked";
|
|
1653
|
+
const readinessStatus = missingPackages.length > 0 ? "blocked" : baseReadinessStatus;
|
|
1654
|
+
const localReadinessChecks = [
|
|
1655
|
+
{
|
|
1656
|
+
name: "qa-report-refs",
|
|
1657
|
+
status: qaReportRefs.length > 0 ? "passed" : "skipped",
|
|
1658
|
+
detail: qaReportRefs.length > 0
|
|
1659
|
+
? `Using ${qaReportRefs.length} validated QA report reference(s).`
|
|
1660
|
+
: "No QA report references were supplied."
|
|
1661
|
+
},
|
|
1662
|
+
{
|
|
1663
|
+
name: "security-report-refs",
|
|
1664
|
+
status: securityReportRefs.length > 0 ? "passed" : "skipped",
|
|
1665
|
+
detail: securityReportRefs.length > 0
|
|
1666
|
+
? `Using ${securityReportRefs.length} validated security report reference(s).`
|
|
1667
|
+
: "No security report references were supplied."
|
|
1668
|
+
},
|
|
1669
|
+
{
|
|
1670
|
+
name: "local-release-evidence",
|
|
1671
|
+
status: evidenceSources.length > 0 ? "passed" : "skipped",
|
|
1672
|
+
detail: evidenceSources.length > 0
|
|
1673
|
+
? `Using ${evidenceSources.length} bounded local release evidence source(s).`
|
|
1674
|
+
: "No additional local release evidence sources were supplied."
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
name: "workspace-version-targets",
|
|
1678
|
+
status: versionCheckStatus,
|
|
1679
|
+
detail: missingPackages.length === 0
|
|
1680
|
+
? `Resolved ${versionResolutions.length} workspace version target(s).`
|
|
1681
|
+
: `Missing workspace package metadata for: ${missingPackages.join(", ")}.`
|
|
1682
|
+
}
|
|
1683
|
+
];
|
|
1684
|
+
const approvalRecommendations = [
|
|
1685
|
+
{
|
|
1686
|
+
action: "publish-packages",
|
|
1687
|
+
classification: readinessStatus === "ready" ? "approval_required" : "deny",
|
|
1688
|
+
reason: readinessStatus === "ready"
|
|
1689
|
+
? "Package publication remains outside the default read-only workflow path and needs explicit release approval."
|
|
1690
|
+
: "Keep package publication blocked until bounded release evidence is complete and normalized."
|
|
1691
|
+
},
|
|
1692
|
+
{
|
|
1693
|
+
action: "create-release-tag",
|
|
1694
|
+
classification: readinessStatus === "ready" ? "approval_required" : "deny",
|
|
1695
|
+
reason: readinessStatus === "ready"
|
|
1696
|
+
? "Tag creation is a release-significant side effect and remains approval-gated."
|
|
1697
|
+
: "Do not create release tags while readiness remains partial or blocked."
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
action: "promote-release",
|
|
1701
|
+
classification: readinessStatus === "ready" ? "approval_required" : "deny",
|
|
1702
|
+
reason: readinessStatus === "ready"
|
|
1703
|
+
? "Promotion remains a release-significant side effect and requires explicit maintainer approval."
|
|
1704
|
+
: "Keep release promotion blocked until bounded QA and security evidence is complete."
|
|
1705
|
+
}
|
|
1706
|
+
];
|
|
1707
|
+
const provenanceRefs = [
|
|
1708
|
+
...normalizedEvidenceSources,
|
|
1709
|
+
...versionResolutions
|
|
1710
|
+
.map((entry) => entry.manifestPath)
|
|
1711
|
+
.filter((value) => Boolean(value))
|
|
1712
|
+
];
|
|
1713
|
+
const normalization = releaseEvidenceNormalizationSchema.parse({
|
|
1714
|
+
qaReportRefs,
|
|
1715
|
+
securityReportRefs,
|
|
1716
|
+
normalizedEvidenceSources,
|
|
1717
|
+
missingEvidenceSources: [],
|
|
1718
|
+
versionResolutions: versionResolutions.map((entry) => ({
|
|
1719
|
+
name: entry.name,
|
|
1720
|
+
targetVersion: entry.targetVersion,
|
|
1721
|
+
currentVersion: entry.currentVersion,
|
|
1722
|
+
status: entry.status
|
|
1723
|
+
})),
|
|
1724
|
+
localReadinessChecks,
|
|
1725
|
+
readinessStatus,
|
|
1726
|
+
approvalRecommendations,
|
|
1727
|
+
provenanceRefs: [...new Set(provenanceRefs)]
|
|
1728
|
+
});
|
|
1729
|
+
return agentOutputSchema.parse({
|
|
1730
|
+
summary: `Normalized release evidence across ${normalization.normalizedEvidenceSources.length} source(s) and ${normalization.versionResolutions.length} version target(s).`,
|
|
1731
|
+
findings: [],
|
|
1732
|
+
proposedActions: [],
|
|
1733
|
+
lifecycleArtifacts: [],
|
|
1734
|
+
requestedTools: [],
|
|
1735
|
+
blockedActionFlags: [],
|
|
1736
|
+
metadata: normalization
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
};
|
|
1740
|
+
const releaseAnalystAgent = {
|
|
1741
|
+
manifest: agentManifestSchema.parse({
|
|
1742
|
+
version: 1,
|
|
1743
|
+
name: "release-analyst",
|
|
1744
|
+
displayName: "Release Analyst",
|
|
1745
|
+
category: "release",
|
|
1746
|
+
runtime: {
|
|
1747
|
+
minVersion: "0.1.0",
|
|
1748
|
+
kind: "reasoning"
|
|
1749
|
+
},
|
|
1750
|
+
permissions: {
|
|
1751
|
+
model: true,
|
|
1752
|
+
network: false,
|
|
1753
|
+
tools: [],
|
|
1754
|
+
readPaths: ["**/*"],
|
|
1755
|
+
writePaths: []
|
|
1756
|
+
},
|
|
1757
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
1758
|
+
outputs: ["lifecycleArtifacts"],
|
|
1759
|
+
contextPolicy: {
|
|
1760
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
1761
|
+
minimalContext: true
|
|
1762
|
+
},
|
|
1763
|
+
catalog: {
|
|
1764
|
+
domain: "release",
|
|
1765
|
+
supportLevel: "internal",
|
|
1766
|
+
maturity: "mvp",
|
|
1767
|
+
trustScope: "official-core-only"
|
|
1768
|
+
},
|
|
1769
|
+
trust: {
|
|
1770
|
+
tier: "core",
|
|
1771
|
+
source: "official",
|
|
1772
|
+
reviewed: true
|
|
1773
|
+
}
|
|
1774
|
+
}),
|
|
1775
|
+
outputSchema: agentOutputSchema,
|
|
1776
|
+
async execute({ state, stateSlice }) {
|
|
1777
|
+
const releaseRequest = getWorkflowInput(stateSlice, "releaseRequest");
|
|
1778
|
+
const releaseIssueRefs = getWorkflowInput(stateSlice, "releaseIssueRefs") ?? [];
|
|
1779
|
+
const releaseGithubRefs = getWorkflowInput(stateSlice, "releaseGithubRefs") ?? [];
|
|
1780
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
1781
|
+
if (!releaseRequest) {
|
|
1782
|
+
throw new Error("release-readiness requires validated release inputs before release analysis.");
|
|
1783
|
+
}
|
|
1784
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1785
|
+
const evidenceMetadata = releaseEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
|
|
1786
|
+
const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
|
|
1787
|
+
const qaReportRefs = normalizedEvidence?.qaReportRefs && normalizedEvidence.qaReportRefs.length > 0
|
|
1788
|
+
? normalizedEvidence.qaReportRefs
|
|
1789
|
+
: asStringArray(intakeMetadata.qaReportRefs).length > 0
|
|
1790
|
+
? asStringArray(intakeMetadata.qaReportRefs)
|
|
1791
|
+
: releaseRequest.qaReportRefs;
|
|
1792
|
+
const securityReportRefs = normalizedEvidence?.securityReportRefs && normalizedEvidence.securityReportRefs.length > 0
|
|
1793
|
+
? normalizedEvidence.securityReportRefs
|
|
1794
|
+
: asStringArray(intakeMetadata.securityReportRefs).length > 0
|
|
1795
|
+
? asStringArray(intakeMetadata.securityReportRefs)
|
|
1796
|
+
: releaseRequest.securityReportRefs;
|
|
1797
|
+
const evidenceSources = normalizedEvidence?.normalizedEvidenceSources && normalizedEvidence.normalizedEvidenceSources.length > 0
|
|
1798
|
+
? normalizedEvidence.normalizedEvidenceSources
|
|
1799
|
+
: asStringArray(intakeMetadata.evidenceSources).length > 0
|
|
1800
|
+
? asStringArray(intakeMetadata.evidenceSources)
|
|
1801
|
+
: releaseRequest.evidenceSources;
|
|
1802
|
+
const constraints = asStringArray(intakeMetadata.constraints);
|
|
1803
|
+
const allEvidenceRefs = [...new Set([...qaReportRefs, ...securityReportRefs, ...evidenceSources])];
|
|
1804
|
+
const versionResolutions = normalizedEvidence?.versionResolutions ?? [];
|
|
1805
|
+
const verificationChecks = normalizedEvidence?.localReadinessChecks ?? [
|
|
1806
|
+
{
|
|
1807
|
+
name: "qa-report-refs",
|
|
1808
|
+
status: qaReportRefs.length > 0 ? "passed" : "skipped",
|
|
1809
|
+
detail: qaReportRefs.length > 0
|
|
1810
|
+
? `Using ${qaReportRefs.length} validated QA report reference(s).`
|
|
1811
|
+
: "No QA report references were supplied."
|
|
1812
|
+
},
|
|
1813
|
+
{
|
|
1814
|
+
name: "security-report-refs",
|
|
1815
|
+
status: securityReportRefs.length > 0 ? "passed" : "skipped",
|
|
1816
|
+
detail: securityReportRefs.length > 0
|
|
1817
|
+
? `Using ${securityReportRefs.length} validated security report reference(s).`
|
|
1818
|
+
: "No security report references were supplied."
|
|
1819
|
+
},
|
|
1820
|
+
{
|
|
1821
|
+
name: "local-release-evidence",
|
|
1822
|
+
status: evidenceSources.length > 0 ? "passed" : "skipped",
|
|
1823
|
+
detail: evidenceSources.length > 0
|
|
1824
|
+
? `Using ${evidenceSources.length} bounded local release evidence source(s).`
|
|
1825
|
+
: "No additional local release evidence sources were supplied."
|
|
1826
|
+
}
|
|
1827
|
+
];
|
|
1828
|
+
const readinessStatus = normalizedEvidence?.readinessStatus ??
|
|
1829
|
+
(qaReportRefs.length > 0 && securityReportRefs.length > 0
|
|
1830
|
+
? "ready"
|
|
1831
|
+
: qaReportRefs.length > 0 || securityReportRefs.length > 0
|
|
1832
|
+
? "partial"
|
|
1833
|
+
: "blocked");
|
|
1834
|
+
const approvalRecommendations = normalizedEvidence?.approvalRecommendations ?? [
|
|
1835
|
+
{
|
|
1836
|
+
action: "publish-packages",
|
|
1837
|
+
classification: readinessStatus === "ready" ? "approval_required" : "deny",
|
|
1838
|
+
reason: readinessStatus === "ready"
|
|
1839
|
+
? "Package publication remains outside the default read-only workflow path and needs explicit release approval."
|
|
1840
|
+
: "Keep package publication blocked until bounded release evidence is complete and normalized."
|
|
1841
|
+
}
|
|
1842
|
+
];
|
|
1843
|
+
const summary = `Release report prepared for ${releaseRequest.releaseScope}.`;
|
|
1844
|
+
const publishingPlan = [
|
|
1845
|
+
...(versionResolutions.length > 0
|
|
1846
|
+
? [`Resolved ${versionResolutions.length} workspace version target(s) before any publish or promotion step.`]
|
|
1847
|
+
: []),
|
|
1848
|
+
"Review the bounded QA and security evidence before invoking any publish or promotion step.",
|
|
1849
|
+
"Run `agentforge release check --json` and `agentforge release verify --json` before any release cut.",
|
|
1850
|
+
...approvalRecommendations.map((recommendation) => `${recommendation.action}: ${recommendation.classification.replaceAll("_", " ")} (${recommendation.reason})`),
|
|
1851
|
+
"Keep trusted publishing and tag or publish actions outside this default read-only workflow path."
|
|
1852
|
+
];
|
|
1853
|
+
const rollbackNotes = [
|
|
1854
|
+
"Use the release report to decide whether to pause or defer promotion before any publish step.",
|
|
1855
|
+
"If readiness remains partial or blocked, keep the current version set unchanged and resolve evidence gaps first."
|
|
1856
|
+
];
|
|
1857
|
+
const externalDependencies = [
|
|
1858
|
+
...(qaReportRefs.length > 0 ? ["Validated QA report inputs remain available for reviewer inspection."] : []),
|
|
1859
|
+
...(securityReportRefs.length > 0 ? ["Validated security report inputs remain available for reviewer inspection."] : [])
|
|
1860
|
+
];
|
|
1861
|
+
const releaseReport = releaseArtifactSchema.parse({
|
|
1862
|
+
...buildLifecycleArtifactEnvelopeBase(state, "Release Readiness", summary, [requestFile ?? ".agentops/requests/release.yaml", ...allEvidenceRefs], releaseIssueRefs, releaseGithubRefs),
|
|
1863
|
+
artifactKind: "release-report",
|
|
1864
|
+
lifecycleDomain: "release",
|
|
1865
|
+
payload: {
|
|
1866
|
+
releaseScope: releaseRequest.releaseScope,
|
|
1867
|
+
versionTargets: releaseRequest.versionTargets,
|
|
1868
|
+
readinessStatus,
|
|
1869
|
+
verificationChecks: verificationChecks.map((check) => ({ ...check })),
|
|
1870
|
+
versionResolutions,
|
|
1871
|
+
approvalRecommendations: approvalRecommendations.map((recommendation) => releaseApprovalRecommendationSchema.parse(recommendation)),
|
|
1872
|
+
publishingPlan,
|
|
1873
|
+
trustStatus: "trusted-publishing-reviewed-separately",
|
|
1874
|
+
publishedPackages: [],
|
|
1875
|
+
tagRefs: [],
|
|
1876
|
+
provenanceRefs: allEvidenceRefs,
|
|
1877
|
+
rollbackNotes,
|
|
1878
|
+
externalDependencies
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
return agentOutputSchema.parse({
|
|
1882
|
+
summary,
|
|
1883
|
+
findings: [],
|
|
1884
|
+
proposedActions: [],
|
|
1885
|
+
lifecycleArtifacts: [releaseReport],
|
|
1886
|
+
requestedTools: [],
|
|
1887
|
+
blockedActionFlags: [],
|
|
1888
|
+
confidence: 0.77,
|
|
1889
|
+
metadata: {
|
|
1890
|
+
deterministicInputs: {
|
|
1891
|
+
versionTargets: releaseRequest.versionTargets,
|
|
1892
|
+
qaReportRefs,
|
|
1893
|
+
securityReportRefs,
|
|
1894
|
+
evidenceSources,
|
|
1895
|
+
constraints,
|
|
1896
|
+
normalizedEvidence: normalizedEvidence ?? null
|
|
1897
|
+
},
|
|
1898
|
+
synthesizedAssessment: {
|
|
1899
|
+
readinessStatus,
|
|
1900
|
+
approvalRecommendations,
|
|
1901
|
+
publishingPlan,
|
|
1902
|
+
rollbackNotes
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1908
|
+
const securityEvidenceNormalizationAgent = {
|
|
1909
|
+
manifest: agentManifestSchema.parse({
|
|
1910
|
+
version: 1,
|
|
1911
|
+
name: "security-evidence-normalizer",
|
|
1912
|
+
displayName: "Security Evidence Normalizer",
|
|
1913
|
+
category: "security",
|
|
1914
|
+
runtime: {
|
|
1915
|
+
minVersion: "0.1.0",
|
|
1916
|
+
kind: "deterministic"
|
|
1917
|
+
},
|
|
1918
|
+
permissions: {
|
|
1919
|
+
model: false,
|
|
1920
|
+
network: false,
|
|
1921
|
+
tools: [],
|
|
1922
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md", "**/package.json"],
|
|
1923
|
+
writePaths: []
|
|
1924
|
+
},
|
|
1925
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
1926
|
+
outputs: ["summary", "metadata"],
|
|
1927
|
+
contextPolicy: {
|
|
1928
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
1929
|
+
minimalContext: true
|
|
1930
|
+
},
|
|
1931
|
+
catalog: {
|
|
1932
|
+
domain: "security",
|
|
1933
|
+
supportLevel: "internal",
|
|
1934
|
+
maturity: "mvp",
|
|
1935
|
+
trustScope: "official-core-only"
|
|
1936
|
+
},
|
|
1937
|
+
trust: {
|
|
1938
|
+
tier: "core",
|
|
1939
|
+
source: "official",
|
|
1940
|
+
reviewed: true
|
|
1941
|
+
}
|
|
1942
|
+
}),
|
|
1943
|
+
outputSchema: agentOutputSchema,
|
|
1944
|
+
async execute({ stateSlice }) {
|
|
1945
|
+
const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
|
|
1946
|
+
if (!securityRequest) {
|
|
1947
|
+
throw new Error("security-review requires validated security request inputs before evidence normalization.");
|
|
1948
|
+
}
|
|
1949
|
+
const repoRoot = stateSlice.repo?.root;
|
|
1950
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
1951
|
+
const targetType = typeof intakeMetadata.targetType === "string" && intakeMetadata.targetType === "artifact-bundle"
|
|
1952
|
+
? "artifact-bundle"
|
|
1953
|
+
: "local-reference";
|
|
1954
|
+
const targetPath = repoRoot ? join(repoRoot, securityRequest.targetRef) : securityRequest.targetRef;
|
|
1955
|
+
if (repoRoot && !existsSync(targetPath)) {
|
|
1956
|
+
throw new Error(`Security target reference not found: ${securityRequest.targetRef}`);
|
|
1957
|
+
}
|
|
1958
|
+
const referencedArtifactKinds = targetType === "artifact-bundle" ? loadBundleArtifactKinds(targetPath) : [];
|
|
1959
|
+
const normalizedEvidenceSources = [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])];
|
|
1960
|
+
const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
|
|
1961
|
+
if (missingEvidenceSources.length > 0) {
|
|
1962
|
+
throw new Error(`Security evidence source not found: ${missingEvidenceSources[0]}`);
|
|
1963
|
+
}
|
|
1964
|
+
const normalizedFocusAreas = securityRequest.focusAreas.length > 0 ? [...new Set(securityRequest.focusAreas)] : ["general-review"];
|
|
1965
|
+
const affectedPackages = targetType === "artifact-bundle"
|
|
1966
|
+
? [...new Set(loadBundleArtifactPayloadPaths(targetPath).map(derivePackageScope).filter((value) => Boolean(value)))]
|
|
1967
|
+
: [];
|
|
1968
|
+
const securitySignals = [
|
|
1969
|
+
...(referencedArtifactKinds.length > 0 ? [`Referenced artifact kinds: ${referencedArtifactKinds.join(", ")}`] : []),
|
|
1970
|
+
...(affectedPackages.length > 0 ? [`Affected packages inferred from bounded artifact payloads: ${affectedPackages.join(", ")}`] : []),
|
|
1971
|
+
...(normalizedFocusAreas.length > 0 ? [`Requested focus areas: ${normalizedFocusAreas.join(", ")}`] : []),
|
|
1972
|
+
"Security evidence collection remains local, read-only, and bounded to validated references."
|
|
1973
|
+
];
|
|
1974
|
+
const provenanceRefs = [
|
|
1975
|
+
securityRequest.targetRef,
|
|
1976
|
+
...securityRequest.evidenceSources,
|
|
1977
|
+
...referencedArtifactKinds.map((artifactKind) => `${securityRequest.targetRef}#${artifactKind}`)
|
|
1978
|
+
];
|
|
1979
|
+
const normalization = securityEvidenceNormalizationSchema.parse({
|
|
1980
|
+
targetRef: securityRequest.targetRef,
|
|
1981
|
+
targetType,
|
|
1982
|
+
referencedArtifactKinds,
|
|
1983
|
+
normalizedEvidenceSources,
|
|
1984
|
+
missingEvidenceSources: [],
|
|
1985
|
+
normalizedFocusAreas,
|
|
1986
|
+
securitySignals,
|
|
1987
|
+
provenanceRefs: [...new Set(provenanceRefs)],
|
|
1988
|
+
affectedPackages
|
|
1989
|
+
});
|
|
1990
|
+
return agentOutputSchema.parse({
|
|
1991
|
+
summary: `Normalized security evidence for ${securityRequest.targetRef}.`,
|
|
1992
|
+
findings: [],
|
|
1993
|
+
proposedActions: [],
|
|
1994
|
+
lifecycleArtifacts: [],
|
|
1995
|
+
requestedTools: [],
|
|
1996
|
+
blockedActionFlags: [],
|
|
1997
|
+
metadata: normalization
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
};
|
|
2001
|
+
const securityAnalystAgent = {
|
|
2002
|
+
manifest: agentManifestSchema.parse({
|
|
2003
|
+
version: 1,
|
|
2004
|
+
name: "security-analyst",
|
|
2005
|
+
displayName: "Security Analyst",
|
|
2006
|
+
category: "security",
|
|
2007
|
+
runtime: {
|
|
2008
|
+
minVersion: "0.1.0",
|
|
2009
|
+
kind: "reasoning"
|
|
2010
|
+
},
|
|
2011
|
+
permissions: {
|
|
2012
|
+
model: true,
|
|
2013
|
+
network: false,
|
|
2014
|
+
tools: [],
|
|
2015
|
+
readPaths: ["**/*"],
|
|
2016
|
+
writePaths: []
|
|
2017
|
+
},
|
|
2018
|
+
inputs: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
2019
|
+
outputs: ["lifecycleArtifacts"],
|
|
2020
|
+
contextPolicy: {
|
|
2021
|
+
sections: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
2022
|
+
minimalContext: true
|
|
2023
|
+
},
|
|
2024
|
+
catalog: {
|
|
2025
|
+
domain: "security",
|
|
2026
|
+
supportLevel: "internal",
|
|
2027
|
+
maturity: "mvp",
|
|
2028
|
+
trustScope: "official-core-only"
|
|
2029
|
+
},
|
|
2030
|
+
trust: {
|
|
2031
|
+
tier: "core",
|
|
2032
|
+
source: "official",
|
|
2033
|
+
reviewed: true
|
|
2034
|
+
}
|
|
2035
|
+
}),
|
|
2036
|
+
outputSchema: agentOutputSchema,
|
|
2037
|
+
async execute({ state, stateSlice }) {
|
|
2038
|
+
const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
|
|
2039
|
+
const securityIssueRefs = getWorkflowInput(stateSlice, "securityIssueRefs") ?? [];
|
|
2040
|
+
const securityGithubRefs = getWorkflowInput(stateSlice, "securityGithubRefs") ?? [];
|
|
2041
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
2042
|
+
if (!securityRequest) {
|
|
2043
|
+
throw new Error("security-review requires validated security inputs before security analysis.");
|
|
2044
|
+
}
|
|
2045
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
2046
|
+
const evidenceMetadata = securityEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
|
|
2047
|
+
const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
|
|
2048
|
+
const referencedArtifactKinds = normalizedEvidence?.referencedArtifactKinds ?? asStringArray(intakeMetadata.referencedArtifactKinds);
|
|
2049
|
+
const normalizedFocusAreas = normalizedEvidence?.normalizedFocusAreas ?? asStringArray(intakeMetadata.focusAreas);
|
|
2050
|
+
const normalizedConstraints = asStringArray(intakeMetadata.constraints);
|
|
2051
|
+
const evidenceSources = normalizedEvidence?.normalizedEvidenceSources && normalizedEvidence.normalizedEvidenceSources.length > 0
|
|
2052
|
+
? normalizedEvidence.normalizedEvidenceSources
|
|
2053
|
+
: asStringArray(intakeMetadata.evidenceSources).length > 0
|
|
2054
|
+
? asStringArray(intakeMetadata.evidenceSources)
|
|
2055
|
+
: [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])];
|
|
2056
|
+
const focusAreas = normalizedFocusAreas.length > 0 ? normalizedFocusAreas : securityRequest.focusAreas;
|
|
2057
|
+
const inferredSeverity = securityRequest.releaseContext === "blocking" ? "high" : securityRequest.releaseContext === "candidate" ? "medium" : "low";
|
|
2058
|
+
const findings = focusAreas.length > 0
|
|
2059
|
+
? focusAreas.map((focusArea, index) => ({
|
|
2060
|
+
id: `security-finding-${index + 1}`,
|
|
2061
|
+
title: `Inspect ${focusArea} evidence before promotion`,
|
|
2062
|
+
summary: `Security review flagged ${focusArea} for bounded follow-up on ${securityRequest.targetRef}.`,
|
|
2063
|
+
severity: inferredSeverity,
|
|
2064
|
+
rationale: "The MVP security workflow synthesizes a structured report from validated references before deterministic evidence normalization lands.",
|
|
2065
|
+
confidence: 0.76,
|
|
2066
|
+
location: securityRequest.targetRef,
|
|
2067
|
+
tags: ["security", focusArea]
|
|
2068
|
+
}))
|
|
2069
|
+
: [
|
|
2070
|
+
{
|
|
2071
|
+
id: "security-finding-1",
|
|
2072
|
+
title: "Inspect referenced security evidence before promotion",
|
|
2073
|
+
summary: `Security review requires bounded interpretation of the referenced evidence for ${securityRequest.targetRef}.`,
|
|
2074
|
+
severity: inferredSeverity,
|
|
2075
|
+
rationale: "The current security workflow is read-only and request-driven, so findings remain tied to validated local references rather than automatic scanning.",
|
|
2076
|
+
confidence: 0.72,
|
|
2077
|
+
location: securityRequest.targetRef,
|
|
2078
|
+
tags: ["security", "evidence"]
|
|
2079
|
+
}
|
|
2080
|
+
];
|
|
2081
|
+
const mitigations = [
|
|
2082
|
+
...focusAreas.map((focusArea) => `Review ${focusArea} evidence and document the release impact before promotion.`),
|
|
2083
|
+
...(normalizedConstraints.length > 0 ? [`Keep security follow-up bounded by: ${normalizedConstraints.join("; ")}.`] : [])
|
|
2084
|
+
];
|
|
2085
|
+
const followUpWork = [
|
|
2086
|
+
...(normalizedEvidence?.securitySignals ?? []),
|
|
2087
|
+
...(referencedArtifactKinds.length > 0
|
|
2088
|
+
? [`Confirm the security posture for referenced artifacts: ${referencedArtifactKinds.join(", ")}.`]
|
|
2089
|
+
: []),
|
|
2090
|
+
"Use deterministic security evidence normalization outputs before broadening the workflow surface."
|
|
2091
|
+
];
|
|
2092
|
+
const summary = `Security report prepared for ${securityRequest.targetRef}.`;
|
|
2093
|
+
const securityReport = securityArtifactSchema.parse({
|
|
2094
|
+
...buildLifecycleArtifactEnvelopeBase(state, "Security Review", summary, [
|
|
2095
|
+
requestFile ?? ".agentops/requests/security.yaml",
|
|
2096
|
+
...(normalizedEvidence?.provenanceRefs ?? [securityRequest.targetRef, ...securityRequest.evidenceSources])
|
|
2097
|
+
], securityIssueRefs, securityGithubRefs),
|
|
2098
|
+
artifactKind: "security-report",
|
|
2099
|
+
lifecycleDomain: "security",
|
|
2100
|
+
redaction: {
|
|
2101
|
+
applied: true,
|
|
2102
|
+
strategyVersion: "1.0.0",
|
|
2103
|
+
categories: ["github-token", "api-key", "aws-key", "bearer-token", "password", "private-key", "security-sensitive"]
|
|
2104
|
+
},
|
|
2105
|
+
payload: {
|
|
2106
|
+
targetRef: securityRequest.targetRef,
|
|
2107
|
+
evidenceSources,
|
|
2108
|
+
findings,
|
|
2109
|
+
severitySummary: `highest severity: ${inferredSeverity}; ${findings.length} synthesized security finding(s).`,
|
|
2110
|
+
mitigations: mitigations.length > 0
|
|
2111
|
+
? mitigations
|
|
2112
|
+
: ["Review the referenced security evidence before promoting this workflow output."],
|
|
2113
|
+
releaseImpact: securityRequest.releaseContext === "blocking"
|
|
2114
|
+
? "release-blocking security findings require resolution before promotion."
|
|
2115
|
+
: securityRequest.releaseContext === "candidate"
|
|
2116
|
+
? "candidate release requires explicit security review before promotion."
|
|
2117
|
+
: "no release context was supplied; security output remains advisory.",
|
|
2118
|
+
followUpWork
|
|
2119
|
+
}
|
|
2120
|
+
});
|
|
2121
|
+
return agentOutputSchema.parse({
|
|
2122
|
+
summary,
|
|
2123
|
+
findings: [],
|
|
2124
|
+
proposedActions: [],
|
|
2125
|
+
lifecycleArtifacts: [securityReport],
|
|
2126
|
+
requestedTools: [],
|
|
2127
|
+
blockedActionFlags: [],
|
|
2128
|
+
confidence: 0.76,
|
|
2129
|
+
metadata: {
|
|
2130
|
+
deterministicInputs: {
|
|
2131
|
+
targetRef: securityRequest.targetRef,
|
|
2132
|
+
evidenceSources,
|
|
2133
|
+
focusAreas,
|
|
2134
|
+
constraints: normalizedConstraints,
|
|
2135
|
+
referencedArtifactKinds,
|
|
2136
|
+
normalizedEvidence: normalizedEvidence ?? null
|
|
2137
|
+
},
|
|
2138
|
+
synthesizedAssessment: {
|
|
2139
|
+
severitySummary: securityReport.payload.severitySummary,
|
|
2140
|
+
mitigations: securityReport.payload.mitigations,
|
|
2141
|
+
followUpWork: securityReport.payload.followUpWork
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
};
|
|
2147
|
+
const qaEvidenceNormalizationAgent = {
|
|
2148
|
+
manifest: agentManifestSchema.parse({
|
|
2149
|
+
version: 1,
|
|
2150
|
+
name: "qa-evidence-normalizer",
|
|
2151
|
+
displayName: "QA Evidence Normalizer",
|
|
2152
|
+
category: "qa",
|
|
2153
|
+
runtime: {
|
|
2154
|
+
minVersion: "0.1.0",
|
|
2155
|
+
kind: "deterministic"
|
|
2156
|
+
},
|
|
2157
|
+
permissions: {
|
|
2158
|
+
model: false,
|
|
2159
|
+
network: false,
|
|
2160
|
+
tools: [],
|
|
2161
|
+
readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/package.json", "**/*.json", "**/*.xml", "**/*.log", "**/*.md"],
|
|
2162
|
+
writePaths: []
|
|
2163
|
+
},
|
|
2164
|
+
inputs: ["workflowInputs", "repo", "agentResults"],
|
|
2165
|
+
outputs: ["summary", "metadata"],
|
|
2166
|
+
contextPolicy: {
|
|
2167
|
+
sections: ["workflowInputs", "repo", "agentResults"],
|
|
2168
|
+
minimalContext: true
|
|
2169
|
+
},
|
|
2170
|
+
catalog: {
|
|
2171
|
+
domain: "test",
|
|
2172
|
+
supportLevel: "internal",
|
|
2173
|
+
maturity: "mvp",
|
|
2174
|
+
trustScope: "official-core-only"
|
|
2175
|
+
},
|
|
2176
|
+
trust: {
|
|
2177
|
+
tier: "core",
|
|
2178
|
+
source: "official",
|
|
2179
|
+
reviewed: true
|
|
2180
|
+
}
|
|
2181
|
+
}),
|
|
2182
|
+
outputSchema: agentOutputSchema,
|
|
2183
|
+
async execute({ stateSlice }) {
|
|
2184
|
+
const qaRequest = getWorkflowInput(stateSlice, "qaRequest");
|
|
2185
|
+
if (!qaRequest) {
|
|
2186
|
+
throw new Error("qa-review requires validated QA request inputs before evidence normalization.");
|
|
2187
|
+
}
|
|
2188
|
+
const repoRoot = stateSlice.repo?.root;
|
|
2189
|
+
const packageManager = stateSlice.repo?.packageManager || "pnpm";
|
|
2190
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
2191
|
+
const targetType = typeof intakeMetadata.targetType === "string" ? intakeMetadata.targetType : "local-reference";
|
|
2192
|
+
const targetPath = repoRoot ? join(repoRoot, qaRequest.targetRef) : qaRequest.targetRef;
|
|
2193
|
+
if (repoRoot && !existsSync(targetPath)) {
|
|
2194
|
+
throw new Error(`QA target reference not found: ${qaRequest.targetRef}`);
|
|
2195
|
+
}
|
|
2196
|
+
const referencedArtifactKinds = targetType === "artifact-bundle" ? loadBundleArtifactKinds(targetPath) : [];
|
|
2197
|
+
const normalizedEvidenceSources = [...new Set([qaRequest.targetRef, ...qaRequest.evidenceSources])];
|
|
2198
|
+
const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
|
|
2199
|
+
if (missingEvidenceSources.length > 0) {
|
|
2200
|
+
throw new Error(`QA evidence source not found: ${missingEvidenceSources[0]}`);
|
|
2201
|
+
}
|
|
2202
|
+
const bundleAffectedPaths = targetType === "artifact-bundle" && referencedArtifactKinds.includes("implementation-proposal") && existsSync(targetPath)
|
|
2203
|
+
? asStringArray((() => {
|
|
2204
|
+
const parsed = JSON.parse(readFileSync(targetPath, "utf8"));
|
|
2205
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
|
|
2206
|
+
return [];
|
|
2207
|
+
}
|
|
2208
|
+
const implementationArtifact = parsed.lifecycleArtifacts.find((artifact) => isRecord(artifact) &&
|
|
2209
|
+
artifact.artifactKind === "implementation-proposal" &&
|
|
2210
|
+
isRecord(artifact.payload) &&
|
|
2211
|
+
Array.isArray(artifact.payload.affectedPaths));
|
|
2212
|
+
return implementationArtifact?.payload?.affectedPaths ?? [];
|
|
2213
|
+
})())
|
|
2214
|
+
: [];
|
|
2215
|
+
const affectedPackages = [...new Set(bundleAffectedPaths.map((pathValue) => derivePackageScope(pathValue)).filter((value) => Boolean(value)))];
|
|
2216
|
+
const allowedValidationCommands = collectValidationCommands(repoRoot, packageManager, affectedPackages).filter((entry) => entry.classification === "approval_required");
|
|
2217
|
+
const allowlistedCommands = new Set(allowedValidationCommands.map((entry) => entry.command));
|
|
2218
|
+
const normalizedExecutedChecks = qaRequest.executedChecks.map(normalizeRequestedCommand);
|
|
2219
|
+
const unrecognizedExecutedChecks = normalizedExecutedChecks.filter((command) => !allowlistedCommands.has(command));
|
|
2220
|
+
const githubActions = normalizeGitHubActionsEvidence(repoRoot, normalizedEvidenceSources);
|
|
2221
|
+
const normalization = qaEvidenceNormalizationSchema.parse({
|
|
2222
|
+
targetRef: qaRequest.targetRef,
|
|
2223
|
+
targetType,
|
|
2224
|
+
referencedArtifactKinds,
|
|
2225
|
+
normalizedEvidenceSources,
|
|
2226
|
+
missingEvidenceSources: [],
|
|
2227
|
+
normalizedExecutedChecks,
|
|
2228
|
+
unrecognizedExecutedChecks,
|
|
2229
|
+
affectedPackages,
|
|
2230
|
+
allowedValidationCommands,
|
|
2231
|
+
githubActions
|
|
2232
|
+
});
|
|
2233
|
+
return agentOutputSchema.parse({
|
|
2234
|
+
summary: `Normalized QA evidence across ${normalization.normalizedEvidenceSources.length} source(s), ${normalization.allowedValidationCommands.length} allowlisted validation command(s), and ${normalization.githubActions.evidence.length} GitHub Actions evidence export(s).`,
|
|
2235
|
+
findings: [],
|
|
2236
|
+
proposedActions: [],
|
|
2237
|
+
lifecycleArtifacts: [],
|
|
2238
|
+
requestedTools: [],
|
|
2239
|
+
blockedActionFlags: [],
|
|
2240
|
+
metadata: normalization
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
};
|
|
2244
|
+
const qaAnalystAgent = {
|
|
2245
|
+
manifest: agentManifestSchema.parse({
|
|
2246
|
+
version: 1,
|
|
2247
|
+
name: "qa-analyst",
|
|
2248
|
+
displayName: "QA Analyst",
|
|
2249
|
+
category: "qa",
|
|
2250
|
+
runtime: {
|
|
2251
|
+
minVersion: "0.1.0",
|
|
2252
|
+
kind: "reasoning"
|
|
2253
|
+
},
|
|
2254
|
+
permissions: {
|
|
2255
|
+
model: true,
|
|
2256
|
+
network: false,
|
|
2257
|
+
tools: [],
|
|
2258
|
+
readPaths: ["**/*"],
|
|
2259
|
+
writePaths: []
|
|
2260
|
+
},
|
|
2261
|
+
inputs: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
2262
|
+
outputs: ["lifecycleArtifacts"],
|
|
2263
|
+
contextPolicy: {
|
|
2264
|
+
sections: ["workflowInputs", "repo", "changes", "agentResults"],
|
|
2265
|
+
minimalContext: true
|
|
2266
|
+
},
|
|
2267
|
+
catalog: {
|
|
2268
|
+
domain: "test",
|
|
2269
|
+
supportLevel: "internal",
|
|
2270
|
+
maturity: "mvp",
|
|
2271
|
+
trustScope: "official-core-only"
|
|
2272
|
+
},
|
|
2273
|
+
trust: {
|
|
2274
|
+
tier: "core",
|
|
2275
|
+
source: "official",
|
|
2276
|
+
reviewed: true
|
|
2277
|
+
}
|
|
2278
|
+
}),
|
|
2279
|
+
outputSchema: agentOutputSchema,
|
|
2280
|
+
async execute({ state, stateSlice }) {
|
|
2281
|
+
const qaRequest = getWorkflowInput(stateSlice, "qaRequest");
|
|
2282
|
+
const qaIssueRefs = getWorkflowInput(stateSlice, "qaIssueRefs") ?? [];
|
|
2283
|
+
const qaGithubRefs = getWorkflowInput(stateSlice, "qaGithubRefs") ?? [];
|
|
2284
|
+
const requestFile = getWorkflowInput(stateSlice, "requestFile");
|
|
2285
|
+
if (!qaRequest) {
|
|
2286
|
+
throw new Error("qa-review requires validated QA inputs before QA analysis.");
|
|
2287
|
+
}
|
|
2288
|
+
const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
|
|
2289
|
+
const evidenceMetadata = qaEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
|
|
2290
|
+
const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
|
|
2291
|
+
const normalizedEvidenceSources = normalizedEvidence?.normalizedEvidenceSources ?? [];
|
|
2292
|
+
const normalizedExecutedChecks = normalizedEvidence?.normalizedExecutedChecks ?? [];
|
|
2293
|
+
const normalizedFocusAreas = asStringArray(intakeMetadata.focusAreas);
|
|
2294
|
+
const normalizedConstraints = asStringArray(intakeMetadata.constraints);
|
|
2295
|
+
const missingEvidenceSources = normalizedEvidence?.missingEvidenceSources ?? [];
|
|
2296
|
+
const unrecognizedExecutedChecks = normalizedEvidence?.unrecognizedExecutedChecks ?? [];
|
|
2297
|
+
const normalizedGithubActions = normalizedEvidence
|
|
2298
|
+
? normalizedEvidence.githubActions
|
|
2299
|
+
: {
|
|
2300
|
+
evidence: [],
|
|
2301
|
+
workflowNames: [],
|
|
2302
|
+
failingChecks: [],
|
|
2303
|
+
provenanceRefs: []
|
|
2304
|
+
};
|
|
2305
|
+
const targetType = normalizedEvidence?.targetType
|
|
2306
|
+
? normalizedEvidence.targetType
|
|
2307
|
+
: typeof intakeMetadata.targetType === "string"
|
|
2308
|
+
? intakeMetadata.targetType
|
|
2309
|
+
: "local-reference";
|
|
2310
|
+
const evidenceSources = normalizedEvidenceSources.length > 0
|
|
2311
|
+
? normalizedEvidenceSources
|
|
2312
|
+
: [...new Set([qaRequest.targetRef, ...qaRequest.evidenceSources])];
|
|
2313
|
+
const executedChecks = normalizedExecutedChecks.length > 0 ? normalizedExecutedChecks : qaRequest.executedChecks;
|
|
2314
|
+
const focusAreas = normalizedFocusAreas.length > 0 ? normalizedFocusAreas : qaRequest.focusAreas;
|
|
2315
|
+
const findings = focusAreas.length > 0
|
|
2316
|
+
? focusAreas.map((focusArea, index) => ({
|
|
2317
|
+
id: `qa-finding-${index + 1}`,
|
|
2318
|
+
title: `Inspect ${focusArea} evidence before promotion`,
|
|
2319
|
+
summary: `QA review flagged ${focusArea} as an area requiring bounded interpretation for ${qaRequest.targetRef}.`,
|
|
2320
|
+
severity: qaRequest.releaseContext === "blocking" ? "high" : qaRequest.releaseContext === "candidate" ? "medium" : "low",
|
|
2321
|
+
rationale: `The MVP QA workflow remains read-only and request-driven, so ${focusArea} still depends on referenced evidence rather than automatic execution.`,
|
|
2322
|
+
confidence: 0.74,
|
|
2323
|
+
location: qaRequest.targetRef,
|
|
2324
|
+
tags: ["qa", focusArea]
|
|
2325
|
+
}))
|
|
2326
|
+
: [
|
|
2327
|
+
{
|
|
2328
|
+
id: "qa-finding-1",
|
|
2329
|
+
title: "Review referenced QA evidence before promotion",
|
|
2330
|
+
summary: `QA review requires bounded interpretation of the referenced evidence for ${qaRequest.targetRef}.`,
|
|
2331
|
+
severity: qaRequest.releaseContext === "blocking" ? "high" : "medium",
|
|
2332
|
+
rationale: "The current QA workflow synthesizes a report from validated references and does not execute arbitrary test commands.",
|
|
2333
|
+
confidence: 0.71,
|
|
2334
|
+
location: qaRequest.targetRef,
|
|
2335
|
+
tags: ["qa", "evidence"]
|
|
2336
|
+
}
|
|
2337
|
+
];
|
|
2338
|
+
const coverageGaps = [
|
|
2339
|
+
...(evidenceSources.length === 0 ? ["No QA evidence sources were provided beyond the target reference."] : []),
|
|
2340
|
+
...missingEvidenceSources.map((pathValue) => `Referenced QA evidence is missing: ${pathValue}`),
|
|
2341
|
+
...(focusAreas.includes("coverage") ? ["Coverage evidence still needs deterministic normalization before it can be promoted to an official QA signal."] : []),
|
|
2342
|
+
...(executedChecks.length === 0 ? ["No executed validation checks were recorded in the request."] : []),
|
|
2343
|
+
...unrecognizedExecutedChecks.map((command) => `Executed check is outside the bounded allowlist and still needs manual interpretation: ${command}`),
|
|
2344
|
+
...normalizedGithubActions.failingChecks.map((checkName) => `GitHub Actions evidence still reports a failing check that needs manual review: ${checkName}`)
|
|
2345
|
+
];
|
|
2346
|
+
const recommendedNextChecks = [
|
|
2347
|
+
...executedChecks.map((command) => `Review the recorded output for \`${command}\` before promotion.`),
|
|
2348
|
+
...focusAreas.map((focusArea) => `Confirm whether ${focusArea} needs additional deterministic QA evidence.`),
|
|
2349
|
+
...normalizedGithubActions.workflowNames.map((workflowName) => `Review the exported GitHub Actions evidence for workflow \`${workflowName}\` before promotion.`),
|
|
2350
|
+
...(normalizedConstraints.length > 0 ? [`Keep QA follow-up bounded by: ${normalizedConstraints.join("; ")}.`] : [])
|
|
2351
|
+
];
|
|
2352
|
+
const summary = `QA report prepared for ${qaRequest.targetRef}.`;
|
|
2353
|
+
const releaseImpactBase = qaRequest.releaseContext === "blocking"
|
|
2354
|
+
? "release-blocking QA findings require resolution before promotion."
|
|
2355
|
+
: qaRequest.releaseContext === "candidate"
|
|
2356
|
+
? "candidate release still requires explicit QA review before promotion."
|
|
2357
|
+
: "no release context was supplied; QA output remains advisory.";
|
|
2358
|
+
const qaReport = qaArtifactSchema.parse({
|
|
2359
|
+
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/qa.yaml", qaRequest.targetRef, ...qaRequest.evidenceSources], qaIssueRefs, qaGithubRefs),
|
|
2360
|
+
artifactKind: "qa-report",
|
|
2361
|
+
lifecycleDomain: "test",
|
|
2362
|
+
workflow: {
|
|
2363
|
+
name: state.workflow,
|
|
2364
|
+
displayName: "QA Review"
|
|
2365
|
+
},
|
|
2366
|
+
payload: {
|
|
2367
|
+
targetRef: qaRequest.targetRef,
|
|
2368
|
+
evidenceSources,
|
|
2369
|
+
executedChecks,
|
|
2370
|
+
findings,
|
|
2371
|
+
coverageGaps,
|
|
2372
|
+
recommendedNextChecks: recommendedNextChecks.length > 0
|
|
2373
|
+
? recommendedNextChecks
|
|
2374
|
+
: ["Capture additional bounded QA evidence before promotion."],
|
|
2375
|
+
releaseImpact: normalizedGithubActions.failingChecks.length > 0
|
|
2376
|
+
? `${releaseImpactBase} GitHub Actions evidence still shows failing checks: ${normalizedGithubActions.failingChecks.join(", ")}.`
|
|
2377
|
+
: releaseImpactBase
|
|
2378
|
+
}
|
|
2379
|
+
});
|
|
2380
|
+
return agentOutputSchema.parse({
|
|
2381
|
+
summary,
|
|
2382
|
+
findings: [],
|
|
2383
|
+
proposedActions: [],
|
|
2384
|
+
lifecycleArtifacts: [qaReport],
|
|
2385
|
+
requestedTools: [],
|
|
2386
|
+
blockedActionFlags: [],
|
|
2387
|
+
confidence: 0.74,
|
|
2388
|
+
metadata: {
|
|
2389
|
+
deterministicInputs: {
|
|
2390
|
+
targetRef: qaRequest.targetRef,
|
|
2391
|
+
targetType,
|
|
2392
|
+
evidenceSources,
|
|
2393
|
+
executedChecks,
|
|
2394
|
+
focusAreas,
|
|
2395
|
+
constraints: normalizedConstraints,
|
|
2396
|
+
githubActions: normalizedGithubActions
|
|
2397
|
+
},
|
|
2398
|
+
synthesizedAssessment: {
|
|
2399
|
+
releaseContext: qaRequest.releaseContext,
|
|
2400
|
+
recommendedNextChecks: qaReport.payload.recommendedNextChecks,
|
|
2401
|
+
coverageGaps: qaReport.payload.coverageGaps
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
};
|
|
2407
|
+
const implementationInventoryAgent = {
|
|
2408
|
+
manifest: agentManifestSchema.parse({
|
|
2409
|
+
version: 1,
|
|
2410
|
+
name: "implementation-inventory",
|
|
2411
|
+
displayName: "Implementation Inventory",
|
|
2412
|
+
category: "implementation",
|
|
2413
|
+
runtime: {
|
|
2414
|
+
minVersion: "0.1.0",
|
|
2415
|
+
kind: "deterministic"
|
|
2416
|
+
},
|
|
2417
|
+
permissions: {
|
|
2418
|
+
model: false,
|
|
2419
|
+
network: false,
|
|
2420
|
+
tools: [],
|
|
2421
|
+
readPaths: ["**/*"],
|
|
2422
|
+
writePaths: []
|
|
2423
|
+
},
|
|
2424
|
+
inputs: ["workflowInputs", "repo", "changes"],
|
|
2425
|
+
outputs: ["summary", "metadata"],
|
|
2426
|
+
contextPolicy: {
|
|
2427
|
+
sections: ["workflowInputs", "repo", "changes"],
|
|
2428
|
+
minimalContext: true
|
|
2429
|
+
},
|
|
2430
|
+
catalog: {
|
|
2431
|
+
domain: "build",
|
|
2432
|
+
supportLevel: "internal",
|
|
2433
|
+
maturity: "mvp",
|
|
2434
|
+
trustScope: "official-core-only"
|
|
2435
|
+
},
|
|
2436
|
+
trust: {
|
|
2437
|
+
tier: "core",
|
|
2438
|
+
source: "official",
|
|
2439
|
+
reviewed: true
|
|
2440
|
+
}
|
|
2441
|
+
}),
|
|
2442
|
+
outputSchema: agentOutputSchema,
|
|
2443
|
+
async execute({ stateSlice }) {
|
|
2444
|
+
const implementationRequest = getWorkflowInput(stateSlice, "implementationRequest");
|
|
2445
|
+
const designRecord = getWorkflowInput(stateSlice, "designRecord");
|
|
2446
|
+
if (!implementationRequest || !designRecord) {
|
|
2447
|
+
throw new Error("implementation-proposal requires deterministic inventory inputs before proposal analysis.");
|
|
2448
|
+
}
|
|
2449
|
+
const repoRoot = stateSlice.repo?.root;
|
|
2450
|
+
const packageManager = stateSlice.repo?.packageManager || "pnpm";
|
|
2451
|
+
const candidatePaths = [
|
|
2452
|
+
...new Set([
|
|
2453
|
+
...implementationRequest.targetPaths,
|
|
2454
|
+
...designRecord.payload.interfacesImpacted,
|
|
2455
|
+
...designRecord.payload.schemaChangesNeeded,
|
|
2456
|
+
...designRecord.payload.policyChangesNeeded
|
|
2457
|
+
])
|
|
2458
|
+
];
|
|
2459
|
+
const resolvedAffectedPaths = candidatePaths.filter((pathValue) => {
|
|
2460
|
+
if (!pathValue) {
|
|
2461
|
+
return false;
|
|
2462
|
+
}
|
|
2463
|
+
if (!repoRoot) {
|
|
2464
|
+
return true;
|
|
2465
|
+
}
|
|
2466
|
+
return existsSync(join(repoRoot, pathValue));
|
|
2467
|
+
});
|
|
2468
|
+
const affectedPackages = [...new Set(resolvedAffectedPaths.map(derivePackageScope).filter((value) => Boolean(value)))];
|
|
2469
|
+
const entrypoints = [
|
|
2470
|
+
...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.endsWith("src/index.ts") || pathValue.endsWith("package.json") || pathValue.endsWith("agent.manifest.json")))
|
|
2471
|
+
];
|
|
2472
|
+
const schemaSurfaces = [...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.includes("schema")))];
|
|
2473
|
+
const policySurfaces = [
|
|
2474
|
+
...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.includes("policy") || pathValue.includes(".agentops/policy.yaml")))
|
|
2475
|
+
];
|
|
2476
|
+
const discoveredValidationCommands = collectValidationCommands(repoRoot, packageManager, affectedPackages);
|
|
692
2477
|
const normalizedRequestedCommands = implementationRequest.validationCommands.map(normalizeRequestedCommand);
|
|
693
2478
|
const allowlistedCommands = new Set(discoveredValidationCommands
|
|
694
2479
|
.filter((entry) => entry.classification === "approval_required")
|
|
@@ -817,7 +2602,7 @@ const implementationPlannerAgent = {
|
|
|
817
2602
|
];
|
|
818
2603
|
const summary = `Implementation proposal prepared for ${implementationRequest.implementationGoal}.`;
|
|
819
2604
|
const implementationProposal = implementationArtifactSchema.parse({
|
|
820
|
-
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/implementation.yaml", implementationRequest.designRecordRef], designRecord.source.issueRefs),
|
|
2605
|
+
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/implementation.yaml", implementationRequest.designRecordRef], designRecord.source.issueRefs, designRecord.source.githubRefs),
|
|
821
2606
|
artifactKind: "implementation-proposal",
|
|
822
2607
|
lifecycleDomain: "build",
|
|
823
2608
|
workflow: {
|
|
@@ -930,7 +2715,7 @@ const designAnalystAgent = {
|
|
|
930
2715
|
];
|
|
931
2716
|
const summary = `Design record prepared for ${designRequest.decisionTarget}.`;
|
|
932
2717
|
const designRecord = designArtifactSchema.parse({
|
|
933
|
-
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/design.yaml", designRequest.planningBriefRef], planningBrief.source.issueRefs),
|
|
2718
|
+
...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/design.yaml", designRequest.planningBriefRef], planningBrief.source.issueRefs, planningBrief.source.githubRefs),
|
|
934
2719
|
artifactKind: "design-record",
|
|
935
2720
|
lifecycleDomain: "design",
|
|
936
2721
|
workflow: {
|
|
@@ -1218,6 +3003,20 @@ export function createBuiltinAgentRegistry() {
|
|
|
1218
3003
|
["design-inventory", designInventoryAgent],
|
|
1219
3004
|
["implementation-intake", implementationIntakeAgent],
|
|
1220
3005
|
["qa-intake", qaIntakeAgent],
|
|
3006
|
+
["security-intake", securityIntakeAgent],
|
|
3007
|
+
["incident-intake", incidentIntakeAgent],
|
|
3008
|
+
["incident-evidence-normalizer", incidentEvidenceNormalizationAgent],
|
|
3009
|
+
["maintenance-intake", maintenanceIntakeAgent],
|
|
3010
|
+
["maintenance-evidence-normalizer", maintenanceEvidenceNormalizerAgent],
|
|
3011
|
+
["maintenance-analyst", maintenanceAnalystAgent],
|
|
3012
|
+
["incident-analyst", incidentAnalystAgent],
|
|
3013
|
+
["release-intake", releaseIntakeAgent],
|
|
3014
|
+
["release-evidence-normalizer", releaseEvidenceNormalizationAgent],
|
|
3015
|
+
["release-analyst", releaseAnalystAgent],
|
|
3016
|
+
["security-evidence-normalizer", securityEvidenceNormalizationAgent],
|
|
3017
|
+
["security-analyst", securityAnalystAgent],
|
|
3018
|
+
["qa-evidence-normalizer", qaEvidenceNormalizationAgent],
|
|
3019
|
+
["qa-analyst", qaAnalystAgent],
|
|
1221
3020
|
["implementation-inventory", implementationInventoryAgent],
|
|
1222
3021
|
["implementation-planner", implementationPlannerAgent],
|
|
1223
3022
|
["design-analyst", designAnalystAgent],
|