@h9-foundry/agentforge-cli 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { agentManifestSchema, agentOutputSchema, designArtifactSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
3
+ import { agentManifestSchema, agentOutputSchema, designArtifactSchema, implementationArtifactSchema, implementationInventorySchema, qaArtifactSchema, qaEvidenceNormalizationSchema, qaRequestSchema, securityArtifactSchema, securityEvidenceNormalizationSchema, securityRequestSchema, planningArtifactSchema } from "@h9-foundry/agentforge-schemas";
4
4
  const contextCollectorAgent = {
5
5
  manifest: agentManifestSchema.parse({
6
6
  version: 1,
@@ -63,12 +63,147 @@ function isRecord(value) {
63
63
  function asStringArray(value) {
64
64
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
65
65
  }
66
+ function parsePackageScripts(packageJsonPath) {
67
+ if (!existsSync(packageJsonPath)) {
68
+ return {};
69
+ }
70
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
71
+ if (!isRecord(parsed) || !isRecord(parsed.scripts)) {
72
+ return {};
73
+ }
74
+ return Object.fromEntries(Object.entries(parsed.scripts).filter((entry) => typeof entry[1] === "string"));
75
+ }
76
+ const allowedValidationScriptNames = new Set(["test", "lint", "typecheck", "build", "build:packages", "release:verify"]);
77
+ function normalizeRequestedCommand(command) {
78
+ return command.trim().replace(/\s+/g, " ");
79
+ }
80
+ function buildValidationCommand(packageManager, scriptName, packageName) {
81
+ if (packageName) {
82
+ return `${packageManager} --filter ${packageName} ${scriptName}`;
83
+ }
84
+ return `${packageManager} ${scriptName}`;
85
+ }
86
+ function collectValidationCommands(repoRoot, packageManager, packageScopes) {
87
+ const discoveredValidationCommands = [];
88
+ const registerScripts = (packageJsonPath, source, packageName) => {
89
+ const scripts = parsePackageScripts(packageJsonPath);
90
+ for (const scriptName of Object.keys(scripts)) {
91
+ const command = buildValidationCommand(packageManager, scriptName, packageName);
92
+ discoveredValidationCommands.push({
93
+ command,
94
+ source,
95
+ classification: allowedValidationScriptNames.has(scriptName) ? "approval_required" : "deny",
96
+ reason: allowedValidationScriptNames.has(scriptName)
97
+ ? "Discovered from a bounded repository script; execution would still require approval."
98
+ : "Command is not in the bounded allowlist for workflow validation."
99
+ });
100
+ }
101
+ };
102
+ if (!repoRoot) {
103
+ return discoveredValidationCommands;
104
+ }
105
+ registerScripts(join(repoRoot, "package.json"), "package-script");
106
+ for (const packageScope of packageScopes) {
107
+ const packageJsonPath = join(repoRoot, packageScope, "package.json");
108
+ if (!existsSync(packageJsonPath)) {
109
+ continue;
110
+ }
111
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
112
+ const packageName = isRecord(parsed) && typeof parsed.name === "string" ? parsed.name : packageScope;
113
+ registerScripts(packageJsonPath, "workspace-script", packageName);
114
+ }
115
+ return discoveredValidationCommands;
116
+ }
117
+ function loadBundleArtifactKinds(bundlePath) {
118
+ if (!existsSync(bundlePath)) {
119
+ return [];
120
+ }
121
+ const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
122
+ if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
123
+ return [];
124
+ }
125
+ return parsed.lifecycleArtifacts
126
+ .map((artifact) => (isRecord(artifact) && typeof artifact.artifactKind === "string" ? artifact.artifactKind : undefined))
127
+ .filter((artifactKind) => Boolean(artifactKind));
128
+ }
129
+ function loadBundleArtifactPayloadPaths(bundlePath) {
130
+ if (!existsSync(bundlePath)) {
131
+ return [];
132
+ }
133
+ const parsed = JSON.parse(readFileSync(bundlePath, "utf8"));
134
+ if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
135
+ return [];
136
+ }
137
+ return parsed.lifecycleArtifacts.flatMap((artifact) => {
138
+ if (!isRecord(artifact) || !isRecord(artifact.payload)) {
139
+ return [];
140
+ }
141
+ const payload = artifact.payload;
142
+ if (Array.isArray(payload.affectedPaths)) {
143
+ return asStringArray(payload.affectedPaths);
144
+ }
145
+ if (Array.isArray(payload.evidenceSources)) {
146
+ return asStringArray(payload.evidenceSources);
147
+ }
148
+ return [];
149
+ });
150
+ }
151
+ function derivePackageScope(pathValue) {
152
+ const segments = pathValue.split("/").filter(Boolean);
153
+ if (segments.length < 2) {
154
+ return undefined;
155
+ }
156
+ const [topLevel, scope] = segments;
157
+ if (topLevel === "packages" || topLevel === "agents" || topLevel === "adapters") {
158
+ return `${topLevel}/${scope}`;
159
+ }
160
+ return undefined;
161
+ }
66
162
  function getWorkflowInput(stateSlice, key) {
67
163
  if (!isRecord(stateSlice.workflowInputs)) {
68
164
  return undefined;
69
165
  }
70
166
  return stateSlice.workflowInputs[key];
71
167
  }
168
+ function buildLifecycleArtifactEnvelopeBase(state, displayName, summary, inputRefs, issueRefs = []) {
169
+ return {
170
+ schemaVersion: state.version,
171
+ workflow: {
172
+ name: state.workflow,
173
+ displayName
174
+ },
175
+ source: {
176
+ sourceType: "workflow-run",
177
+ runId: state.runId,
178
+ inputRefs: [...inputRefs],
179
+ issueRefs: [...issueRefs]
180
+ },
181
+ status: "complete",
182
+ generatedAt: new Date().toISOString(),
183
+ repo: {
184
+ root: state.repo.root,
185
+ name: state.repo.name,
186
+ branch: state.repo.branch
187
+ },
188
+ provenance: {
189
+ generatedBy: "agentforge-runtime",
190
+ schemaVersion: state.version,
191
+ executionEnvironment: state.context.ciExecution ? "ci" : "local",
192
+ repoRoot: state.repo.root
193
+ },
194
+ redaction: {
195
+ applied: true,
196
+ strategyVersion: "1.0.0",
197
+ categories: ["github-token", "api-key", "aws-key", "bearer-token", "password", "private-key"]
198
+ },
199
+ auditLink: {
200
+ entryIds: [],
201
+ findingIds: [],
202
+ proposedActionIds: []
203
+ },
204
+ summary
205
+ };
206
+ }
72
207
  function buildArtifactEnvelopeBase(state, summary, inputRefs, issueRefs) {
73
208
  return {
74
209
  schemaVersion: state.version,
@@ -436,6 +571,913 @@ const designInventoryAgent = {
436
571
  });
437
572
  }
438
573
  };
574
+ const implementationIntakeAgent = {
575
+ manifest: agentManifestSchema.parse({
576
+ version: 1,
577
+ name: "implementation-intake",
578
+ displayName: "Implementation Intake",
579
+ category: "implementation",
580
+ runtime: {
581
+ minVersion: "0.1.0",
582
+ kind: "deterministic"
583
+ },
584
+ permissions: {
585
+ model: false,
586
+ network: false,
587
+ tools: [],
588
+ readPaths: [".agentops/requests/**", ".agentops/runs/**"],
589
+ writePaths: []
590
+ },
591
+ inputs: ["workflowInputs", "repo"],
592
+ outputs: ["summary", "metadata"],
593
+ contextPolicy: {
594
+ sections: ["workflowInputs", "repo", "context"],
595
+ minimalContext: true
596
+ },
597
+ catalog: {
598
+ domain: "build",
599
+ supportLevel: "internal",
600
+ maturity: "mvp",
601
+ trustScope: "official-core-only"
602
+ },
603
+ trust: {
604
+ tier: "core",
605
+ source: "official",
606
+ reviewed: true
607
+ }
608
+ }),
609
+ outputSchema: agentOutputSchema,
610
+ async execute({ stateSlice }) {
611
+ const implementationRequest = getWorkflowInput(stateSlice, "implementationRequest");
612
+ const designRecord = getWorkflowInput(stateSlice, "designRecord");
613
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
614
+ if (!implementationRequest || !designRecord) {
615
+ throw new Error("implementation-proposal requires a validated implementation request and design record before runtime execution.");
616
+ }
617
+ return agentOutputSchema.parse({
618
+ summary: `Loaded implementation request from ${requestFile ?? ".agentops/requests/implementation.yaml"} with design record ${implementationRequest.designRecordRef}.`,
619
+ findings: [],
620
+ proposedActions: [],
621
+ lifecycleArtifacts: [],
622
+ requestedTools: [],
623
+ blockedActionFlags: [],
624
+ metadata: {
625
+ requestFile,
626
+ designRecordRef: implementationRequest.designRecordRef,
627
+ implementationGoal: implementationRequest.implementationGoal,
628
+ approvalMode: implementationRequest.approvalMode,
629
+ targetPaths: implementationRequest.targetPaths,
630
+ validationCommands: implementationRequest.validationCommands,
631
+ designDecisionSummary: designRecord.payload.decisionSummary
632
+ }
633
+ });
634
+ }
635
+ };
636
+ const qaIntakeAgent = {
637
+ manifest: agentManifestSchema.parse({
638
+ version: 1,
639
+ name: "qa-intake",
640
+ displayName: "QA Intake",
641
+ category: "qa",
642
+ runtime: {
643
+ minVersion: "0.1.0",
644
+ kind: "deterministic"
645
+ },
646
+ permissions: {
647
+ model: false,
648
+ network: false,
649
+ tools: [],
650
+ readPaths: [".agentops/requests/**", ".agentops/runs/**"],
651
+ writePaths: []
652
+ },
653
+ inputs: ["workflowInputs", "repo"],
654
+ outputs: ["summary", "metadata"],
655
+ contextPolicy: {
656
+ sections: ["workflowInputs", "repo", "context"],
657
+ minimalContext: true
658
+ },
659
+ catalog: {
660
+ domain: "test",
661
+ supportLevel: "internal",
662
+ maturity: "mvp",
663
+ trustScope: "official-core-only"
664
+ },
665
+ trust: {
666
+ tier: "core",
667
+ source: "official",
668
+ reviewed: true
669
+ }
670
+ }),
671
+ outputSchema: agentOutputSchema,
672
+ async execute({ stateSlice }) {
673
+ const qaRequest = getWorkflowInput(stateSlice, "qaRequest");
674
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
675
+ if (!qaRequest) {
676
+ throw new Error("qa-review requires a validated QA request before runtime execution.");
677
+ }
678
+ const targetType = qaRequest.targetRef.endsWith("bundle.json")
679
+ ? "artifact-bundle"
680
+ : qaRequest.targetRef.endsWith(".xml") || qaRequest.targetRef.endsWith(".json") || qaRequest.targetRef.endsWith(".log")
681
+ ? "validation-output"
682
+ : "local-reference";
683
+ return agentOutputSchema.parse({
684
+ summary: `Loaded QA request from ${requestFile ?? ".agentops/requests/qa.yaml"} targeting ${qaRequest.targetRef}.`,
685
+ findings: [],
686
+ proposedActions: [],
687
+ lifecycleArtifacts: [],
688
+ requestedTools: [],
689
+ blockedActionFlags: [],
690
+ metadata: {
691
+ ...qaRequestSchema.parse({
692
+ ...qaRequest,
693
+ evidenceSources: [...new Set([qaRequest.targetRef, ...qaRequest.evidenceSources])]
694
+ }),
695
+ targetType
696
+ }
697
+ });
698
+ }
699
+ };
700
+ const securityIntakeAgent = {
701
+ manifest: agentManifestSchema.parse({
702
+ version: 1,
703
+ name: "security-intake",
704
+ displayName: "Security Intake",
705
+ category: "security",
706
+ runtime: {
707
+ minVersion: "0.1.0",
708
+ kind: "deterministic"
709
+ },
710
+ permissions: {
711
+ model: false,
712
+ network: false,
713
+ tools: [],
714
+ readPaths: [".agentops/requests/**", ".agentops/runs/**"],
715
+ writePaths: []
716
+ },
717
+ inputs: ["workflowInputs", "repo"],
718
+ outputs: ["summary", "metadata"],
719
+ contextPolicy: {
720
+ sections: ["workflowInputs", "repo", "context"],
721
+ minimalContext: true
722
+ },
723
+ catalog: {
724
+ domain: "security",
725
+ supportLevel: "internal",
726
+ maturity: "mvp",
727
+ trustScope: "official-core-only"
728
+ },
729
+ trust: {
730
+ tier: "core",
731
+ source: "official",
732
+ reviewed: true
733
+ }
734
+ }),
735
+ outputSchema: agentOutputSchema,
736
+ async execute({ stateSlice }) {
737
+ const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
738
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
739
+ const referencedArtifactKinds = getWorkflowInput(stateSlice, "securityTargetArtifactKinds") ?? [];
740
+ if (!securityRequest) {
741
+ throw new Error("security-review requires a validated security request before runtime execution.");
742
+ }
743
+ const targetType = securityRequest.targetRef.endsWith("bundle.json") ? "artifact-bundle" : "local-reference";
744
+ return agentOutputSchema.parse({
745
+ summary: `Loaded security request from ${requestFile ?? ".agentops/requests/security.yaml"} targeting ${securityRequest.targetRef}.`,
746
+ findings: [],
747
+ proposedActions: [],
748
+ lifecycleArtifacts: [],
749
+ requestedTools: [],
750
+ blockedActionFlags: [],
751
+ metadata: {
752
+ ...securityRequestSchema.parse({
753
+ ...securityRequest,
754
+ evidenceSources: [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])]
755
+ }),
756
+ targetType,
757
+ referencedArtifactKinds
758
+ }
759
+ });
760
+ }
761
+ };
762
+ const securityEvidenceNormalizationAgent = {
763
+ manifest: agentManifestSchema.parse({
764
+ version: 1,
765
+ name: "security-evidence-normalizer",
766
+ displayName: "Security Evidence Normalizer",
767
+ category: "security",
768
+ runtime: {
769
+ minVersion: "0.1.0",
770
+ kind: "deterministic"
771
+ },
772
+ permissions: {
773
+ model: false,
774
+ network: false,
775
+ tools: [],
776
+ readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/*.json", "**/*.md", "**/package.json"],
777
+ writePaths: []
778
+ },
779
+ inputs: ["workflowInputs", "repo", "agentResults"],
780
+ outputs: ["summary", "metadata"],
781
+ contextPolicy: {
782
+ sections: ["workflowInputs", "repo", "agentResults"],
783
+ minimalContext: true
784
+ },
785
+ catalog: {
786
+ domain: "security",
787
+ supportLevel: "internal",
788
+ maturity: "mvp",
789
+ trustScope: "official-core-only"
790
+ },
791
+ trust: {
792
+ tier: "core",
793
+ source: "official",
794
+ reviewed: true
795
+ }
796
+ }),
797
+ outputSchema: agentOutputSchema,
798
+ async execute({ stateSlice }) {
799
+ const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
800
+ if (!securityRequest) {
801
+ throw new Error("security-review requires validated security request inputs before evidence normalization.");
802
+ }
803
+ const repoRoot = stateSlice.repo?.root;
804
+ const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
805
+ const targetType = typeof intakeMetadata.targetType === "string" && intakeMetadata.targetType === "artifact-bundle"
806
+ ? "artifact-bundle"
807
+ : "local-reference";
808
+ const targetPath = repoRoot ? join(repoRoot, securityRequest.targetRef) : securityRequest.targetRef;
809
+ if (repoRoot && !existsSync(targetPath)) {
810
+ throw new Error(`Security target reference not found: ${securityRequest.targetRef}`);
811
+ }
812
+ const referencedArtifactKinds = targetType === "artifact-bundle" ? loadBundleArtifactKinds(targetPath) : [];
813
+ const normalizedEvidenceSources = [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])];
814
+ const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
815
+ if (missingEvidenceSources.length > 0) {
816
+ throw new Error(`Security evidence source not found: ${missingEvidenceSources[0]}`);
817
+ }
818
+ const normalizedFocusAreas = securityRequest.focusAreas.length > 0 ? [...new Set(securityRequest.focusAreas)] : ["general-review"];
819
+ const affectedPackages = targetType === "artifact-bundle"
820
+ ? [...new Set(loadBundleArtifactPayloadPaths(targetPath).map(derivePackageScope).filter((value) => Boolean(value)))]
821
+ : [];
822
+ const securitySignals = [
823
+ ...(referencedArtifactKinds.length > 0 ? [`Referenced artifact kinds: ${referencedArtifactKinds.join(", ")}`] : []),
824
+ ...(affectedPackages.length > 0 ? [`Affected packages inferred from bounded artifact payloads: ${affectedPackages.join(", ")}`] : []),
825
+ ...(normalizedFocusAreas.length > 0 ? [`Requested focus areas: ${normalizedFocusAreas.join(", ")}`] : []),
826
+ "Security evidence collection remains local, read-only, and bounded to validated references."
827
+ ];
828
+ const provenanceRefs = [
829
+ securityRequest.targetRef,
830
+ ...securityRequest.evidenceSources,
831
+ ...referencedArtifactKinds.map((artifactKind) => `${securityRequest.targetRef}#${artifactKind}`)
832
+ ];
833
+ const normalization = securityEvidenceNormalizationSchema.parse({
834
+ targetRef: securityRequest.targetRef,
835
+ targetType,
836
+ referencedArtifactKinds,
837
+ normalizedEvidenceSources,
838
+ missingEvidenceSources: [],
839
+ normalizedFocusAreas,
840
+ securitySignals,
841
+ provenanceRefs: [...new Set(provenanceRefs)],
842
+ affectedPackages
843
+ });
844
+ return agentOutputSchema.parse({
845
+ summary: `Normalized security evidence for ${securityRequest.targetRef}.`,
846
+ findings: [],
847
+ proposedActions: [],
848
+ lifecycleArtifacts: [],
849
+ requestedTools: [],
850
+ blockedActionFlags: [],
851
+ metadata: normalization
852
+ });
853
+ }
854
+ };
855
+ const securityAnalystAgent = {
856
+ manifest: agentManifestSchema.parse({
857
+ version: 1,
858
+ name: "security-analyst",
859
+ displayName: "Security Analyst",
860
+ category: "security",
861
+ runtime: {
862
+ minVersion: "0.1.0",
863
+ kind: "reasoning"
864
+ },
865
+ permissions: {
866
+ model: true,
867
+ network: false,
868
+ tools: [],
869
+ readPaths: ["**/*"],
870
+ writePaths: []
871
+ },
872
+ inputs: ["workflowInputs", "repo", "changes", "agentResults"],
873
+ outputs: ["lifecycleArtifacts"],
874
+ contextPolicy: {
875
+ sections: ["workflowInputs", "repo", "changes", "agentResults"],
876
+ minimalContext: true
877
+ },
878
+ catalog: {
879
+ domain: "security",
880
+ supportLevel: "internal",
881
+ maturity: "mvp",
882
+ trustScope: "official-core-only"
883
+ },
884
+ trust: {
885
+ tier: "core",
886
+ source: "official",
887
+ reviewed: true
888
+ }
889
+ }),
890
+ outputSchema: agentOutputSchema,
891
+ async execute({ state, stateSlice }) {
892
+ const securityRequest = getWorkflowInput(stateSlice, "securityRequest");
893
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
894
+ if (!securityRequest) {
895
+ throw new Error("security-review requires validated security inputs before security analysis.");
896
+ }
897
+ const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
898
+ const evidenceMetadata = securityEvidenceNormalizationSchema.safeParse(stateSlice.agentResults?.evidence?.metadata);
899
+ const normalizedEvidence = evidenceMetadata.success ? evidenceMetadata.data : undefined;
900
+ const referencedArtifactKinds = normalizedEvidence?.referencedArtifactKinds ?? asStringArray(intakeMetadata.referencedArtifactKinds);
901
+ const normalizedFocusAreas = normalizedEvidence?.normalizedFocusAreas ?? asStringArray(intakeMetadata.focusAreas);
902
+ const normalizedConstraints = asStringArray(intakeMetadata.constraints);
903
+ const evidenceSources = normalizedEvidence?.normalizedEvidenceSources && normalizedEvidence.normalizedEvidenceSources.length > 0
904
+ ? normalizedEvidence.normalizedEvidenceSources
905
+ : asStringArray(intakeMetadata.evidenceSources).length > 0
906
+ ? asStringArray(intakeMetadata.evidenceSources)
907
+ : [...new Set([securityRequest.targetRef, ...securityRequest.evidenceSources])];
908
+ const focusAreas = normalizedFocusAreas.length > 0 ? normalizedFocusAreas : securityRequest.focusAreas;
909
+ const inferredSeverity = securityRequest.releaseContext === "blocking" ? "high" : securityRequest.releaseContext === "candidate" ? "medium" : "low";
910
+ const findings = focusAreas.length > 0
911
+ ? focusAreas.map((focusArea, index) => ({
912
+ id: `security-finding-${index + 1}`,
913
+ title: `Inspect ${focusArea} evidence before promotion`,
914
+ summary: `Security review flagged ${focusArea} for bounded follow-up on ${securityRequest.targetRef}.`,
915
+ severity: inferredSeverity,
916
+ rationale: "The MVP security workflow synthesizes a structured report from validated references before deterministic evidence normalization lands.",
917
+ confidence: 0.76,
918
+ location: securityRequest.targetRef,
919
+ tags: ["security", focusArea]
920
+ }))
921
+ : [
922
+ {
923
+ id: "security-finding-1",
924
+ title: "Inspect referenced security evidence before promotion",
925
+ summary: `Security review requires bounded interpretation of the referenced evidence for ${securityRequest.targetRef}.`,
926
+ severity: inferredSeverity,
927
+ rationale: "The current security workflow is read-only and request-driven, so findings remain tied to validated local references rather than automatic scanning.",
928
+ confidence: 0.72,
929
+ location: securityRequest.targetRef,
930
+ tags: ["security", "evidence"]
931
+ }
932
+ ];
933
+ const mitigations = [
934
+ ...focusAreas.map((focusArea) => `Review ${focusArea} evidence and document the release impact before promotion.`),
935
+ ...(normalizedConstraints.length > 0 ? [`Keep security follow-up bounded by: ${normalizedConstraints.join("; ")}.`] : [])
936
+ ];
937
+ const followUpWork = [
938
+ ...(normalizedEvidence?.securitySignals ?? []),
939
+ ...(referencedArtifactKinds.length > 0
940
+ ? [`Confirm the security posture for referenced artifacts: ${referencedArtifactKinds.join(", ")}.`]
941
+ : []),
942
+ "Use deterministic security evidence normalization outputs before broadening the workflow surface."
943
+ ];
944
+ const summary = `Security report prepared for ${securityRequest.targetRef}.`;
945
+ const securityReport = securityArtifactSchema.parse({
946
+ ...buildLifecycleArtifactEnvelopeBase(state, "Security Review", summary, [
947
+ requestFile ?? ".agentops/requests/security.yaml",
948
+ ...(normalizedEvidence?.provenanceRefs ?? [securityRequest.targetRef, ...securityRequest.evidenceSources])
949
+ ]),
950
+ artifactKind: "security-report",
951
+ lifecycleDomain: "security",
952
+ redaction: {
953
+ applied: true,
954
+ strategyVersion: "1.0.0",
955
+ categories: ["github-token", "api-key", "aws-key", "bearer-token", "password", "private-key", "security-sensitive"]
956
+ },
957
+ payload: {
958
+ targetRef: securityRequest.targetRef,
959
+ evidenceSources,
960
+ findings,
961
+ severitySummary: `highest severity: ${inferredSeverity}; ${findings.length} synthesized security finding(s).`,
962
+ mitigations: mitigations.length > 0
963
+ ? mitigations
964
+ : ["Review the referenced security evidence before promoting this workflow output."],
965
+ releaseImpact: securityRequest.releaseContext === "blocking"
966
+ ? "release-blocking security findings require resolution before promotion."
967
+ : securityRequest.releaseContext === "candidate"
968
+ ? "candidate release requires explicit security review before promotion."
969
+ : "no release context was supplied; security output remains advisory.",
970
+ followUpWork
971
+ }
972
+ });
973
+ return agentOutputSchema.parse({
974
+ summary,
975
+ findings: [],
976
+ proposedActions: [],
977
+ lifecycleArtifacts: [securityReport],
978
+ requestedTools: [],
979
+ blockedActionFlags: [],
980
+ confidence: 0.76,
981
+ metadata: {
982
+ deterministicInputs: {
983
+ targetRef: securityRequest.targetRef,
984
+ evidenceSources,
985
+ focusAreas,
986
+ constraints: normalizedConstraints,
987
+ referencedArtifactKinds,
988
+ normalizedEvidence: normalizedEvidence ?? null
989
+ },
990
+ synthesizedAssessment: {
991
+ severitySummary: securityReport.payload.severitySummary,
992
+ mitigations: securityReport.payload.mitigations,
993
+ followUpWork: securityReport.payload.followUpWork
994
+ }
995
+ }
996
+ });
997
+ }
998
+ };
999
+ const qaEvidenceNormalizationAgent = {
1000
+ manifest: agentManifestSchema.parse({
1001
+ version: 1,
1002
+ name: "qa-evidence-normalizer",
1003
+ displayName: "QA Evidence Normalizer",
1004
+ category: "qa",
1005
+ runtime: {
1006
+ minVersion: "0.1.0",
1007
+ kind: "deterministic"
1008
+ },
1009
+ permissions: {
1010
+ model: false,
1011
+ network: false,
1012
+ tools: [],
1013
+ readPaths: [".agentops/requests/**", ".agentops/runs/**", "**/package.json", "**/*.json", "**/*.xml", "**/*.log", "**/*.md"],
1014
+ writePaths: []
1015
+ },
1016
+ inputs: ["workflowInputs", "repo", "agentResults"],
1017
+ outputs: ["summary", "metadata"],
1018
+ contextPolicy: {
1019
+ sections: ["workflowInputs", "repo", "agentResults"],
1020
+ minimalContext: true
1021
+ },
1022
+ catalog: {
1023
+ domain: "test",
1024
+ supportLevel: "internal",
1025
+ maturity: "mvp",
1026
+ trustScope: "official-core-only"
1027
+ },
1028
+ trust: {
1029
+ tier: "core",
1030
+ source: "official",
1031
+ reviewed: true
1032
+ }
1033
+ }),
1034
+ outputSchema: agentOutputSchema,
1035
+ async execute({ stateSlice }) {
1036
+ const qaRequest = getWorkflowInput(stateSlice, "qaRequest");
1037
+ if (!qaRequest) {
1038
+ throw new Error("qa-review requires validated QA request inputs before evidence normalization.");
1039
+ }
1040
+ const repoRoot = stateSlice.repo?.root;
1041
+ const packageManager = stateSlice.repo?.packageManager || "pnpm";
1042
+ const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
1043
+ const targetType = typeof intakeMetadata.targetType === "string" ? intakeMetadata.targetType : "local-reference";
1044
+ const targetPath = repoRoot ? join(repoRoot, qaRequest.targetRef) : qaRequest.targetRef;
1045
+ if (repoRoot && !existsSync(targetPath)) {
1046
+ throw new Error(`QA target reference not found: ${qaRequest.targetRef}`);
1047
+ }
1048
+ const referencedArtifactKinds = targetType === "artifact-bundle" ? loadBundleArtifactKinds(targetPath) : [];
1049
+ const normalizedEvidenceSources = [...new Set([qaRequest.targetRef, ...qaRequest.evidenceSources])];
1050
+ const missingEvidenceSources = normalizedEvidenceSources.filter((pathValue) => repoRoot && !existsSync(join(repoRoot, pathValue)));
1051
+ if (missingEvidenceSources.length > 0) {
1052
+ throw new Error(`QA evidence source not found: ${missingEvidenceSources[0]}`);
1053
+ }
1054
+ const bundleAffectedPaths = targetType === "artifact-bundle" && referencedArtifactKinds.includes("implementation-proposal") && existsSync(targetPath)
1055
+ ? asStringArray((() => {
1056
+ const parsed = JSON.parse(readFileSync(targetPath, "utf8"));
1057
+ if (!isRecord(parsed) || !Array.isArray(parsed.lifecycleArtifacts)) {
1058
+ return [];
1059
+ }
1060
+ const implementationArtifact = parsed.lifecycleArtifacts.find((artifact) => isRecord(artifact) &&
1061
+ artifact.artifactKind === "implementation-proposal" &&
1062
+ isRecord(artifact.payload) &&
1063
+ Array.isArray(artifact.payload.affectedPaths));
1064
+ return implementationArtifact?.payload?.affectedPaths ?? [];
1065
+ })())
1066
+ : [];
1067
+ const affectedPackages = [...new Set(bundleAffectedPaths.map((pathValue) => derivePackageScope(pathValue)).filter((value) => Boolean(value)))];
1068
+ const allowedValidationCommands = collectValidationCommands(repoRoot, packageManager, affectedPackages).filter((entry) => entry.classification === "approval_required");
1069
+ const allowlistedCommands = new Set(allowedValidationCommands.map((entry) => entry.command));
1070
+ const normalizedExecutedChecks = qaRequest.executedChecks.map(normalizeRequestedCommand);
1071
+ const unrecognizedExecutedChecks = normalizedExecutedChecks.filter((command) => !allowlistedCommands.has(command));
1072
+ const normalization = qaEvidenceNormalizationSchema.parse({
1073
+ targetRef: qaRequest.targetRef,
1074
+ targetType,
1075
+ referencedArtifactKinds,
1076
+ normalizedEvidenceSources,
1077
+ missingEvidenceSources: [],
1078
+ normalizedExecutedChecks,
1079
+ unrecognizedExecutedChecks,
1080
+ affectedPackages,
1081
+ allowedValidationCommands
1082
+ });
1083
+ return agentOutputSchema.parse({
1084
+ summary: `Normalized QA evidence across ${normalization.normalizedEvidenceSources.length} source(s) and ${normalization.allowedValidationCommands.length} allowlisted validation command(s).`,
1085
+ findings: [],
1086
+ proposedActions: [],
1087
+ lifecycleArtifacts: [],
1088
+ requestedTools: [],
1089
+ blockedActionFlags: [],
1090
+ metadata: normalization
1091
+ });
1092
+ }
1093
+ };
1094
+ const qaAnalystAgent = {
1095
+ manifest: agentManifestSchema.parse({
1096
+ version: 1,
1097
+ name: "qa-analyst",
1098
+ displayName: "QA Analyst",
1099
+ category: "qa",
1100
+ runtime: {
1101
+ minVersion: "0.1.0",
1102
+ kind: "reasoning"
1103
+ },
1104
+ permissions: {
1105
+ model: true,
1106
+ network: false,
1107
+ tools: [],
1108
+ readPaths: ["**/*"],
1109
+ writePaths: []
1110
+ },
1111
+ inputs: ["workflowInputs", "repo", "changes", "agentResults"],
1112
+ outputs: ["lifecycleArtifacts"],
1113
+ contextPolicy: {
1114
+ sections: ["workflowInputs", "repo", "changes", "agentResults"],
1115
+ minimalContext: true
1116
+ },
1117
+ catalog: {
1118
+ domain: "test",
1119
+ supportLevel: "internal",
1120
+ maturity: "mvp",
1121
+ trustScope: "official-core-only"
1122
+ },
1123
+ trust: {
1124
+ tier: "core",
1125
+ source: "official",
1126
+ reviewed: true
1127
+ }
1128
+ }),
1129
+ outputSchema: agentOutputSchema,
1130
+ async execute({ state, stateSlice }) {
1131
+ const qaRequest = getWorkflowInput(stateSlice, "qaRequest");
1132
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
1133
+ if (!qaRequest) {
1134
+ throw new Error("qa-review requires validated QA inputs before QA analysis.");
1135
+ }
1136
+ const intakeMetadata = isRecord(stateSlice.agentResults?.intake?.metadata) ? stateSlice.agentResults.intake.metadata : {};
1137
+ const evidenceMetadata = isRecord(stateSlice.agentResults?.evidence?.metadata) ? stateSlice.agentResults.evidence.metadata : {};
1138
+ const normalizedEvidenceSources = asStringArray(evidenceMetadata.normalizedEvidenceSources);
1139
+ const normalizedExecutedChecks = asStringArray(evidenceMetadata.normalizedExecutedChecks);
1140
+ const normalizedFocusAreas = asStringArray(intakeMetadata.focusAreas);
1141
+ const normalizedConstraints = asStringArray(intakeMetadata.constraints);
1142
+ const missingEvidenceSources = asStringArray(evidenceMetadata.missingEvidenceSources);
1143
+ const unrecognizedExecutedChecks = asStringArray(evidenceMetadata.unrecognizedExecutedChecks);
1144
+ const targetType = typeof evidenceMetadata.targetType === "string"
1145
+ ? evidenceMetadata.targetType
1146
+ : typeof intakeMetadata.targetType === "string"
1147
+ ? intakeMetadata.targetType
1148
+ : "local-reference";
1149
+ const evidenceSources = normalizedEvidenceSources.length > 0
1150
+ ? normalizedEvidenceSources
1151
+ : [...new Set([qaRequest.targetRef, ...qaRequest.evidenceSources])];
1152
+ const executedChecks = normalizedExecutedChecks.length > 0 ? normalizedExecutedChecks : qaRequest.executedChecks;
1153
+ const focusAreas = normalizedFocusAreas.length > 0 ? normalizedFocusAreas : qaRequest.focusAreas;
1154
+ const findings = focusAreas.length > 0
1155
+ ? focusAreas.map((focusArea, index) => ({
1156
+ id: `qa-finding-${index + 1}`,
1157
+ title: `Inspect ${focusArea} evidence before promotion`,
1158
+ summary: `QA review flagged ${focusArea} as an area requiring bounded interpretation for ${qaRequest.targetRef}.`,
1159
+ severity: qaRequest.releaseContext === "blocking" ? "high" : qaRequest.releaseContext === "candidate" ? "medium" : "low",
1160
+ rationale: `The MVP QA workflow remains read-only and request-driven, so ${focusArea} still depends on referenced evidence rather than automatic execution.`,
1161
+ confidence: 0.74,
1162
+ location: qaRequest.targetRef,
1163
+ tags: ["qa", focusArea]
1164
+ }))
1165
+ : [
1166
+ {
1167
+ id: "qa-finding-1",
1168
+ title: "Review referenced QA evidence before promotion",
1169
+ summary: `QA review requires bounded interpretation of the referenced evidence for ${qaRequest.targetRef}.`,
1170
+ severity: qaRequest.releaseContext === "blocking" ? "high" : "medium",
1171
+ rationale: "The current QA workflow synthesizes a report from validated references and does not execute arbitrary test commands.",
1172
+ confidence: 0.71,
1173
+ location: qaRequest.targetRef,
1174
+ tags: ["qa", "evidence"]
1175
+ }
1176
+ ];
1177
+ const coverageGaps = [
1178
+ ...(evidenceSources.length === 0 ? ["No QA evidence sources were provided beyond the target reference."] : []),
1179
+ ...missingEvidenceSources.map((pathValue) => `Referenced QA evidence is missing: ${pathValue}`),
1180
+ ...(focusAreas.includes("coverage") ? ["Coverage evidence still needs deterministic normalization before it can be promoted to an official QA signal."] : []),
1181
+ ...(executedChecks.length === 0 ? ["No executed validation checks were recorded in the request."] : []),
1182
+ ...unrecognizedExecutedChecks.map((command) => `Executed check is outside the bounded allowlist and still needs manual interpretation: ${command}`)
1183
+ ];
1184
+ const recommendedNextChecks = [
1185
+ ...executedChecks.map((command) => `Review the recorded output for \`${command}\` before promotion.`),
1186
+ ...focusAreas.map((focusArea) => `Confirm whether ${focusArea} needs additional deterministic QA evidence.`),
1187
+ ...(normalizedConstraints.length > 0 ? [`Keep QA follow-up bounded by: ${normalizedConstraints.join("; ")}.`] : [])
1188
+ ];
1189
+ const summary = `QA report prepared for ${qaRequest.targetRef}.`;
1190
+ const qaReport = qaArtifactSchema.parse({
1191
+ ...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/qa.yaml", qaRequest.targetRef, ...qaRequest.evidenceSources], []),
1192
+ artifactKind: "qa-report",
1193
+ lifecycleDomain: "test",
1194
+ workflow: {
1195
+ name: state.workflow,
1196
+ displayName: "QA Review"
1197
+ },
1198
+ payload: {
1199
+ targetRef: qaRequest.targetRef,
1200
+ evidenceSources,
1201
+ executedChecks,
1202
+ findings,
1203
+ coverageGaps,
1204
+ recommendedNextChecks: recommendedNextChecks.length > 0
1205
+ ? recommendedNextChecks
1206
+ : ["Capture additional bounded QA evidence before promotion."],
1207
+ releaseImpact: qaRequest.releaseContext === "blocking"
1208
+ ? "release-blocking QA findings require resolution before promotion."
1209
+ : qaRequest.releaseContext === "candidate"
1210
+ ? "candidate release still requires explicit QA review before promotion."
1211
+ : "no release context was supplied; QA output remains advisory."
1212
+ }
1213
+ });
1214
+ return agentOutputSchema.parse({
1215
+ summary,
1216
+ findings: [],
1217
+ proposedActions: [],
1218
+ lifecycleArtifacts: [qaReport],
1219
+ requestedTools: [],
1220
+ blockedActionFlags: [],
1221
+ confidence: 0.74,
1222
+ metadata: {
1223
+ deterministicInputs: {
1224
+ targetRef: qaRequest.targetRef,
1225
+ targetType,
1226
+ evidenceSources,
1227
+ executedChecks,
1228
+ focusAreas,
1229
+ constraints: normalizedConstraints
1230
+ },
1231
+ synthesizedAssessment: {
1232
+ releaseContext: qaRequest.releaseContext,
1233
+ recommendedNextChecks: qaReport.payload.recommendedNextChecks,
1234
+ coverageGaps: qaReport.payload.coverageGaps
1235
+ }
1236
+ }
1237
+ });
1238
+ }
1239
+ };
1240
+ const implementationInventoryAgent = {
1241
+ manifest: agentManifestSchema.parse({
1242
+ version: 1,
1243
+ name: "implementation-inventory",
1244
+ displayName: "Implementation Inventory",
1245
+ category: "implementation",
1246
+ runtime: {
1247
+ minVersion: "0.1.0",
1248
+ kind: "deterministic"
1249
+ },
1250
+ permissions: {
1251
+ model: false,
1252
+ network: false,
1253
+ tools: [],
1254
+ readPaths: ["**/*"],
1255
+ writePaths: []
1256
+ },
1257
+ inputs: ["workflowInputs", "repo", "changes"],
1258
+ outputs: ["summary", "metadata"],
1259
+ contextPolicy: {
1260
+ sections: ["workflowInputs", "repo", "changes"],
1261
+ minimalContext: true
1262
+ },
1263
+ catalog: {
1264
+ domain: "build",
1265
+ supportLevel: "internal",
1266
+ maturity: "mvp",
1267
+ trustScope: "official-core-only"
1268
+ },
1269
+ trust: {
1270
+ tier: "core",
1271
+ source: "official",
1272
+ reviewed: true
1273
+ }
1274
+ }),
1275
+ outputSchema: agentOutputSchema,
1276
+ async execute({ stateSlice }) {
1277
+ const implementationRequest = getWorkflowInput(stateSlice, "implementationRequest");
1278
+ const designRecord = getWorkflowInput(stateSlice, "designRecord");
1279
+ if (!implementationRequest || !designRecord) {
1280
+ throw new Error("implementation-proposal requires deterministic inventory inputs before proposal analysis.");
1281
+ }
1282
+ const repoRoot = stateSlice.repo?.root;
1283
+ const packageManager = stateSlice.repo?.packageManager || "pnpm";
1284
+ const candidatePaths = [
1285
+ ...new Set([
1286
+ ...implementationRequest.targetPaths,
1287
+ ...designRecord.payload.interfacesImpacted,
1288
+ ...designRecord.payload.schemaChangesNeeded,
1289
+ ...designRecord.payload.policyChangesNeeded
1290
+ ])
1291
+ ];
1292
+ const resolvedAffectedPaths = candidatePaths.filter((pathValue) => {
1293
+ if (!pathValue) {
1294
+ return false;
1295
+ }
1296
+ if (!repoRoot) {
1297
+ return true;
1298
+ }
1299
+ return existsSync(join(repoRoot, pathValue));
1300
+ });
1301
+ const affectedPackages = [...new Set(resolvedAffectedPaths.map(derivePackageScope).filter((value) => Boolean(value)))];
1302
+ const entrypoints = [
1303
+ ...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.endsWith("src/index.ts") || pathValue.endsWith("package.json") || pathValue.endsWith("agent.manifest.json")))
1304
+ ];
1305
+ const schemaSurfaces = [...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.includes("schema")))];
1306
+ const policySurfaces = [
1307
+ ...new Set(resolvedAffectedPaths.filter((pathValue) => pathValue.includes("policy") || pathValue.includes(".agentops/policy.yaml")))
1308
+ ];
1309
+ const discoveredValidationCommands = collectValidationCommands(repoRoot, packageManager, affectedPackages);
1310
+ const normalizedRequestedCommands = implementationRequest.validationCommands.map(normalizeRequestedCommand);
1311
+ const allowlistedCommands = new Set(discoveredValidationCommands
1312
+ .filter((entry) => entry.classification === "approval_required")
1313
+ .map((entry) => entry.command));
1314
+ for (const requestedCommand of normalizedRequestedCommands) {
1315
+ if (!allowlistedCommands.has(requestedCommand)) {
1316
+ throw new Error(`Implementation request contains non-allowlisted validation command: ${requestedCommand}`);
1317
+ }
1318
+ discoveredValidationCommands.push({
1319
+ command: requestedCommand,
1320
+ source: "request",
1321
+ classification: "approval_required",
1322
+ reason: "Requested command matches a discovered allowlisted validation script."
1323
+ });
1324
+ }
1325
+ const inventory = implementationInventorySchema.parse({
1326
+ requestedTargetPaths: implementationRequest.targetPaths,
1327
+ resolvedAffectedPaths,
1328
+ affectedPackages,
1329
+ entrypoints,
1330
+ schemaSurfaces,
1331
+ policySurfaces,
1332
+ discoveredValidationCommands
1333
+ });
1334
+ return agentOutputSchema.parse({
1335
+ summary: `Collected deterministic implementation inventory across ${inventory.resolvedAffectedPaths.length} path(s) and ${inventory.discoveredValidationCommands.length} validation command(s).`,
1336
+ findings: [],
1337
+ proposedActions: [],
1338
+ lifecycleArtifacts: [],
1339
+ requestedTools: [],
1340
+ blockedActionFlags: [],
1341
+ metadata: inventory
1342
+ });
1343
+ }
1344
+ };
1345
+ const implementationPlannerAgent = {
1346
+ manifest: agentManifestSchema.parse({
1347
+ version: 1,
1348
+ name: "implementation-planner",
1349
+ displayName: "Implementation Planner",
1350
+ category: "implementation",
1351
+ runtime: {
1352
+ minVersion: "0.1.0",
1353
+ kind: "reasoning"
1354
+ },
1355
+ permissions: {
1356
+ model: true,
1357
+ network: false,
1358
+ tools: [],
1359
+ readPaths: ["**/*"],
1360
+ writePaths: []
1361
+ },
1362
+ inputs: ["workflowInputs", "repo", "changes", "agentResults"],
1363
+ outputs: ["lifecycleArtifacts"],
1364
+ contextPolicy: {
1365
+ sections: ["workflowInputs", "repo", "changes", "agentResults"],
1366
+ minimalContext: true
1367
+ },
1368
+ catalog: {
1369
+ domain: "build",
1370
+ supportLevel: "official",
1371
+ maturity: "mvp",
1372
+ trustScope: "official-core-only"
1373
+ },
1374
+ trust: {
1375
+ tier: "core",
1376
+ source: "official",
1377
+ reviewed: true
1378
+ }
1379
+ }),
1380
+ outputSchema: agentOutputSchema,
1381
+ async execute({ state, stateSlice }) {
1382
+ const implementationRequest = getWorkflowInput(stateSlice, "implementationRequest");
1383
+ const designRecord = getWorkflowInput(stateSlice, "designRecord");
1384
+ const requestFile = getWorkflowInput(stateSlice, "requestFile");
1385
+ if (!implementationRequest || !designRecord) {
1386
+ throw new Error("implementation-proposal requires validated implementation inputs before proposal analysis.");
1387
+ }
1388
+ const inventoryMetadata = implementationInventorySchema.safeParse(stateSlice.agentResults?.inventory?.metadata);
1389
+ const inventory = inventoryMetadata.success ? inventoryMetadata.data : undefined;
1390
+ const normalizedAffectedPaths = inventory && inventory.resolvedAffectedPaths.length > 0
1391
+ ? inventory.resolvedAffectedPaths
1392
+ : [
1393
+ ...new Set([
1394
+ ...implementationRequest.targetPaths,
1395
+ ...designRecord.payload.interfacesImpacted,
1396
+ ...designRecord.payload.schemaChangesNeeded,
1397
+ ...designRecord.payload.policyChangesNeeded
1398
+ ])
1399
+ ];
1400
+ const finalAffectedPaths = normalizedAffectedPaths.length > 0
1401
+ ? normalizedAffectedPaths
1402
+ : ["Repository paths still need deterministic build-surface confirmation."];
1403
+ const proposedChanges = [
1404
+ `Prepare a bounded implementation plan for ${implementationRequest.implementationGoal}.`,
1405
+ ...finalAffectedPaths.slice(0, 5).map((pathValue) => `Plan targeted edits for ${pathValue}.`)
1406
+ ];
1407
+ const selectedCommands = inventory
1408
+ ? inventory.discoveredValidationCommands.filter((entry) => entry.classification === "approval_required" &&
1409
+ (implementationRequest.validationCommands.length === 0 || entry.source === "request"))
1410
+ : [];
1411
+ const validationPlan = selectedCommands.length > 0
1412
+ ? selectedCommands.map((entry) => `Command \`${entry.command}\` is available but approval-required before execution.`)
1413
+ : ["Confirm allowlisted validation commands in the next deterministic implementation slice before execution."];
1414
+ const approvalRequiredSteps = implementationRequest.approvalMode === "apply-capable"
1415
+ ? [
1416
+ "Any future patch application requires explicit approval before execution.",
1417
+ "Any future build or validation execution requires approval after allowlist review."
1418
+ ]
1419
+ : ["The default path remains proposal-only; any patch or build execution requires a separate approved workflow."];
1420
+ const risks = [
1421
+ ...(implementationRequest.targetPaths.length === 0
1422
+ ? ["Target paths were not supplied, so affected surfaces may still broaden after deterministic discovery."]
1423
+ : []),
1424
+ ...(implementationRequest.validationCommands.length === 0
1425
+ ? ["Validation commands are not yet specified and will need deterministic allowlist confirmation later."]
1426
+ : [])
1427
+ ];
1428
+ const openQuestions = [
1429
+ ...(implementationRequest.constraints.length === 0
1430
+ ? ["Which additional implementation constraints should be captured before execution work begins?"]
1431
+ : []),
1432
+ ...(designRecord.payload.policyChangesNeeded.length > 0
1433
+ ? ["Do the policy surfaces identified in the design record require a separate approval review?"]
1434
+ : [])
1435
+ ];
1436
+ const summary = `Implementation proposal prepared for ${implementationRequest.implementationGoal}.`;
1437
+ const implementationProposal = implementationArtifactSchema.parse({
1438
+ ...buildArtifactEnvelopeBase(state, summary, [requestFile ?? ".agentops/requests/implementation.yaml", implementationRequest.designRecordRef], designRecord.source.issueRefs),
1439
+ artifactKind: "implementation-proposal",
1440
+ lifecycleDomain: "build",
1441
+ workflow: {
1442
+ name: state.workflow,
1443
+ displayName: "Implementation Proposal"
1444
+ },
1445
+ payload: {
1446
+ designRecordRef: implementationRequest.designRecordRef,
1447
+ implementationGoal: implementationRequest.implementationGoal,
1448
+ affectedPaths: finalAffectedPaths,
1449
+ proposedChanges,
1450
+ validationPlan,
1451
+ approvalRequiredSteps,
1452
+ risks,
1453
+ openQuestions
1454
+ }
1455
+ });
1456
+ return agentOutputSchema.parse({
1457
+ summary,
1458
+ findings: [],
1459
+ proposedActions: [],
1460
+ lifecycleArtifacts: [implementationProposal],
1461
+ requestedTools: [],
1462
+ blockedActionFlags: [],
1463
+ confidence: 0.76,
1464
+ metadata: {
1465
+ deterministicInputs: {
1466
+ targetPaths: implementationRequest.targetPaths,
1467
+ validationCommands: implementationRequest.validationCommands,
1468
+ constraints: implementationRequest.constraints,
1469
+ designInterfaces: designRecord.payload.interfacesImpacted,
1470
+ inventory: inventory ?? null
1471
+ },
1472
+ synthesizedProposal: {
1473
+ affectedPaths: finalAffectedPaths,
1474
+ approvalRequiredSteps,
1475
+ openQuestions
1476
+ }
1477
+ }
1478
+ });
1479
+ }
1480
+ };
439
1481
  const designAnalystAgent = {
440
1482
  manifest: agentManifestSchema.parse({
441
1483
  version: 1,
@@ -792,6 +1834,15 @@ export function createBuiltinAgentRegistry() {
792
1834
  ["planning-analyst", planningAnalystAgent],
793
1835
  ["design-intake", designIntakeAgent],
794
1836
  ["design-inventory", designInventoryAgent],
1837
+ ["implementation-intake", implementationIntakeAgent],
1838
+ ["qa-intake", qaIntakeAgent],
1839
+ ["security-intake", securityIntakeAgent],
1840
+ ["security-evidence-normalizer", securityEvidenceNormalizationAgent],
1841
+ ["security-analyst", securityAnalystAgent],
1842
+ ["qa-evidence-normalizer", qaEvidenceNormalizationAgent],
1843
+ ["qa-analyst", qaAnalystAgent],
1844
+ ["implementation-inventory", implementationInventoryAgent],
1845
+ ["implementation-planner", implementationPlannerAgent],
795
1846
  ["design-analyst", designAnalystAgent],
796
1847
  ["code-review", codeReviewAgent],
797
1848
  ["security-audit", securityAuditAgent],