@amistio/cli 0.1.11 → 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 +1 -1
- package/dist/index.js +320 -2
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only pe
|
|
|
39
39
|
|
|
40
40
|
`amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
|
|
41
41
|
|
|
42
|
-
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. Implementation is queued separately only after the user approves
|
|
42
|
+
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can also claim read-only `securityPostureScan` jobs from the workspace Security panel or the hosted nightly cron scheduler. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, and arbitrary local paths stay local. Implementation is queued separately only after the user approves an issue analysis or security remediation plan in the app.
|
|
43
43
|
|
|
44
44
|
Runner setup and local-tool execution use bounded failure controls. `amistio run --watch` retries Git worktree preflight failures by releasing the claim for another attempt, then fails the work item after `--max-preflight-attempts` attempts, defaulting to 3. Active local-tool runs renew the work lease, and `--tool-timeout-seconds` caps tool execution, defaulting to 1800 seconds.
|
|
45
45
|
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,9 @@ var itemTypeSchema = z.enum([
|
|
|
21
21
|
"brainDocument",
|
|
22
22
|
"generatedDraft",
|
|
23
23
|
"issue",
|
|
24
|
+
"securityScan",
|
|
25
|
+
"securityFinding",
|
|
26
|
+
"securityPostureSnapshot",
|
|
24
27
|
"syncCursor",
|
|
25
28
|
"syncConflict",
|
|
26
29
|
"workItem",
|
|
@@ -67,7 +70,7 @@ var workStatusSchema = z.enum([
|
|
|
67
70
|
]);
|
|
68
71
|
var sourceSchema = z.enum(["web", "repo", "generated", "runner"]);
|
|
69
72
|
var executionModeSchema = z.enum(["localRunner", "cloudSandbox"]);
|
|
70
|
-
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis"]);
|
|
73
|
+
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan"]);
|
|
71
74
|
var generatedDraftStatusSchema = z.enum(["queued", "generating", "reviewing", "approved", "rejected", "changesRequested", "failed"]);
|
|
72
75
|
var assistantQuestionModeSchema = z.enum(["brainOnly", "sourceAware"]);
|
|
73
76
|
var impactRiskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
|
|
@@ -76,6 +79,13 @@ var issueCategorySchema = z.enum(["bug", "regression", "brokenWorkflow", "securi
|
|
|
76
79
|
var issueSeveritySchema = z.enum(["low", "medium", "high", "critical"]);
|
|
77
80
|
var issueStatusSchema = z.enum(["queued", "diagnosing", "analysisReady", "approved", "changesRequested", "rejected", "implementationQueued", "implemented", "failed"]);
|
|
78
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"]);
|
|
79
89
|
var activityEventTypeSchema = z.enum([
|
|
80
90
|
"commandSubmitted",
|
|
81
91
|
"generationQueued",
|
|
@@ -99,6 +109,14 @@ var activityEventTypeSchema = z.enum([
|
|
|
99
109
|
"issueDiagnosisFailed",
|
|
100
110
|
"issueReview",
|
|
101
111
|
"issueImplementationQueued",
|
|
112
|
+
"securityScanQueued",
|
|
113
|
+
"securityScanStarted",
|
|
114
|
+
"securityScanCompleted",
|
|
115
|
+
"securityScanFailed",
|
|
116
|
+
"securityFindingOpened",
|
|
117
|
+
"securityFindingReviewed",
|
|
118
|
+
"securityRemediationPlanCreated",
|
|
119
|
+
"securityRemediationQueued",
|
|
102
120
|
"handoffExported"
|
|
103
121
|
]);
|
|
104
122
|
var activityEventActorSchema = z.enum(["webUser", "runner", "cli", "system"]);
|
|
@@ -349,6 +367,8 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
349
367
|
sourceWish: z.string().min(1).optional(),
|
|
350
368
|
generatedDraftId: z.string().min(1).optional(),
|
|
351
369
|
issueId: z.string().min(1).optional(),
|
|
370
|
+
securityScanId: z.string().min(1).optional(),
|
|
371
|
+
securityFindingId: z.string().min(1).optional(),
|
|
352
372
|
repositoryLinkId: z.string().min(1).optional(),
|
|
353
373
|
reviewThreadId: z.string().min(1).optional(),
|
|
354
374
|
reviewDocumentId: z.string().min(1).optional(),
|
|
@@ -773,6 +793,107 @@ var issueItemSchema = baseItemSchema.extend({
|
|
|
773
793
|
runnerId: z.string().min(1).optional(),
|
|
774
794
|
lastDiagnosisError: z.string().optional()
|
|
775
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
|
+
});
|
|
776
897
|
var activityEventItemSchema = baseItemSchema.extend({
|
|
777
898
|
type: z.literal("activityEvent"),
|
|
778
899
|
projectId: z.string().min(1),
|
|
@@ -788,6 +909,8 @@ var activityEventItemSchema = baseItemSchema.extend({
|
|
|
788
909
|
relatedWorkItemId: z.string().min(1).optional(),
|
|
789
910
|
relatedDocumentId: z.string().min(1).optional(),
|
|
790
911
|
relatedIssueId: z.string().min(1).optional(),
|
|
912
|
+
relatedSecurityScanId: z.string().min(1).optional(),
|
|
913
|
+
relatedSecurityFindingId: z.string().min(1).optional(),
|
|
791
914
|
generatedDraftId: z.string().min(1).optional(),
|
|
792
915
|
runnerId: z.string().min(1).optional(),
|
|
793
916
|
repositoryLinkId: z.string().min(1).optional(),
|
|
@@ -845,6 +968,9 @@ var projectItemUnionSchema = z.discriminatedUnion("type", [
|
|
|
845
968
|
brainDocumentItemSchema,
|
|
846
969
|
generatedDraftItemSchema,
|
|
847
970
|
issueItemSchema,
|
|
971
|
+
securityScanItemSchema,
|
|
972
|
+
securityFindingItemSchema,
|
|
973
|
+
securityPostureSnapshotItemSchema,
|
|
848
974
|
syncCursorItemSchema,
|
|
849
975
|
syncConflictItemSchema,
|
|
850
976
|
workItemSchema,
|
|
@@ -1756,6 +1882,16 @@ var ApiClient = class {
|
|
|
1756
1882
|
}
|
|
1757
1883
|
);
|
|
1758
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
|
+
}
|
|
1759
1895
|
async request(urlPath, schema, init) {
|
|
1760
1896
|
const response = await fetch(resolveApiUrl(this.options.apiUrl, urlPath), {
|
|
1761
1897
|
...init,
|
|
@@ -3726,6 +3862,8 @@ var impactPreviewStart = "AMISTIO_IMPACT_PREVIEW_START";
|
|
|
3726
3862
|
var impactPreviewEnd = "AMISTIO_IMPACT_PREVIEW_END";
|
|
3727
3863
|
var issueDiagnosisStart = "AMISTIO_ISSUE_DIAGNOSIS_START";
|
|
3728
3864
|
var issueDiagnosisEnd = "AMISTIO_ISSUE_DIAGNOSIS_END";
|
|
3865
|
+
var securityPostureStart = "AMISTIO_SECURITY_POSTURE_START";
|
|
3866
|
+
var securityPostureEnd = "AMISTIO_SECURITY_POSTURE_END";
|
|
3729
3867
|
function createWorkExecutionPrompt(workItem, context) {
|
|
3730
3868
|
if (workItem.workKind === "brainGeneration") {
|
|
3731
3869
|
return createBrainGenerationPrompt(workItem);
|
|
@@ -3742,6 +3880,9 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
3742
3880
|
if (workItem.workKind === "issueDiagnosis") {
|
|
3743
3881
|
return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
|
|
3744
3882
|
}
|
|
3883
|
+
if (workItem.workKind === "securityPostureScan") {
|
|
3884
|
+
return createSecurityPostureScanPrompt(workItem, context?.securityPostureScan);
|
|
3885
|
+
}
|
|
3745
3886
|
return [
|
|
3746
3887
|
"# Amistio Work Execution",
|
|
3747
3888
|
"",
|
|
@@ -3770,6 +3911,63 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
3770
3911
|
"- Run relevant verification commands when feasible and summarize results."
|
|
3771
3912
|
].join("\n");
|
|
3772
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
|
+
}
|
|
3773
3971
|
function createIssueDiagnosisPrompt(workItem, context) {
|
|
3774
3972
|
const issue = context?.issue;
|
|
3775
3973
|
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 16).map((document) => [
|
|
@@ -4006,6 +4204,16 @@ function parseIssueDiagnosisResult(output) {
|
|
|
4006
4204
|
const parsed = JSON.parse(stripJsonFence(payload));
|
|
4007
4205
|
return issueDiagnosisResultSchema.parse(parsed);
|
|
4008
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
|
+
}
|
|
4009
4217
|
function createBrainGenerationPrompt(workItem) {
|
|
4010
4218
|
const wish = workItem.sourceWish ?? workItem.title;
|
|
4011
4219
|
return [
|
|
@@ -4718,7 +4926,7 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
|
|
|
4718
4926
|
var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
|
|
4719
4927
|
var RUNNER_WORK_LEASE_SECONDS = 300;
|
|
4720
4928
|
var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
|
|
4721
|
-
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis"];
|
|
4929
|
+
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan"];
|
|
4722
4930
|
program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
|
|
4723
4931
|
program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
4724
4932
|
const created = await initControlPlane(options.root);
|
|
@@ -5699,6 +5907,24 @@ async function runNextWorkItem({
|
|
|
5699
5907
|
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
5700
5908
|
}
|
|
5701
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
|
+
}
|
|
5702
5928
|
const finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
|
|
5703
5929
|
const durationMs = Date.now() - startedAt;
|
|
5704
5930
|
const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
|
|
@@ -6326,6 +6552,92 @@ ${toolResult.stderr}`);
|
|
|
6326
6552
|
console.error(diagnosisError ?? "Local runner issue diagnosis failed.");
|
|
6327
6553
|
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
6328
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
|
+
}
|
|
6329
6641
|
async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
6330
6642
|
if (workItem.workKind === "assistantQuestion") {
|
|
6331
6643
|
const [{ documents: documents2 }, { messages: messages2 }] = await Promise.all([
|
|
@@ -6360,6 +6672,12 @@ async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
|
6360
6672
|
}
|
|
6361
6673
|
});
|
|
6362
6674
|
}
|
|
6675
|
+
if (workItem.workKind === "securityPostureScan") {
|
|
6676
|
+
const { documents: documents2 } = await apiClient.listBrainDocuments(projectId);
|
|
6677
|
+
return createWorkExecutionPrompt(workItem, {
|
|
6678
|
+
securityPostureScan: { documents: documents2 }
|
|
6679
|
+
});
|
|
6680
|
+
}
|
|
6363
6681
|
if (workItem.workKind !== "planRevision" || !workItem.reviewDocumentId) {
|
|
6364
6682
|
return createWorkExecutionPrompt(workItem);
|
|
6365
6683
|
}
|