@amistio/cli 0.1.10 → 0.1.12
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/README.md +4 -0
- package/dist/index.js +613 -2
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,6 +20,10 @@ var itemTypeSchema = z.enum([
|
|
|
20
20
|
"repositoryLink",
|
|
21
21
|
"brainDocument",
|
|
22
22
|
"generatedDraft",
|
|
23
|
+
"issue",
|
|
24
|
+
"securityScan",
|
|
25
|
+
"securityFinding",
|
|
26
|
+
"securityPostureSnapshot",
|
|
23
27
|
"syncCursor",
|
|
24
28
|
"syncConflict",
|
|
25
29
|
"workItem",
|
|
@@ -66,11 +70,22 @@ var workStatusSchema = z.enum([
|
|
|
66
70
|
]);
|
|
67
71
|
var sourceSchema = z.enum(["web", "repo", "generated", "runner"]);
|
|
68
72
|
var executionModeSchema = z.enum(["localRunner", "cloudSandbox"]);
|
|
69
|
-
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview"]);
|
|
73
|
+
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan"]);
|
|
70
74
|
var generatedDraftStatusSchema = z.enum(["queued", "generating", "reviewing", "approved", "rejected", "changesRequested", "failed"]);
|
|
71
75
|
var assistantQuestionModeSchema = z.enum(["brainOnly", "sourceAware"]);
|
|
72
76
|
var impactRiskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
|
|
73
77
|
var impactReportStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale"]);
|
|
78
|
+
var issueCategorySchema = z.enum(["bug", "regression", "brokenWorkflow", "security", "operational", "other"]);
|
|
79
|
+
var issueSeveritySchema = z.enum(["low", "medium", "high", "critical"]);
|
|
80
|
+
var issueStatusSchema = z.enum(["queued", "diagnosing", "analysisReady", "approved", "changesRequested", "rejected", "implementationQueued", "implemented", "failed"]);
|
|
81
|
+
var issueApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "rejected", "implemented", "blocked"]);
|
|
82
|
+
var securityScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
|
|
83
|
+
var securityScanSourceSchema = z.enum(["nightly", "manual"]);
|
|
84
|
+
var securityFindingCategorySchema = z.enum(["owasp", "dependency", "supplyChain", "secretHygiene", "authentication", "authorization", "tenantIsolation", "headers", "redirects", "csrf", "cors", "ciCd", "logging", "runnerBoundary", "other"]);
|
|
85
|
+
var securityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
|
|
86
|
+
var securityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
87
|
+
var securityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "remediationQueued", "resolved", "failed"]);
|
|
88
|
+
var securityApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
|
|
74
89
|
var activityEventTypeSchema = z.enum([
|
|
75
90
|
"commandSubmitted",
|
|
76
91
|
"generationQueued",
|
|
@@ -89,6 +104,19 @@ var activityEventTypeSchema = z.enum([
|
|
|
89
104
|
"impactPreviewCompleted",
|
|
90
105
|
"impactPreviewFailed",
|
|
91
106
|
"impactPreviewStale",
|
|
107
|
+
"issueDiagnosisQueued",
|
|
108
|
+
"issueDiagnosisCompleted",
|
|
109
|
+
"issueDiagnosisFailed",
|
|
110
|
+
"issueReview",
|
|
111
|
+
"issueImplementationQueued",
|
|
112
|
+
"securityScanQueued",
|
|
113
|
+
"securityScanStarted",
|
|
114
|
+
"securityScanCompleted",
|
|
115
|
+
"securityScanFailed",
|
|
116
|
+
"securityFindingOpened",
|
|
117
|
+
"securityFindingReviewed",
|
|
118
|
+
"securityRemediationPlanCreated",
|
|
119
|
+
"securityRemediationQueued",
|
|
92
120
|
"handoffExported"
|
|
93
121
|
]);
|
|
94
122
|
var activityEventActorSchema = z.enum(["webUser", "runner", "cli", "system"]);
|
|
@@ -338,6 +366,9 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
338
366
|
approvedByUserId: z.string().min(1).optional(),
|
|
339
367
|
sourceWish: z.string().min(1).optional(),
|
|
340
368
|
generatedDraftId: z.string().min(1).optional(),
|
|
369
|
+
issueId: z.string().min(1).optional(),
|
|
370
|
+
securityScanId: z.string().min(1).optional(),
|
|
371
|
+
securityFindingId: z.string().min(1).optional(),
|
|
341
372
|
repositoryLinkId: z.string().min(1).optional(),
|
|
342
373
|
reviewThreadId: z.string().min(1).optional(),
|
|
343
374
|
reviewDocumentId: z.string().min(1).optional(),
|
|
@@ -386,6 +417,7 @@ var runnerHeartbeatItemSchema = baseItemSchema.extend({
|
|
|
386
417
|
mode: z.enum(["foreground", "background"]).optional(),
|
|
387
418
|
hostname: z.string().min(1).optional(),
|
|
388
419
|
runnerName: z.string().min(1).optional(),
|
|
420
|
+
supportedWorkKinds: z.array(workKindSchema).optional(),
|
|
389
421
|
supportsBranchIsolation: z.boolean().optional(),
|
|
390
422
|
supportsGitWorktreeIsolation: z.boolean().optional(),
|
|
391
423
|
currentWorkItemId: z.string().min(1).optional(),
|
|
@@ -720,6 +752,148 @@ var impactPreviewResultSchema = z.object({
|
|
|
720
752
|
analyzedRepoRevision: z.number().int().nonnegative().optional(),
|
|
721
753
|
repoFingerprint: z.string().min(1).optional()
|
|
722
754
|
});
|
|
755
|
+
var issueDiagnosisResultSchema = z.object({
|
|
756
|
+
summary: z.string().trim().min(1),
|
|
757
|
+
impact: z.string().trim().min(1),
|
|
758
|
+
evidence: z.array(z.string().trim().min(1)).default([]),
|
|
759
|
+
affectedSurfaces: z.array(z.string().trim().min(1)).default([]),
|
|
760
|
+
rootCauseAnalysis: z.string().trim().min(1),
|
|
761
|
+
falsifiableHypothesis: z.string().trim().min(1).optional(),
|
|
762
|
+
nextCheck: z.string().trim().min(1).optional(),
|
|
763
|
+
proposedFix: z.string().trim().min(1),
|
|
764
|
+
expectedBehavior: z.string().trim().min(1),
|
|
765
|
+
verificationPlan: z.array(z.string().trim().min(1)).min(1),
|
|
766
|
+
riskLevel: impactRiskLevelSchema.default("medium"),
|
|
767
|
+
likelyPaths: z.array(impactPathSchema).default([]),
|
|
768
|
+
approvalState: issueApprovalStateSchema.default("proposed")
|
|
769
|
+
});
|
|
770
|
+
var issueItemSchema = baseItemSchema.extend({
|
|
771
|
+
type: z.literal("issue"),
|
|
772
|
+
projectId: z.string().min(1),
|
|
773
|
+
issueId: z.string().min(1),
|
|
774
|
+
title: z.string().trim().min(1).max(200),
|
|
775
|
+
description: z.string().trim().min(1).max(12e3),
|
|
776
|
+
category: issueCategorySchema.default("bug"),
|
|
777
|
+
severity: issueSeveritySchema.default("medium"),
|
|
778
|
+
status: issueStatusSchema,
|
|
779
|
+
approvalState: issueApprovalStateSchema.default("proposed"),
|
|
780
|
+
submittedByUserId: z.string().min(1).optional(),
|
|
781
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
782
|
+
diagnosisWorkItemId: z.string().min(1).optional(),
|
|
783
|
+
implementationWorkItemId: z.string().min(1).optional(),
|
|
784
|
+
diagnosis: issueDiagnosisResultSchema.optional(),
|
|
785
|
+
reviewNotes: z.string().trim().min(1).optional(),
|
|
786
|
+
approvedByUserId: z.string().min(1).optional(),
|
|
787
|
+
approvedAt: isoDateTimeSchema.optional(),
|
|
788
|
+
requestedChangesByUserId: z.string().min(1).optional(),
|
|
789
|
+
requestedChangesAt: isoDateTimeSchema.optional(),
|
|
790
|
+
rejectedByUserId: z.string().min(1).optional(),
|
|
791
|
+
rejectedAt: isoDateTimeSchema.optional(),
|
|
792
|
+
diagnosedAt: isoDateTimeSchema.optional(),
|
|
793
|
+
runnerId: z.string().min(1).optional(),
|
|
794
|
+
lastDiagnosisError: z.string().optional()
|
|
795
|
+
});
|
|
796
|
+
var securityStandardReferenceSchema = z.object({
|
|
797
|
+
standard: z.string().trim().min(1).max(120),
|
|
798
|
+
control: z.string().trim().min(1).max(160),
|
|
799
|
+
url: z.string().url().optional()
|
|
800
|
+
});
|
|
801
|
+
var securitySeverityCountsSchema = z.object({
|
|
802
|
+
info: z.number().int().nonnegative().default(0),
|
|
803
|
+
low: z.number().int().nonnegative().default(0),
|
|
804
|
+
medium: z.number().int().nonnegative().default(0),
|
|
805
|
+
high: z.number().int().nonnegative().default(0),
|
|
806
|
+
critical: z.number().int().nonnegative().default(0)
|
|
807
|
+
});
|
|
808
|
+
var defaultSecuritySeverityCounts = { info: 0, low: 0, medium: 0, high: 0, critical: 0 };
|
|
809
|
+
var securityCategorySummarySchema = z.object({
|
|
810
|
+
category: securityFindingCategorySchema,
|
|
811
|
+
status: z.enum(["pass", "warning", "fail", "unknown"]),
|
|
812
|
+
summary: z.string().trim().min(1).max(600)
|
|
813
|
+
});
|
|
814
|
+
var securityFindingResultSchema = z.object({
|
|
815
|
+
title: z.string().trim().min(1).max(200),
|
|
816
|
+
category: securityFindingCategorySchema,
|
|
817
|
+
severity: securityFindingSeveritySchema,
|
|
818
|
+
confidence: securityFindingConfidenceSchema.default("medium"),
|
|
819
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
820
|
+
standardReferences: z.array(securityStandardReferenceSchema).default([]),
|
|
821
|
+
affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
822
|
+
evidence: z.array(z.string().trim().min(1).max(600)).default([]),
|
|
823
|
+
recommendedRemediation: z.string().trim().min(1).max(4e3),
|
|
824
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
|
|
825
|
+
safePaths: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
826
|
+
status: securityFindingStatusSchema.default("open")
|
|
827
|
+
});
|
|
828
|
+
var securityPostureScanResultSchema = z.object({
|
|
829
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
830
|
+
baselineVersion: z.string().trim().min(1).max(80),
|
|
831
|
+
postureScore: z.number().min(0).max(100).optional(),
|
|
832
|
+
postureGrade: z.enum(["A", "B", "C", "D", "F", "Unknown"]).default("Unknown"),
|
|
833
|
+
categorySummaries: z.array(securityCategorySummarySchema).default([]),
|
|
834
|
+
findings: z.array(securityFindingResultSchema).default([]),
|
|
835
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1)
|
|
836
|
+
});
|
|
837
|
+
var securityScanItemSchema = baseItemSchema.extend({
|
|
838
|
+
type: z.literal("securityScan"),
|
|
839
|
+
projectId: z.string().min(1),
|
|
840
|
+
securityScanId: z.string().min(1),
|
|
841
|
+
scheduledDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
842
|
+
source: securityScanSourceSchema,
|
|
843
|
+
status: securityScanStatusSchema,
|
|
844
|
+
baselineVersion: z.string().trim().min(1).max(80).default("amistio-security-baseline-v1"),
|
|
845
|
+
workItemId: z.string().min(1).optional(),
|
|
846
|
+
runnerId: z.string().min(1).optional(),
|
|
847
|
+
findingIds: z.array(z.string().min(1)).default([]),
|
|
848
|
+
summary: z.string().trim().min(1).max(2e3).optional(),
|
|
849
|
+
severityCounts: securitySeverityCountsSchema.default(defaultSecuritySeverityCounts),
|
|
850
|
+
startedAt: isoDateTimeSchema.optional(),
|
|
851
|
+
completedAt: isoDateTimeSchema.optional(),
|
|
852
|
+
failedAt: isoDateTimeSchema.optional(),
|
|
853
|
+
error: z.string().max(1e3).optional()
|
|
854
|
+
});
|
|
855
|
+
var securityFindingItemSchema = baseItemSchema.extend({
|
|
856
|
+
type: z.literal("securityFinding"),
|
|
857
|
+
projectId: z.string().min(1),
|
|
858
|
+
securityFindingId: z.string().min(1),
|
|
859
|
+
securityScanId: z.string().min(1),
|
|
860
|
+
title: z.string().trim().min(1).max(200),
|
|
861
|
+
category: securityFindingCategorySchema,
|
|
862
|
+
severity: securityFindingSeveritySchema,
|
|
863
|
+
confidence: securityFindingConfidenceSchema.default("medium"),
|
|
864
|
+
status: securityFindingStatusSchema,
|
|
865
|
+
approvalState: securityApprovalStateSchema.default("proposed"),
|
|
866
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
867
|
+
standardReferences: z.array(securityStandardReferenceSchema).default([]),
|
|
868
|
+
affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
869
|
+
evidence: z.array(z.string().trim().min(1).max(600)).default([]),
|
|
870
|
+
safePaths: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
871
|
+
recommendedRemediation: z.string().trim().min(1).max(4e3),
|
|
872
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
|
|
873
|
+
remediationPlanDocumentId: z.string().min(1).optional(),
|
|
874
|
+
remediationPromptDocumentId: z.string().min(1).optional(),
|
|
875
|
+
implementationWorkItemId: z.string().min(1).optional(),
|
|
876
|
+
reviewNotes: z.string().trim().min(1).optional(),
|
|
877
|
+
reviewedByUserId: z.string().min(1).optional(),
|
|
878
|
+
reviewedAt: isoDateTimeSchema.optional(),
|
|
879
|
+
resolvedAt: isoDateTimeSchema.optional(),
|
|
880
|
+
lastReviewAction: z.enum(["approve", "requestChanges", "dismiss", "acceptRisk", "reopen"]).optional()
|
|
881
|
+
});
|
|
882
|
+
var securityPostureSnapshotItemSchema = baseItemSchema.extend({
|
|
883
|
+
type: z.literal("securityPostureSnapshot"),
|
|
884
|
+
projectId: z.string().min(1),
|
|
885
|
+
securityPostureSnapshotId: z.string().min(1),
|
|
886
|
+
latestScanId: z.string().min(1).optional(),
|
|
887
|
+
status: z.enum(["unknown", "fresh", "stale", "running", "failed"]),
|
|
888
|
+
baselineVersion: z.string().trim().min(1).max(80).default("amistio-security-baseline-v1"),
|
|
889
|
+
postureScore: z.number().min(0).max(100).optional(),
|
|
890
|
+
postureGrade: z.enum(["A", "B", "C", "D", "F", "Unknown"]).default("Unknown"),
|
|
891
|
+
severityCounts: securitySeverityCountsSchema.default(defaultSecuritySeverityCounts),
|
|
892
|
+
categorySummaries: z.array(securityCategorySummarySchema).default([]),
|
|
893
|
+
lastScannedAt: isoDateTimeSchema.optional(),
|
|
894
|
+
nextScheduledAt: isoDateTimeSchema.optional(),
|
|
895
|
+
summary: z.string().trim().min(1).max(2e3).optional()
|
|
896
|
+
});
|
|
723
897
|
var activityEventItemSchema = baseItemSchema.extend({
|
|
724
898
|
type: z.literal("activityEvent"),
|
|
725
899
|
projectId: z.string().min(1),
|
|
@@ -734,6 +908,9 @@ var activityEventItemSchema = baseItemSchema.extend({
|
|
|
734
908
|
occurredAt: isoDateTimeSchema,
|
|
735
909
|
relatedWorkItemId: z.string().min(1).optional(),
|
|
736
910
|
relatedDocumentId: z.string().min(1).optional(),
|
|
911
|
+
relatedIssueId: z.string().min(1).optional(),
|
|
912
|
+
relatedSecurityScanId: z.string().min(1).optional(),
|
|
913
|
+
relatedSecurityFindingId: z.string().min(1).optional(),
|
|
737
914
|
generatedDraftId: z.string().min(1).optional(),
|
|
738
915
|
runnerId: z.string().min(1).optional(),
|
|
739
916
|
repositoryLinkId: z.string().min(1).optional(),
|
|
@@ -790,6 +967,10 @@ var projectItemUnionSchema = z.discriminatedUnion("type", [
|
|
|
790
967
|
repositoryLinkItemSchema,
|
|
791
968
|
brainDocumentItemSchema,
|
|
792
969
|
generatedDraftItemSchema,
|
|
970
|
+
issueItemSchema,
|
|
971
|
+
securityScanItemSchema,
|
|
972
|
+
securityFindingItemSchema,
|
|
973
|
+
securityPostureSnapshotItemSchema,
|
|
793
974
|
syncCursorItemSchema,
|
|
794
975
|
syncConflictItemSchema,
|
|
795
976
|
workItemSchema,
|
|
@@ -1527,6 +1708,13 @@ var ApiClient = class {
|
|
|
1527
1708
|
{ method: "GET" }
|
|
1528
1709
|
);
|
|
1529
1710
|
}
|
|
1711
|
+
async listIssues(projectId) {
|
|
1712
|
+
return this.request(
|
|
1713
|
+
`/projects/${projectId}/issues`,
|
|
1714
|
+
z3.object({ issues: z3.array(issueItemSchema) }),
|
|
1715
|
+
{ method: "GET" }
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1530
1718
|
async pushBrainDocuments(projectId, documents) {
|
|
1531
1719
|
return this.request(
|
|
1532
1720
|
`/projects/${projectId}/brain-documents`,
|
|
@@ -1684,6 +1872,26 @@ var ApiClient = class {
|
|
|
1684
1872
|
}
|
|
1685
1873
|
);
|
|
1686
1874
|
}
|
|
1875
|
+
async submitIssueDiagnosisResult(projectId, workItemId, result) {
|
|
1876
|
+
return this.request(
|
|
1877
|
+
`/projects/${projectId}/work-items/${workItemId}/issue-result`,
|
|
1878
|
+
z3.object({ issue: issueItemSchema, workItem: workItemSchema }),
|
|
1879
|
+
{
|
|
1880
|
+
method: "POST",
|
|
1881
|
+
body: JSON.stringify(result)
|
|
1882
|
+
}
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
async submitSecurityPostureScanResult(projectId, workItemId, result) {
|
|
1886
|
+
return this.request(
|
|
1887
|
+
`/projects/${projectId}/work-items/${workItemId}/security-result`,
|
|
1888
|
+
z3.object({ scan: securityScanItemSchema, findings: z3.array(securityFindingItemSchema), snapshot: securityPostureSnapshotItemSchema, workItem: workItemSchema }),
|
|
1889
|
+
{
|
|
1890
|
+
method: "POST",
|
|
1891
|
+
body: JSON.stringify(result)
|
|
1892
|
+
}
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1687
1895
|
async request(urlPath, schema, init) {
|
|
1688
1896
|
const response = await fetch(resolveApiUrl(this.options.apiUrl, urlPath), {
|
|
1689
1897
|
...init,
|
|
@@ -3652,6 +3860,10 @@ var assistantAnswerStart = "AMISTIO_ASSISTANT_ANSWER_START";
|
|
|
3652
3860
|
var assistantAnswerEnd = "AMISTIO_ASSISTANT_ANSWER_END";
|
|
3653
3861
|
var impactPreviewStart = "AMISTIO_IMPACT_PREVIEW_START";
|
|
3654
3862
|
var impactPreviewEnd = "AMISTIO_IMPACT_PREVIEW_END";
|
|
3863
|
+
var issueDiagnosisStart = "AMISTIO_ISSUE_DIAGNOSIS_START";
|
|
3864
|
+
var issueDiagnosisEnd = "AMISTIO_ISSUE_DIAGNOSIS_END";
|
|
3865
|
+
var securityPostureStart = "AMISTIO_SECURITY_POSTURE_START";
|
|
3866
|
+
var securityPostureEnd = "AMISTIO_SECURITY_POSTURE_END";
|
|
3655
3867
|
function createWorkExecutionPrompt(workItem, context) {
|
|
3656
3868
|
if (workItem.workKind === "brainGeneration") {
|
|
3657
3869
|
return createBrainGenerationPrompt(workItem);
|
|
@@ -3665,6 +3877,12 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
3665
3877
|
if (workItem.workKind === "impactPreview") {
|
|
3666
3878
|
return createImpactPreviewPrompt(workItem, context?.impactPreview);
|
|
3667
3879
|
}
|
|
3880
|
+
if (workItem.workKind === "issueDiagnosis") {
|
|
3881
|
+
return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
|
|
3882
|
+
}
|
|
3883
|
+
if (workItem.workKind === "securityPostureScan") {
|
|
3884
|
+
return createSecurityPostureScanPrompt(workItem, context?.securityPostureScan);
|
|
3885
|
+
}
|
|
3668
3886
|
return [
|
|
3669
3887
|
"# Amistio Work Execution",
|
|
3670
3888
|
"",
|
|
@@ -3693,6 +3911,113 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
3693
3911
|
"- Run relevant verification commands when feasible and summarize results."
|
|
3694
3912
|
].join("\n");
|
|
3695
3913
|
}
|
|
3914
|
+
function createSecurityPostureScanPrompt(workItem, context) {
|
|
3915
|
+
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 20).map((document) => [
|
|
3916
|
+
`### ${document.title}`,
|
|
3917
|
+
`documentId: ${document.documentId}`,
|
|
3918
|
+
`documentType: ${document.documentType}`,
|
|
3919
|
+
`repoPath: ${document.repoPath}`,
|
|
3920
|
+
`revision: ${document.revision}`,
|
|
3921
|
+
document.content.slice(0, 3e3)
|
|
3922
|
+
].join("\n")).join("\n\n");
|
|
3923
|
+
return [
|
|
3924
|
+
"# Amistio Security Posture Scan",
|
|
3925
|
+
"",
|
|
3926
|
+
"You are running locally through the Amistio CLI inside the user's repository.",
|
|
3927
|
+
"Run a read-only security posture scan and produce approval-gated remediation findings.",
|
|
3928
|
+
"Do not modify files, create branches, commit, run implementation prompts, install packages, perform exploit attempts, call external services, or make unaudited network calls.",
|
|
3929
|
+
"Use only safe local inspection and focused diagnostic commands that do not expose secrets.",
|
|
3930
|
+
"",
|
|
3931
|
+
"## Work Item",
|
|
3932
|
+
"",
|
|
3933
|
+
`Title: ${workItem.title}`,
|
|
3934
|
+
`Work item ID: ${workItem.workItemId}`,
|
|
3935
|
+
`Project ID: ${workItem.projectId}`,
|
|
3936
|
+
`Security scan ID: ${workItem.securityScanId ?? "unknown"}`,
|
|
3937
|
+
"",
|
|
3938
|
+
"## Scan Request",
|
|
3939
|
+
"",
|
|
3940
|
+
workItem.sourceWish ?? "Run the Amistio security baseline.",
|
|
3941
|
+
"",
|
|
3942
|
+
"## Approved Project Brain Context",
|
|
3943
|
+
"",
|
|
3944
|
+
approvedContext || "No approved project-brain records were loaded. Inspect local repository files as needed and explain the gap in the summary.",
|
|
3945
|
+
"",
|
|
3946
|
+
"## Baseline Requirements",
|
|
3947
|
+
"",
|
|
3948
|
+
"- Cover OWASP Top 10 and OWASP ASVS Level 1 style controls.",
|
|
3949
|
+
"- Check dependency and supply-chain posture: audit advisories, lockfiles, CI verification, release provenance, and trusted publishing where applicable.",
|
|
3950
|
+
"- Check secret and credential hygiene using redacted evidence only.",
|
|
3951
|
+
"- Check authentication, authorization, tenant isolation, redirects/callbacks, CSRF, CORS, security headers, logging redaction, and rate limiting.",
|
|
3952
|
+
"- Check Amistio controls: local-runner-only execution, supported work-kind gates, approval gates, worktree isolation, runner credential binding, and redacted telemetry.",
|
|
3953
|
+
"",
|
|
3954
|
+
"## Data Safety",
|
|
3955
|
+
"",
|
|
3956
|
+
"- Persist summaries, controls, repository-relative paths, and redacted evidence only.",
|
|
3957
|
+
"- Do not include raw source, secrets, env vars, command lines, process lists, absolute local paths, credential values, tokens, provider sessions, or exploit payloads.",
|
|
3958
|
+
"- Use safePaths for repo-relative paths only; never include /absolute paths or ../ traversal.",
|
|
3959
|
+
"",
|
|
3960
|
+
"## Output Contract",
|
|
3961
|
+
"",
|
|
3962
|
+
"Print exactly one JSON object between the markers below. The CLI will submit only this structured scan result back to Amistio.",
|
|
3963
|
+
"",
|
|
3964
|
+
securityPostureStart,
|
|
3965
|
+
'{"summary":"Security posture is mostly healthy, but dependency advisory review is not automated.","baselineVersion":"amistio-security-baseline-v1","postureScore":82,"postureGrade":"B","categorySummaries":[{"category":"dependency","status":"warning","summary":"Dependency audit automation is incomplete."}],"findings":[{"title":"Dependency audit is not enforced in CI","category":"dependency","severity":"medium","confidence":"high","summary":"The repository has a lockfile, but CI does not appear to fail on known vulnerable dependencies.","standardReferences":[{"standard":"OWASP ASVS","control":"V14.2 Dependency"}],"affectedSurfaces":["CI verification"],"evidence":["No dependency audit step was found in the CI verification summary."],"recommendedRemediation":"Add a dependency advisory check to the existing verification pipeline and document how failures are handled.","verificationPlan":["Run the updated CI verification command locally","Confirm a known advisory fails the check in a controlled test"],"safePaths":["package.json","pnpm-lock.yaml"]}],"verificationPlan":["Run focused dependency and CI checks","Review Security panel findings for approval"]}',
|
|
3966
|
+
securityPostureEnd,
|
|
3967
|
+
"",
|
|
3968
|
+
"Do not put Markdown fences around the markers. Do not implement remediation."
|
|
3969
|
+
].join("\n");
|
|
3970
|
+
}
|
|
3971
|
+
function createIssueDiagnosisPrompt(workItem, context) {
|
|
3972
|
+
const issue = context?.issue;
|
|
3973
|
+
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 16).map((document) => [
|
|
3974
|
+
`### ${document.title}`,
|
|
3975
|
+
`documentId: ${document.documentId}`,
|
|
3976
|
+
`documentType: ${document.documentType}`,
|
|
3977
|
+
`repoPath: ${document.repoPath}`,
|
|
3978
|
+
`revision: ${document.revision}`,
|
|
3979
|
+
document.content.slice(0, 3e3)
|
|
3980
|
+
].join("\n")).join("\n\n");
|
|
3981
|
+
return [
|
|
3982
|
+
"# Amistio Issue Diagnosis",
|
|
3983
|
+
"",
|
|
3984
|
+
"You are running locally through the Amistio CLI inside the user's repository.",
|
|
3985
|
+
"Diagnose the submitted bug or operational issue and prepare the approval-gated fix analysis.",
|
|
3986
|
+
"This is a diagnosis-only task. Do not modify files, create branches, run implementation commands, or commit changes.",
|
|
3987
|
+
"",
|
|
3988
|
+
"## Submitted Issue",
|
|
3989
|
+
"",
|
|
3990
|
+
`Issue ID: ${workItem.issueId ?? issue?.issueId ?? "unknown"}`,
|
|
3991
|
+
`Title: ${issue?.title ?? workItem.title}`,
|
|
3992
|
+
`Category: ${issue?.category ?? "bug"}`,
|
|
3993
|
+
`Severity: ${issue?.severity ?? "medium"}`,
|
|
3994
|
+
"",
|
|
3995
|
+
issue?.description ?? workItem.sourceWish ?? workItem.title,
|
|
3996
|
+
"",
|
|
3997
|
+
"## Approved Project Brain Context",
|
|
3998
|
+
"",
|
|
3999
|
+
approvedContext || "No approved project-brain records were loaded. Inspect local repository files as needed and explain the gap in evidence.",
|
|
4000
|
+
"",
|
|
4001
|
+
"## Analysis Requirements",
|
|
4002
|
+
"",
|
|
4003
|
+
"- Document user-visible impact and concrete evidence or reproduction status.",
|
|
4004
|
+
"- Name affected surfaces and likely paths using concise path summaries only.",
|
|
4005
|
+
"- Provide rootCauseAnalysis. If unconfirmed, include falsifiableHypothesis and nextCheck.",
|
|
4006
|
+
"- Propose the fix and expected behavior, then provide a verification plan.",
|
|
4007
|
+
"- Keep repository source and secrets local. Do not include raw source dumps, credentials, local secret paths, or provider session references.",
|
|
4008
|
+
"- Leave approvalState as proposed. Implementation must wait for user approval in Amistio.",
|
|
4009
|
+
"",
|
|
4010
|
+
"## Output Contract",
|
|
4011
|
+
"",
|
|
4012
|
+
"Print exactly one JSON object between the markers below. The CLI will submit only this structured diagnosis back to Amistio.",
|
|
4013
|
+
"",
|
|
4014
|
+
issueDiagnosisStart,
|
|
4015
|
+
'{"summary":"The issue is caused by a missing state transition in the workspace action flow.","impact":"Users see a submitted action but no follow-up analysis appears.","evidence":["Reproduced by submitting an issue from the workspace UI"],"affectedSurfaces":["Workspace UI","Runner claim API"],"rootCauseAnalysis":"The issue path queues normal implementation work instead of diagnosis work.","falsifiableHypothesis":"Adding issueDiagnosis work and a dedicated result route will produce analysis without source mutation.","nextCheck":"Run focused web and CLI typechecks after wiring the route.","proposedFix":"Add a first-class issue diagnosis work kind, result parser, and approval-gated implementation queue.","expectedBehavior":"Submitted issues show a complete analysis and only queue implementation after approval.","verificationPlan":["Run shared, web, and CLI typechecks","Submit an issue with a compatible runner and verify analysis appears"],"riskLevel":"medium","likelyPaths":[{"repoPath":"src/apps/web/legacy/api/projects/[projectId]/issues.ts","reason":"Issue submission and approval route"}],"approvalState":"proposed"}',
|
|
4016
|
+
issueDiagnosisEnd,
|
|
4017
|
+
"",
|
|
4018
|
+
"Do not put Markdown fences around the markers. Do not implement the fix."
|
|
4019
|
+
].join("\n");
|
|
4020
|
+
}
|
|
3696
4021
|
function createImpactPreviewPrompt(workItem, context) {
|
|
3697
4022
|
const implementationPrompt = context?.implementationPrompt;
|
|
3698
4023
|
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 16).map((document) => [
|
|
@@ -3869,6 +4194,26 @@ function parseImpactPreviewResult(output) {
|
|
|
3869
4194
|
const parsed = JSON.parse(stripJsonFence(payload));
|
|
3870
4195
|
return impactPreviewResultSchema.parse(parsed);
|
|
3871
4196
|
}
|
|
4197
|
+
function parseIssueDiagnosisResult(output) {
|
|
4198
|
+
const start = output.indexOf(issueDiagnosisStart);
|
|
4199
|
+
const end = output.indexOf(issueDiagnosisEnd, start + issueDiagnosisStart.length);
|
|
4200
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
4201
|
+
throw new Error("Local AI diagnosis did not return an Amistio issue diagnosis block.");
|
|
4202
|
+
}
|
|
4203
|
+
const payload = output.slice(start + issueDiagnosisStart.length, end).trim();
|
|
4204
|
+
const parsed = JSON.parse(stripJsonFence(payload));
|
|
4205
|
+
return issueDiagnosisResultSchema.parse(parsed);
|
|
4206
|
+
}
|
|
4207
|
+
function parseSecurityPostureScanResult(output) {
|
|
4208
|
+
const start = output.indexOf(securityPostureStart);
|
|
4209
|
+
const end = output.indexOf(securityPostureEnd, start + securityPostureStart.length);
|
|
4210
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
4211
|
+
throw new Error("Local AI scan did not return an Amistio security posture block.");
|
|
4212
|
+
}
|
|
4213
|
+
const payload = output.slice(start + securityPostureStart.length, end).trim();
|
|
4214
|
+
const parsed = JSON.parse(stripJsonFence(payload));
|
|
4215
|
+
return securityPostureScanResultSchema.parse(parsed);
|
|
4216
|
+
}
|
|
3872
4217
|
function createBrainGenerationPrompt(workItem) {
|
|
3873
4218
|
const wish = workItem.sourceWish ?? workItem.title;
|
|
3874
4219
|
return [
|
|
@@ -4412,6 +4757,39 @@ async function runOfficialCliUpdate() {
|
|
|
4412
4757
|
}
|
|
4413
4758
|
return { succeeded: false, message: "Official Amistio CLI update command failed.", error: result.output || `npm exited with code ${result.exitCode}.` };
|
|
4414
4759
|
}
|
|
4760
|
+
async function runOfficialCliUpdateWithRuntimeRefresh(options) {
|
|
4761
|
+
const updateResult = await (options.runUpdate ?? runOfficialCliUpdate)();
|
|
4762
|
+
if (!updateResult.succeeded) {
|
|
4763
|
+
return updateResult;
|
|
4764
|
+
}
|
|
4765
|
+
if (options.mode === "foreground") {
|
|
4766
|
+
return {
|
|
4767
|
+
succeeded: true,
|
|
4768
|
+
stopRunner: true,
|
|
4769
|
+
message: `${updateResult.message} Restart the local runner command to load the updated CLI version.`
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
if (!options.restartBackgroundRunner) {
|
|
4773
|
+
return {
|
|
4774
|
+
succeeded: false,
|
|
4775
|
+
message: `${updateResult.message} Background runner restart was not available after update. Restart the runner manually to load the updated CLI version.`,
|
|
4776
|
+
error: "Background runner restart hook was not provided."
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
const restartResult = await options.restartBackgroundRunner();
|
|
4780
|
+
if (restartResult.succeeded) {
|
|
4781
|
+
return {
|
|
4782
|
+
succeeded: true,
|
|
4783
|
+
stopRunner: true,
|
|
4784
|
+
message: `${updateResult.message} ${restartResult.message}`
|
|
4785
|
+
};
|
|
4786
|
+
}
|
|
4787
|
+
return {
|
|
4788
|
+
succeeded: false,
|
|
4789
|
+
message: `${updateResult.message} Background runner restart failed after update. Restart the runner manually to load the updated CLI version.`,
|
|
4790
|
+
error: restartResult.error ?? restartResult.message
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4415
4793
|
function runOfficialUpdateProcess(command, args, timeoutMs) {
|
|
4416
4794
|
return new Promise((resolve) => {
|
|
4417
4795
|
const child = spawn4(command, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -4548,6 +4926,7 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
|
|
|
4548
4926
|
var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
|
|
4549
4927
|
var RUNNER_WORK_LEASE_SECONDS = 300;
|
|
4550
4928
|
var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
|
|
4929
|
+
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan"];
|
|
4551
4930
|
program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
|
|
4552
4931
|
program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
4553
4932
|
const created = await initControlPlane(options.root);
|
|
@@ -5510,6 +5889,42 @@ async function runNextWorkItem({
|
|
|
5510
5889
|
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
5511
5890
|
}
|
|
5512
5891
|
}
|
|
5892
|
+
if (result.workItem.workKind === "issueDiagnosis") {
|
|
5893
|
+
try {
|
|
5894
|
+
return await finalizeIssueDiagnosisWork({
|
|
5895
|
+
apiClient,
|
|
5896
|
+
durationMs: Date.now() - startedAt,
|
|
5897
|
+
projectId,
|
|
5898
|
+
repositoryLinkId,
|
|
5899
|
+
runnerId,
|
|
5900
|
+
sessionContext,
|
|
5901
|
+
toolConfig,
|
|
5902
|
+
toolName: preview.toolName,
|
|
5903
|
+
toolResult,
|
|
5904
|
+
workItem: result.workItem
|
|
5905
|
+
});
|
|
5906
|
+
} catch (error) {
|
|
5907
|
+
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
5908
|
+
}
|
|
5909
|
+
}
|
|
5910
|
+
if (result.workItem.workKind === "securityPostureScan") {
|
|
5911
|
+
try {
|
|
5912
|
+
return await finalizeSecurityPostureScanWork({
|
|
5913
|
+
apiClient,
|
|
5914
|
+
durationMs: Date.now() - startedAt,
|
|
5915
|
+
projectId,
|
|
5916
|
+
repositoryLinkId,
|
|
5917
|
+
runnerId,
|
|
5918
|
+
sessionContext,
|
|
5919
|
+
toolConfig,
|
|
5920
|
+
toolName: preview.toolName,
|
|
5921
|
+
toolResult,
|
|
5922
|
+
workItem: result.workItem
|
|
5923
|
+
});
|
|
5924
|
+
} catch (error) {
|
|
5925
|
+
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5513
5928
|
const finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
|
|
5514
5929
|
const durationMs = Date.now() - startedAt;
|
|
5515
5930
|
const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
|
|
@@ -5712,6 +6127,7 @@ async function recordRunnerMilestone(apiClient, projectId, workItem, runnerId, r
|
|
|
5712
6127
|
repositoryLinkId,
|
|
5713
6128
|
relatedWorkItemId: workItem.workItemId,
|
|
5714
6129
|
...workItem.reviewDocumentId ? { relatedDocumentId: workItem.reviewDocumentId } : workItem.impactDocumentId ? { relatedDocumentId: workItem.impactDocumentId } : {},
|
|
6130
|
+
...workItem.issueId ? { relatedIssueId: workItem.issueId } : {},
|
|
5715
6131
|
...workItem.generatedDraftId ? { generatedDraftId: workItem.generatedDraftId } : {},
|
|
5716
6132
|
...input
|
|
5717
6133
|
}).catch(() => void 0);
|
|
@@ -5759,7 +6175,10 @@ async function executeRunnerCommand(command, context) {
|
|
|
5759
6175
|
if (command.commandKind === "restart") {
|
|
5760
6176
|
return restartCurrentRunner(context);
|
|
5761
6177
|
}
|
|
5762
|
-
return
|
|
6178
|
+
return runOfficialCliUpdateWithRuntimeRefresh({
|
|
6179
|
+
mode: currentRunnerMode(),
|
|
6180
|
+
restartBackgroundRunner: () => restartCurrentRunner(context)
|
|
6181
|
+
});
|
|
5763
6182
|
}
|
|
5764
6183
|
async function restartCurrentRunner(context) {
|
|
5765
6184
|
if (currentRunnerMode() !== "background") {
|
|
@@ -6047,6 +6466,178 @@ ${toolResult.stderr}`);
|
|
|
6047
6466
|
console.error(previewError ?? "Local runner impact preview failed.");
|
|
6048
6467
|
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
6049
6468
|
}
|
|
6469
|
+
async function finalizeIssueDiagnosisWork({
|
|
6470
|
+
apiClient,
|
|
6471
|
+
durationMs,
|
|
6472
|
+
projectId,
|
|
6473
|
+
repositoryLinkId,
|
|
6474
|
+
runnerId,
|
|
6475
|
+
sessionContext,
|
|
6476
|
+
toolConfig,
|
|
6477
|
+
toolName,
|
|
6478
|
+
toolResult,
|
|
6479
|
+
workItem
|
|
6480
|
+
}) {
|
|
6481
|
+
let diagnosis = void 0;
|
|
6482
|
+
let diagnosisError;
|
|
6483
|
+
if (toolResult.exitCode === 0) {
|
|
6484
|
+
try {
|
|
6485
|
+
diagnosis = parseIssueDiagnosisResult(`${toolResult.stdout}
|
|
6486
|
+
${toolResult.stderr}`);
|
|
6487
|
+
} catch (error) {
|
|
6488
|
+
diagnosisError = errorMessage3(error);
|
|
6489
|
+
}
|
|
6490
|
+
} else {
|
|
6491
|
+
diagnosisError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
6492
|
+
}
|
|
6493
|
+
const finalStatus = diagnosis ? "completed" : "failed";
|
|
6494
|
+
const updatedToolSession = await finalizeToolSession({
|
|
6495
|
+
apiClient,
|
|
6496
|
+
projectId,
|
|
6497
|
+
status: finalStatus,
|
|
6498
|
+
runnerId,
|
|
6499
|
+
workItemId: workItem.workItemId,
|
|
6500
|
+
stdout: toolResult.stdout,
|
|
6501
|
+
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
6502
|
+
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
6503
|
+
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
6504
|
+
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
6505
|
+
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
6506
|
+
});
|
|
6507
|
+
const sessionTelemetry = {
|
|
6508
|
+
sessionPolicy: sessionContext.policy,
|
|
6509
|
+
sessionDecision: sessionContext.decision,
|
|
6510
|
+
sessionDecisionReason: sessionContext.reason,
|
|
6511
|
+
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
6512
|
+
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
6513
|
+
};
|
|
6514
|
+
if (diagnosis) {
|
|
6515
|
+
const result = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
|
|
6516
|
+
status: "completed",
|
|
6517
|
+
runnerId,
|
|
6518
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
|
|
6519
|
+
diagnosis,
|
|
6520
|
+
tool: toolName,
|
|
6521
|
+
durationMs,
|
|
6522
|
+
...sessionTelemetry,
|
|
6523
|
+
message: `${toolName} returned an issue root-cause analysis.`
|
|
6524
|
+
});
|
|
6525
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
6526
|
+
status: "completed",
|
|
6527
|
+
summary: `${toolName} returned an issue root-cause analysis.`,
|
|
6528
|
+
idempotencyKey: `runner_milestone_issue_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
|
|
6529
|
+
metadata: { tool: toolName, durationMs, riskLevel: diagnosis.riskLevel, likelyPathCount: diagnosis.likelyPaths.length, verificationSummary: diagnosis.verificationPlan.join(" | ") }
|
|
6530
|
+
});
|
|
6531
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
6532
|
+
console.log("Issue diagnosis returned for approval.");
|
|
6533
|
+
return { status: "completed", exitCode: 0 };
|
|
6534
|
+
}
|
|
6535
|
+
const failedResult = await apiClient.submitIssueDiagnosisResult(projectId, workItem.workItemId, {
|
|
6536
|
+
status: "failed",
|
|
6537
|
+
runnerId,
|
|
6538
|
+
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
|
|
6539
|
+
tool: toolName,
|
|
6540
|
+
durationMs,
|
|
6541
|
+
...sessionTelemetry,
|
|
6542
|
+
message: `${toolName} did not produce a valid issue diagnosis.`,
|
|
6543
|
+
...diagnosisError ? { error: diagnosisError } : {}
|
|
6544
|
+
});
|
|
6545
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
6546
|
+
status: "failed",
|
|
6547
|
+
summary: diagnosisError ?? `${toolName} did not produce a valid issue diagnosis.`,
|
|
6548
|
+
idempotencyKey: `runner_milestone_issue_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
|
|
6549
|
+
metadata: { tool: toolName, durationMs, verificationSummary: "Issue diagnosis output did not include valid structured JSON." }
|
|
6550
|
+
});
|
|
6551
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
6552
|
+
console.error(diagnosisError ?? "Local runner issue diagnosis failed.");
|
|
6553
|
+
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
6554
|
+
}
|
|
6555
|
+
async function finalizeSecurityPostureScanWork({
|
|
6556
|
+
apiClient,
|
|
6557
|
+
durationMs,
|
|
6558
|
+
projectId,
|
|
6559
|
+
repositoryLinkId,
|
|
6560
|
+
runnerId,
|
|
6561
|
+
sessionContext,
|
|
6562
|
+
toolConfig,
|
|
6563
|
+
toolName,
|
|
6564
|
+
toolResult,
|
|
6565
|
+
workItem
|
|
6566
|
+
}) {
|
|
6567
|
+
let scanResult = void 0;
|
|
6568
|
+
let scanError;
|
|
6569
|
+
if (toolResult.exitCode === 0) {
|
|
6570
|
+
try {
|
|
6571
|
+
scanResult = parseSecurityPostureScanResult(`${toolResult.stdout}
|
|
6572
|
+
${toolResult.stderr}`);
|
|
6573
|
+
} catch (error) {
|
|
6574
|
+
scanError = errorMessage3(error);
|
|
6575
|
+
}
|
|
6576
|
+
} else {
|
|
6577
|
+
scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
6578
|
+
}
|
|
6579
|
+
const finalStatus = scanResult ? "completed" : "failed";
|
|
6580
|
+
const updatedToolSession = await finalizeToolSession({
|
|
6581
|
+
apiClient,
|
|
6582
|
+
projectId,
|
|
6583
|
+
status: finalStatus,
|
|
6584
|
+
runnerId,
|
|
6585
|
+
workItemId: workItem.workItemId,
|
|
6586
|
+
stdout: toolResult.stdout,
|
|
6587
|
+
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
6588
|
+
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
6589
|
+
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
6590
|
+
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
6591
|
+
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
6592
|
+
});
|
|
6593
|
+
const sessionTelemetry = {
|
|
6594
|
+
sessionPolicy: sessionContext.policy,
|
|
6595
|
+
sessionDecision: sessionContext.decision,
|
|
6596
|
+
sessionDecisionReason: sessionContext.reason,
|
|
6597
|
+
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
6598
|
+
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
6599
|
+
};
|
|
6600
|
+
if (scanResult) {
|
|
6601
|
+
const result = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
|
|
6602
|
+
status: "completed",
|
|
6603
|
+
runnerId,
|
|
6604
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
|
|
6605
|
+
result: scanResult,
|
|
6606
|
+
tool: toolName,
|
|
6607
|
+
durationMs,
|
|
6608
|
+
...sessionTelemetry,
|
|
6609
|
+
message: `${toolName} returned a security posture scan.`
|
|
6610
|
+
});
|
|
6611
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
6612
|
+
status: "completed",
|
|
6613
|
+
summary: `${toolName} returned a security posture scan.`,
|
|
6614
|
+
idempotencyKey: `runner_milestone_security_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
|
|
6615
|
+
metadata: { tool: toolName, durationMs, findingCount: scanResult.findings.length, critical: result.scan.severityCounts.critical, high: result.scan.severityCounts.high, verificationSummary: scanResult.verificationPlan.join(" | ") }
|
|
6616
|
+
});
|
|
6617
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
6618
|
+
console.log("Security posture scan returned for review.");
|
|
6619
|
+
return { status: "completed", exitCode: 0 };
|
|
6620
|
+
}
|
|
6621
|
+
const failedResult = await apiClient.submitSecurityPostureScanResult(projectId, workItem.workItemId, {
|
|
6622
|
+
status: "failed",
|
|
6623
|
+
runnerId,
|
|
6624
|
+
idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
|
|
6625
|
+
tool: toolName,
|
|
6626
|
+
durationMs,
|
|
6627
|
+
...sessionTelemetry,
|
|
6628
|
+
message: `${toolName} did not produce a valid security posture scan.`,
|
|
6629
|
+
...scanError ? { error: scanError } : {}
|
|
6630
|
+
});
|
|
6631
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
6632
|
+
status: "failed",
|
|
6633
|
+
summary: scanError ?? `${toolName} did not produce a valid security posture scan.`,
|
|
6634
|
+
idempotencyKey: `runner_milestone_security_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
|
|
6635
|
+
metadata: { tool: toolName, durationMs, verificationSummary: "Security posture output did not include valid structured JSON." }
|
|
6636
|
+
});
|
|
6637
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
6638
|
+
console.error(scanError ?? "Local runner security posture scan failed.");
|
|
6639
|
+
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
6640
|
+
}
|
|
6050
6641
|
async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
6051
6642
|
if (workItem.workKind === "assistantQuestion") {
|
|
6052
6643
|
const [{ documents: documents2 }, { messages: messages2 }] = await Promise.all([
|
|
@@ -6068,6 +6659,25 @@ async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
|
6068
6659
|
}
|
|
6069
6660
|
});
|
|
6070
6661
|
}
|
|
6662
|
+
if (workItem.workKind === "issueDiagnosis") {
|
|
6663
|
+
const [{ documents: documents2 }, { issues }] = await Promise.all([
|
|
6664
|
+
apiClient.listBrainDocuments(projectId),
|
|
6665
|
+
apiClient.listIssues(projectId)
|
|
6666
|
+
]);
|
|
6667
|
+
const issue = issues.find((item) => item.issueId === workItem.issueId || item.id === workItem.issueId);
|
|
6668
|
+
return createWorkExecutionPrompt(workItem, {
|
|
6669
|
+
issueDiagnosis: {
|
|
6670
|
+
documents: documents2,
|
|
6671
|
+
...issue ? { issue } : {}
|
|
6672
|
+
}
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
if (workItem.workKind === "securityPostureScan") {
|
|
6676
|
+
const { documents: documents2 } = await apiClient.listBrainDocuments(projectId);
|
|
6677
|
+
return createWorkExecutionPrompt(workItem, {
|
|
6678
|
+
securityPostureScan: { documents: documents2 }
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6071
6681
|
if (workItem.workKind !== "planRevision" || !workItem.reviewDocumentId) {
|
|
6072
6682
|
return createWorkExecutionPrompt(workItem);
|
|
6073
6683
|
}
|
|
@@ -6545,6 +7155,7 @@ function toRunnerToolCapabilities(tools) {
|
|
|
6545
7155
|
function runnerIsolationCapabilityMetadata() {
|
|
6546
7156
|
return {
|
|
6547
7157
|
machineId: runnerMachineId(),
|
|
7158
|
+
supportedWorkKinds: runnerSupportedWorkKinds,
|
|
6548
7159
|
supportsBranchIsolation: true,
|
|
6549
7160
|
supportsGitWorktreeIsolation: true
|
|
6550
7161
|
};
|