@amistio/cli 0.1.24 → 0.1.26
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 +8 -3
- package/dist/index.js +2017 -247
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24,6 +24,10 @@ var itemTypeSchema = z.enum([
|
|
|
24
24
|
"securityScan",
|
|
25
25
|
"securityFinding",
|
|
26
26
|
"securityPostureSnapshot",
|
|
27
|
+
"testProfile",
|
|
28
|
+
"testQualityScan",
|
|
29
|
+
"testQualityFinding",
|
|
30
|
+
"implementationTestGate",
|
|
27
31
|
"appEvaluationScan",
|
|
28
32
|
"appEvaluationFinding",
|
|
29
33
|
"implementationVerification",
|
|
@@ -79,9 +83,89 @@ var workStatusSchema = z.enum([
|
|
|
79
83
|
]);
|
|
80
84
|
var sourceSchema = z.enum(["web", "repo", "generated", "runner"]);
|
|
81
85
|
var executionModeSchema = z.enum(["localRunner", "cloudSandbox"]);
|
|
82
|
-
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"]);
|
|
86
|
+
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate", "runnerCommand"]);
|
|
87
|
+
var autopilotModeSchema = z.enum(["disabled", "enabled", "paused"]);
|
|
88
|
+
var autopilotClassificationOutcomeSchema = z.enum(["safeAutopilotEligible", "requiresReview", "blocked", "unsafe"]);
|
|
89
|
+
var autopilotCandidateTypeSchema = z.enum([
|
|
90
|
+
"generatedDraftApproval",
|
|
91
|
+
"projectBrainSync",
|
|
92
|
+
"issueDiagnosis",
|
|
93
|
+
"issueFixHandoff",
|
|
94
|
+
"securityPostureScan",
|
|
95
|
+
"securityRemediationHandoff",
|
|
96
|
+
"appEvaluationScan",
|
|
97
|
+
"appEvaluationPlanHandoff",
|
|
98
|
+
"appEvaluationCleanup",
|
|
99
|
+
"projectContextRefresh",
|
|
100
|
+
"impactPreview",
|
|
101
|
+
"implementationQueue",
|
|
102
|
+
"implementationVerification",
|
|
103
|
+
"testQualityScan",
|
|
104
|
+
"implementationTestGate",
|
|
105
|
+
"requeueWork",
|
|
106
|
+
"runnerCommand"
|
|
107
|
+
]);
|
|
108
|
+
var autopilotReasonCodeSchema = z.enum([
|
|
109
|
+
"allowedLowRisk",
|
|
110
|
+
"disabled",
|
|
111
|
+
"paused",
|
|
112
|
+
"runnerRequired",
|
|
113
|
+
"runnerIncompatible",
|
|
114
|
+
"repositoryRequired",
|
|
115
|
+
"staleRevision",
|
|
116
|
+
"duplicateInFlight",
|
|
117
|
+
"budgetExceeded",
|
|
118
|
+
"protectedSurface",
|
|
119
|
+
"destructiveAction",
|
|
120
|
+
"rawSourceUpload",
|
|
121
|
+
"secretRisk",
|
|
122
|
+
"primaryCheckoutMutation",
|
|
123
|
+
"policyBlocked",
|
|
124
|
+
"reviewRequired",
|
|
125
|
+
"cooldownActive",
|
|
126
|
+
"expired",
|
|
127
|
+
"failureBudgetExceeded",
|
|
128
|
+
"verificationFailed",
|
|
129
|
+
"redactionHit",
|
|
130
|
+
"candidateTypeNotAllowed",
|
|
131
|
+
"riskTierExceeded",
|
|
132
|
+
"unsafePath",
|
|
133
|
+
"schemaInvalid",
|
|
134
|
+
"notAdditive",
|
|
135
|
+
"scopeMismatch",
|
|
136
|
+
"sizeLimitExceeded",
|
|
137
|
+
"worktreeIsolationRequired",
|
|
138
|
+
"verificationRequired",
|
|
139
|
+
"organizationPolicyRequired",
|
|
140
|
+
"unsafe"
|
|
141
|
+
]);
|
|
142
|
+
var autopilotGuardCheckSchema = z.object({
|
|
143
|
+
name: z.string().trim().min(1).max(120),
|
|
144
|
+
status: z.enum(["passed", "failed", "warning"]),
|
|
145
|
+
summary: z.string().trim().min(1).max(600)
|
|
146
|
+
});
|
|
83
147
|
var implementationHandoffStatusSchema = z.enum(["notStarted", "noChanges", "prReady", "blocked", "failed"]);
|
|
84
148
|
var implementationHandoffCleanupStatusSchema = z.enum(["notApplicable", "pending", "completed", "failed"]);
|
|
149
|
+
var safeRepoPathSchema = z.string().trim().min(1).max(300).refine((value) => {
|
|
150
|
+
if (value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return !value.split(/[\\/]+/).includes("..");
|
|
154
|
+
}, "Path must be repository-relative without traversal");
|
|
155
|
+
var implementationHandoffArtifactStatusSchema = z.enum(["included", "skipped", "blocked"]);
|
|
156
|
+
var implementationHandoffArtifactSchema = z.object({
|
|
157
|
+
documentId: z.string().trim().min(1).max(200),
|
|
158
|
+
title: z.string().trim().min(1).max(200).optional(),
|
|
159
|
+
repoPath: safeRepoPathSchema,
|
|
160
|
+
contentFormat: documentContentFormatSchema,
|
|
161
|
+
status: implementationHandoffArtifactStatusSchema,
|
|
162
|
+
message: z.string().trim().min(1).max(600).optional()
|
|
163
|
+
}).strict();
|
|
164
|
+
var implementationHandoffArtifactsSchema = z.object({
|
|
165
|
+
included: z.array(implementationHandoffArtifactSchema).max(50).default([]),
|
|
166
|
+
skipped: z.array(implementationHandoffArtifactSchema).max(50).default([]),
|
|
167
|
+
blocked: z.array(implementationHandoffArtifactSchema).max(50).default([])
|
|
168
|
+
}).strict();
|
|
85
169
|
var implementationVerificationOutcomeSchema = z.enum(["verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked"]);
|
|
86
170
|
var implementationVerificationStatusSchema = z.enum(["queued", "running", "completed", "failed", "blocked", "stale"]);
|
|
87
171
|
var implementationProofStatusSchema = z.enum(["unverified", "verificationQueued", "verificationRunning", "verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked", "humanOverride"]);
|
|
@@ -96,12 +180,97 @@ var implementationHandoffSchema = z.object({
|
|
|
96
180
|
prUrl: z.string().trim().url().max(500).optional(),
|
|
97
181
|
cleanupStatus: implementationHandoffCleanupStatusSchema.optional(),
|
|
98
182
|
cleanupMessage: z.string().trim().min(1).max(600).optional(),
|
|
183
|
+
artifacts: implementationHandoffArtifactsSchema.optional(),
|
|
99
184
|
message: z.string().trim().min(1).max(600).optional(),
|
|
100
185
|
error: z.string().trim().min(1).max(1200).optional()
|
|
101
186
|
}).strict();
|
|
102
187
|
var generatedDraftStatusSchema = z.enum(["queued", "generating", "reviewing", "approved", "rejected", "changesRequested", "failed"]);
|
|
103
188
|
var assistantQuestionModeSchema = z.enum(["brainOnly", "sourceAware"]);
|
|
104
189
|
var impactRiskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
|
|
190
|
+
var autopilotBudgetWindowSchema = z.object({
|
|
191
|
+
name: z.string().trim().min(1).max(80),
|
|
192
|
+
limit: z.number().int().nonnegative(),
|
|
193
|
+
used: z.number().int().nonnegative(),
|
|
194
|
+
remaining: z.number().int().nonnegative().optional(),
|
|
195
|
+
windowStartedAt: isoDateTimeSchema,
|
|
196
|
+
windowEndsAt: isoDateTimeSchema
|
|
197
|
+
}).strict();
|
|
198
|
+
var autopilotPolicySnapshotSchema = z.object({
|
|
199
|
+
policyVersion: z.string().trim().min(1).max(80),
|
|
200
|
+
mode: autopilotModeSchema,
|
|
201
|
+
enabledByUserId: z.string().min(1).optional(),
|
|
202
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
203
|
+
runnerId: z.string().min(1).optional(),
|
|
204
|
+
allowedWorkKinds: z.array(workKindSchema).max(20).default([]),
|
|
205
|
+
allowedCandidateTypes: z.array(autopilotCandidateTypeSchema).max(30).default([]),
|
|
206
|
+
maxRiskLevel: impactRiskLevelSchema.default("low"),
|
|
207
|
+
dailyWorkLimit: z.number().int().positive().max(100).optional(),
|
|
208
|
+
dailyPrLimit: z.number().int().nonnegative().max(100).optional(),
|
|
209
|
+
concurrentWorkLimit: z.number().int().positive().max(20).optional(),
|
|
210
|
+
runnerMinuteBudget: z.number().int().nonnegative().max(1440).optional(),
|
|
211
|
+
failureBudget: z.number().int().nonnegative().max(50).optional(),
|
|
212
|
+
expiresAt: isoDateTimeSchema.optional(),
|
|
213
|
+
reviewAfter: isoDateTimeSchema.optional(),
|
|
214
|
+
cooldownUntil: isoDateTimeSchema.optional(),
|
|
215
|
+
pausedReason: z.string().trim().min(1).max(600).optional(),
|
|
216
|
+
budgetWindows: z.array(autopilotBudgetWindowSchema).max(10).default([])
|
|
217
|
+
}).strict();
|
|
218
|
+
var autopilotCandidateLinksSchema = z.object({
|
|
219
|
+
workItemId: z.string().min(1).optional(),
|
|
220
|
+
documentId: z.string().min(1).optional(),
|
|
221
|
+
generatedDraftId: z.string().min(1).optional(),
|
|
222
|
+
issueId: z.string().min(1).optional(),
|
|
223
|
+
securityScanId: z.string().min(1).optional(),
|
|
224
|
+
securityFindingId: z.string().min(1).optional(),
|
|
225
|
+
appEvaluationScanId: z.string().min(1).optional(),
|
|
226
|
+
appEvaluationFindingId: z.string().min(1).optional(),
|
|
227
|
+
implementationVerificationId: z.string().min(1).optional(),
|
|
228
|
+
testQualityScanId: z.string().min(1).optional(),
|
|
229
|
+
testQualityFindingId: z.string().min(1).optional(),
|
|
230
|
+
implementationTestGateId: z.string().min(1).optional(),
|
|
231
|
+
projectContextRefreshId: z.string().min(1).optional(),
|
|
232
|
+
runnerCommandId: z.string().min(1).optional(),
|
|
233
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
234
|
+
runnerId: z.string().min(1).optional(),
|
|
235
|
+
branchName: z.string().trim().min(1).max(200).optional(),
|
|
236
|
+
worktreeKey: z.string().trim().min(1).max(300).optional(),
|
|
237
|
+
pullRequestUrl: z.string().trim().url().max(500).optional()
|
|
238
|
+
}).strict();
|
|
239
|
+
var autopilotCandidateActionSchema = z.object({
|
|
240
|
+
candidateId: z.string().trim().min(1).max(160),
|
|
241
|
+
candidateType: autopilotCandidateTypeSchema,
|
|
242
|
+
subjectId: z.string().trim().min(1).max(200),
|
|
243
|
+
workKind: workKindSchema.optional(),
|
|
244
|
+
riskLevel: impactRiskLevelSchema.default("low"),
|
|
245
|
+
summary: z.string().trim().min(1).max(600),
|
|
246
|
+
safeRepoPaths: z.array(safeRepoPathSchema).max(50).default([]),
|
|
247
|
+
evidenceSummaries: z.array(z.string().trim().min(1).max(300)).max(20).default([]),
|
|
248
|
+
links: autopilotCandidateLinksSchema.default({}),
|
|
249
|
+
policySnapshot: autopilotPolicySnapshotSchema.optional(),
|
|
250
|
+
outcome: autopilotClassificationOutcomeSchema.optional(),
|
|
251
|
+
reasonCodes: z.array(autopilotReasonCodeSchema).max(30).optional(),
|
|
252
|
+
guardChecks: z.array(autopilotGuardCheckSchema).max(30).optional()
|
|
253
|
+
}).strict();
|
|
254
|
+
var autopilotAuthorizationSchema = z.object({
|
|
255
|
+
authorizationId: z.string().trim().min(1).max(160),
|
|
256
|
+
policyVersion: z.string().trim().min(1).max(80),
|
|
257
|
+
mode: autopilotModeSchema,
|
|
258
|
+
outcome: autopilotClassificationOutcomeSchema,
|
|
259
|
+
candidateId: z.string().trim().min(1).max(160).optional(),
|
|
260
|
+
candidateType: autopilotCandidateTypeSchema.optional(),
|
|
261
|
+
subjectId: z.string().trim().min(1).max(200).optional(),
|
|
262
|
+
riskLevel: impactRiskLevelSchema,
|
|
263
|
+
reasonCodes: z.array(autopilotReasonCodeSchema).min(1),
|
|
264
|
+
guardChecks: z.array(autopilotGuardCheckSchema).default([]),
|
|
265
|
+
policySnapshot: autopilotPolicySnapshotSchema.optional(),
|
|
266
|
+
budgetWindows: z.array(autopilotBudgetWindowSchema).max(10).default([]),
|
|
267
|
+
linkedRecords: autopilotCandidateLinksSchema.optional(),
|
|
268
|
+
evidenceSummaries: z.array(z.string().trim().min(1).max(300)).max(20).default([]),
|
|
269
|
+
authorizedAt: isoDateTimeSchema.optional(),
|
|
270
|
+
authorizedBy: z.literal("autopilot").optional(),
|
|
271
|
+
reviewedByUserId: z.string().min(1).optional(),
|
|
272
|
+
summary: z.string().trim().min(1).max(600)
|
|
273
|
+
}).strict();
|
|
105
274
|
var impactReportStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale"]);
|
|
106
275
|
var issueCategorySchema = z.enum(["bug", "regression", "brokenWorkflow", "security", "operational", "other"]);
|
|
107
276
|
var issueSeveritySchema = z.enum(["low", "medium", "high", "critical"]);
|
|
@@ -114,6 +283,22 @@ var securityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "cr
|
|
|
114
283
|
var securityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
115
284
|
var securityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "remediationQueued", "resolved", "failed"]);
|
|
116
285
|
var securityApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
|
|
286
|
+
var testProfileStatusSchema = z.enum(["detected", "reviewReady", "approved", "disabled", "stale"]);
|
|
287
|
+
var testPackageManagerSchema = z.enum(["pnpm", "npm", "yarn", "bun", "dotnet", "maven", "gradle", "mixed", "unknown"]);
|
|
288
|
+
var testCommandKindSchema = z.enum(["verify", "test", "coverage", "lint", "typecheck", "build", "focused"]);
|
|
289
|
+
var testCommandSourceSchema = z.enum(["detected", "approved", "manual"]);
|
|
290
|
+
var testCoverageMetricStatusSchema = z.enum(["met", "belowThreshold", "missing", "blocked", "unknown"]);
|
|
291
|
+
var testCommandStatusSchema = z.enum(["passed", "failed", "blocked", "skipped", "missing"]);
|
|
292
|
+
var testRedactionStatusSchema = z.enum(["clean", "redacted", "blocked"]);
|
|
293
|
+
var testQualityScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
|
|
294
|
+
var testQualityFindingCategorySchema = z.enum(["missingTests", "missingCoverage", "missingCommand", "lowCoverage", "failingTests", "failingQuality", "failingQualityCheck", "staleScan", "blockedEnvironment", "weakTests", "flakyTests", "unverifiedImplementation", "testGap", "other"]);
|
|
295
|
+
var testQualityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
|
|
296
|
+
var testQualityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
297
|
+
var testQualityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "implementationQueued", "resolved", "failed"]);
|
|
298
|
+
var testQualityApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
|
|
299
|
+
var implementationTestGateStatusSchema = z.enum(["queued", "running", "passed", "failed", "blocked", "overridden", "stale"]);
|
|
300
|
+
var implementationTestGateOutcomeSchema = z.enum(["passed", "failed", "blocked", "missingTests", "belowThreshold", "overridden"]);
|
|
301
|
+
var implementationTestGateSourceSchema = z.enum(["manual", "runner", "completion", "prHandoff", "push", "system"]);
|
|
117
302
|
var appEvaluationScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
|
|
118
303
|
var appEvaluationFindingCategorySchema = z.enum(["verification", "productDrift", "planCleanup", "promptRot", "missingMemory", "releaseReadiness", "ux", "accessibility", "performance", "reliability", "securityPosture", "other"]);
|
|
119
304
|
var appEvaluationFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
|
|
@@ -122,6 +307,10 @@ var appEvaluationFindingStatusSchema = z.enum(["open", "planReady", "approved",
|
|
|
122
307
|
var appEvaluationApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "dismissed", "acceptedRisk", "implemented", "blocked"]);
|
|
123
308
|
var appEvaluationPlanLifecycleActionSchema = z.enum(["createPlan", "updatePlan", "markCompleted", "markImplemented", "markSuperseded", "markBlocked", "archive", "keepActive", "none"]);
|
|
124
309
|
var activityEventTypeSchema = z.enum([
|
|
310
|
+
"autopilotAuthorized",
|
|
311
|
+
"autopilotReviewRequired",
|
|
312
|
+
"autopilotBlocked",
|
|
313
|
+
"autopilotPaused",
|
|
125
314
|
"commandSubmitted",
|
|
126
315
|
"generationQueued",
|
|
127
316
|
"generationCompleted",
|
|
@@ -161,6 +350,22 @@ var activityEventTypeSchema = z.enum([
|
|
|
161
350
|
"appEvaluationFindingReviewed",
|
|
162
351
|
"appEvaluationPlanCreated",
|
|
163
352
|
"appEvaluationImplementationQueued",
|
|
353
|
+
"testProfileDetected",
|
|
354
|
+
"testProfileUpdated",
|
|
355
|
+
"testQualityScanQueued",
|
|
356
|
+
"testQualityScanStarted",
|
|
357
|
+
"testQualityScanCompleted",
|
|
358
|
+
"testQualityScanFailed",
|
|
359
|
+
"testQualityFindingOpened",
|
|
360
|
+
"testQualityFindingReviewed",
|
|
361
|
+
"testQualityPlanCreated",
|
|
362
|
+
"implementationTestGateQueued",
|
|
363
|
+
"implementationTestGateStarted",
|
|
364
|
+
"implementationTestGatePassed",
|
|
365
|
+
"implementationTestGateFailed",
|
|
366
|
+
"implementationTestGateBlocked",
|
|
367
|
+
"implementationTestGateOverride",
|
|
368
|
+
"ciWorkflowRetired",
|
|
164
369
|
"workRequeueRequested",
|
|
165
370
|
"workRequeueQueued",
|
|
166
371
|
"workRequeueBlocked",
|
|
@@ -288,6 +493,7 @@ var runnerResourceUsageSchema = z.object({
|
|
|
288
493
|
systemLoadAverage5m: z.number().nonnegative().optional(),
|
|
289
494
|
systemLoadAverage15m: z.number().nonnegative().optional()
|
|
290
495
|
});
|
|
496
|
+
var runnerClaimLaneIdSchema = z.string().trim().min(1).max(80);
|
|
291
497
|
var workIsolationModeSchema = z.enum(["none", "primaryCheckout", "branch", "gitWorktree"]);
|
|
292
498
|
var repositoryLinkSourceSchema = z.enum(["web", "cli"]);
|
|
293
499
|
var repositoryCloneStatusSchema = z.enum(["notCloned", "cloned", "validated", "failed"]);
|
|
@@ -370,6 +576,25 @@ var repositoryLinkItemSchema = baseItemSchema.extend({
|
|
|
370
576
|
cloneStatus: repositoryCloneStatusSchema.optional(),
|
|
371
577
|
autoSyncEnabled: z.boolean().optional(),
|
|
372
578
|
appEvaluationEnabled: z.boolean().optional(),
|
|
579
|
+
testQualityEnabled: z.boolean().optional(),
|
|
580
|
+
autopilotMode: autopilotModeSchema.optional(),
|
|
581
|
+
autopilotPolicyVersion: z.string().trim().min(1).max(80).optional(),
|
|
582
|
+
autopilotSafeWorkKinds: z.array(workKindSchema).optional(),
|
|
583
|
+
autopilotAllowedCandidateTypes: z.array(autopilotCandidateTypeSchema).optional(),
|
|
584
|
+
autopilotMaxRiskLevel: impactRiskLevelSchema.optional(),
|
|
585
|
+
autopilotRunnerId: z.string().min(1).optional(),
|
|
586
|
+
autopilotPausedReason: z.string().trim().min(1).max(600).optional(),
|
|
587
|
+
autopilotDailyWorkLimit: z.number().int().positive().max(100).optional(),
|
|
588
|
+
autopilotDailyPrLimit: z.number().int().nonnegative().max(100).optional(),
|
|
589
|
+
autopilotConcurrentWorkLimit: z.number().int().positive().max(20).optional(),
|
|
590
|
+
autopilotRunnerMinuteBudget: z.number().int().nonnegative().max(1440).optional(),
|
|
591
|
+
autopilotFailureBudget: z.number().int().nonnegative().max(50).optional(),
|
|
592
|
+
autopilotExpiresAt: isoDateTimeSchema.optional(),
|
|
593
|
+
autopilotReviewAfter: isoDateTimeSchema.optional(),
|
|
594
|
+
autopilotCooldownUntil: isoDateTimeSchema.optional(),
|
|
595
|
+
autopilotBudgetWindows: z.array(autopilotBudgetWindowSchema).max(10).optional(),
|
|
596
|
+
autopilotUpdatedByUserId: z.string().min(1).optional(),
|
|
597
|
+
autopilotUpdatedAt: isoDateTimeSchema.optional(),
|
|
373
598
|
lastValidatedAt: isoDateTimeSchema.optional(),
|
|
374
599
|
status: z.enum(["active", "revoked"]).default("active")
|
|
375
600
|
});
|
|
@@ -441,6 +666,9 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
441
666
|
appEvaluationScanId: z.string().min(1).optional(),
|
|
442
667
|
appEvaluationFindingId: z.string().min(1).optional(),
|
|
443
668
|
implementationVerificationId: z.string().min(1).optional(),
|
|
669
|
+
testQualityScanId: z.string().min(1).optional(),
|
|
670
|
+
testQualityFindingId: z.string().min(1).optional(),
|
|
671
|
+
implementationTestGateId: z.string().min(1).optional(),
|
|
444
672
|
projectContextRefreshId: z.string().min(1).optional(),
|
|
445
673
|
contextMissId: z.string().min(1).optional(),
|
|
446
674
|
sourceWorkItemId: z.string().min(1).optional(),
|
|
@@ -468,6 +696,7 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
468
696
|
claimedByRunnerId: z.string().optional(),
|
|
469
697
|
pairedByUserId: z.string().min(1).optional(),
|
|
470
698
|
machineId: z.string().min(1).optional(),
|
|
699
|
+
claimLaneId: runnerClaimLaneIdSchema.optional(),
|
|
471
700
|
claimLeaseId: z.string().min(1).optional(),
|
|
472
701
|
claimAttempt: z.number().int().nonnegative().optional(),
|
|
473
702
|
leaseExpiresAt: isoDateTimeSchema.optional(),
|
|
@@ -488,6 +717,12 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
488
717
|
sessionDecision: sessionDecisionSchema.optional(),
|
|
489
718
|
sessionDecisionReason: z.string().min(1).optional(),
|
|
490
719
|
implementationHandoff: implementationHandoffSchema.optional(),
|
|
720
|
+
autopilotAuthorization: autopilotAuthorizationSchema.optional(),
|
|
721
|
+
autopilotAuthorizationId: z.string().min(1).optional(),
|
|
722
|
+
autopilotCandidateId: z.string().min(1).optional(),
|
|
723
|
+
autopilotCandidateType: autopilotCandidateTypeSchema.optional(),
|
|
724
|
+
autopilotClassificationOutcome: autopilotClassificationOutcomeSchema.optional(),
|
|
725
|
+
autopilotPolicyVersion: z.string().trim().min(1).max(80).optional(),
|
|
491
726
|
lastStatusMessage: z.string().optional(),
|
|
492
727
|
lastStatusAt: isoDateTimeSchema
|
|
493
728
|
});
|
|
@@ -507,6 +742,8 @@ var runnerHeartbeatItemSchema = baseItemSchema.extend({
|
|
|
507
742
|
supportedWorkKinds: z.array(workKindSchema).optional(),
|
|
508
743
|
supportsBranchIsolation: z.boolean().optional(),
|
|
509
744
|
supportsGitWorktreeIsolation: z.boolean().optional(),
|
|
745
|
+
maxConcurrentWork: z.number().int().min(1).max(4).optional(),
|
|
746
|
+
activeClaimLaneIds: z.array(runnerClaimLaneIdSchema).max(4).optional(),
|
|
510
747
|
currentWorkItemId: z.string().min(1).optional(),
|
|
511
748
|
currentImplementationScopeId: z.string().min(1).optional(),
|
|
512
749
|
currentWorktreeKey: z.string().min(1).optional(),
|
|
@@ -565,6 +802,7 @@ var runnerExecutionLogItemSchema = baseItemSchema.extend({
|
|
|
565
802
|
executionWorktreeKey: z.string().min(1).optional(),
|
|
566
803
|
isolationMode: workIsolationModeSchema.optional(),
|
|
567
804
|
repositoryLockId: z.string().min(1).optional(),
|
|
805
|
+
claimLaneId: runnerClaimLaneIdSchema.optional(),
|
|
568
806
|
claimLeaseId: z.string().min(1).optional(),
|
|
569
807
|
status: z.enum(["claimed", "running", "completed", "failed", "blocked", "idle"]),
|
|
570
808
|
executionMode: executionModeSchema.optional(),
|
|
@@ -796,12 +1034,7 @@ var projectContextFreshnessSchema = z.enum(["fresh", "stale", "partial", "missin
|
|
|
796
1034
|
var projectContextMapStatusSchema = z.enum(["draft", "proposed", "approved", "stale", "archived"]);
|
|
797
1035
|
var projectContextRefreshStatusSchema = z.enum(["queued", "running", "completed", "failed", "skipped"]);
|
|
798
1036
|
var contextMissStatusSchema = z.enum(["open", "resolved", "dismissed"]);
|
|
799
|
-
var projectContextRepoPathSchema =
|
|
800
|
-
if (value.startsWith("/") || /^[A-Za-z]:[\\/]/.test(value)) {
|
|
801
|
-
return false;
|
|
802
|
-
}
|
|
803
|
-
return !value.split(/[\\/]+/).includes("..");
|
|
804
|
-
}, "Path must be repository-relative without traversal");
|
|
1037
|
+
var projectContextRepoPathSchema = safeRepoPathSchema;
|
|
805
1038
|
var projectContextCitationSchema = assistantCitationSchema.extend({
|
|
806
1039
|
repoPath: projectContextRepoPathSchema.optional()
|
|
807
1040
|
});
|
|
@@ -1269,6 +1502,179 @@ var securityPostureSnapshotItemSchema = baseItemSchema.extend({
|
|
|
1269
1502
|
nextScheduledAt: isoDateTimeSchema.optional(),
|
|
1270
1503
|
summary: z.string().trim().min(1).max(2e3).optional()
|
|
1271
1504
|
});
|
|
1505
|
+
var testCoverageThresholdsSchema = z.object({
|
|
1506
|
+
lines: z.number().min(0).max(100).optional(),
|
|
1507
|
+
statements: z.number().min(0).max(100).optional(),
|
|
1508
|
+
branches: z.number().min(0).max(100).optional(),
|
|
1509
|
+
functions: z.number().min(0).max(100).optional()
|
|
1510
|
+
}).strict();
|
|
1511
|
+
var testProfileCommandSchema = z.object({
|
|
1512
|
+
commandId: z.string().trim().min(1).max(160),
|
|
1513
|
+
kind: testCommandKindSchema,
|
|
1514
|
+
label: z.string().trim().min(1).max(200),
|
|
1515
|
+
source: testCommandSourceSchema.default("detected"),
|
|
1516
|
+
packageName: z.string().trim().min(1).max(200).optional(),
|
|
1517
|
+
scriptName: z.string().trim().min(1).max(120).optional(),
|
|
1518
|
+
tool: z.string().trim().min(1).max(120).optional(),
|
|
1519
|
+
workingDirectory: projectContextRepoPathSchema.optional(),
|
|
1520
|
+
required: z.boolean().default(false),
|
|
1521
|
+
estimatedDurationSeconds: z.number().int().positive().max(7200).optional()
|
|
1522
|
+
}).strict();
|
|
1523
|
+
var testProfileItemSchema = baseItemSchema.extend({
|
|
1524
|
+
type: z.literal("testProfile"),
|
|
1525
|
+
projectId: z.string().min(1),
|
|
1526
|
+
testProfileId: z.string().min(1),
|
|
1527
|
+
repositoryLinkId: z.string().min(1),
|
|
1528
|
+
status: testProfileStatusSchema,
|
|
1529
|
+
enabled: z.boolean().default(true),
|
|
1530
|
+
detectedPackageManager: testPackageManagerSchema.default("unknown"),
|
|
1531
|
+
packageManagers: z.array(testPackageManagerSchema).default([]),
|
|
1532
|
+
commands: z.array(testProfileCommandSchema).max(40).default([]),
|
|
1533
|
+
coverageThresholds: testCoverageThresholdsSchema.default({}),
|
|
1534
|
+
defaultWholeAppCommandId: z.string().trim().min(1).max(160).optional(),
|
|
1535
|
+
focusedCommandIds: z.array(z.string().trim().min(1).max(160)).max(20).default([]),
|
|
1536
|
+
lastDetectedAt: isoDateTimeSchema.optional(),
|
|
1537
|
+
lastReviewedRevision: z.string().trim().min(1).max(160).optional(),
|
|
1538
|
+
reviewedByUserId: z.string().min(1).optional(),
|
|
1539
|
+
reviewedAt: isoDateTimeSchema.optional()
|
|
1540
|
+
});
|
|
1541
|
+
var testCommandSummarySchema = z.object({
|
|
1542
|
+
commandId: z.string().trim().min(1).max(160).optional(),
|
|
1543
|
+
kind: testCommandKindSchema,
|
|
1544
|
+
label: z.string().trim().min(1).max(200),
|
|
1545
|
+
status: testCommandStatusSchema,
|
|
1546
|
+
durationMs: z.number().int().nonnegative().optional(),
|
|
1547
|
+
exitCode: z.number().int().min(0).max(255).optional(),
|
|
1548
|
+
summary: z.string().trim().min(1).max(1200),
|
|
1549
|
+
outputExcerpt: z.string().trim().min(1).max(1200).optional(),
|
|
1550
|
+
safePaths: z.array(projectContextRepoPathSchema).max(30).default([])
|
|
1551
|
+
}).strict();
|
|
1552
|
+
var testCoverageSummarySchema = z.object({
|
|
1553
|
+
status: testCoverageMetricStatusSchema,
|
|
1554
|
+
lines: z.number().min(0).max(100).optional(),
|
|
1555
|
+
statements: z.number().min(0).max(100).optional(),
|
|
1556
|
+
branches: z.number().min(0).max(100).optional(),
|
|
1557
|
+
functions: z.number().min(0).max(100).optional(),
|
|
1558
|
+
thresholds: testCoverageThresholdsSchema.default({}),
|
|
1559
|
+
reportPath: projectContextRepoPathSchema.optional(),
|
|
1560
|
+
summary: z.string().trim().min(1).max(1200).optional()
|
|
1561
|
+
}).strict();
|
|
1562
|
+
var testRedactionStateSchema = z.object({
|
|
1563
|
+
status: testRedactionStatusSchema.default("clean"),
|
|
1564
|
+
redactedFields: z.array(z.string().trim().min(1).max(120)).max(40).default([]),
|
|
1565
|
+
summary: z.string().trim().min(1).max(600).optional()
|
|
1566
|
+
}).strict();
|
|
1567
|
+
var testQualityFindingResultSchema = z.object({
|
|
1568
|
+
title: z.string().trim().min(1).max(200),
|
|
1569
|
+
category: testQualityFindingCategorySchema,
|
|
1570
|
+
severity: testQualityFindingSeveritySchema,
|
|
1571
|
+
confidence: testQualityFindingConfidenceSchema.default("medium"),
|
|
1572
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
1573
|
+
affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
1574
|
+
evidence: z.array(z.string().trim().min(1).max(600)).default([]),
|
|
1575
|
+
safePaths: z.array(projectContextRepoPathSchema).max(30).default([]),
|
|
1576
|
+
suggestedAction: z.string().trim().min(1).max(4e3),
|
|
1577
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
|
|
1578
|
+
proposedPlanTitle: z.string().trim().min(1).max(200).optional(),
|
|
1579
|
+
proposedPlanRepoPath: projectContextRepoPathSchema.optional(),
|
|
1580
|
+
proposedPlanContent: z.string().trim().min(1).max(3e4).optional(),
|
|
1581
|
+
dedupeKey: z.string().trim().min(1).max(240).optional(),
|
|
1582
|
+
status: testQualityFindingStatusSchema.default("open")
|
|
1583
|
+
}).strict();
|
|
1584
|
+
var testQualityScanResultSchema = z.object({
|
|
1585
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
1586
|
+
profile: testProfileItemSchema.omit({ id: true, type: true, schemaVersion: true, accountId: true, projectId: true, createdAt: true, updatedAt: true }).optional(),
|
|
1587
|
+
commandSummaries: z.array(testCommandSummarySchema).max(40).default([]),
|
|
1588
|
+
coverage: testCoverageSummarySchema.optional(),
|
|
1589
|
+
findings: z.array(testQualityFindingResultSchema).max(50).default([]),
|
|
1590
|
+
blockedReasons: z.array(z.string().trim().min(1).max(600)).max(20).default([]),
|
|
1591
|
+
redactionState: testRedactionStateSchema.default({ status: "clean", redactedFields: [] }),
|
|
1592
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
|
|
1593
|
+
warnings: z.array(z.string().trim().min(1).max(600)).default([])
|
|
1594
|
+
}).strict();
|
|
1595
|
+
var testQualityScanItemSchema = baseItemSchema.extend({
|
|
1596
|
+
type: z.literal("testQualityScan"),
|
|
1597
|
+
projectId: z.string().min(1),
|
|
1598
|
+
testQualityScanId: z.string().min(1),
|
|
1599
|
+
scheduledDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
|
1600
|
+
source: z.enum(["runner", "manual"]),
|
|
1601
|
+
status: testQualityScanStatusSchema,
|
|
1602
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
1603
|
+
runnerId: z.string().min(1).optional(),
|
|
1604
|
+
workItemId: z.string().min(1).optional(),
|
|
1605
|
+
testProfileId: z.string().min(1).optional(),
|
|
1606
|
+
findingIds: z.array(z.string().min(1)).default([]),
|
|
1607
|
+
summary: z.string().trim().min(1).max(2e3).optional(),
|
|
1608
|
+
result: testQualityScanResultSchema.optional(),
|
|
1609
|
+
startedAt: isoDateTimeSchema.optional(),
|
|
1610
|
+
completedAt: isoDateTimeSchema.optional(),
|
|
1611
|
+
failedAt: isoDateTimeSchema.optional(),
|
|
1612
|
+
error: z.string().max(1e3).optional()
|
|
1613
|
+
});
|
|
1614
|
+
var testQualityFindingItemSchema = baseItemSchema.extend({
|
|
1615
|
+
type: z.literal("testQualityFinding"),
|
|
1616
|
+
projectId: z.string().min(1),
|
|
1617
|
+
testQualityFindingId: z.string().min(1),
|
|
1618
|
+
testQualityScanId: z.string().min(1).optional(),
|
|
1619
|
+
implementationTestGateId: z.string().min(1).optional(),
|
|
1620
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
1621
|
+
title: z.string().trim().min(1).max(200),
|
|
1622
|
+
category: testQualityFindingCategorySchema,
|
|
1623
|
+
severity: testQualityFindingSeveritySchema,
|
|
1624
|
+
confidence: testQualityFindingConfidenceSchema.default("medium"),
|
|
1625
|
+
status: testQualityFindingStatusSchema,
|
|
1626
|
+
approvalState: testQualityApprovalStateSchema.default("proposed"),
|
|
1627
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
1628
|
+
affectedSurfaces: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
1629
|
+
evidence: z.array(z.string().trim().min(1).max(600)).default([]),
|
|
1630
|
+
safePaths: z.array(projectContextRepoPathSchema).max(30).default([]),
|
|
1631
|
+
suggestedAction: z.string().trim().min(1).max(4e3),
|
|
1632
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).min(1),
|
|
1633
|
+
dedupeKey: z.string().trim().min(1).max(240).optional(),
|
|
1634
|
+
proposedPlanDocumentId: z.string().min(1).optional(),
|
|
1635
|
+
implementationWorkItemId: z.string().min(1).optional(),
|
|
1636
|
+
reviewNotes: z.string().trim().min(1).optional(),
|
|
1637
|
+
reviewedByUserId: z.string().min(1).optional(),
|
|
1638
|
+
reviewedAt: isoDateTimeSchema.optional(),
|
|
1639
|
+
resolvedAt: isoDateTimeSchema.optional(),
|
|
1640
|
+
lastReviewAction: z.enum(["approve", "requestChanges", "dismiss", "acceptRisk", "reopen"]).optional()
|
|
1641
|
+
});
|
|
1642
|
+
var implementationTestGateResultSchema = z.object({
|
|
1643
|
+
outcome: implementationTestGateOutcomeSchema,
|
|
1644
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
1645
|
+
commandSummaries: z.array(testCommandSummarySchema).max(40).default([]),
|
|
1646
|
+
coverage: testCoverageSummarySchema.optional(),
|
|
1647
|
+
findings: z.array(testQualityFindingResultSchema).max(50).default([]),
|
|
1648
|
+
testGapJustification: z.string().trim().min(1).max(1200).optional(),
|
|
1649
|
+
blockedReasons: z.array(z.string().trim().min(1).max(600)).max(20).default([]),
|
|
1650
|
+
redactionState: testRedactionStateSchema.default({ status: "clean", redactedFields: [] }),
|
|
1651
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
1652
|
+
warnings: z.array(z.string().trim().min(1).max(600)).default([])
|
|
1653
|
+
}).strict();
|
|
1654
|
+
var implementationTestGateItemSchema = baseItemSchema.extend({
|
|
1655
|
+
type: z.literal("implementationTestGate"),
|
|
1656
|
+
projectId: z.string().min(1),
|
|
1657
|
+
implementationTestGateId: z.string().min(1),
|
|
1658
|
+
status: implementationTestGateStatusSchema,
|
|
1659
|
+
source: implementationTestGateSourceSchema.default("system"),
|
|
1660
|
+
sourceWorkItemId: z.string().min(1),
|
|
1661
|
+
gateWorkItemId: z.string().min(1).optional(),
|
|
1662
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
1663
|
+
runnerId: z.string().min(1).optional(),
|
|
1664
|
+
planDocumentId: z.string().min(1).optional(),
|
|
1665
|
+
planDocumentRevision: z.number().int().nonnegative().optional(),
|
|
1666
|
+
testProfileId: z.string().min(1).optional(),
|
|
1667
|
+
outcome: implementationTestGateOutcomeSchema.optional(),
|
|
1668
|
+
result: implementationTestGateResultSchema.optional(),
|
|
1669
|
+
summary: z.string().trim().min(1).max(2e3).optional(),
|
|
1670
|
+
startedAt: isoDateTimeSchema.optional(),
|
|
1671
|
+
completedAt: isoDateTimeSchema.optional(),
|
|
1672
|
+
failedAt: isoDateTimeSchema.optional(),
|
|
1673
|
+
overriddenByUserId: z.string().min(1).optional(),
|
|
1674
|
+
overrideReason: z.string().trim().min(1).max(1e3).optional(),
|
|
1675
|
+
overriddenAt: isoDateTimeSchema.optional(),
|
|
1676
|
+
error: z.string().max(1e3).optional()
|
|
1677
|
+
});
|
|
1272
1678
|
var appEvaluationFindingResultSchema = z.object({
|
|
1273
1679
|
title: z.string().trim().min(1).max(200),
|
|
1274
1680
|
category: appEvaluationFindingCategorySchema,
|
|
@@ -1358,13 +1764,19 @@ var activityEventItemSchema = baseItemSchema.extend({
|
|
|
1358
1764
|
relatedAppEvaluationScanId: z.string().min(1).optional(),
|
|
1359
1765
|
relatedAppEvaluationFindingId: z.string().min(1).optional(),
|
|
1360
1766
|
relatedImplementationVerificationId: z.string().min(1).optional(),
|
|
1767
|
+
relatedTestQualityScanId: z.string().min(1).optional(),
|
|
1768
|
+
relatedTestQualityFindingId: z.string().min(1).optional(),
|
|
1769
|
+
relatedImplementationTestGateId: z.string().min(1).optional(),
|
|
1361
1770
|
relatedProjectContextRefreshId: z.string().min(1).optional(),
|
|
1362
1771
|
relatedProjectSystemDiagramId: z.string().min(1).optional(),
|
|
1363
1772
|
relatedContextMissId: z.string().min(1).optional(),
|
|
1773
|
+
relatedAutopilotAuthorizationId: z.string().min(1).optional(),
|
|
1774
|
+
relatedAutopilotCandidateId: z.string().min(1).optional(),
|
|
1364
1775
|
generatedDraftId: z.string().min(1).optional(),
|
|
1365
1776
|
runnerId: z.string().min(1).optional(),
|
|
1366
1777
|
repositoryLinkId: z.string().min(1).optional(),
|
|
1367
1778
|
runnerLogId: z.string().min(1).optional(),
|
|
1779
|
+
claimLaneId: runnerClaimLaneIdSchema.optional(),
|
|
1368
1780
|
commandId: z.string().min(1).optional()
|
|
1369
1781
|
});
|
|
1370
1782
|
var activityHandoffExportSchema = z.object({
|
|
@@ -1421,6 +1833,10 @@ var projectItemUnionSchema = z.discriminatedUnion("type", [
|
|
|
1421
1833
|
securityScanItemSchema,
|
|
1422
1834
|
securityFindingItemSchema,
|
|
1423
1835
|
securityPostureSnapshotItemSchema,
|
|
1836
|
+
testProfileItemSchema,
|
|
1837
|
+
testQualityScanItemSchema,
|
|
1838
|
+
testQualityFindingItemSchema,
|
|
1839
|
+
implementationTestGateItemSchema,
|
|
1424
1840
|
appEvaluationScanItemSchema,
|
|
1425
1841
|
appEvaluationFindingItemSchema,
|
|
1426
1842
|
implementationVerificationItemSchema,
|
|
@@ -2526,6 +2942,26 @@ var ApiClient = class {
|
|
|
2526
2942
|
}
|
|
2527
2943
|
);
|
|
2528
2944
|
}
|
|
2945
|
+
async submitTestQualityScanResult(projectId, workItemId, result) {
|
|
2946
|
+
return this.request(
|
|
2947
|
+
`/projects/${projectId}/work-items/${workItemId}/test-quality-result`,
|
|
2948
|
+
z3.object({ scan: testQualityScanItemSchema, findings: z3.array(testQualityFindingItemSchema), workItem: workItemSchema }),
|
|
2949
|
+
{
|
|
2950
|
+
method: "POST",
|
|
2951
|
+
body: JSON.stringify(result)
|
|
2952
|
+
}
|
|
2953
|
+
);
|
|
2954
|
+
}
|
|
2955
|
+
async submitImplementationTestGateResult(projectId, workItemId, result) {
|
|
2956
|
+
return this.request(
|
|
2957
|
+
`/projects/${projectId}/work-items/${workItemId}/implementation-test-gate-result`,
|
|
2958
|
+
z3.object({ gate: implementationTestGateItemSchema, findings: z3.array(testQualityFindingItemSchema), workItem: workItemSchema, sourceWorkItem: workItemSchema.optional() }),
|
|
2959
|
+
{
|
|
2960
|
+
method: "POST",
|
|
2961
|
+
body: JSON.stringify(result)
|
|
2962
|
+
}
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2529
2965
|
async request(urlPath, schema, init) {
|
|
2530
2966
|
const response = await fetch(resolveApiUrl(this.options.apiUrl, urlPath), {
|
|
2531
2967
|
...init,
|
|
@@ -2691,6 +3127,115 @@ var brainGenerationFinalizationEntrySchema = z4.object({
|
|
|
2691
3127
|
updatedAt: z4.string().min(1),
|
|
2692
3128
|
lastError: z4.string().optional()
|
|
2693
3129
|
});
|
|
3130
|
+
var resultFinalizationTelemetrySchema = z4.object({
|
|
3131
|
+
runnerId: z4.string().min(1),
|
|
3132
|
+
idempotencyKey: z4.string().min(1),
|
|
3133
|
+
tool: z4.string().min(1).optional(),
|
|
3134
|
+
durationMs: z4.number().int().nonnegative().optional(),
|
|
3135
|
+
sessionPolicy: sessionPolicySchema.optional(),
|
|
3136
|
+
sessionGroupKey: z4.string().min(1).optional(),
|
|
3137
|
+
toolSessionId: z4.string().min(1).optional(),
|
|
3138
|
+
sessionDecision: sessionDecisionSchema.optional(),
|
|
3139
|
+
sessionDecisionReason: z4.string().min(1).optional(),
|
|
3140
|
+
message: z4.string().optional()
|
|
3141
|
+
});
|
|
3142
|
+
var resultFinalizationFailureSchema = resultFinalizationTelemetrySchema.extend({
|
|
3143
|
+
status: z4.literal("failed"),
|
|
3144
|
+
error: z4.string().optional()
|
|
3145
|
+
});
|
|
3146
|
+
var assistantResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3147
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3148
|
+
status: z4.literal("completed"),
|
|
3149
|
+
answer: assistantAnswerResultSchema.shape.answer,
|
|
3150
|
+
sourceBoundary: assistantAnswerResultSchema.shape.sourceBoundary.optional(),
|
|
3151
|
+
citations: assistantAnswerResultSchema.shape.citations.optional()
|
|
3152
|
+
}),
|
|
3153
|
+
resultFinalizationFailureSchema
|
|
3154
|
+
]);
|
|
3155
|
+
var impactPreviewResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3156
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3157
|
+
status: z4.literal("completed"),
|
|
3158
|
+
report: impactPreviewResultSchema
|
|
3159
|
+
}),
|
|
3160
|
+
resultFinalizationFailureSchema
|
|
3161
|
+
]);
|
|
3162
|
+
var issueDiagnosisResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3163
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3164
|
+
status: z4.literal("completed"),
|
|
3165
|
+
diagnosis: issueDiagnosisResultSchema
|
|
3166
|
+
}),
|
|
3167
|
+
resultFinalizationFailureSchema
|
|
3168
|
+
]);
|
|
3169
|
+
var securityPostureScanResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3170
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3171
|
+
status: z4.literal("completed"),
|
|
3172
|
+
result: securityPostureScanResultSchema
|
|
3173
|
+
}),
|
|
3174
|
+
resultFinalizationFailureSchema
|
|
3175
|
+
]);
|
|
3176
|
+
var appEvaluationScanResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3177
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3178
|
+
status: z4.literal("completed"),
|
|
3179
|
+
result: appEvaluationScanResultSchema
|
|
3180
|
+
}),
|
|
3181
|
+
resultFinalizationFailureSchema
|
|
3182
|
+
]);
|
|
3183
|
+
var projectContextRefreshResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3184
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3185
|
+
status: z4.literal("completed"),
|
|
3186
|
+
result: projectContextRefreshResultSchema
|
|
3187
|
+
}),
|
|
3188
|
+
resultFinalizationFailureSchema
|
|
3189
|
+
]);
|
|
3190
|
+
var implementationVerificationResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3191
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3192
|
+
status: z4.literal("completed"),
|
|
3193
|
+
result: implementationVerificationResultSchema
|
|
3194
|
+
}),
|
|
3195
|
+
resultFinalizationFailureSchema
|
|
3196
|
+
]);
|
|
3197
|
+
var testQualityScanResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3198
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3199
|
+
status: z4.literal("completed"),
|
|
3200
|
+
result: testQualityScanResultSchema
|
|
3201
|
+
}),
|
|
3202
|
+
resultFinalizationFailureSchema
|
|
3203
|
+
]);
|
|
3204
|
+
var implementationTestGateResultMutationSchema = z4.discriminatedUnion("status", [
|
|
3205
|
+
resultFinalizationTelemetrySchema.extend({
|
|
3206
|
+
status: z4.literal("completed"),
|
|
3207
|
+
result: implementationTestGateResultSchema
|
|
3208
|
+
}),
|
|
3209
|
+
resultFinalizationFailureSchema
|
|
3210
|
+
]);
|
|
3211
|
+
var durableResultMutationSchema = z4.discriminatedUnion("resultKind", [
|
|
3212
|
+
z4.object({ resultKind: z4.literal("assistantResult"), result: assistantResultMutationSchema }),
|
|
3213
|
+
z4.object({ resultKind: z4.literal("impactPreviewResult"), result: impactPreviewResultMutationSchema }),
|
|
3214
|
+
z4.object({ resultKind: z4.literal("issueDiagnosisResult"), result: issueDiagnosisResultMutationSchema }),
|
|
3215
|
+
z4.object({ resultKind: z4.literal("securityPostureScanResult"), result: securityPostureScanResultMutationSchema }),
|
|
3216
|
+
z4.object({ resultKind: z4.literal("appEvaluationScanResult"), result: appEvaluationScanResultMutationSchema }),
|
|
3217
|
+
z4.object({ resultKind: z4.literal("projectContextRefreshResult"), result: projectContextRefreshResultMutationSchema }),
|
|
3218
|
+
z4.object({ resultKind: z4.literal("implementationVerificationResult"), result: implementationVerificationResultMutationSchema }),
|
|
3219
|
+
z4.object({ resultKind: z4.literal("testQualityScanResult"), result: testQualityScanResultMutationSchema }),
|
|
3220
|
+
z4.object({ resultKind: z4.literal("implementationTestGateResult"), result: implementationTestGateResultMutationSchema })
|
|
3221
|
+
]);
|
|
3222
|
+
var durableResultFinalizationEntrySchema = z4.object({
|
|
3223
|
+
schemaVersion: z4.literal(1),
|
|
3224
|
+
kind: z4.literal("runnerResult"),
|
|
3225
|
+
status: z4.enum(["pending", "terminal"]),
|
|
3226
|
+
accountId: z4.string().min(1),
|
|
3227
|
+
projectId: z4.string().min(1),
|
|
3228
|
+
repositoryLinkId: z4.string().min(1),
|
|
3229
|
+
runnerId: z4.string().min(1),
|
|
3230
|
+
workItemId: z4.string().min(1),
|
|
3231
|
+
workKind: z4.enum(["assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate"]),
|
|
3232
|
+
attempt: z4.number().int().nonnegative(),
|
|
3233
|
+
idempotencyKey: z4.string().min(1),
|
|
3234
|
+
retryCount: z4.number().int().nonnegative(),
|
|
3235
|
+
createdAt: z4.string().min(1),
|
|
3236
|
+
updatedAt: z4.string().min(1),
|
|
3237
|
+
lastError: z4.string().optional()
|
|
3238
|
+
}).and(durableResultMutationSchema);
|
|
2694
3239
|
function defaultResultFinalizationOutboxDir() {
|
|
2695
3240
|
return path5.join(os2.homedir(), ".config", "amistio", "result-finalizations");
|
|
2696
3241
|
}
|
|
@@ -2713,6 +3258,26 @@ function createBrainGenerationFinalizationEntry(input, now = (/* @__PURE__ */ ne
|
|
|
2713
3258
|
updatedAt: now
|
|
2714
3259
|
});
|
|
2715
3260
|
}
|
|
3261
|
+
function createDurableResultFinalizationEntry(input, now = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3262
|
+
return durableResultFinalizationEntrySchema.parse({
|
|
3263
|
+
schemaVersion: 1,
|
|
3264
|
+
kind: "runnerResult",
|
|
3265
|
+
status: "pending",
|
|
3266
|
+
accountId: input.accountId,
|
|
3267
|
+
projectId: input.projectId,
|
|
3268
|
+
repositoryLinkId: input.repositoryLinkId,
|
|
3269
|
+
runnerId: input.runnerId,
|
|
3270
|
+
workItemId: input.workItemId,
|
|
3271
|
+
workKind: input.workKind,
|
|
3272
|
+
attempt: input.attempt,
|
|
3273
|
+
idempotencyKey: input.idempotencyKey,
|
|
3274
|
+
resultKind: input.resultKind,
|
|
3275
|
+
result: input.result,
|
|
3276
|
+
retryCount: 0,
|
|
3277
|
+
createdAt: now,
|
|
3278
|
+
updatedAt: now
|
|
3279
|
+
});
|
|
3280
|
+
}
|
|
2716
3281
|
async function upsertBrainGenerationFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
2717
3282
|
await mkdir5(outboxDir, { recursive: true });
|
|
2718
3283
|
const filePath = brainGenerationFinalizationEntryPath(entry, outboxDir);
|
|
@@ -2745,6 +3310,38 @@ async function listPendingBrainGenerationFinalizations(scope, outboxDir = defaul
|
|
|
2745
3310
|
}
|
|
2746
3311
|
return pending.sort((first, second) => Date.parse(first.createdAt) - Date.parse(second.createdAt));
|
|
2747
3312
|
}
|
|
3313
|
+
async function upsertDurableResultFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
3314
|
+
await mkdir5(outboxDir, { recursive: true });
|
|
3315
|
+
const filePath = durableResultFinalizationEntryPath(entry, outboxDir);
|
|
3316
|
+
const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
3317
|
+
await writeFile4(tempPath, `${JSON.stringify(entry, null, 2)}
|
|
3318
|
+
`, "utf8");
|
|
3319
|
+
await rename(tempPath, filePath);
|
|
3320
|
+
}
|
|
3321
|
+
async function listPendingDurableResultFinalizations(scope, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
3322
|
+
const entries = await readdir2(outboxDir, { withFileTypes: true }).catch((error) => {
|
|
3323
|
+
if (error.code === "ENOENT") return [];
|
|
3324
|
+
throw error;
|
|
3325
|
+
});
|
|
3326
|
+
const pending = [];
|
|
3327
|
+
for (const entry of entries) {
|
|
3328
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
3329
|
+
const filePath = path5.join(outboxDir, entry.name);
|
|
3330
|
+
let raw;
|
|
3331
|
+
try {
|
|
3332
|
+
raw = JSON.parse(await readFile3(filePath, "utf8"));
|
|
3333
|
+
} catch {
|
|
3334
|
+
continue;
|
|
3335
|
+
}
|
|
3336
|
+
const parsed = durableResultFinalizationEntrySchema.safeParse(raw);
|
|
3337
|
+
if (!parsed.success) continue;
|
|
3338
|
+
const value = parsed.data;
|
|
3339
|
+
if (value.status !== "pending") continue;
|
|
3340
|
+
if (value.accountId !== scope.accountId || value.projectId !== scope.projectId || value.repositoryLinkId !== scope.repositoryLinkId || value.runnerId !== scope.runnerId) continue;
|
|
3341
|
+
pending.push(value);
|
|
3342
|
+
}
|
|
3343
|
+
return pending.sort((first, second) => Date.parse(first.createdAt) - Date.parse(second.createdAt));
|
|
3344
|
+
}
|
|
2748
3345
|
async function markBrainGenerationFinalizationRetry(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
2749
3346
|
const updated = brainGenerationFinalizationEntrySchema.parse({
|
|
2750
3347
|
...entry,
|
|
@@ -2769,6 +3366,30 @@ async function markBrainGenerationFinalizationTerminal(entry, error, outboxDir =
|
|
|
2769
3366
|
async function deleteBrainGenerationFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
2770
3367
|
await rm(brainGenerationFinalizationEntryPath(entry, outboxDir), { force: true });
|
|
2771
3368
|
}
|
|
3369
|
+
async function markDurableResultFinalizationRetry(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
3370
|
+
const updated = durableResultFinalizationEntrySchema.parse({
|
|
3371
|
+
...entry,
|
|
3372
|
+
status: "pending",
|
|
3373
|
+
retryCount: entry.retryCount + 1,
|
|
3374
|
+
lastError: truncateLocalError(error),
|
|
3375
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3376
|
+
});
|
|
3377
|
+
await upsertDurableResultFinalizationEntry(updated, outboxDir);
|
|
3378
|
+
return updated;
|
|
3379
|
+
}
|
|
3380
|
+
async function markDurableResultFinalizationTerminal(entry, error, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
3381
|
+
const updated = durableResultFinalizationEntrySchema.parse({
|
|
3382
|
+
...entry,
|
|
3383
|
+
status: "terminal",
|
|
3384
|
+
lastError: truncateLocalError(error),
|
|
3385
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3386
|
+
});
|
|
3387
|
+
await upsertDurableResultFinalizationEntry(updated, outboxDir);
|
|
3388
|
+
return updated;
|
|
3389
|
+
}
|
|
3390
|
+
async function deleteDurableResultFinalizationEntry(entry, outboxDir = defaultResultFinalizationOutboxDir()) {
|
|
3391
|
+
await rm(durableResultFinalizationEntryPath(entry, outboxDir), { force: true });
|
|
3392
|
+
}
|
|
2772
3393
|
function brainGenerationFinalizationEntryPath(entry, outboxDir) {
|
|
2773
3394
|
return path5.join(outboxDir, `${brainGenerationFinalizationEntryKey(entry)}.json`);
|
|
2774
3395
|
}
|
|
@@ -2776,6 +3397,13 @@ function brainGenerationFinalizationEntryKey(entry) {
|
|
|
2776
3397
|
const hash = createHash2("sha256").update([entry.accountId, entry.projectId, entry.repositoryLinkId, entry.runnerId, entry.workItemId, String(entry.attempt), entry.idempotencyKey].join("\0")).digest("hex").slice(0, 32);
|
|
2777
3398
|
return `brain-generation-${hash}`;
|
|
2778
3399
|
}
|
|
3400
|
+
function durableResultFinalizationEntryPath(entry, outboxDir) {
|
|
3401
|
+
return path5.join(outboxDir, `${durableResultFinalizationEntryKey(entry)}.json`);
|
|
3402
|
+
}
|
|
3403
|
+
function durableResultFinalizationEntryKey(entry) {
|
|
3404
|
+
const hash = createHash2("sha256").update([entry.accountId, entry.projectId, entry.repositoryLinkId, entry.runnerId, entry.workItemId, String(entry.attempt), entry.idempotencyKey, entry.resultKind].join("\0")).digest("hex").slice(0, 32);
|
|
3405
|
+
return `runner-result-${hash}`;
|
|
3406
|
+
}
|
|
2779
3407
|
function truncateLocalError(error) {
|
|
2780
3408
|
const trimmed = error.trim();
|
|
2781
3409
|
return trimmed.length > 600 ? `${trimmed.slice(0, 600)}...` : trimmed;
|
|
@@ -4725,6 +5353,10 @@ var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
|
|
|
4725
5353
|
var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
|
|
4726
5354
|
var implementationVerificationStart = "AMISTIO_IMPLEMENTATION_VERIFICATION_START";
|
|
4727
5355
|
var implementationVerificationEnd = "AMISTIO_IMPLEMENTATION_VERIFICATION_END";
|
|
5356
|
+
var testQualityStart = "AMISTIO_TEST_QUALITY_START";
|
|
5357
|
+
var testQualityEnd = "AMISTIO_TEST_QUALITY_END";
|
|
5358
|
+
var implementationTestGateStart = "AMISTIO_IMPLEMENTATION_TEST_GATE_START";
|
|
5359
|
+
var implementationTestGateEnd = "AMISTIO_IMPLEMENTATION_TEST_GATE_END";
|
|
4728
5360
|
var projectContextMissingAreaMaxLength = 160;
|
|
4729
5361
|
var projectContextCoverageWarningMaxLength = 300;
|
|
4730
5362
|
var projectContextVerificationPlanMaxLength = 300;
|
|
@@ -4732,6 +5364,156 @@ var projectContextTopLevelWarningMaxLength = 600;
|
|
|
4732
5364
|
var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
|
|
4733
5365
|
var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
|
|
4734
5366
|
var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
|
|
5367
|
+
var canonicalAppEvaluationCategories = /* @__PURE__ */ new Map([
|
|
5368
|
+
["verification", "verification"],
|
|
5369
|
+
["verify", "verification"],
|
|
5370
|
+
["test", "verification"],
|
|
5371
|
+
["tests", "verification"],
|
|
5372
|
+
["testing", "verification"],
|
|
5373
|
+
["check", "verification"],
|
|
5374
|
+
["checks", "verification"],
|
|
5375
|
+
["ci", "verification"],
|
|
5376
|
+
["continuousintegration", "verification"],
|
|
5377
|
+
["qa", "verification"],
|
|
5378
|
+
["qualityassurance", "verification"],
|
|
5379
|
+
["build", "verification"],
|
|
5380
|
+
["lint", "verification"],
|
|
5381
|
+
["linting", "verification"],
|
|
5382
|
+
["typecheck", "verification"],
|
|
5383
|
+
["typechecking", "verification"],
|
|
5384
|
+
["productdrift", "productDrift"],
|
|
5385
|
+
["drift", "productDrift"],
|
|
5386
|
+
["docs", "productDrift"],
|
|
5387
|
+
["doc", "productDrift"],
|
|
5388
|
+
["documentation", "productDrift"],
|
|
5389
|
+
["docsdrift", "productDrift"],
|
|
5390
|
+
["documentationdrift", "productDrift"],
|
|
5391
|
+
["contextdrift", "productDrift"],
|
|
5392
|
+
["productdocumentationdrift", "productDrift"],
|
|
5393
|
+
["plancleanup", "planCleanup"],
|
|
5394
|
+
["plan", "planCleanup"],
|
|
5395
|
+
["plans", "planCleanup"],
|
|
5396
|
+
["staleplan", "planCleanup"],
|
|
5397
|
+
["staleplans", "planCleanup"],
|
|
5398
|
+
["cleanup", "planCleanup"],
|
|
5399
|
+
["cleanupplan", "planCleanup"],
|
|
5400
|
+
["superseded", "planCleanup"],
|
|
5401
|
+
["obsolete", "planCleanup"],
|
|
5402
|
+
["promptrot", "promptRot"],
|
|
5403
|
+
["prompt", "promptRot"],
|
|
5404
|
+
["prompts", "promptRot"],
|
|
5405
|
+
["template", "promptRot"],
|
|
5406
|
+
["templates", "promptRot"],
|
|
5407
|
+
["instruction", "promptRot"],
|
|
5408
|
+
["instructions", "promptRot"],
|
|
5409
|
+
["runnerprompt", "promptRot"],
|
|
5410
|
+
["runnerprompttemplate", "promptRot"],
|
|
5411
|
+
["prompttemplate", "promptRot"],
|
|
5412
|
+
["missingmemory", "missingMemory"],
|
|
5413
|
+
["memory", "missingMemory"],
|
|
5414
|
+
["memories", "missingMemory"],
|
|
5415
|
+
["lesson", "missingMemory"],
|
|
5416
|
+
["lessons", "missingMemory"],
|
|
5417
|
+
["workflow", "missingMemory"],
|
|
5418
|
+
["workflowlesson", "missingMemory"],
|
|
5419
|
+
["workflows", "missingMemory"],
|
|
5420
|
+
["releasereadiness", "releaseReadiness"],
|
|
5421
|
+
["release", "releaseReadiness"],
|
|
5422
|
+
["deploy", "releaseReadiness"],
|
|
5423
|
+
["deployment", "releaseReadiness"],
|
|
5424
|
+
["readiness", "releaseReadiness"],
|
|
5425
|
+
["production", "releaseReadiness"],
|
|
5426
|
+
["productionreadiness", "releaseReadiness"],
|
|
5427
|
+
["ux", "ux"],
|
|
5428
|
+
["ui", "ux"],
|
|
5429
|
+
["userexperience", "ux"],
|
|
5430
|
+
["accessibility", "accessibility"],
|
|
5431
|
+
["a11y", "accessibility"],
|
|
5432
|
+
["accessible", "accessibility"],
|
|
5433
|
+
["performance", "performance"],
|
|
5434
|
+
["perf", "performance"],
|
|
5435
|
+
["reliability", "reliability"],
|
|
5436
|
+
["resilience", "reliability"],
|
|
5437
|
+
["resiliency", "reliability"],
|
|
5438
|
+
["stability", "reliability"],
|
|
5439
|
+
["uptime", "reliability"],
|
|
5440
|
+
["securityposture", "securityPosture"],
|
|
5441
|
+
["security", "securityPosture"],
|
|
5442
|
+
["posture", "securityPosture"],
|
|
5443
|
+
["vulnerability", "securityPosture"],
|
|
5444
|
+
["vulnerabilities", "securityPosture"],
|
|
5445
|
+
["vulnerabilityposture", "securityPosture"],
|
|
5446
|
+
["other", "other"],
|
|
5447
|
+
["technicaldebt", "other"],
|
|
5448
|
+
["debt", "other"],
|
|
5449
|
+
["maintainability", "other"],
|
|
5450
|
+
["codequality", "other"],
|
|
5451
|
+
["architecture", "other"],
|
|
5452
|
+
["operations", "other"],
|
|
5453
|
+
["ops", "other"]
|
|
5454
|
+
]);
|
|
5455
|
+
var canonicalTestCommandKinds = /* @__PURE__ */ new Map([
|
|
5456
|
+
["verify", "verify"],
|
|
5457
|
+
["verification", "verify"],
|
|
5458
|
+
["wholeapp", "verify"],
|
|
5459
|
+
["wholeappverification", "verify"],
|
|
5460
|
+
["full", "verify"],
|
|
5461
|
+
["fullcheck", "verify"],
|
|
5462
|
+
["fullchecks", "verify"],
|
|
5463
|
+
["allcheck", "verify"],
|
|
5464
|
+
["allchecks", "verify"],
|
|
5465
|
+
["ci", "verify"],
|
|
5466
|
+
["continuousintegration", "verify"],
|
|
5467
|
+
["test", "test"],
|
|
5468
|
+
["tests", "test"],
|
|
5469
|
+
["testing", "test"],
|
|
5470
|
+
["unittest", "test"],
|
|
5471
|
+
["unittests", "test"],
|
|
5472
|
+
["integrationtest", "test"],
|
|
5473
|
+
["integrationtests", "test"],
|
|
5474
|
+
["e2etest", "test"],
|
|
5475
|
+
["e2etests", "test"],
|
|
5476
|
+
["endtoend", "test"],
|
|
5477
|
+
["endtoendtest", "test"],
|
|
5478
|
+
["spec", "test"],
|
|
5479
|
+
["specs", "test"],
|
|
5480
|
+
["suite", "test"],
|
|
5481
|
+
["testsuite", "test"],
|
|
5482
|
+
["jest", "test"],
|
|
5483
|
+
["vitest", "test"],
|
|
5484
|
+
["coverage", "coverage"],
|
|
5485
|
+
["coveragereport", "coverage"],
|
|
5486
|
+
["lint", "lint"],
|
|
5487
|
+
["linting", "lint"],
|
|
5488
|
+
["eslint", "lint"],
|
|
5489
|
+
["format", "lint"],
|
|
5490
|
+
["formatting", "lint"],
|
|
5491
|
+
["style", "lint"],
|
|
5492
|
+
["quality", "lint"],
|
|
5493
|
+
["qualitycheck", "lint"],
|
|
5494
|
+
["staticanalysis", "lint"],
|
|
5495
|
+
["typecheck", "typecheck"],
|
|
5496
|
+
["typechecks", "typecheck"],
|
|
5497
|
+
["typechecking", "typecheck"],
|
|
5498
|
+
["typecheck", "typecheck"],
|
|
5499
|
+
["tsc", "typecheck"],
|
|
5500
|
+
["typescript", "typecheck"],
|
|
5501
|
+
["build", "build"],
|
|
5502
|
+
["compile", "build"],
|
|
5503
|
+
["focused", "focused"],
|
|
5504
|
+
["focus", "focused"],
|
|
5505
|
+
["targeted", "focused"],
|
|
5506
|
+
["scoped", "focused"],
|
|
5507
|
+
["package", "focused"],
|
|
5508
|
+
["packaged", "focused"],
|
|
5509
|
+
["affected", "focused"],
|
|
5510
|
+
["affectedtests", "focused"],
|
|
5511
|
+
["touched", "focused"],
|
|
5512
|
+
["touchedtests", "focused"],
|
|
5513
|
+
["changed", "focused"],
|
|
5514
|
+
["changedtests", "focused"],
|
|
5515
|
+
["other", "focused"]
|
|
5516
|
+
]);
|
|
4735
5517
|
var canonicalProjectContextCitationSources = /* @__PURE__ */ new Map([
|
|
4736
5518
|
["projectbrain", "projectBrain"],
|
|
4737
5519
|
["approvedbrain", "projectBrain"],
|
|
@@ -4756,6 +5538,7 @@ function createImplementationVerificationPrompt(workItem) {
|
|
|
4756
5538
|
`Project ID: ${workItem.projectId}`,
|
|
4757
5539
|
`Implementation verification ID: ${workItem.implementationVerificationId ?? "unknown"}`,
|
|
4758
5540
|
`Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
|
|
5541
|
+
...autopilotPromptLines(workItem),
|
|
4759
5542
|
"",
|
|
4760
5543
|
"## Verification Request",
|
|
4761
5544
|
"",
|
|
@@ -4789,22 +5572,114 @@ function createImplementationVerificationPrompt(workItem) {
|
|
|
4789
5572
|
"Do not put Markdown fences around the markers. Do not implement or fix anything during this verification pass."
|
|
4790
5573
|
].join("\n");
|
|
4791
5574
|
}
|
|
4792
|
-
function
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
5575
|
+
function createTestQualityScanPrompt(workItem, context) {
|
|
5576
|
+
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 20).map((document) => [
|
|
5577
|
+
`### ${document.title}`,
|
|
5578
|
+
`documentId: ${document.documentId}`,
|
|
5579
|
+
`documentType: ${document.documentType}`,
|
|
5580
|
+
`repoPath: ${document.repoPath}`,
|
|
5581
|
+
`revision: ${document.revision}`,
|
|
5582
|
+
document.content.slice(0, 3e3)
|
|
5583
|
+
].join("\n")).join("\n\n");
|
|
5584
|
+
return [
|
|
5585
|
+
"# Amistio Test Quality Scan",
|
|
5586
|
+
"",
|
|
5587
|
+
"You are running locally through the Amistio CLI inside the user's repository.",
|
|
5588
|
+
"Run a non-mutating daily test, coverage, and quality scan for this repository.",
|
|
5589
|
+
"Do not modify files, create branches, commit, push, install packages, run implementation prompts, or make unaudited network calls.",
|
|
5590
|
+
"Use safe local metadata to infer package managers and scripts, then run existing lint, typecheck, test, coverage, and build commands only when they already exist.",
|
|
5591
|
+
"Return missing tests, missing coverage, low coverage, failing quality commands, and broken whole-app verification as reviewable findings that can become plans.",
|
|
5592
|
+
"",
|
|
5593
|
+
"## Work Item",
|
|
5594
|
+
"",
|
|
5595
|
+
`Title: ${workItem.title}`,
|
|
5596
|
+
`Work item ID: ${workItem.workItemId}`,
|
|
5597
|
+
`Project ID: ${workItem.projectId}`,
|
|
5598
|
+
`Test quality scan ID: ${workItem.testQualityScanId ?? "unknown"}`,
|
|
5599
|
+
"",
|
|
5600
|
+
"## Approved Project Brain Context",
|
|
5601
|
+
"",
|
|
5602
|
+
approvedContext || "No approved project-brain records were loaded. Inspect safe local metadata and summarize the context gap.",
|
|
5603
|
+
"",
|
|
5604
|
+
"## Requirements",
|
|
5605
|
+
"",
|
|
5606
|
+
"- Prefer the repository whole-app verification script when one exists, then focused package scripts.",
|
|
5607
|
+
"- Detect package managers from lockfiles and package metadata without uploading raw source.",
|
|
5608
|
+
"- Capture bounded summaries, exit codes, coverage metrics, and repository-relative safe paths only.",
|
|
5609
|
+
"- If coverage cannot be measured, return a missingCoverage finding instead of guessing.",
|
|
5610
|
+
"- If tests are missing or impossible to run, return a missingTests or testGap finding with a concrete plan and verification steps.",
|
|
5611
|
+
"- Redact secrets, absolute paths, env vars, process lists, tokens, provider sessions, and command lines containing secret-looking values.",
|
|
5612
|
+
"",
|
|
5613
|
+
"## Output Contract",
|
|
5614
|
+
"",
|
|
5615
|
+
"Print exactly one JSON object between the markers below. The CLI will submit only this structured test scan result back to Amistio.",
|
|
5616
|
+
"Accepted command kind values: verify, test, coverage, lint, typecheck, build, focused.",
|
|
5617
|
+
"Accepted command status values: passed, failed, skipped, missing, blocked.",
|
|
5618
|
+
"Accepted finding categories: missingTests, missingCoverage, lowCoverage, failingTests, failingQuality, missingCommand, flakyTests, unverifiedImplementation, testGap, other.",
|
|
5619
|
+
"",
|
|
5620
|
+
testQualityStart,
|
|
5621
|
+
'{"summary":"Whole-app verification exists, but coverage reporting is missing for one package.","profile":{"testProfileId":"test_profile_detected","repositoryLinkId":"repository_link_placeholder","status":"detected","enabled":true,"detectedPackageManager":"pnpm","packageManagers":["pnpm"],"commands":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","source":"detected","scriptName":"verify","workingDirectory":".","required":true}],"coverageThresholds":{"lines":80},"defaultWholeAppCommandId":"verify","focusedCommandIds":[],"lastDetectedAt":"2026-01-01T00:00:00.000Z"},"commandSummaries":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","status":"passed","exitCode":0,"summary":"The repository verification script completed successfully.","safePaths":["package.json"]}],"coverage":{"status":"missing","thresholds":{"lines":80},"summary":"No coverage report was found."},"findings":[{"title":"Coverage report is missing","category":"missingCoverage","severity":"medium","confidence":"high","summary":"The repository test profile does not produce a coverage summary.","affectedSurfaces":["Test verification"],"evidence":["The scan found a test command but no coverage output."],"safePaths":["package.json"],"suggestedAction":"Add or document a coverage command for the affected package and include it in whole-app verification.","verificationPlan":["Run the new coverage command locally","Confirm coverage appears in the Test panel"],"dedupeKey":"missing-coverage"}],"blockedReasons":[],"redactionState":{"status":"clean","redactedFields":[]},"verificationPlan":["Review generated Test findings","Run the whole-app verification command before implementation handoff"],"warnings":[]}',
|
|
5622
|
+
testQualityEnd,
|
|
5623
|
+
"",
|
|
5624
|
+
"Do not put Markdown fences around the markers. Do not implement fixes during this scan."
|
|
5625
|
+
].join("\n");
|
|
5626
|
+
}
|
|
5627
|
+
function createImplementationTestGatePrompt(workItem) {
|
|
5628
|
+
return [
|
|
5629
|
+
"# Amistio Implementation Test Gate",
|
|
5630
|
+
"",
|
|
5631
|
+
"You are running locally through the Amistio CLI inside the user's repository.",
|
|
5632
|
+
"Run the required test gate before implementation completion, PR handoff, or runner-managed push.",
|
|
5633
|
+
"Do not modify files, install packages unless an already-approved preflight policy permits it, commit, push, or upload raw source.",
|
|
5634
|
+
"Run focused checks for the touched scope first when available, then the whole-app verification command when practical.",
|
|
5635
|
+
"If tests are missing or impossible, return blocked with a test-gap justification and a finding instead of marking the gate passed.",
|
|
5636
|
+
"",
|
|
5637
|
+
"## Work Item",
|
|
5638
|
+
"",
|
|
5639
|
+
`Title: ${workItem.title}`,
|
|
5640
|
+
`Work item ID: ${workItem.workItemId}`,
|
|
5641
|
+
`Project ID: ${workItem.projectId}`,
|
|
5642
|
+
`Implementation test gate ID: ${workItem.implementationTestGateId ?? "unknown"}`,
|
|
5643
|
+
`Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
|
|
5644
|
+
"",
|
|
5645
|
+
"## Gate Request",
|
|
5646
|
+
"",
|
|
5647
|
+
workItem.sourceWish ?? "Run focused checks and whole-app verification before marking implementation complete.",
|
|
5648
|
+
"",
|
|
5649
|
+
"## Data Safety",
|
|
5650
|
+
"",
|
|
5651
|
+
"- Persist only bounded summaries, coverage metrics, exit codes, and repository-relative paths.",
|
|
5652
|
+
"- Do not include raw source, secrets, env vars, process lists, absolute local paths, credential values, tokens, provider sessions, or destructive shell output.",
|
|
5653
|
+
"- Use safePaths for repository-relative paths only; never include /absolute paths or ../ traversal.",
|
|
5654
|
+
"",
|
|
5655
|
+
"## Output Contract",
|
|
5656
|
+
"",
|
|
5657
|
+
"Print exactly one JSON object between the markers below. The CLI will submit only this structured gate result back to Amistio.",
|
|
5658
|
+
"Accepted outcome values: passed, failed, blocked, overridden.",
|
|
5659
|
+
"",
|
|
5660
|
+
implementationTestGateStart,
|
|
5661
|
+
'{"outcome":"passed","summary":"Focused checks and whole-app verification passed.","commandSummaries":[{"commandId":"verify","kind":"verify","label":"Whole-app verification","status":"passed","exitCode":0,"summary":"The repository verification script completed successfully.","safePaths":["package.json"]}],"coverage":{"status":"unknown","thresholds":{},"summary":"No coverage threshold was configured for this gate."},"findings":[],"blockedReasons":[],"redactionState":{"status":"clean","redactedFields":[]},"verificationPlan":["Record this gate result before marking implementation complete"],"warnings":[]}',
|
|
5662
|
+
implementationTestGateEnd,
|
|
5663
|
+
"",
|
|
5664
|
+
"Do not put Markdown fences around the markers."
|
|
5665
|
+
].join("\n");
|
|
5666
|
+
}
|
|
5667
|
+
function createWorkExecutionPrompt(workItem, context) {
|
|
5668
|
+
if (workItem.workKind === "brainGeneration") {
|
|
5669
|
+
return createBrainGenerationPrompt(workItem);
|
|
5670
|
+
}
|
|
5671
|
+
if (workItem.workKind === "planRevision") {
|
|
5672
|
+
return createPlanRevisionPrompt(workItem, context?.planRevision);
|
|
5673
|
+
}
|
|
5674
|
+
if (workItem.workKind === "assistantQuestion") {
|
|
5675
|
+
return createAssistantQuestionPrompt(workItem, context?.assistantQuestion);
|
|
5676
|
+
}
|
|
5677
|
+
if (workItem.workKind === "impactPreview") {
|
|
5678
|
+
return createImpactPreviewPrompt(workItem, context?.impactPreview);
|
|
5679
|
+
}
|
|
5680
|
+
if (workItem.workKind === "issueDiagnosis") {
|
|
5681
|
+
return createIssueDiagnosisPrompt(workItem, context?.issueDiagnosis);
|
|
5682
|
+
}
|
|
4808
5683
|
if (workItem.workKind === "securityPostureScan") {
|
|
4809
5684
|
return createSecurityPostureScanPrompt(workItem, context?.securityPostureScan);
|
|
4810
5685
|
}
|
|
@@ -4817,6 +5692,12 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
4817
5692
|
if (workItem.workKind === "implementationVerification") {
|
|
4818
5693
|
return createImplementationVerificationPrompt(workItem);
|
|
4819
5694
|
}
|
|
5695
|
+
if (workItem.workKind === "testQualityScan") {
|
|
5696
|
+
return createTestQualityScanPrompt(workItem, context?.testQualityScan);
|
|
5697
|
+
}
|
|
5698
|
+
if (workItem.workKind === "implementationTestGate") {
|
|
5699
|
+
return createImplementationTestGatePrompt(workItem);
|
|
5700
|
+
}
|
|
4820
5701
|
return [
|
|
4821
5702
|
"# Amistio Work Execution",
|
|
4822
5703
|
"",
|
|
@@ -4831,6 +5712,7 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
4831
5712
|
`Implementation scope: ${workItem.implementationScopeId ?? workItem.controllingAdrId ?? "work-item"}`,
|
|
4832
5713
|
`Execution branch: ${workItem.executionBranch ?? "managed by Amistio CLI"}`,
|
|
4833
5714
|
`Execution worktree key: ${workItem.executionWorktreeKey ?? "managed by Amistio CLI"}`,
|
|
5715
|
+
...autopilotPromptLines(workItem),
|
|
4834
5716
|
"",
|
|
4835
5717
|
"## Rules",
|
|
4836
5718
|
"",
|
|
@@ -4845,6 +5727,27 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
4845
5727
|
"- Run relevant verification commands when feasible and summarize results."
|
|
4846
5728
|
].join("\n");
|
|
4847
5729
|
}
|
|
5730
|
+
function autopilotPromptLines(workItem) {
|
|
5731
|
+
const autopilot = autopilotWorkMetadata(workItem);
|
|
5732
|
+
if (!autopilot.autopilotAuthorizationId) {
|
|
5733
|
+
return [];
|
|
5734
|
+
}
|
|
5735
|
+
const lines = [
|
|
5736
|
+
`Autopilot authorization ID: ${autopilot.autopilotAuthorizationId}`,
|
|
5737
|
+
...autopilot.autopilotCandidateId ? [`Autopilot candidate ID: ${autopilot.autopilotCandidateId}`] : [],
|
|
5738
|
+
...autopilot.autopilotCandidateType ? [`Autopilot candidate type: ${autopilot.autopilotCandidateType}`] : [],
|
|
5739
|
+
`Autopilot outcome: ${autopilot.autopilotClassificationOutcome ?? "unknown"}`,
|
|
5740
|
+
`Autopilot policy: ${autopilot.autopilotPolicyVersion ?? "unknown"}`
|
|
5741
|
+
];
|
|
5742
|
+
if (autopilot.autopilotAuthorization?.summary) {
|
|
5743
|
+
lines.push(`Autopilot summary: ${autopilot.autopilotAuthorization.summary}`);
|
|
5744
|
+
}
|
|
5745
|
+
lines.push("Autopilot satisfies the app approval gate only for the audited low-risk work item; it does not loosen local repository, credential, or data-safety boundaries.");
|
|
5746
|
+
return lines;
|
|
5747
|
+
}
|
|
5748
|
+
function autopilotWorkMetadata(workItem) {
|
|
5749
|
+
return workItem;
|
|
5750
|
+
}
|
|
4848
5751
|
function createProjectContextRefreshPrompt(workItem, context) {
|
|
4849
5752
|
const approvedContext = (context?.documents ?? []).filter((document) => document.status === "approved" || document.syncState === "approved" || document.syncState === "synced").slice(0, 24).map((document) => [
|
|
4850
5753
|
`### ${document.title}`,
|
|
@@ -5016,6 +5919,8 @@ function createAppEvaluationScanPrompt(workItem, context) {
|
|
|
5016
5919
|
"- Check app verification health: lint, typecheck, test, build, CI references, flaky or missing gates.",
|
|
5017
5920
|
"- Check product and docs drift between approved context, plans, prompts, and current implementation.",
|
|
5018
5921
|
"- Check old plans and prompts for cleanup candidates, but only propose lifecycle actions such as markCompleted, markSuperseded, archive, keepActive, or none.",
|
|
5922
|
+
"- Treat active proposed, approved, ready, or in-progress plans/prompts with accepted controlling ADRs, features, or work artifacts as active work. Do not classify them as cleanup material solely because they are older or not yet fully implemented; use keepActive when they still describe valid pending or approved work.",
|
|
5923
|
+
"- When lifecycle metadata disagrees across indexes, frontmatter, feature specs, ADRs, and implementation evidence, cite the conflict and propose a metadata correction or verification step instead of archival/removal.",
|
|
5019
5924
|
"- Check missing memory or workflow updates when repeated lessons or operational rules are visible.",
|
|
5020
5925
|
"- Check release readiness, UX, accessibility, performance, reliability, and security-posture follow-through at a summary level.",
|
|
5021
5926
|
"",
|
|
@@ -5028,6 +5933,10 @@ function createAppEvaluationScanPrompt(workItem, context) {
|
|
|
5028
5933
|
"## Output Contract",
|
|
5029
5934
|
"",
|
|
5030
5935
|
"Print exactly one JSON object between the markers below. The CLI will submit only this structured scan result back to Amistio.",
|
|
5936
|
+
"Accepted category values: verification, productDrift, planCleanup, promptRot, missingMemory, releaseReadiness, ux, accessibility, performance, reliability, securityPosture, other.",
|
|
5937
|
+
"Accepted severity values: info, low, medium, high, critical.",
|
|
5938
|
+
"Accepted confidence values: low, medium, high.",
|
|
5939
|
+
"Accepted proposedLifecycleAction values: createPlan, updatePlan, markCompleted, markImplemented, markSuperseded, markBlocked, archive, keepActive, none.",
|
|
5031
5940
|
"",
|
|
5032
5941
|
appEvaluationStart,
|
|
5033
5942
|
'{"summary":"The app is generally healthy, but one release-readiness plan needs cleanup and verification evidence should be refreshed.","baselineVersion":"amistio-app-evaluation-v1","findings":[{"title":"Stale release plan should be marked superseded","category":"planCleanup","severity":"low","confidence":"high","summary":"A release plan appears to describe a workflow that has since been replaced by a newer accepted plan.","affectedSurfaces":["docs/plans"],"evidence":["A newer plan references the same scope and status, while the older plan remains proposed."],"suggestedAction":"Prepare a cleanup update that marks the stale plan superseded and links to the newer plan.","verificationPlan":["Review both plan files","Confirm the newer plan is accepted before changing lifecycle state"],"safePaths":["docs/plans/PLAN-example.md"],"proposedLifecycleAction":"markSuperseded","relatedArtifactIds":[]}],"verificationPlan":["Review App Evaluation findings for approval","Run the canonical verify gate before implementing approved actions"]}',
|
|
@@ -5358,7 +6267,8 @@ function parseAppEvaluationScanResult(output) {
|
|
|
5358
6267
|
}
|
|
5359
6268
|
const payload = output.slice(start + appEvaluationStart.length, end).trim();
|
|
5360
6269
|
const parsed = JSON.parse(stripJsonFence(payload));
|
|
5361
|
-
|
|
6270
|
+
const normalized = normalizeAppEvaluationScanResultPaths(normalizeAppEvaluationScanResultEnums(parsed));
|
|
6271
|
+
return appEvaluationScanResultSchema.parse(normalized);
|
|
5362
6272
|
}
|
|
5363
6273
|
function parseProjectContextRefreshResult(output, options = {}) {
|
|
5364
6274
|
const start = output.indexOf(projectContextRefreshStart);
|
|
@@ -5381,6 +6291,68 @@ function parseImplementationVerificationResult(output) {
|
|
|
5381
6291
|
const parsed = JSON.parse(stripJsonFence(payload));
|
|
5382
6292
|
return implementationVerificationResultSchema.parse(parsed);
|
|
5383
6293
|
}
|
|
6294
|
+
function parseTestQualityScanResult(output) {
|
|
6295
|
+
const start = output.indexOf(testQualityStart);
|
|
6296
|
+
const end = output.indexOf(testQualityEnd, start + testQualityStart.length);
|
|
6297
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
6298
|
+
throw new Error("Local AI scan did not return an Amistio test quality block.");
|
|
6299
|
+
}
|
|
6300
|
+
const payload = output.slice(start + testQualityStart.length, end).trim();
|
|
6301
|
+
const parsed = JSON.parse(stripJsonFence(payload));
|
|
6302
|
+
return testQualityScanResultSchema.parse(normalizeTestQualityScanResultCommandKinds(parsed));
|
|
6303
|
+
}
|
|
6304
|
+
function normalizeTestQualityScanResultCommandKinds(value) {
|
|
6305
|
+
if (!isObjectRecord(value)) {
|
|
6306
|
+
return value;
|
|
6307
|
+
}
|
|
6308
|
+
const normalized = { ...value };
|
|
6309
|
+
if (isObjectRecord(normalized.profile) && Array.isArray(normalized.profile.commands)) {
|
|
6310
|
+
normalized.profile = {
|
|
6311
|
+
...normalized.profile,
|
|
6312
|
+
commands: normalized.profile.commands.map(normalizeTestCommandKindObject)
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
if (Array.isArray(normalized.commandSummaries)) {
|
|
6316
|
+
normalized.commandSummaries = normalized.commandSummaries.map(normalizeTestCommandKindObject);
|
|
6317
|
+
}
|
|
6318
|
+
return normalized;
|
|
6319
|
+
}
|
|
6320
|
+
function normalizeTestCommandKindObject(value) {
|
|
6321
|
+
if (!isObjectRecord(value)) {
|
|
6322
|
+
return value;
|
|
6323
|
+
}
|
|
6324
|
+
return { ...value, kind: normalizeTestCommandKind(value.kind) };
|
|
6325
|
+
}
|
|
6326
|
+
function normalizeTestCommandKind(value) {
|
|
6327
|
+
if (typeof value !== "string") {
|
|
6328
|
+
return value;
|
|
6329
|
+
}
|
|
6330
|
+
const trimmed = value.trim();
|
|
6331
|
+
if (!trimmed) {
|
|
6332
|
+
return value;
|
|
6333
|
+
}
|
|
6334
|
+
const direct = canonicalTestCommandKinds.get(normalizeEnumKey(trimmed));
|
|
6335
|
+
if (direct) {
|
|
6336
|
+
return direct;
|
|
6337
|
+
}
|
|
6338
|
+
for (const segment of trimmed.split(/[\/,|]+/)) {
|
|
6339
|
+
const segmentKind = canonicalTestCommandKinds.get(normalizeEnumKey(segment));
|
|
6340
|
+
if (segmentKind) {
|
|
6341
|
+
return segmentKind;
|
|
6342
|
+
}
|
|
6343
|
+
}
|
|
6344
|
+
return "focused";
|
|
6345
|
+
}
|
|
6346
|
+
function parseImplementationTestGateResult(output) {
|
|
6347
|
+
const start = output.indexOf(implementationTestGateStart);
|
|
6348
|
+
const end = output.indexOf(implementationTestGateEnd, start + implementationTestGateStart.length);
|
|
6349
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
6350
|
+
throw new Error("Local AI gate did not return an Amistio implementation test gate block.");
|
|
6351
|
+
}
|
|
6352
|
+
const payload = output.slice(start + implementationTestGateStart.length, end).trim();
|
|
6353
|
+
const parsed = JSON.parse(stripJsonFence(payload));
|
|
6354
|
+
return implementationTestGateResultSchema.parse(parsed);
|
|
6355
|
+
}
|
|
5384
6356
|
function projectContextRefreshSubmissionFailureSummary(result) {
|
|
5385
6357
|
if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
|
|
5386
6358
|
return void 0;
|
|
@@ -5482,6 +6454,75 @@ function normalizeProjectContextRefreshEnums(value) {
|
|
|
5482
6454
|
}
|
|
5483
6455
|
return normalized;
|
|
5484
6456
|
}
|
|
6457
|
+
function normalizeAppEvaluationScanResultEnums(value) {
|
|
6458
|
+
if (!isObjectRecord(value)) {
|
|
6459
|
+
return value;
|
|
6460
|
+
}
|
|
6461
|
+
const normalized = { ...value };
|
|
6462
|
+
if (Array.isArray(normalized.findings)) {
|
|
6463
|
+
normalized.findings = normalized.findings.map((finding) => {
|
|
6464
|
+
if (!isObjectRecord(finding)) {
|
|
6465
|
+
return finding;
|
|
6466
|
+
}
|
|
6467
|
+
const normalizedFinding = { ...finding };
|
|
6468
|
+
normalizedFinding.category = normalizeAppEvaluationFindingCategory(finding.category);
|
|
6469
|
+
return normalizedFinding;
|
|
6470
|
+
});
|
|
6471
|
+
}
|
|
6472
|
+
return normalized;
|
|
6473
|
+
}
|
|
6474
|
+
function normalizeAppEvaluationFindingCategory(value) {
|
|
6475
|
+
if (typeof value !== "string") {
|
|
6476
|
+
return value;
|
|
6477
|
+
}
|
|
6478
|
+
const trimmed = value.trim();
|
|
6479
|
+
if (!trimmed) {
|
|
6480
|
+
return "other";
|
|
6481
|
+
}
|
|
6482
|
+
const direct = canonicalAppEvaluationCategories.get(normalizeEnumKey(trimmed));
|
|
6483
|
+
if (direct) {
|
|
6484
|
+
return direct;
|
|
6485
|
+
}
|
|
6486
|
+
for (const segment of trimmed.split(/[\/,|]+/)) {
|
|
6487
|
+
const segmentCategory = canonicalAppEvaluationCategories.get(normalizeEnumKey(segment));
|
|
6488
|
+
if (segmentCategory) {
|
|
6489
|
+
return segmentCategory;
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6492
|
+
return "other";
|
|
6493
|
+
}
|
|
6494
|
+
function normalizeEnumKey(value) {
|
|
6495
|
+
return value.trim().replace(/[^A-Za-z0-9]+/g, "").toLowerCase();
|
|
6496
|
+
}
|
|
6497
|
+
function normalizeAppEvaluationScanResultPaths(value) {
|
|
6498
|
+
if (!isObjectRecord(value)) {
|
|
6499
|
+
return value;
|
|
6500
|
+
}
|
|
6501
|
+
const normalized = { ...value };
|
|
6502
|
+
if (Array.isArray(normalized.findings)) {
|
|
6503
|
+
normalized.findings = normalized.findings.map((finding) => {
|
|
6504
|
+
if (!isObjectRecord(finding)) {
|
|
6505
|
+
return finding;
|
|
6506
|
+
}
|
|
6507
|
+
const normalizedFinding = { ...finding };
|
|
6508
|
+
if (Array.isArray(normalizedFinding.safePaths)) {
|
|
6509
|
+
normalizedFinding.safePaths = normalizedFinding.safePaths.map((safePath) => typeof safePath === "string" ? normalizeAppEvaluationRepoPath(safePath) : safePath);
|
|
6510
|
+
}
|
|
6511
|
+
if (typeof normalizedFinding.proposedPlanRepoPath === "string") {
|
|
6512
|
+
normalizedFinding.proposedPlanRepoPath = normalizeAppEvaluationRepoPath(normalizedFinding.proposedPlanRepoPath);
|
|
6513
|
+
}
|
|
6514
|
+
return normalizedFinding;
|
|
6515
|
+
});
|
|
6516
|
+
}
|
|
6517
|
+
return normalized;
|
|
6518
|
+
}
|
|
6519
|
+
function normalizeAppEvaluationRepoPath(value) {
|
|
6520
|
+
try {
|
|
6521
|
+
return normalizeRelativeProjectContextPath(value);
|
|
6522
|
+
} catch {
|
|
6523
|
+
throw new Error("App evaluation scan contains an unsafe repository path.");
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
5485
6526
|
function normalizeProjectContextSliceKind(value) {
|
|
5486
6527
|
if (typeof value !== "string") {
|
|
5487
6528
|
return "unknown";
|
|
@@ -6188,6 +7229,7 @@ function buildBackgroundRunnerArgs(options) {
|
|
|
6188
7229
|
if (options.maxIterations !== void 0) {
|
|
6189
7230
|
args.push("--max-iterations", String(options.maxIterations));
|
|
6190
7231
|
}
|
|
7232
|
+
args.push("--max-concurrent-work", String(options.maxConcurrentWork));
|
|
6191
7233
|
args.push("--max-preflight-attempts", String(options.maxPreflightAttempts));
|
|
6192
7234
|
args.push("--tool-timeout-seconds", String(options.toolTimeoutSeconds));
|
|
6193
7235
|
if (!options.stream) {
|
|
@@ -6271,10 +7313,12 @@ function truncateProcessOutput(value) {
|
|
|
6271
7313
|
|
|
6272
7314
|
// src/git-worktree.ts
|
|
6273
7315
|
import { execFile as execFile5 } from "node:child_process";
|
|
6274
|
-
import { mkdir as mkdir10, stat as stat5 } from "node:fs/promises";
|
|
7316
|
+
import { copyFile, lstat, mkdir as mkdir10, readdir as readdir6, stat as stat5 } from "node:fs/promises";
|
|
6275
7317
|
import path14 from "node:path";
|
|
6276
7318
|
import { promisify as promisify5 } from "node:util";
|
|
6277
7319
|
var execFileAsync5 = promisify5(execFile5);
|
|
7320
|
+
var exactLocalEnvironmentFiles = /* @__PURE__ */ new Set([".env", ".env.local", ".env.development", ".env.development.local", ".env.test", ".env.test.local", ".env.production", ".env.production.local"]);
|
|
7321
|
+
var localEnvironmentFilePattern = /^\.env\.[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)*\.local$/;
|
|
6278
7322
|
function needsGitWorktreeIsolation(workItem) {
|
|
6279
7323
|
return (workItem.workKind ?? "implementation") === "implementation";
|
|
6280
7324
|
}
|
|
@@ -6299,7 +7343,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
|
|
|
6299
7343
|
const worktreePath = localWorktreePath(repoRoot, identity.worktreeKey);
|
|
6300
7344
|
if (await pathExists(worktreePath)) {
|
|
6301
7345
|
await assertExistingWorktree(worktreePath, identity.branch);
|
|
6302
|
-
|
|
7346
|
+
const preparedLocalEnvironmentFileCount2 = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
|
|
7347
|
+
return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount2 ? { preparedLocalEnvironmentFileCount: preparedLocalEnvironmentFileCount2 } : {} };
|
|
6303
7348
|
}
|
|
6304
7349
|
await mkdir10(path14.dirname(worktreePath), { recursive: true });
|
|
6305
7350
|
const branchExists = await gitCommandSucceeds(repoRoot, ["show-ref", "--verify", "--quiet", `refs/heads/${identity.branch}`]);
|
|
@@ -6307,7 +7352,8 @@ async function prepareGitWorktreeIsolation(rootDir, workItem) {
|
|
|
6307
7352
|
await gitOutput(repoRoot, worktreeArgs).catch((error) => {
|
|
6308
7353
|
throw new Error(`Could not create Git worktree ${identity.worktreeKey} on ${identity.branch}: ${errorMessage2(error)}`);
|
|
6309
7354
|
});
|
|
6310
|
-
|
|
7355
|
+
const preparedLocalEnvironmentFileCount = await prepareLocalWorktreeEnvironment(repoRoot, worktreePath);
|
|
7356
|
+
return { ...identity, baseRevision, worktreePath, ...preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount } : {} };
|
|
6311
7357
|
}
|
|
6312
7358
|
function localWorktreePath(repoRoot, worktreeKey) {
|
|
6313
7359
|
const repoName = path14.basename(repoRoot);
|
|
@@ -6334,6 +7380,55 @@ async function assertBaseRevision(repoRoot, baseRevision, currentHead) {
|
|
|
6334
7380
|
throw new Error(`Work item base revision ${baseRevision} is not an ancestor of ${currentHead}; refresh the work item before implementation.`);
|
|
6335
7381
|
}
|
|
6336
7382
|
}
|
|
7383
|
+
async function prepareLocalWorktreeEnvironment(repoRoot, worktreePath) {
|
|
7384
|
+
const candidates = await localEnvironmentFileCandidates(repoRoot);
|
|
7385
|
+
let preparedCount = 0;
|
|
7386
|
+
for (const candidate of candidates) {
|
|
7387
|
+
const sourcePath = path14.join(repoRoot, candidate);
|
|
7388
|
+
const targetPath = path14.join(worktreePath, candidate);
|
|
7389
|
+
if (await pathExists(targetPath)) {
|
|
7390
|
+
continue;
|
|
7391
|
+
}
|
|
7392
|
+
if (await gitCommandSucceeds(repoRoot, ["ls-files", "--error-unmatch", "--", candidate])) {
|
|
7393
|
+
continue;
|
|
7394
|
+
}
|
|
7395
|
+
if (!await gitCommandSucceeds(worktreePath, ["check-ignore", "--quiet", "--", candidate])) {
|
|
7396
|
+
continue;
|
|
7397
|
+
}
|
|
7398
|
+
try {
|
|
7399
|
+
await copyFile(sourcePath, targetPath);
|
|
7400
|
+
preparedCount += 1;
|
|
7401
|
+
} catch (error) {
|
|
7402
|
+
throw new Error(`Could not prepare local worktree environment files: ${safeFileError(error)}`);
|
|
7403
|
+
}
|
|
7404
|
+
}
|
|
7405
|
+
return preparedCount;
|
|
7406
|
+
}
|
|
7407
|
+
async function localEnvironmentFileCandidates(repoRoot) {
|
|
7408
|
+
const names = new Set(exactLocalEnvironmentFiles);
|
|
7409
|
+
for (const entry of await readdir6(repoRoot)) {
|
|
7410
|
+
if (isAllowedLocalEnvironmentFile(entry)) {
|
|
7411
|
+
names.add(entry);
|
|
7412
|
+
}
|
|
7413
|
+
}
|
|
7414
|
+
const candidates = [];
|
|
7415
|
+
for (const name of [...names].sort()) {
|
|
7416
|
+
if (!isRootFileName(name)) {
|
|
7417
|
+
continue;
|
|
7418
|
+
}
|
|
7419
|
+
const source = await lstat(path14.join(repoRoot, name)).catch(() => void 0);
|
|
7420
|
+
if (source?.isFile()) {
|
|
7421
|
+
candidates.push(name);
|
|
7422
|
+
}
|
|
7423
|
+
}
|
|
7424
|
+
return candidates;
|
|
7425
|
+
}
|
|
7426
|
+
function isAllowedLocalEnvironmentFile(name) {
|
|
7427
|
+
return exactLocalEnvironmentFiles.has(name) || localEnvironmentFilePattern.test(name);
|
|
7428
|
+
}
|
|
7429
|
+
function isRootFileName(name) {
|
|
7430
|
+
return name === path14.basename(name) && !name.includes("/") && !name.includes("\\");
|
|
7431
|
+
}
|
|
6337
7432
|
async function gitOutput(cwd, args) {
|
|
6338
7433
|
const { stdout } = await execFileAsync5("git", args, { cwd, maxBuffer: 1024 * 1024 });
|
|
6339
7434
|
return stdout.trim();
|
|
@@ -6356,6 +7451,12 @@ function slugify(value) {
|
|
|
6356
7451
|
function errorMessage2(error) {
|
|
6357
7452
|
return error instanceof Error ? error.message : String(error);
|
|
6358
7453
|
}
|
|
7454
|
+
function safeFileError(error) {
|
|
7455
|
+
if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") {
|
|
7456
|
+
return error.code;
|
|
7457
|
+
}
|
|
7458
|
+
return error instanceof Error ? error.name : "unknown file error";
|
|
7459
|
+
}
|
|
6359
7460
|
|
|
6360
7461
|
// src/implementation-handoff.ts
|
|
6361
7462
|
import { execFile as execFile6 } from "node:child_process";
|
|
@@ -6375,9 +7476,17 @@ async function completeImplementationHandoff(input) {
|
|
|
6375
7476
|
headBranch
|
|
6376
7477
|
};
|
|
6377
7478
|
try {
|
|
7479
|
+
const artifactResult = await materializeApprovedArtifacts(input);
|
|
7480
|
+
if (artifactResult.blocked.length) {
|
|
7481
|
+
return blockedHandoff({
|
|
7482
|
+
...common,
|
|
7483
|
+
artifacts: artifactResult,
|
|
7484
|
+
message: `Implementation handoff is blocked because ${artifactResult.blocked.length} approved artifact${artifactResult.blocked.length === 1 ? "" : "s"} could not be materialized safely.`
|
|
7485
|
+
});
|
|
7486
|
+
}
|
|
6378
7487
|
const unmergedFiles = await gitOutput2(run, input.worktreePath, ["diff", "--name-only", "--diff-filter=U"]);
|
|
6379
7488
|
if (unmergedFiles.trim()) {
|
|
6380
|
-
return blockedHandoff({ ...common, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
|
|
7489
|
+
return blockedHandoff({ ...common, artifacts: artifactResult, message: "Implementation handoff is blocked because the worktree has unresolved merge conflicts." });
|
|
6381
7490
|
}
|
|
6382
7491
|
const status = await gitOutput2(run, input.worktreePath, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
6383
7492
|
if (!status) {
|
|
@@ -6385,6 +7494,7 @@ async function completeImplementationHandoff(input) {
|
|
|
6385
7494
|
...common,
|
|
6386
7495
|
status: "noChanges",
|
|
6387
7496
|
cleanupStatus: "notApplicable",
|
|
7497
|
+
artifacts: artifactResult,
|
|
6388
7498
|
message: "Local execution completed with no repository changes to hand off."
|
|
6389
7499
|
};
|
|
6390
7500
|
}
|
|
@@ -6396,14 +7506,16 @@ async function completeImplementationHandoff(input) {
|
|
|
6396
7506
|
...common,
|
|
6397
7507
|
provider,
|
|
6398
7508
|
remoteName,
|
|
7509
|
+
artifacts: artifactResult,
|
|
6399
7510
|
message: "Automated pull request handoff currently requires a GitHub remote. Commit and push manually, or link a GitHub repository."
|
|
6400
7511
|
});
|
|
6401
7512
|
}
|
|
6402
7513
|
await gitOutput2(run, input.worktreePath, ["add", "-A"]);
|
|
6403
7514
|
await gitOutput2(run, input.worktreePath, ["commit", "-m", commitSubject(input.workItem), "-m", commitBody(input)]);
|
|
7515
|
+
await rebaseBranchFromRemoteBase(run, input.worktreePath, { baseBranch, remoteName });
|
|
6404
7516
|
const commitSha = await gitOutput2(run, input.worktreePath, ["rev-parse", "HEAD"]);
|
|
6405
7517
|
await gitOutput2(run, input.worktreePath, ["push", "--set-upstream", remoteName, headBranch]);
|
|
6406
|
-
const pullRequest = await ensureGithubPullRequest(run, input.worktreePath, { baseBranch, headBranch, workItem: input.workItem, ...input.verificationSummary ? { verificationSummary: input.verificationSummary } : {} });
|
|
7518
|
+
const pullRequest = await ensureGithubPullRequest(run, input.worktreePath, { artifacts: artifactResult, baseBranch, headBranch, workItem: input.workItem, ...input.verificationSummary ? { verificationSummary: input.verificationSummary } : {} });
|
|
6407
7519
|
const cleanup = await cleanupWorktree(run, input);
|
|
6408
7520
|
return {
|
|
6409
7521
|
provider: "github",
|
|
@@ -6416,12 +7528,70 @@ async function completeImplementationHandoff(input) {
|
|
|
6416
7528
|
prUrl: pullRequest.url,
|
|
6417
7529
|
cleanupStatus: cleanup.status,
|
|
6418
7530
|
...cleanup.message ? { cleanupMessage: cleanup.message } : {},
|
|
7531
|
+
artifacts: artifactResult,
|
|
6419
7532
|
message: cleanup.status === "completed" ? "GitHub pull request is ready for review and the local worktree was cleaned up." : "GitHub pull request is ready for review; local worktree cleanup needs attention."
|
|
6420
7533
|
};
|
|
6421
7534
|
} catch (error) {
|
|
6422
7535
|
return blockedHandoff({ ...common, message: "Implementation handoff is blocked and the local worktree was preserved for recovery.", error: safeErrorMessage(error) });
|
|
6423
7536
|
}
|
|
6424
7537
|
}
|
|
7538
|
+
async function materializeApprovedArtifacts(input) {
|
|
7539
|
+
const { selected: artifacts, skipped } = selectApprovedWorkArtifacts(input.workItem, input.approvedArtifacts ?? []);
|
|
7540
|
+
if (!artifacts.length) {
|
|
7541
|
+
return { included: [], skipped, blocked: [] };
|
|
7542
|
+
}
|
|
7543
|
+
let materialized;
|
|
7544
|
+
try {
|
|
7545
|
+
materialized = await materializeBrainDocuments(input.worktreePath, artifacts);
|
|
7546
|
+
} catch (error) {
|
|
7547
|
+
return { included: [], skipped, blocked: artifacts.map((artifact) => artifactStatus(artifact, "blocked", safeErrorMessage(error))) };
|
|
7548
|
+
}
|
|
7549
|
+
return {
|
|
7550
|
+
included: materialized.written.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "included", "Approved artifact materialized into the implementation worktree.")),
|
|
7551
|
+
skipped: [...skipped, ...materialized.skipped.map((repoPath) => artifactStatus(findArtifactByPath(artifacts, repoPath), "skipped", "Approved artifact was already current in the implementation worktree."))],
|
|
7552
|
+
blocked: materialized.conflicts.map((conflict) => artifactStatus(findArtifactByPath(artifacts, conflictRepoPath(conflict)), "blocked", conflict))
|
|
7553
|
+
};
|
|
7554
|
+
}
|
|
7555
|
+
function selectApprovedWorkArtifacts(workItem, documents) {
|
|
7556
|
+
const draftId = workItem.generatedDraftId;
|
|
7557
|
+
const explicitDocumentIds = new Set([workItem.reviewDocumentId, workItem.impactDocumentId].filter((value) => Boolean(value)));
|
|
7558
|
+
const scopeIds = new Set([workItem.workItemId, workItem.controllingAdrId, workItem.implementationScopeId].filter((value) => Boolean(value)));
|
|
7559
|
+
const skipped = [];
|
|
7560
|
+
const selected = documents.filter((document) => {
|
|
7561
|
+
const inScope = Boolean(draftId && document.frontmatter.generatedDraftId === draftId || explicitDocumentIds.has(document.documentId) || explicitDocumentIds.has(document.id) || [document.frontmatter.workItemId, document.frontmatter.generationWorkItemId, document.frontmatter.revisionWorkItemId, document.frontmatter.controllingAdrId, document.frontmatter.adrId, document.frontmatter.implementationScopeId].some((value) => typeof value === "string" && scopeIds.has(value)));
|
|
7562
|
+
if (!inScope) return false;
|
|
7563
|
+
if (document.status !== "approved" && document.syncState !== "approved" && document.syncState !== "synced") {
|
|
7564
|
+
skipped.push(artifactStatus(document, "skipped", "Artifact is not approved for repository inclusion."));
|
|
7565
|
+
return false;
|
|
7566
|
+
}
|
|
7567
|
+
if (!["markdown", "html", void 0].includes(document.contentFormat)) {
|
|
7568
|
+
skipped.push(artifactStatus(document, "skipped", "Artifact format is not supported for implementation PR inclusion."));
|
|
7569
|
+
return false;
|
|
7570
|
+
}
|
|
7571
|
+
return true;
|
|
7572
|
+
});
|
|
7573
|
+
const byDocumentId = /* @__PURE__ */ new Map();
|
|
7574
|
+
for (const document of selected) {
|
|
7575
|
+
byDocumentId.set(document.documentId, document);
|
|
7576
|
+
}
|
|
7577
|
+
return { selected: [...byDocumentId.values()], skipped };
|
|
7578
|
+
}
|
|
7579
|
+
function findArtifactByPath(artifacts, repoPath) {
|
|
7580
|
+
return artifacts.find((artifact) => artifact.repoPath === repoPath) ?? artifacts[0];
|
|
7581
|
+
}
|
|
7582
|
+
function artifactStatus(artifact, status, message) {
|
|
7583
|
+
return {
|
|
7584
|
+
documentId: artifact.documentId,
|
|
7585
|
+
title: truncate(artifact.title, 200),
|
|
7586
|
+
repoPath: artifact.repoPath,
|
|
7587
|
+
contentFormat: artifact.contentFormat ?? "markdown",
|
|
7588
|
+
status,
|
|
7589
|
+
message: truncate(message, 600)
|
|
7590
|
+
};
|
|
7591
|
+
}
|
|
7592
|
+
function conflictRepoPath(conflict) {
|
|
7593
|
+
return conflict.split(":")[0]?.trim() ?? conflict;
|
|
7594
|
+
}
|
|
6425
7595
|
async function ensureGithubPullRequest(run, cwd, input) {
|
|
6426
7596
|
const existing = await run("gh", ["pr", "list", "--head", input.headBranch, "--base", input.baseBranch, "--state", "open", "--json", "number,url", "--limit", "1"], { cwd });
|
|
6427
7597
|
const parsed = parsePullRequestList(existing.stdout);
|
|
@@ -6458,6 +7628,10 @@ async function resolveRemoteName(run, cwd) {
|
|
|
6458
7628
|
}
|
|
6459
7629
|
return remotes.includes("origin") ? "origin" : remotes[0];
|
|
6460
7630
|
}
|
|
7631
|
+
async function rebaseBranchFromRemoteBase(run, cwd, input) {
|
|
7632
|
+
await gitOutput2(run, cwd, ["fetch", input.remoteName, input.baseBranch]);
|
|
7633
|
+
await gitOutput2(run, cwd, ["rebase", "FETCH_HEAD"]);
|
|
7634
|
+
}
|
|
6461
7635
|
async function gitOutput2(run, cwd, args) {
|
|
6462
7636
|
const result = await run("git", args, { cwd });
|
|
6463
7637
|
return result.stdout.trim();
|
|
@@ -6513,9 +7687,23 @@ function pullRequestBody(input) {
|
|
|
6513
7687
|
`Base branch: ${input.baseBranch}`,
|
|
6514
7688
|
`Head branch: ${input.headBranch}`,
|
|
6515
7689
|
"",
|
|
7690
|
+
...artifactSummaryLines(input.artifacts),
|
|
7691
|
+
"",
|
|
6516
7692
|
input.verificationSummary ?? "Verification summary was not reported by the local runner."
|
|
6517
7693
|
].join("\n");
|
|
6518
7694
|
}
|
|
7695
|
+
function artifactSummaryLines(artifacts) {
|
|
7696
|
+
if (!artifacts || !artifacts.included.length && !artifacts.skipped.length && !artifacts.blocked.length) {
|
|
7697
|
+
return ["Approved artifacts: none materialized for this handoff."];
|
|
7698
|
+
}
|
|
7699
|
+
const lines = [
|
|
7700
|
+
`Approved artifacts: ${artifacts.included.length} included, ${artifacts.skipped.length} already current, ${artifacts.blocked.length} blocked.`
|
|
7701
|
+
];
|
|
7702
|
+
for (const artifact of artifacts.included.slice(0, 12)) {
|
|
7703
|
+
lines.push(`- ${artifact.repoPath}${artifact.title ? ` (${artifact.title})` : ""}`);
|
|
7704
|
+
}
|
|
7705
|
+
return lines;
|
|
7706
|
+
}
|
|
6519
7707
|
function truncate(value, maxLength) {
|
|
6520
7708
|
return value.length <= maxLength ? value : value.slice(0, maxLength - 1).trimEnd();
|
|
6521
7709
|
}
|
|
@@ -6556,7 +7744,8 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
|
|
|
6556
7744
|
var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
|
|
6557
7745
|
var RUNNER_WORK_LEASE_SECONDS = 300;
|
|
6558
7746
|
var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
|
|
6559
|
-
var
|
|
7747
|
+
var MAX_CONCURRENT_RUNNER_WORK = 4;
|
|
7748
|
+
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification", "testQualityScan", "implementationTestGate"];
|
|
6560
7749
|
program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
|
|
6561
7750
|
program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
6562
7751
|
const created = await initControlPlane(options.root);
|
|
@@ -6839,7 +8028,7 @@ work.command("list").description("List queued work without claiming it").option(
|
|
|
6839
8028
|
return;
|
|
6840
8029
|
}
|
|
6841
8030
|
for (const item of workItems) {
|
|
6842
|
-
console.log(`${item.workItemId} [${item.status}] ${item.title}`);
|
|
8031
|
+
console.log(`${item.workItemId} [${item.status}] ${item.title}${formatAutopilotListSuffix(item)}`);
|
|
6843
8032
|
}
|
|
6844
8033
|
});
|
|
6845
8034
|
work.command("prompt").description("Print or write an approved work prompt without claiming a runner lease").argument("[workItemId]", "Work item ID. Defaults to the newest approved work item.").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--out <path>", "Write the prompt to a file instead of stdout").action(async (workItemId, options) => {
|
|
@@ -6905,7 +8094,7 @@ program.command("orchestrate").description("Update the Amistio control plane thr
|
|
|
6905
8094
|
process.exitCode = result.exitCode;
|
|
6906
8095
|
}
|
|
6907
8096
|
});
|
|
6908
|
-
program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
|
|
8097
|
+
program.command("run").description("Claim and run approved Amistio work locally").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--runner-id <runnerId>", "Stable runner ID").option("--root <path>", "Repository root", defaultRoot).option("--tool <name>", "Local tool to use: auto, none, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--tool-command <command>", "Custom local command. Use {promptFile} and {root} placeholders when supported").option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--dry-run", "Claim work and print the generated execution prompt without running a tool").option("--watch", "Keep polling for approved work until stopped").option("--background", "Start a detached background runner that watches for approved work").option("--interval-seconds <seconds>", "Polling interval for --watch", parsePositiveInteger, 10).option("--max-iterations <count>", "Stop watch mode after this many polling attempts", parsePositiveInteger).option("--max-concurrent-work <count>", "Maximum approved work items to run in parallel in --watch mode", parsePositiveInteger, 1).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors while watching").action(async (options, command) => {
|
|
6909
8098
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
6910
8099
|
if (!context) {
|
|
6911
8100
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
@@ -6923,6 +8112,11 @@ program.command("run").description("Claim and run approved Amistio work locally"
|
|
|
6923
8112
|
machineId: runnerMachineId()
|
|
6924
8113
|
});
|
|
6925
8114
|
const resolvedOptions = { ...options, runnerId };
|
|
8115
|
+
if (resolvedOptions.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
8116
|
+
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
8117
|
+
process.exitCode = 1;
|
|
8118
|
+
return;
|
|
8119
|
+
}
|
|
6926
8120
|
if (options.background) {
|
|
6927
8121
|
if (options.dryRun) {
|
|
6928
8122
|
console.log("Background runners cannot be started in dry-run mode.");
|
|
@@ -7077,7 +8271,7 @@ runner.command("stop").description("Stop a background runner for the paired repo
|
|
|
7077
8271
|
console.log(stopResult === "stopped" ? `Stopped background runner ${record.runnerId}.` : `Marked background runner ${record.runnerId} stopped; process was not running.`);
|
|
7078
8272
|
});
|
|
7079
8273
|
var runnerService = runner.command("service").description("Manage a user-level startup service for the paired runner");
|
|
7080
|
-
runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
|
|
8274
|
+
runnerService.command("install").description("Install a user-level startup service for this paired repository runner").option("--api-url <url>", apiUrlOptionDescription, defaultApiUrl()).option("--root <path>", "Repository root", defaultRoot).option("--runner-id <runnerId>", "Stable runner ID").option("--tool <name>", "Local tool to use: auto, opencode, claude, codex, copilot, gemini, aider, cursor-agent").option("--invocation-channel <channel>", "Local invocation channel: auto, sdk, or command", parseInvocationChannel).option("--model <model>", "Model to request when the selected local tool supports model selection").option("--provider <providerId>", "Provider id for provider-backed model configuration").option("--model-id <modelId>", "Provider catalog model id to request").option("--model-variant <variant>", "Provider catalog model variant to request").option("--reasoning-effort <effort>", "Reasoning effort: auto, low, medium, high, or xhigh", parseReasoningEffort).option("--session <policy>", "Tool session policy: auto, new, continue:<toolSessionId>, or none", "auto").option("--interval-seconds <seconds>", "Polling interval for the service runner", parsePositiveInteger, 10).option("--max-concurrent-work <count>", "Maximum approved work items to run in parallel in --watch mode", parsePositiveInteger, 1).option("--max-preflight-attempts <count>", "Fail setup/preflight failures after this many claimed attempts", parsePositiveInteger, DEFAULT_MAX_PREFLIGHT_ATTEMPTS).option("--tool-timeout-seconds <seconds>", "Fail local tool execution after this many seconds", parsePositiveInteger, DEFAULT_TOOL_TIMEOUT_SECONDS).option("--no-stream", "Capture local tool output instead of streaming it").option("--verbose", "Print detailed runner errors").option("--dry-run", "Print the startup service descriptor without installing it").action(async (options) => {
|
|
7081
8275
|
const context = await loadPairedApiContext(options.root, options.apiUrl);
|
|
7082
8276
|
if (!context) {
|
|
7083
8277
|
console.log("Repository is not paired. Run `amistio pair` first.");
|
|
@@ -7100,6 +8294,11 @@ runnerService.command("install").description("Install a user-level startup servi
|
|
|
7100
8294
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
7101
8295
|
machineId: runnerMachineId()
|
|
7102
8296
|
});
|
|
8297
|
+
if (options.maxConcurrentWork > MAX_CONCURRENT_RUNNER_WORK) {
|
|
8298
|
+
console.log(`--max-concurrent-work is capped at ${MAX_CONCURRENT_RUNNER_WORK}.`);
|
|
8299
|
+
process.exitCode = 1;
|
|
8300
|
+
return;
|
|
8301
|
+
}
|
|
7103
8302
|
const args = buildBackgroundRunnerArgs({ ...options, runnerId, apiUrl: options.apiUrl, root: options.root });
|
|
7104
8303
|
const serviceInput = {
|
|
7105
8304
|
accountId: context.metadata.amistioAccountId,
|
|
@@ -7178,7 +8377,7 @@ async function runWatchIteration({ command, context, options, runnerId }) {
|
|
|
7178
8377
|
});
|
|
7179
8378
|
}
|
|
7180
8379
|
if (!options.dryRun) {
|
|
7181
|
-
const replayResult = await
|
|
8380
|
+
const replayResult = await replayPendingResultFinalizations({
|
|
7182
8381
|
accountId: context.metadata.amistioAccountId,
|
|
7183
8382
|
apiClient: context.client,
|
|
7184
8383
|
projectId: context.metadata.amistioProjectId,
|
|
@@ -7189,7 +8388,8 @@ async function runWatchIteration({ command, context, options, runnerId }) {
|
|
|
7189
8388
|
return replayResult;
|
|
7190
8389
|
}
|
|
7191
8390
|
}
|
|
7192
|
-
|
|
8391
|
+
const activeClaimLaneIds = claimLaneIds(options.maxConcurrentWork);
|
|
8392
|
+
const runLane = (claimLaneId, laneIndex) => runNextWorkItem({
|
|
7193
8393
|
apiClient: context.client,
|
|
7194
8394
|
projectId: context.metadata.amistioProjectId,
|
|
7195
8395
|
repositoryLinkId: context.metadata.repositoryLinkId,
|
|
@@ -7218,8 +8418,16 @@ async function runWatchIteration({ command, context, options, runnerId }) {
|
|
|
7218
8418
|
suppressIdleOutput: Boolean(options.watch),
|
|
7219
8419
|
maxPreflightAttempts: options.maxPreflightAttempts,
|
|
7220
8420
|
toolTimeoutMs: options.toolTimeoutSeconds * 1e3,
|
|
7221
|
-
verbose: Boolean(options.verbose)
|
|
8421
|
+
verbose: Boolean(options.verbose),
|
|
8422
|
+
claimLaneId,
|
|
8423
|
+
maxConcurrentWork: options.maxConcurrentWork,
|
|
8424
|
+
activeClaimLaneIds,
|
|
8425
|
+
skipRunnerCommands: laneIndex > 0
|
|
7222
8426
|
});
|
|
8427
|
+
if (options.watch && !options.dryRun && options.maxConcurrentWork > 1) {
|
|
8428
|
+
return aggregateRunnerLaneResults(await Promise.all(activeClaimLaneIds.map((claimLaneId, laneIndex) => runLane(claimLaneId, laneIndex))));
|
|
8429
|
+
}
|
|
8430
|
+
return await runLane("default", 0);
|
|
7223
8431
|
} catch (error) {
|
|
7224
8432
|
if (!options.watch) {
|
|
7225
8433
|
throw error;
|
|
@@ -7247,6 +8455,23 @@ ${detail}`);
|
|
|
7247
8455
|
return { status: "failed", exitCode: 1, message };
|
|
7248
8456
|
}
|
|
7249
8457
|
}
|
|
8458
|
+
function claimLaneIds(maxConcurrentWork) {
|
|
8459
|
+
return Array.from({ length: maxConcurrentWork }, (_, index) => index === 0 ? "default" : `lane_${index + 1}`);
|
|
8460
|
+
}
|
|
8461
|
+
function supportsConcurrentLocalToolExecution(toolConfig) {
|
|
8462
|
+
return toolConfig.tool === "none" || toolConfig.effectiveInvocationChannel === "command" || toolConfig.effectiveTool === "custom";
|
|
8463
|
+
}
|
|
8464
|
+
function aggregateRunnerLaneResults(results) {
|
|
8465
|
+
const stopResult = results.find((result) => result.stopRunner);
|
|
8466
|
+
if (stopResult) return stopResult;
|
|
8467
|
+
const failedResult = results.find((result) => result.status === "failed");
|
|
8468
|
+
if (failedResult) return failedResult;
|
|
8469
|
+
const blockedResult = results.find((result) => result.status === "blocked");
|
|
8470
|
+
if (blockedResult) return blockedResult;
|
|
8471
|
+
const completedResult = results.find((result) => result.status === "completed" || result.status === "preview");
|
|
8472
|
+
if (completedResult) return completedResult;
|
|
8473
|
+
return results.find((result) => result.status === "idle") ?? { status: "idle", exitCode: 0 };
|
|
8474
|
+
}
|
|
7250
8475
|
async function runAutoSyncCycle({ context, maxFileKb, quiet, quietDisabled, root, runnerId }) {
|
|
7251
8476
|
const projectId = context.metadata.amistioProjectId;
|
|
7252
8477
|
const repositoryLinkId = context.metadata.repositoryLinkId;
|
|
@@ -7326,7 +8551,11 @@ async function runNextWorkItem({
|
|
|
7326
8551
|
commandContext,
|
|
7327
8552
|
suppressIdleOutput,
|
|
7328
8553
|
toolTimeoutMs,
|
|
7329
|
-
verbose
|
|
8554
|
+
verbose,
|
|
8555
|
+
claimLaneId,
|
|
8556
|
+
maxConcurrentWork,
|
|
8557
|
+
activeClaimLaneIds,
|
|
8558
|
+
skipRunnerCommands
|
|
7330
8559
|
}) {
|
|
7331
8560
|
const toolConfig = await resolveRunnerToolConfig({
|
|
7332
8561
|
apiClient,
|
|
@@ -7340,19 +8569,27 @@ async function runNextWorkItem({
|
|
|
7340
8569
|
...explicitTool ? { explicitTool } : {},
|
|
7341
8570
|
...toolCommand ? { toolCommand } : {}
|
|
7342
8571
|
});
|
|
7343
|
-
|
|
7344
|
-
const
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
8572
|
+
const effectiveMaxConcurrentWork = supportsConcurrentLocalToolExecution(toolConfig) ? maxConcurrentWork : 1;
|
|
8573
|
+
const effectiveActiveClaimLaneIds = claimLaneIds(effectiveMaxConcurrentWork);
|
|
8574
|
+
const heartbeatConcurrency = { maxConcurrentWork: effectiveMaxConcurrentWork, activeClaimLaneIds: effectiveActiveClaimLaneIds };
|
|
8575
|
+
if (claimLaneId !== "default" && effectiveMaxConcurrentWork === 1) {
|
|
8576
|
+
return { status: "idle", exitCode: 0 };
|
|
8577
|
+
}
|
|
8578
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, toolConfig.ready ? "online" : "blocked", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
|
|
8579
|
+
if (!skipRunnerCommands) {
|
|
8580
|
+
const commandResult = await runPendingRunnerCommand(apiClient, commandContext, runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
|
|
8581
|
+
if (commandResult.handled) {
|
|
8582
|
+
if (commandResult.message) {
|
|
8583
|
+
console.log(commandResult.message);
|
|
8584
|
+
}
|
|
8585
|
+
return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
|
|
7348
8586
|
}
|
|
7349
|
-
return { status: commandResult.succeeded ? "completed" : "failed", exitCode: commandResult.succeeded ? 0 : 1, ...commandResult.stopRunner ? { stopRunner: true } : {} };
|
|
7350
8587
|
}
|
|
7351
8588
|
if (!toolConfig.ready) {
|
|
7352
8589
|
console.log(toolConfig.message);
|
|
7353
8590
|
return { status: "blocked", exitCode: 1 };
|
|
7354
8591
|
}
|
|
7355
|
-
const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata());
|
|
8592
|
+
const result = await apiClient.claimWork(projectId, runnerId, repositoryLinkId, RUNNER_WORK_LEASE_SECONDS, runnerIsolationCapabilityMetadata({ claimLaneId, maxConcurrentWork: effectiveMaxConcurrentWork }));
|
|
7356
8593
|
if (!result.workItem) {
|
|
7357
8594
|
const nextAction = await loadProjectNextAction(apiClient, projectId, repositoryLinkId, root);
|
|
7358
8595
|
const message = formatProjectNextAction(nextAction);
|
|
@@ -7370,17 +8607,17 @@ async function runNextWorkItem({
|
|
|
7370
8607
|
});
|
|
7371
8608
|
if (dryRun || toolConfig.tool === "none") {
|
|
7372
8609
|
console.log(prompt);
|
|
7373
|
-
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
8610
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
|
|
7374
8611
|
return { status: "preview", exitCode: 0 };
|
|
7375
8612
|
}
|
|
7376
|
-
const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
|
|
8613
|
+
const worktreeIsolation = await prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem: result.workItem });
|
|
7377
8614
|
if (worktreeIsolation.status !== "ready") {
|
|
7378
8615
|
return { status: worktreeIsolation.status === "failed" ? "failed" : "blocked", exitCode: 1, message: worktreeIsolation.message };
|
|
7379
8616
|
}
|
|
7380
8617
|
const executionRoot = worktreeIsolation.isolation?.worktreePath ?? root;
|
|
7381
8618
|
const isolationTelemetry = workItemIsolationTelemetry(result.workItem, worktreeIsolation.isolation);
|
|
7382
8619
|
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
|
|
7383
|
-
...runnerHeartbeatMetadata(toolConfig),
|
|
8620
|
+
...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
|
|
7384
8621
|
currentWorkItemId: result.workItem.workItemId,
|
|
7385
8622
|
...isolationTelemetry.implementationScopeId ? { currentImplementationScopeId: isolationTelemetry.implementationScopeId } : {},
|
|
7386
8623
|
...isolationTelemetry.executionWorktreeKey ? { currentWorktreeKey: isolationTelemetry.executionWorktreeKey } : {},
|
|
@@ -7403,6 +8640,10 @@ async function runNextWorkItem({
|
|
|
7403
8640
|
isolationTelemetry
|
|
7404
8641
|
});
|
|
7405
8642
|
console.log(`Claimed ${result.workItem.workItemId}. Running ${preview.toolName}: ${preview.displayCommand}`);
|
|
8643
|
+
const autopilotClaimLine = formatAutopilotClaimLine(result.workItem);
|
|
8644
|
+
if (autopilotClaimLine) {
|
|
8645
|
+
console.log(autopilotClaimLine);
|
|
8646
|
+
}
|
|
7406
8647
|
await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
|
|
7407
8648
|
status: "running",
|
|
7408
8649
|
summary: `Local runner started ${preview.toolName} execution.`,
|
|
@@ -7413,7 +8654,7 @@ async function runNextWorkItem({
|
|
|
7413
8654
|
const providerSessionStore = new LocalToolSessionStore();
|
|
7414
8655
|
const providerSessionId = sessionContext.toolSession ? await providerSessionStore.getProviderSessionId(sessionContext.toolSession.toolSessionId, preview.toolName) : void 0;
|
|
7415
8656
|
let toolResult;
|
|
7416
|
-
const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry });
|
|
8657
|
+
const stopLeaseRenewal = startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem: result.workItem, telemetry: isolationTelemetry, heartbeatConcurrency });
|
|
7417
8658
|
try {
|
|
7418
8659
|
toolResult = await runLocalTool({
|
|
7419
8660
|
rootDir: executionRoot,
|
|
@@ -7439,7 +8680,7 @@ async function runNextWorkItem({
|
|
|
7439
8680
|
const durationMs2 = Date.now() - startedAt;
|
|
7440
8681
|
const message = `${preview.toolName} failed before returning a result.`;
|
|
7441
8682
|
const settlements = await Promise.allSettled([
|
|
7442
|
-
apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig)),
|
|
8683
|
+
apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency)),
|
|
7443
8684
|
markToolSessionBlocked(apiClient, projectId, sessionContext.toolSession, errorMessage3(error)),
|
|
7444
8685
|
apiClient.updateWorkStatus(projectId, result.workItem.workItemId, "failed", `run_failed_${result.workItem.workItemId}_${result.workItem.attempt}_${runnerId}`, runnerId, {
|
|
7445
8686
|
...isolationTelemetry,
|
|
@@ -7622,6 +8863,42 @@ async function runNextWorkItem({
|
|
|
7622
8863
|
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
7623
8864
|
}
|
|
7624
8865
|
}
|
|
8866
|
+
if (result.workItem.workKind === "testQualityScan") {
|
|
8867
|
+
try {
|
|
8868
|
+
return await finalizeTestQualityScanWork({
|
|
8869
|
+
apiClient,
|
|
8870
|
+
durationMs: Date.now() - startedAt,
|
|
8871
|
+
projectId,
|
|
8872
|
+
repositoryLinkId,
|
|
8873
|
+
runnerId,
|
|
8874
|
+
sessionContext,
|
|
8875
|
+
toolConfig,
|
|
8876
|
+
toolName: preview.toolName,
|
|
8877
|
+
toolResult,
|
|
8878
|
+
workItem: result.workItem
|
|
8879
|
+
});
|
|
8880
|
+
} catch (error) {
|
|
8881
|
+
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
8882
|
+
}
|
|
8883
|
+
}
|
|
8884
|
+
if (result.workItem.workKind === "implementationTestGate") {
|
|
8885
|
+
try {
|
|
8886
|
+
return await finalizeImplementationTestGateWork({
|
|
8887
|
+
apiClient,
|
|
8888
|
+
durationMs: Date.now() - startedAt,
|
|
8889
|
+
projectId,
|
|
8890
|
+
repositoryLinkId,
|
|
8891
|
+
runnerId,
|
|
8892
|
+
sessionContext,
|
|
8893
|
+
toolConfig,
|
|
8894
|
+
toolName: preview.toolName,
|
|
8895
|
+
toolResult,
|
|
8896
|
+
workItem: result.workItem
|
|
8897
|
+
});
|
|
8898
|
+
} catch (error) {
|
|
8899
|
+
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
8900
|
+
}
|
|
8901
|
+
}
|
|
7625
8902
|
let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
|
|
7626
8903
|
const durationMs = Date.now() - startedAt;
|
|
7627
8904
|
const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
|
|
@@ -7636,14 +8913,27 @@ async function runNextWorkItem({
|
|
|
7636
8913
|
metadata: { executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
|
|
7637
8914
|
});
|
|
7638
8915
|
const repositoryLink = await loadWorkItemRepositoryLink(apiClient, projectId, result.workItem.repositoryLinkId ?? repositoryLinkId);
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
|
|
8916
|
+
const approvedArtifacts = await apiClient.listBrainDocuments(projectId).then((response) => response.documents).catch((error) => {
|
|
8917
|
+
implementationHandoff = {
|
|
8918
|
+
status: "blocked",
|
|
8919
|
+
cleanupStatus: "pending",
|
|
8920
|
+
artifacts: { included: [], skipped: [], blocked: [] },
|
|
8921
|
+
message: "Implementation handoff is blocked because approved artifact metadata could not be loaded.",
|
|
8922
|
+
error: truncateLogExcerpt(errorDetail(error))
|
|
8923
|
+
};
|
|
8924
|
+
return void 0;
|
|
7646
8925
|
});
|
|
8926
|
+
if (!implementationHandoff) {
|
|
8927
|
+
implementationHandoff = await completeImplementationHandoff({
|
|
8928
|
+
...approvedArtifacts ? { approvedArtifacts } : {},
|
|
8929
|
+
primaryRepoRoot: root,
|
|
8930
|
+
...repositoryLink ? { repositoryLink } : {},
|
|
8931
|
+
verificationSummary: "Local execution reported completion.",
|
|
8932
|
+
workItem: result.workItem,
|
|
8933
|
+
...worktreeIsolation.isolation ? { worktreeIsolation: worktreeIsolation.isolation } : {},
|
|
8934
|
+
worktreePath: executionRoot
|
|
8935
|
+
});
|
|
8936
|
+
}
|
|
7647
8937
|
finalStatus = implementationHandoff.status === "prReady" || implementationHandoff.status === "noChanges" ? "completed" : "blocked";
|
|
7648
8938
|
finalMessage = implementationHandoff.message ?? "Implementation handoff finished.";
|
|
7649
8939
|
finalError = implementationHandoff.error;
|
|
@@ -7661,31 +8951,48 @@ async function runNextWorkItem({
|
|
|
7661
8951
|
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
7662
8952
|
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
7663
8953
|
});
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
|
|
8954
|
+
let statusResult;
|
|
8955
|
+
try {
|
|
8956
|
+
statusResult = await apiClient.updateWorkStatus(
|
|
8957
|
+
projectId,
|
|
8958
|
+
result.workItem.workItemId,
|
|
8959
|
+
finalStatus,
|
|
8960
|
+
`run_${result.workItem.workItemId}_${randomUUID()}`,
|
|
8961
|
+
runnerId,
|
|
8962
|
+
{
|
|
8963
|
+
tool: preview.toolName,
|
|
8964
|
+
...toolResult.model ? { model: toolResult.model } : {},
|
|
8965
|
+
durationMs,
|
|
8966
|
+
message: finalMessage,
|
|
8967
|
+
...isolationTelemetry,
|
|
8968
|
+
...finalStatus === "blocked" ? { blockerReason: finalMessage } : {},
|
|
8969
|
+
sessionPolicy: sessionContext.policy,
|
|
8970
|
+
sessionDecision: sessionContext.decision,
|
|
8971
|
+
sessionDecisionReason: sessionContext.reason,
|
|
8972
|
+
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8973
|
+
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {},
|
|
8974
|
+
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8975
|
+
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8976
|
+
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {},
|
|
8977
|
+
...implementationHandoff ? { implementationHandoff } : {},
|
|
8978
|
+
...finalError ? { error: finalError } : {}
|
|
8979
|
+
}
|
|
8980
|
+
);
|
|
8981
|
+
} catch (error) {
|
|
8982
|
+
if (error instanceof AmistioApiError && error.status === 409 && error.detail.includes("implementation_test_gate_required")) {
|
|
8983
|
+
const gateMessage = "Implementation test gate was queued and must pass before completion or PR handoff is finalized.";
|
|
8984
|
+
await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
|
|
8985
|
+
status: "queued",
|
|
8986
|
+
summary: gateMessage,
|
|
8987
|
+
idempotencyKey: `runner_milestone_test_gate_required_${result.workItem.workItemId}_${result.workItem.attempt}`,
|
|
8988
|
+
metadata: { tool: preview.toolName, durationMs, executionWorktreeKey: isolationTelemetry.executionWorktreeKey ?? "", executionBranch: isolationTelemetry.executionBranch ?? "" }
|
|
8989
|
+
});
|
|
8990
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
|
|
8991
|
+
console.log(gateMessage);
|
|
8992
|
+
return { status: "blocked", exitCode: 0 };
|
|
8993
|
+
}
|
|
8994
|
+
throw error;
|
|
8995
|
+
}
|
|
7689
8996
|
await recordRunnerMilestone(apiClient, projectId, result.workItem, runnerId, repositoryLinkId, {
|
|
7690
8997
|
status: finalStatus,
|
|
7691
8998
|
summary: finalMessage,
|
|
@@ -7699,19 +9006,27 @@ async function runNextWorkItem({
|
|
|
7699
9006
|
executionBranch: isolationTelemetry.executionBranch ?? "",
|
|
7700
9007
|
...implementationHandoff?.status ? { handoffStatus: implementationHandoff.status } : {},
|
|
7701
9008
|
...implementationHandoff?.prUrl ? { prUrl: implementationHandoff.prUrl } : {},
|
|
7702
|
-
...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {}
|
|
9009
|
+
...implementationHandoff?.cleanupStatus ? { cleanupStatus: implementationHandoff.cleanupStatus } : {},
|
|
9010
|
+
...implementationHandoff?.artifacts ? artifactHandoffMetadata(implementationHandoff.artifacts) : {}
|
|
7703
9011
|
}
|
|
7704
9012
|
});
|
|
7705
|
-
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
9013
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency));
|
|
7706
9014
|
const durationSeconds = Math.round(durationMs / 1e3);
|
|
7707
9015
|
console.log(`Marked ${statusResult.workItem.workItemId} ${statusResult.workItem.status} after ${durationSeconds}s.`);
|
|
7708
9016
|
return { status: finalStatus, exitCode: finalStatus === "completed" ? toolResult.exitCode : 1 };
|
|
7709
9017
|
}
|
|
9018
|
+
function artifactHandoffMetadata(artifacts) {
|
|
9019
|
+
return {
|
|
9020
|
+
artifactIncludedCount: artifacts.included.length,
|
|
9021
|
+
artifactSkippedCount: artifacts.skipped.length,
|
|
9022
|
+
artifactBlockedCount: artifacts.blocked.length
|
|
9023
|
+
};
|
|
9024
|
+
}
|
|
7710
9025
|
async function loadWorkItemRepositoryLink(apiClient, projectId, repositoryLinkId) {
|
|
7711
9026
|
const { repositoryLinks } = await apiClient.listRepositoryLinks(projectId);
|
|
7712
9027
|
return repositoryLinks.find((link) => link.repositoryLinkId === repositoryLinkId && link.status !== "revoked");
|
|
7713
9028
|
}
|
|
7714
|
-
async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
|
|
9029
|
+
async function prepareWorktreeForClaimedItem({ apiClient, heartbeatConcurrency, maxPreflightAttempts, projectId, repositoryLinkId, root, runnerId, toolConfig, workItem }) {
|
|
7715
9030
|
if (!needsGitWorktreeIsolation(workItem)) {
|
|
7716
9031
|
return { status: "ready" };
|
|
7717
9032
|
}
|
|
@@ -7726,9 +9041,9 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
|
|
|
7726
9041
|
const isolation = await prepareGitWorktreeIsolation(root, workItem);
|
|
7727
9042
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
7728
9043
|
status: "running",
|
|
7729
|
-
summary: `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
|
|
9044
|
+
summary: isolation.preparedLocalEnvironmentFileCount ? `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch} and local environment files.` : `Prepared Git worktree ${isolation.worktreeKey} on ${isolation.branch}.`,
|
|
7730
9045
|
idempotencyKey: `runner_milestone_worktree_${workItem.workItemId}_${workItem.attempt}`,
|
|
7731
|
-
metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId }
|
|
9046
|
+
metadata: { executionWorktreeKey: isolation.worktreeKey, executionBranch: isolation.branch, implementationScopeId: isolation.implementationScopeId, ...isolation.preparedLocalEnvironmentFileCount ? { preparedLocalEnvironmentFileCount: isolation.preparedLocalEnvironmentFileCount } : {} }
|
|
7732
9047
|
});
|
|
7733
9048
|
return { status: "ready", isolation };
|
|
7734
9049
|
} catch (error) {
|
|
@@ -7749,14 +9064,14 @@ async function prepareWorktreeForClaimedItem({ apiClient, maxPreflightAttempts,
|
|
|
7749
9064
|
metadata: { executionWorktreeKey: telemetry.executionWorktreeKey ?? "", executionBranch: telemetry.executionBranch ?? "", implementationScopeId: telemetry.implementationScopeId ?? "", attempt: workItem.attempt, maxAttempts: maxPreflightAttempts, error: message }
|
|
7750
9065
|
});
|
|
7751
9066
|
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", {
|
|
7752
|
-
...runnerHeartbeatMetadata(toolConfig),
|
|
9067
|
+
...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
|
|
7753
9068
|
preferenceMessage: statusMessage
|
|
7754
9069
|
});
|
|
7755
9070
|
console.error(statusMessage);
|
|
7756
9071
|
return { status: finalAttempt ? "failed" : "retrying", message: statusMessage };
|
|
7757
9072
|
}
|
|
7758
9073
|
}
|
|
7759
|
-
function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry }) {
|
|
9074
|
+
function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerId, toolConfig, workItem, telemetry, heartbeatConcurrency }) {
|
|
7760
9075
|
let stopped = false;
|
|
7761
9076
|
const renew = async () => {
|
|
7762
9077
|
if (stopped) return;
|
|
@@ -7767,7 +9082,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
|
|
|
7767
9082
|
leaseExpiresAt
|
|
7768
9083
|
});
|
|
7769
9084
|
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "running", {
|
|
7770
|
-
...runnerHeartbeatMetadata(toolConfig),
|
|
9085
|
+
...runnerHeartbeatMetadata(toolConfig, currentRunnerMode(), heartbeatConcurrency),
|
|
7771
9086
|
currentWorkItemId: workItem.workItemId,
|
|
7772
9087
|
...telemetry.implementationScopeId ? { currentImplementationScopeId: telemetry.implementationScopeId } : {},
|
|
7773
9088
|
...telemetry.executionWorktreeKey ? { currentWorktreeKey: telemetry.executionWorktreeKey } : {},
|
|
@@ -7783,6 +9098,7 @@ function startWorkLeaseRenewal({ apiClient, projectId, repositoryLinkId, runnerI
|
|
|
7783
9098
|
workItemId: workItem.workItemId,
|
|
7784
9099
|
workTitle: workItem.title,
|
|
7785
9100
|
...workItem.workKind ? { workKind: workItem.workKind } : {},
|
|
9101
|
+
...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
|
|
7786
9102
|
message: "Runner could not renew the active work lease.",
|
|
7787
9103
|
error: detail,
|
|
7788
9104
|
machineId: runnerMachineId()
|
|
@@ -7822,6 +9138,7 @@ async function recordFinalizationFailure({ apiClient, durationMs, error, isolati
|
|
|
7822
9138
|
workItemId: workItem.workItemId,
|
|
7823
9139
|
workTitle: workItem.title,
|
|
7824
9140
|
...workItem.workKind ? { workKind: workItem.workKind } : {},
|
|
9141
|
+
...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
|
|
7825
9142
|
tool: toolName,
|
|
7826
9143
|
durationMs,
|
|
7827
9144
|
message,
|
|
@@ -7846,6 +9163,7 @@ function workItemIsolationTelemetry(workItem, isolation) {
|
|
|
7846
9163
|
const repositoryLockId = isolation?.repositoryLockId ?? workItem.repositoryLockId;
|
|
7847
9164
|
return {
|
|
7848
9165
|
...needsGitWorktreeIsolation(workItem) ? { isolationMode: "gitWorktree" } : workItem.isolationMode ? { isolationMode: workItem.isolationMode } : {},
|
|
9166
|
+
...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
|
|
7849
9167
|
...workItem.claimLeaseId ? { claimLeaseId: workItem.claimLeaseId } : {},
|
|
7850
9168
|
...workItem.controllingAdrId ? { controllingAdrId: workItem.controllingAdrId } : {},
|
|
7851
9169
|
...implementationScopeId ? { implementationScopeId } : {},
|
|
@@ -7857,17 +9175,60 @@ function workItemIsolationTelemetry(workItem, isolation) {
|
|
|
7857
9175
|
};
|
|
7858
9176
|
}
|
|
7859
9177
|
async function recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, input) {
|
|
9178
|
+
const autopilot = autopilotWorkMetadata2(workItem);
|
|
9179
|
+
const metadata = { ...autopilotRunnerMetadata(workItem), ...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {}, ...input.metadata ?? {} };
|
|
7860
9180
|
await apiClient.recordActivityEvent(projectId, {
|
|
7861
9181
|
eventType: "runnerMilestone",
|
|
7862
9182
|
runnerId,
|
|
7863
9183
|
repositoryLinkId,
|
|
9184
|
+
...workItem.claimLaneId ? { claimLaneId: workItem.claimLaneId } : {},
|
|
7864
9185
|
relatedWorkItemId: workItem.workItemId,
|
|
7865
9186
|
...workItem.reviewDocumentId ? { relatedDocumentId: workItem.reviewDocumentId } : workItem.impactDocumentId ? { relatedDocumentId: workItem.impactDocumentId } : {},
|
|
7866
9187
|
...workItem.issueId ? { relatedIssueId: workItem.issueId } : {},
|
|
9188
|
+
...autopilot.autopilotAuthorizationId ? { relatedAutopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
|
|
9189
|
+
...autopilot.autopilotCandidateId ? { relatedAutopilotCandidateId: autopilot.autopilotCandidateId } : {},
|
|
7867
9190
|
...workItem.generatedDraftId ? { generatedDraftId: workItem.generatedDraftId } : {},
|
|
7868
|
-
...input
|
|
9191
|
+
...input,
|
|
9192
|
+
...Object.keys(metadata).length ? { metadata } : {}
|
|
7869
9193
|
}).catch(() => void 0);
|
|
7870
9194
|
}
|
|
9195
|
+
function formatAutopilotListSuffix(workItem) {
|
|
9196
|
+
const autopilot = autopilotWorkMetadata2(workItem);
|
|
9197
|
+
if (!autopilot.autopilotAuthorizationId) {
|
|
9198
|
+
return "";
|
|
9199
|
+
}
|
|
9200
|
+
const details = [
|
|
9201
|
+
autopilot.autopilotClassificationOutcome ?? "authorized",
|
|
9202
|
+
workItem.workKind ?? "implementation",
|
|
9203
|
+
autopilot.autopilotCandidateType,
|
|
9204
|
+
autopilot.autopilotPolicyVersion ? `policy ${autopilot.autopilotPolicyVersion}` : void 0,
|
|
9205
|
+
`auth ${autopilot.autopilotAuthorizationId}`,
|
|
9206
|
+
autopilot.autopilotCandidateId ? `candidate ${autopilot.autopilotCandidateId}` : void 0
|
|
9207
|
+
].filter(Boolean).join(", ");
|
|
9208
|
+
return ` (autopilot: ${details})`;
|
|
9209
|
+
}
|
|
9210
|
+
function formatAutopilotClaimLine(workItem) {
|
|
9211
|
+
const autopilot = autopilotWorkMetadata2(workItem);
|
|
9212
|
+
if (!autopilot.autopilotAuthorizationId) {
|
|
9213
|
+
return void 0;
|
|
9214
|
+
}
|
|
9215
|
+
const candidate = autopilot.autopilotCandidateId ? `, candidate ${autopilot.autopilotCandidateId}` : "";
|
|
9216
|
+
const candidateType = autopilot.autopilotCandidateType ? `, ${autopilot.autopilotCandidateType}` : "";
|
|
9217
|
+
return `Autopilot authorization: ${autopilot.autopilotAuthorizationId} (${autopilot.autopilotClassificationOutcome ?? "authorized"}, ${workItem.workKind ?? "implementation"}${candidateType}, policy ${autopilot.autopilotPolicyVersion ?? "unknown"}${candidate}). Local runner safety rules still apply.`;
|
|
9218
|
+
}
|
|
9219
|
+
function autopilotRunnerMetadata(workItem) {
|
|
9220
|
+
const autopilot = autopilotWorkMetadata2(workItem);
|
|
9221
|
+
return {
|
|
9222
|
+
...autopilot.autopilotAuthorizationId ? { autopilotAuthorizationId: autopilot.autopilotAuthorizationId } : {},
|
|
9223
|
+
...autopilot.autopilotCandidateId ? { autopilotCandidateId: autopilot.autopilotCandidateId } : {},
|
|
9224
|
+
...autopilot.autopilotCandidateType ? { autopilotCandidateType: autopilot.autopilotCandidateType } : {},
|
|
9225
|
+
...autopilot.autopilotClassificationOutcome ? { autopilotOutcome: autopilot.autopilotClassificationOutcome } : {},
|
|
9226
|
+
...autopilot.autopilotPolicyVersion ? { autopilotPolicyVersion: autopilot.autopilotPolicyVersion } : {}
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
9229
|
+
function autopilotWorkMetadata2(workItem) {
|
|
9230
|
+
return workItem;
|
|
9231
|
+
}
|
|
7871
9232
|
function logRejectedSettlements(action, settlements) {
|
|
7872
9233
|
for (const settlement of settlements) {
|
|
7873
9234
|
if (settlement.status === "rejected") {
|
|
@@ -7954,6 +9315,28 @@ async function replayPendingBrainGenerationFinalizations({ accountId, apiClient,
|
|
|
7954
9315
|
console.log(message);
|
|
7955
9316
|
return { status: "completed", exitCode: 0, message };
|
|
7956
9317
|
}
|
|
9318
|
+
async function replayPendingResultFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId }) {
|
|
9319
|
+
const brainReplay = await replayPendingBrainGenerationFinalizations({ accountId, apiClient, projectId, repositoryLinkId, runnerId });
|
|
9320
|
+
if (brainReplay) {
|
|
9321
|
+
return brainReplay;
|
|
9322
|
+
}
|
|
9323
|
+
const pendingEntries = await listPendingDurableResultFinalizations({ accountId, projectId, repositoryLinkId, runnerId });
|
|
9324
|
+
if (!pendingEntries.length) {
|
|
9325
|
+
return void 0;
|
|
9326
|
+
}
|
|
9327
|
+
let completedCount = 0;
|
|
9328
|
+
for (const entry of pendingEntries) {
|
|
9329
|
+
const replay = await submitDurableResultFinalizationEntry(apiClient, entry, { recordReplayTelemetry: true });
|
|
9330
|
+
if (replay.status === "completed") {
|
|
9331
|
+
completedCount += 1;
|
|
9332
|
+
continue;
|
|
9333
|
+
}
|
|
9334
|
+
return { status: "failed", exitCode: 1, message: replay.message };
|
|
9335
|
+
}
|
|
9336
|
+
const message = `Replayed ${completedCount} pending runner result finalization${completedCount === 1 ? "" : "s"}.`;
|
|
9337
|
+
console.log(message);
|
|
9338
|
+
return { status: "completed", exitCode: 0, message };
|
|
9339
|
+
}
|
|
7957
9340
|
async function submitBrainGenerationFinalizationEntry(apiClient, entry, options = {}) {
|
|
7958
9341
|
let result;
|
|
7959
9342
|
try {
|
|
@@ -7988,6 +9371,127 @@ async function submitBrainGenerationFinalizationEntry(apiClient, entry, options
|
|
|
7988
9371
|
}
|
|
7989
9372
|
return { status: "completed", workItem: result.workItem, documentCount: result.documents.length };
|
|
7990
9373
|
}
|
|
9374
|
+
async function submitDurableResultFinalizationEntry(apiClient, entry, options = {}) {
|
|
9375
|
+
let response;
|
|
9376
|
+
try {
|
|
9377
|
+
response = await submitDurableResultMutation(apiClient, entry);
|
|
9378
|
+
} catch (error) {
|
|
9379
|
+
const detail = truncateLogExcerpt(errorMessage3(error));
|
|
9380
|
+
if (isRetryableApiError(error)) {
|
|
9381
|
+
const updated = await markDurableResultFinalizationRetry(entry, detail);
|
|
9382
|
+
const message2 = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} could not be replayed yet (${updated.retryCount} attempt${updated.retryCount === 1 ? "" : "s"}): ${detail}`;
|
|
9383
|
+
console.error(message2);
|
|
9384
|
+
return { status: "failed", message: message2, retryable: true };
|
|
9385
|
+
}
|
|
9386
|
+
await markDurableResultFinalizationTerminal(entry, detail);
|
|
9387
|
+
const message = `Pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} reached a terminal API failure: ${detail}`;
|
|
9388
|
+
console.error(message);
|
|
9389
|
+
return { status: "failed", message, retryable: false };
|
|
9390
|
+
}
|
|
9391
|
+
const workItem = durableResultResponseWorkItem(response);
|
|
9392
|
+
await deleteDurableResultFinalizationEntry(entry).catch((error) => {
|
|
9393
|
+
console.error(`delete pending ${runnerResultFinalizationLabel(entry)} finalization ${entry.workItemId} failed: ${errorMessage3(error)}`);
|
|
9394
|
+
});
|
|
9395
|
+
if (options.recordReplayTelemetry) {
|
|
9396
|
+
await recordRunnerMilestone(apiClient, entry.projectId, workItem, entry.runnerId, entry.repositoryLinkId, {
|
|
9397
|
+
status: entry.result.status,
|
|
9398
|
+
summary: entry.result.status === "completed" ? `Replayed pending ${runnerResultFinalizationLabel(entry)} finalization.` : `Replayed pending ${runnerResultFinalizationLabel(entry)} failure finalization.`,
|
|
9399
|
+
idempotencyKey: `runner_milestone_result_replayed_${entry.workItemId}_${workItem.idempotencyKey}`,
|
|
9400
|
+
metadata: { replayedFinalization: true, resultKind: entry.resultKind, workKind: entry.workKind }
|
|
9401
|
+
});
|
|
9402
|
+
await apiClient.sendRunnerHeartbeat(entry.projectId, entry.runnerId, entry.repositoryLinkId, "online", {
|
|
9403
|
+
...runnerHeartbeatMetadata(),
|
|
9404
|
+
preferenceMessage: "Pending result finalization replayed."
|
|
9405
|
+
}).catch(() => void 0);
|
|
9406
|
+
}
|
|
9407
|
+
return { status: "completed", workItem, response };
|
|
9408
|
+
}
|
|
9409
|
+
async function submitDurableResultMutation(apiClient, entry) {
|
|
9410
|
+
if (entry.resultKind === "assistantResult") {
|
|
9411
|
+
return apiClient.submitAssistantResult(entry.projectId, entry.workItemId, entry.result);
|
|
9412
|
+
}
|
|
9413
|
+
if (entry.resultKind === "impactPreviewResult") {
|
|
9414
|
+
return apiClient.submitImpactPreviewResult(entry.projectId, entry.workItemId, entry.result);
|
|
9415
|
+
}
|
|
9416
|
+
if (entry.resultKind === "issueDiagnosisResult") {
|
|
9417
|
+
return apiClient.submitIssueDiagnosisResult(entry.projectId, entry.workItemId, entry.result);
|
|
9418
|
+
}
|
|
9419
|
+
if (entry.resultKind === "securityPostureScanResult") {
|
|
9420
|
+
return apiClient.submitSecurityPostureScanResult(entry.projectId, entry.workItemId, entry.result);
|
|
9421
|
+
}
|
|
9422
|
+
if (entry.resultKind === "appEvaluationScanResult") {
|
|
9423
|
+
return apiClient.submitAppEvaluationScanResult(entry.projectId, entry.workItemId, entry.result);
|
|
9424
|
+
}
|
|
9425
|
+
if (entry.resultKind === "projectContextRefreshResult") {
|
|
9426
|
+
return apiClient.submitProjectContextRefreshResult(entry.projectId, entry.workItemId, entry.result);
|
|
9427
|
+
}
|
|
9428
|
+
if (entry.resultKind === "testQualityScanResult") {
|
|
9429
|
+
return apiClient.submitTestQualityScanResult(entry.projectId, entry.workItemId, entry.result);
|
|
9430
|
+
}
|
|
9431
|
+
if (entry.resultKind === "implementationTestGateResult") {
|
|
9432
|
+
return apiClient.submitImplementationTestGateResult(entry.projectId, entry.workItemId, entry.result);
|
|
9433
|
+
}
|
|
9434
|
+
return apiClient.submitImplementationVerificationResult(entry.projectId, entry.workItemId, entry.result);
|
|
9435
|
+
}
|
|
9436
|
+
function durableResultResponseWorkItem(response) {
|
|
9437
|
+
if (response && typeof response === "object" && "workItem" in response) {
|
|
9438
|
+
return response.workItem;
|
|
9439
|
+
}
|
|
9440
|
+
throw new Error("Runner result finalization response did not include a work item.");
|
|
9441
|
+
}
|
|
9442
|
+
function runnerResultFinalizationLabel(entry) {
|
|
9443
|
+
if (entry.workKind === "appEvaluationScan") return "app evaluation scan";
|
|
9444
|
+
if (entry.workKind === "securityPostureScan") return "security posture scan";
|
|
9445
|
+
if (entry.workKind === "projectContextRefresh") return "project context refresh";
|
|
9446
|
+
if (entry.workKind === "implementationVerification") return "implementation verification";
|
|
9447
|
+
if (entry.workKind === "testQualityScan") return "test quality scan";
|
|
9448
|
+
if (entry.workKind === "implementationTestGate") return "implementation test gate";
|
|
9449
|
+
if (entry.workKind === "issueDiagnosis") return "issue diagnosis";
|
|
9450
|
+
if (entry.workKind === "impactPreview") return "impact preview";
|
|
9451
|
+
return "assistant answer";
|
|
9452
|
+
}
|
|
9453
|
+
async function submitStagedDurableResultFinalization(apiClient, input) {
|
|
9454
|
+
const entry = createDurableResultFinalizationEntry(input);
|
|
9455
|
+
await upsertDurableResultFinalizationEntry(entry);
|
|
9456
|
+
return submitDurableResultFinalizationEntry(apiClient, entry);
|
|
9457
|
+
}
|
|
9458
|
+
async function submitPrimaryRunnerResult(apiClient, input) {
|
|
9459
|
+
const replay = await submitStagedDurableResultFinalization(apiClient, input);
|
|
9460
|
+
if (replay.status === "failed") {
|
|
9461
|
+
if (!replay.retryable) {
|
|
9462
|
+
throw new Error(replay.message);
|
|
9463
|
+
}
|
|
9464
|
+
return void 0;
|
|
9465
|
+
}
|
|
9466
|
+
return replay.response;
|
|
9467
|
+
}
|
|
9468
|
+
function pendingSessionTelemetry(sessionContext) {
|
|
9469
|
+
return {
|
|
9470
|
+
sessionPolicy: sessionContext.policy,
|
|
9471
|
+
sessionDecision: sessionContext.decision,
|
|
9472
|
+
sessionDecisionReason: sessionContext.reason,
|
|
9473
|
+
...sessionContext.toolSession ? { toolSessionId: sessionContext.toolSession.toolSessionId } : {},
|
|
9474
|
+
...sessionContext.toolSession?.sessionGroupKey ? { sessionGroupKey: sessionContext.toolSession.sessionGroupKey } : {}
|
|
9475
|
+
};
|
|
9476
|
+
}
|
|
9477
|
+
async function finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status, toolResult, workItemId }) {
|
|
9478
|
+
const settlements = await Promise.allSettled([
|
|
9479
|
+
finalizeToolSession({
|
|
9480
|
+
apiClient,
|
|
9481
|
+
projectId,
|
|
9482
|
+
status,
|
|
9483
|
+
runnerId,
|
|
9484
|
+
workItemId,
|
|
9485
|
+
stdout: toolResult.stdout,
|
|
9486
|
+
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
9487
|
+
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
9488
|
+
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
9489
|
+
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
9490
|
+
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
9491
|
+
})
|
|
9492
|
+
]);
|
|
9493
|
+
logRejectedSettlements("finalize runner result tool session", settlements);
|
|
9494
|
+
}
|
|
7991
9495
|
async function finalizeBrainGenerationWork({
|
|
7992
9496
|
apiClient,
|
|
7993
9497
|
durationMs,
|
|
@@ -8145,28 +9649,9 @@ ${toolResult.stderr}`);
|
|
|
8145
9649
|
answerError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8146
9650
|
}
|
|
8147
9651
|
const finalStatus = answerResult ? "completed" : "failed";
|
|
8148
|
-
const
|
|
8149
|
-
apiClient,
|
|
8150
|
-
projectId,
|
|
8151
|
-
status: finalStatus,
|
|
8152
|
-
runnerId,
|
|
8153
|
-
workItemId: workItem.workItemId,
|
|
8154
|
-
stdout: toolResult.stdout,
|
|
8155
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8156
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8157
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8158
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8159
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8160
|
-
});
|
|
8161
|
-
const sessionTelemetry = {
|
|
8162
|
-
sessionPolicy: sessionContext.policy,
|
|
8163
|
-
sessionDecision: sessionContext.decision,
|
|
8164
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8165
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8166
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8167
|
-
};
|
|
9652
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8168
9653
|
if (answerResult) {
|
|
8169
|
-
const
|
|
9654
|
+
const resultMutation = {
|
|
8170
9655
|
status: "completed",
|
|
8171
9656
|
runnerId,
|
|
8172
9657
|
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8177,7 +9662,21 @@ ${toolResult.stderr}`);
|
|
|
8177
9662
|
durationMs,
|
|
8178
9663
|
...sessionTelemetry,
|
|
8179
9664
|
message: `${toolName} returned a project knowledge answer.`
|
|
9665
|
+
};
|
|
9666
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
9667
|
+
accountId: workItem.accountId,
|
|
9668
|
+
projectId,
|
|
9669
|
+
repositoryLinkId,
|
|
9670
|
+
runnerId,
|
|
9671
|
+
workItemId: workItem.workItemId,
|
|
9672
|
+
workKind: "assistantQuestion",
|
|
9673
|
+
resultKind: "assistantResult",
|
|
9674
|
+
attempt: workItem.attempt,
|
|
9675
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
9676
|
+
result: resultMutation
|
|
8180
9677
|
});
|
|
9678
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
9679
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8181
9680
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8182
9681
|
status: "completed",
|
|
8183
9682
|
summary: `${toolName} returned a project knowledge answer.`,
|
|
@@ -8188,7 +9687,7 @@ ${toolResult.stderr}`);
|
|
|
8188
9687
|
console.log("Project knowledge answer returned.");
|
|
8189
9688
|
return { status: "completed", exitCode: 0 };
|
|
8190
9689
|
}
|
|
8191
|
-
const
|
|
9690
|
+
const failedMutation = {
|
|
8192
9691
|
status: "failed",
|
|
8193
9692
|
runnerId,
|
|
8194
9693
|
idempotencyKey: `assistant_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8197,7 +9696,21 @@ ${toolResult.stderr}`);
|
|
|
8197
9696
|
...sessionTelemetry,
|
|
8198
9697
|
message: `${toolName} did not produce a valid project knowledge answer.`,
|
|
8199
9698
|
...answerError ? { error: answerError } : {}
|
|
9699
|
+
};
|
|
9700
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
9701
|
+
accountId: workItem.accountId,
|
|
9702
|
+
projectId,
|
|
9703
|
+
repositoryLinkId,
|
|
9704
|
+
runnerId,
|
|
9705
|
+
workItemId: workItem.workItemId,
|
|
9706
|
+
workKind: "assistantQuestion",
|
|
9707
|
+
resultKind: "assistantResult",
|
|
9708
|
+
attempt: workItem.attempt,
|
|
9709
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
9710
|
+
result: failedMutation
|
|
8200
9711
|
});
|
|
9712
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
9713
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8201
9714
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8202
9715
|
status: "failed",
|
|
8203
9716
|
summary: answerError ?? `${toolName} did not produce a valid project knowledge answer.`,
|
|
@@ -8234,29 +9747,10 @@ ${toolResult.stderr}`);
|
|
|
8234
9747
|
previewError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8235
9748
|
}
|
|
8236
9749
|
const finalStatus = report ? "completed" : "failed";
|
|
8237
|
-
const
|
|
8238
|
-
apiClient,
|
|
8239
|
-
projectId,
|
|
8240
|
-
status: finalStatus,
|
|
8241
|
-
runnerId,
|
|
8242
|
-
workItemId: workItem.workItemId,
|
|
8243
|
-
stdout: toolResult.stdout,
|
|
8244
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8245
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8246
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8247
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8248
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8249
|
-
});
|
|
8250
|
-
const sessionTelemetry = {
|
|
8251
|
-
sessionPolicy: sessionContext.policy,
|
|
8252
|
-
sessionDecision: sessionContext.decision,
|
|
8253
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8254
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8255
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8256
|
-
};
|
|
9750
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8257
9751
|
if (report) {
|
|
8258
9752
|
const metadata = await readProjectLink(root).catch(() => void 0);
|
|
8259
|
-
const
|
|
9753
|
+
const resultMutation = {
|
|
8260
9754
|
status: "completed",
|
|
8261
9755
|
runnerId,
|
|
8262
9756
|
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8268,7 +9762,21 @@ ${toolResult.stderr}`);
|
|
|
8268
9762
|
durationMs,
|
|
8269
9763
|
...sessionTelemetry,
|
|
8270
9764
|
message: `${toolName} returned an implementation impact preview.`
|
|
9765
|
+
};
|
|
9766
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
9767
|
+
accountId: workItem.accountId,
|
|
9768
|
+
projectId,
|
|
9769
|
+
repositoryLinkId,
|
|
9770
|
+
runnerId,
|
|
9771
|
+
workItemId: workItem.workItemId,
|
|
9772
|
+
workKind: "impactPreview",
|
|
9773
|
+
resultKind: "impactPreviewResult",
|
|
9774
|
+
attempt: workItem.attempt,
|
|
9775
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
9776
|
+
result: resultMutation
|
|
8271
9777
|
});
|
|
9778
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
9779
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8272
9780
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8273
9781
|
status: "completed",
|
|
8274
9782
|
summary: `${toolName} returned an implementation impact preview.`,
|
|
@@ -8279,7 +9787,7 @@ ${toolResult.stderr}`);
|
|
|
8279
9787
|
console.log(result.implementationWorkItem ? "Impact preview returned; implementation work is now queued." : "Impact preview returned.");
|
|
8280
9788
|
return { status: "completed", exitCode: 0 };
|
|
8281
9789
|
}
|
|
8282
|
-
const
|
|
9790
|
+
const failedMutation = {
|
|
8283
9791
|
status: "failed",
|
|
8284
9792
|
runnerId,
|
|
8285
9793
|
idempotencyKey: `impact_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8288,7 +9796,21 @@ ${toolResult.stderr}`);
|
|
|
8288
9796
|
...sessionTelemetry,
|
|
8289
9797
|
message: `${toolName} did not produce a valid impact preview.`,
|
|
8290
9798
|
...previewError ? { error: previewError } : {}
|
|
9799
|
+
};
|
|
9800
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
9801
|
+
accountId: workItem.accountId,
|
|
9802
|
+
projectId,
|
|
9803
|
+
repositoryLinkId,
|
|
9804
|
+
runnerId,
|
|
9805
|
+
workItemId: workItem.workItemId,
|
|
9806
|
+
workKind: "impactPreview",
|
|
9807
|
+
resultKind: "impactPreviewResult",
|
|
9808
|
+
attempt: workItem.attempt,
|
|
9809
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
9810
|
+
result: failedMutation
|
|
8291
9811
|
});
|
|
9812
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
9813
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8292
9814
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8293
9815
|
status: "failed",
|
|
8294
9816
|
summary: previewError ?? `${toolName} did not produce a valid impact preview.`,
|
|
@@ -8324,28 +9846,9 @@ ${toolResult.stderr}`);
|
|
|
8324
9846
|
diagnosisError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8325
9847
|
}
|
|
8326
9848
|
const finalStatus = diagnosis ? "completed" : "failed";
|
|
8327
|
-
const
|
|
8328
|
-
apiClient,
|
|
8329
|
-
projectId,
|
|
8330
|
-
status: finalStatus,
|
|
8331
|
-
runnerId,
|
|
8332
|
-
workItemId: workItem.workItemId,
|
|
8333
|
-
stdout: toolResult.stdout,
|
|
8334
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8335
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8336
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8337
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8338
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8339
|
-
});
|
|
8340
|
-
const sessionTelemetry = {
|
|
8341
|
-
sessionPolicy: sessionContext.policy,
|
|
8342
|
-
sessionDecision: sessionContext.decision,
|
|
8343
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8344
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8345
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8346
|
-
};
|
|
9849
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8347
9850
|
if (diagnosis) {
|
|
8348
|
-
const
|
|
9851
|
+
const resultMutation = {
|
|
8349
9852
|
status: "completed",
|
|
8350
9853
|
runnerId,
|
|
8351
9854
|
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8354,7 +9857,21 @@ ${toolResult.stderr}`);
|
|
|
8354
9857
|
durationMs,
|
|
8355
9858
|
...sessionTelemetry,
|
|
8356
9859
|
message: `${toolName} returned an issue root-cause analysis.`
|
|
9860
|
+
};
|
|
9861
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
9862
|
+
accountId: workItem.accountId,
|
|
9863
|
+
projectId,
|
|
9864
|
+
repositoryLinkId,
|
|
9865
|
+
runnerId,
|
|
9866
|
+
workItemId: workItem.workItemId,
|
|
9867
|
+
workKind: "issueDiagnosis",
|
|
9868
|
+
resultKind: "issueDiagnosisResult",
|
|
9869
|
+
attempt: workItem.attempt,
|
|
9870
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
9871
|
+
result: resultMutation
|
|
8357
9872
|
});
|
|
9873
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
9874
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8358
9875
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8359
9876
|
status: "completed",
|
|
8360
9877
|
summary: `${toolName} returned an issue root-cause analysis.`,
|
|
@@ -8365,7 +9882,7 @@ ${toolResult.stderr}`);
|
|
|
8365
9882
|
console.log("Issue diagnosis returned for approval.");
|
|
8366
9883
|
return { status: "completed", exitCode: 0 };
|
|
8367
9884
|
}
|
|
8368
|
-
const
|
|
9885
|
+
const failedMutation = {
|
|
8369
9886
|
status: "failed",
|
|
8370
9887
|
runnerId,
|
|
8371
9888
|
idempotencyKey: `issue_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8374,7 +9891,21 @@ ${toolResult.stderr}`);
|
|
|
8374
9891
|
...sessionTelemetry,
|
|
8375
9892
|
message: `${toolName} did not produce a valid issue diagnosis.`,
|
|
8376
9893
|
...diagnosisError ? { error: diagnosisError } : {}
|
|
9894
|
+
};
|
|
9895
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
9896
|
+
accountId: workItem.accountId,
|
|
9897
|
+
projectId,
|
|
9898
|
+
repositoryLinkId,
|
|
9899
|
+
runnerId,
|
|
9900
|
+
workItemId: workItem.workItemId,
|
|
9901
|
+
workKind: "issueDiagnosis",
|
|
9902
|
+
resultKind: "issueDiagnosisResult",
|
|
9903
|
+
attempt: workItem.attempt,
|
|
9904
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
9905
|
+
result: failedMutation
|
|
8377
9906
|
});
|
|
9907
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
9908
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8378
9909
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8379
9910
|
status: "failed",
|
|
8380
9911
|
summary: diagnosisError ?? `${toolName} did not produce a valid issue diagnosis.`,
|
|
@@ -8410,28 +9941,9 @@ ${toolResult.stderr}`);
|
|
|
8410
9941
|
scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8411
9942
|
}
|
|
8412
9943
|
const finalStatus = scanResult ? "completed" : "failed";
|
|
8413
|
-
const
|
|
8414
|
-
apiClient,
|
|
8415
|
-
projectId,
|
|
8416
|
-
status: finalStatus,
|
|
8417
|
-
runnerId,
|
|
8418
|
-
workItemId: workItem.workItemId,
|
|
8419
|
-
stdout: toolResult.stdout,
|
|
8420
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8421
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8422
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8423
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8424
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8425
|
-
});
|
|
8426
|
-
const sessionTelemetry = {
|
|
8427
|
-
sessionPolicy: sessionContext.policy,
|
|
8428
|
-
sessionDecision: sessionContext.decision,
|
|
8429
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8430
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8431
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8432
|
-
};
|
|
9944
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8433
9945
|
if (scanResult) {
|
|
8434
|
-
const
|
|
9946
|
+
const resultMutation = {
|
|
8435
9947
|
status: "completed",
|
|
8436
9948
|
runnerId,
|
|
8437
9949
|
idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8440,7 +9952,21 @@ ${toolResult.stderr}`);
|
|
|
8440
9952
|
durationMs,
|
|
8441
9953
|
...sessionTelemetry,
|
|
8442
9954
|
message: `${toolName} returned a security posture scan.`
|
|
9955
|
+
};
|
|
9956
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
9957
|
+
accountId: workItem.accountId,
|
|
9958
|
+
projectId,
|
|
9959
|
+
repositoryLinkId,
|
|
9960
|
+
runnerId,
|
|
9961
|
+
workItemId: workItem.workItemId,
|
|
9962
|
+
workKind: "securityPostureScan",
|
|
9963
|
+
resultKind: "securityPostureScanResult",
|
|
9964
|
+
attempt: workItem.attempt,
|
|
9965
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
9966
|
+
result: resultMutation
|
|
8443
9967
|
});
|
|
9968
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
9969
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8444
9970
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8445
9971
|
status: "completed",
|
|
8446
9972
|
summary: `${toolName} returned a security posture scan.`,
|
|
@@ -8451,7 +9977,7 @@ ${toolResult.stderr}`);
|
|
|
8451
9977
|
console.log("Security posture scan returned for review.");
|
|
8452
9978
|
return { status: "completed", exitCode: 0 };
|
|
8453
9979
|
}
|
|
8454
|
-
const
|
|
9980
|
+
const failedMutation = {
|
|
8455
9981
|
status: "failed",
|
|
8456
9982
|
runnerId,
|
|
8457
9983
|
idempotencyKey: `security_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8460,7 +9986,21 @@ ${toolResult.stderr}`);
|
|
|
8460
9986
|
...sessionTelemetry,
|
|
8461
9987
|
message: `${toolName} did not produce a valid security posture scan.`,
|
|
8462
9988
|
...scanError ? { error: scanError } : {}
|
|
9989
|
+
};
|
|
9990
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
9991
|
+
accountId: workItem.accountId,
|
|
9992
|
+
projectId,
|
|
9993
|
+
repositoryLinkId,
|
|
9994
|
+
runnerId,
|
|
9995
|
+
workItemId: workItem.workItemId,
|
|
9996
|
+
workKind: "securityPostureScan",
|
|
9997
|
+
resultKind: "securityPostureScanResult",
|
|
9998
|
+
attempt: workItem.attempt,
|
|
9999
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10000
|
+
result: failedMutation
|
|
8463
10001
|
});
|
|
10002
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10003
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8464
10004
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8465
10005
|
status: "failed",
|
|
8466
10006
|
summary: scanError ?? `${toolName} did not produce a valid security posture scan.`,
|
|
@@ -8496,28 +10036,9 @@ ${toolResult.stderr}`);
|
|
|
8496
10036
|
scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8497
10037
|
}
|
|
8498
10038
|
const finalStatus = scanResult ? "completed" : "failed";
|
|
8499
|
-
const
|
|
8500
|
-
apiClient,
|
|
8501
|
-
projectId,
|
|
8502
|
-
status: finalStatus,
|
|
8503
|
-
runnerId,
|
|
8504
|
-
workItemId: workItem.workItemId,
|
|
8505
|
-
stdout: toolResult.stdout,
|
|
8506
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8507
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8508
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8509
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8510
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8511
|
-
});
|
|
8512
|
-
const sessionTelemetry = {
|
|
8513
|
-
sessionPolicy: sessionContext.policy,
|
|
8514
|
-
sessionDecision: sessionContext.decision,
|
|
8515
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8516
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8517
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8518
|
-
};
|
|
10039
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8519
10040
|
if (scanResult) {
|
|
8520
|
-
const
|
|
10041
|
+
const resultMutation = {
|
|
8521
10042
|
status: "completed",
|
|
8522
10043
|
runnerId,
|
|
8523
10044
|
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8526,7 +10047,21 @@ ${toolResult.stderr}`);
|
|
|
8526
10047
|
durationMs,
|
|
8527
10048
|
...sessionTelemetry,
|
|
8528
10049
|
message: `${toolName} returned an app evaluation scan.`
|
|
10050
|
+
};
|
|
10051
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
10052
|
+
accountId: workItem.accountId,
|
|
10053
|
+
projectId,
|
|
10054
|
+
repositoryLinkId,
|
|
10055
|
+
runnerId,
|
|
10056
|
+
workItemId: workItem.workItemId,
|
|
10057
|
+
workKind: "appEvaluationScan",
|
|
10058
|
+
resultKind: "appEvaluationScanResult",
|
|
10059
|
+
attempt: workItem.attempt,
|
|
10060
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
10061
|
+
result: resultMutation
|
|
8529
10062
|
});
|
|
10063
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
10064
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8530
10065
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8531
10066
|
status: "completed",
|
|
8532
10067
|
summary: `${toolName} returned an app evaluation scan.`,
|
|
@@ -8537,7 +10072,7 @@ ${toolResult.stderr}`);
|
|
|
8537
10072
|
console.log("App evaluation scan returned for review.");
|
|
8538
10073
|
return { status: "completed", exitCode: 0 };
|
|
8539
10074
|
}
|
|
8540
|
-
const
|
|
10075
|
+
const failedMutation = {
|
|
8541
10076
|
status: "failed",
|
|
8542
10077
|
runnerId,
|
|
8543
10078
|
idempotencyKey: `app_evaluation_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8546,7 +10081,21 @@ ${toolResult.stderr}`);
|
|
|
8546
10081
|
...sessionTelemetry,
|
|
8547
10082
|
message: `${toolName} did not produce a valid app evaluation scan.`,
|
|
8548
10083
|
...scanError ? { error: scanError } : {}
|
|
10084
|
+
};
|
|
10085
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
10086
|
+
accountId: workItem.accountId,
|
|
10087
|
+
projectId,
|
|
10088
|
+
repositoryLinkId,
|
|
10089
|
+
runnerId,
|
|
10090
|
+
workItemId: workItem.workItemId,
|
|
10091
|
+
workKind: "appEvaluationScan",
|
|
10092
|
+
resultKind: "appEvaluationScanResult",
|
|
10093
|
+
attempt: workItem.attempt,
|
|
10094
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10095
|
+
result: failedMutation
|
|
8549
10096
|
});
|
|
10097
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10098
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8550
10099
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8551
10100
|
status: "failed",
|
|
8552
10101
|
summary: scanError ?? `${toolName} did not produce a valid app evaluation scan.`,
|
|
@@ -8583,28 +10132,9 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
8583
10132
|
refreshError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8584
10133
|
}
|
|
8585
10134
|
const finalStatus = refreshResult ? "completed" : "failed";
|
|
8586
|
-
const
|
|
8587
|
-
apiClient,
|
|
8588
|
-
projectId,
|
|
8589
|
-
status: finalStatus,
|
|
8590
|
-
runnerId,
|
|
8591
|
-
workItemId: workItem.workItemId,
|
|
8592
|
-
stdout: toolResult.stdout,
|
|
8593
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8594
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8595
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8596
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8597
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8598
|
-
});
|
|
8599
|
-
const sessionTelemetry = {
|
|
8600
|
-
sessionPolicy: sessionContext.policy,
|
|
8601
|
-
sessionDecision: sessionContext.decision,
|
|
8602
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8603
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8604
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8605
|
-
};
|
|
10135
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8606
10136
|
if (refreshResult) {
|
|
8607
|
-
const
|
|
10137
|
+
const resultMutation = {
|
|
8608
10138
|
status: "completed",
|
|
8609
10139
|
runnerId,
|
|
8610
10140
|
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8613,7 +10143,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
8613
10143
|
durationMs,
|
|
8614
10144
|
...sessionTelemetry,
|
|
8615
10145
|
message: `${toolName} returned a project context refresh.`
|
|
10146
|
+
};
|
|
10147
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
10148
|
+
accountId: workItem.accountId,
|
|
10149
|
+
projectId,
|
|
10150
|
+
repositoryLinkId,
|
|
10151
|
+
runnerId,
|
|
10152
|
+
workItemId: workItem.workItemId,
|
|
10153
|
+
workKind: "projectContextRefresh",
|
|
10154
|
+
resultKind: "projectContextRefreshResult",
|
|
10155
|
+
attempt: workItem.attempt,
|
|
10156
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
10157
|
+
result: resultMutation
|
|
8616
10158
|
});
|
|
10159
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
10160
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8617
10161
|
const failureSummary = projectContextRefreshSubmissionFailureSummary(result);
|
|
8618
10162
|
if (failureSummary) {
|
|
8619
10163
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
@@ -8636,7 +10180,7 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
8636
10180
|
console.log("Project context refresh returned for review.");
|
|
8637
10181
|
return { status: "completed", exitCode: 0 };
|
|
8638
10182
|
}
|
|
8639
|
-
const
|
|
10183
|
+
const failedMutation = {
|
|
8640
10184
|
status: "failed",
|
|
8641
10185
|
runnerId,
|
|
8642
10186
|
idempotencyKey: `project_context_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8645,7 +10189,21 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
8645
10189
|
...sessionTelemetry,
|
|
8646
10190
|
message: `${toolName} did not produce a valid project context refresh.`,
|
|
8647
10191
|
...refreshError ? { error: refreshError } : {}
|
|
10192
|
+
};
|
|
10193
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
10194
|
+
accountId: workItem.accountId,
|
|
10195
|
+
projectId,
|
|
10196
|
+
repositoryLinkId,
|
|
10197
|
+
runnerId,
|
|
10198
|
+
workItemId: workItem.workItemId,
|
|
10199
|
+
workKind: "projectContextRefresh",
|
|
10200
|
+
resultKind: "projectContextRefreshResult",
|
|
10201
|
+
attempt: workItem.attempt,
|
|
10202
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10203
|
+
result: failedMutation
|
|
8648
10204
|
});
|
|
10205
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10206
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8649
10207
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8650
10208
|
status: "failed",
|
|
8651
10209
|
summary: refreshError ?? `${toolName} did not produce a valid project context refresh.`,
|
|
@@ -8681,28 +10239,9 @@ ${toolResult.stderr}`);
|
|
|
8681
10239
|
verificationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8682
10240
|
}
|
|
8683
10241
|
const finalStatus = verificationResult ? "completed" : "failed";
|
|
8684
|
-
const
|
|
8685
|
-
apiClient,
|
|
8686
|
-
projectId,
|
|
8687
|
-
status: finalStatus,
|
|
8688
|
-
runnerId,
|
|
8689
|
-
workItemId: workItem.workItemId,
|
|
8690
|
-
stdout: toolResult.stdout,
|
|
8691
|
-
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8692
|
-
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8693
|
-
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8694
|
-
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8695
|
-
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8696
|
-
});
|
|
8697
|
-
const sessionTelemetry = {
|
|
8698
|
-
sessionPolicy: sessionContext.policy,
|
|
8699
|
-
sessionDecision: sessionContext.decision,
|
|
8700
|
-
sessionDecisionReason: sessionContext.reason,
|
|
8701
|
-
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8702
|
-
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8703
|
-
};
|
|
10242
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
8704
10243
|
if (verificationResult) {
|
|
8705
|
-
const
|
|
10244
|
+
const resultMutation = {
|
|
8706
10245
|
status: "completed",
|
|
8707
10246
|
runnerId,
|
|
8708
10247
|
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8711,7 +10250,21 @@ ${toolResult.stderr}`);
|
|
|
8711
10250
|
durationMs,
|
|
8712
10251
|
...sessionTelemetry,
|
|
8713
10252
|
message: `${toolName} returned implementation verification proof.`
|
|
10253
|
+
};
|
|
10254
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
10255
|
+
accountId: workItem.accountId,
|
|
10256
|
+
projectId,
|
|
10257
|
+
repositoryLinkId,
|
|
10258
|
+
runnerId,
|
|
10259
|
+
workItemId: workItem.workItemId,
|
|
10260
|
+
workKind: "implementationVerification",
|
|
10261
|
+
resultKind: "implementationVerificationResult",
|
|
10262
|
+
attempt: workItem.attempt,
|
|
10263
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
10264
|
+
result: resultMutation
|
|
8714
10265
|
});
|
|
10266
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
10267
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8715
10268
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8716
10269
|
status: verificationResult.outcome === "verifiedImplemented" ? "completed" : verificationResult.outcome === "verificationBlocked" ? "blocked" : "warning",
|
|
8717
10270
|
summary: verificationResult.summary,
|
|
@@ -8722,7 +10275,7 @@ ${toolResult.stderr}`);
|
|
|
8722
10275
|
console.log("Implementation verification returned for review.");
|
|
8723
10276
|
return { status: "completed", exitCode: 0 };
|
|
8724
10277
|
}
|
|
8725
|
-
const
|
|
10278
|
+
const failedMutation = {
|
|
8726
10279
|
status: "failed",
|
|
8727
10280
|
runnerId,
|
|
8728
10281
|
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
|
|
@@ -8731,7 +10284,21 @@ ${toolResult.stderr}`);
|
|
|
8731
10284
|
...sessionTelemetry,
|
|
8732
10285
|
message: `${toolName} did not produce valid implementation verification proof.`,
|
|
8733
10286
|
...verificationError ? { error: verificationError } : {}
|
|
10287
|
+
};
|
|
10288
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
10289
|
+
accountId: workItem.accountId,
|
|
10290
|
+
projectId,
|
|
10291
|
+
repositoryLinkId,
|
|
10292
|
+
runnerId,
|
|
10293
|
+
workItemId: workItem.workItemId,
|
|
10294
|
+
workKind: "implementationVerification",
|
|
10295
|
+
resultKind: "implementationVerificationResult",
|
|
10296
|
+
attempt: workItem.attempt,
|
|
10297
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10298
|
+
result: failedMutation
|
|
8734
10299
|
});
|
|
10300
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10301
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
8735
10302
|
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8736
10303
|
status: "failed",
|
|
8737
10304
|
summary: verificationError ?? `${toolName} did not produce valid implementation verification proof.`,
|
|
@@ -8742,6 +10309,196 @@ ${toolResult.stderr}`);
|
|
|
8742
10309
|
console.error(verificationError ?? "Local runner implementation verification failed.");
|
|
8743
10310
|
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
8744
10311
|
}
|
|
10312
|
+
async function finalizeTestQualityScanWork({
|
|
10313
|
+
apiClient,
|
|
10314
|
+
durationMs,
|
|
10315
|
+
projectId,
|
|
10316
|
+
repositoryLinkId,
|
|
10317
|
+
runnerId,
|
|
10318
|
+
sessionContext,
|
|
10319
|
+
toolConfig,
|
|
10320
|
+
toolName,
|
|
10321
|
+
toolResult,
|
|
10322
|
+
workItem
|
|
10323
|
+
}) {
|
|
10324
|
+
let scanResult = void 0;
|
|
10325
|
+
let scanError;
|
|
10326
|
+
if (toolResult.exitCode === 0) {
|
|
10327
|
+
try {
|
|
10328
|
+
scanResult = parseTestQualityScanResult(`${toolResult.stdout}
|
|
10329
|
+
${toolResult.stderr}`);
|
|
10330
|
+
} catch (error) {
|
|
10331
|
+
scanError = errorMessage3(error);
|
|
10332
|
+
}
|
|
10333
|
+
} else {
|
|
10334
|
+
scanError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
10335
|
+
}
|
|
10336
|
+
const finalStatus = scanResult ? "completed" : "failed";
|
|
10337
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
10338
|
+
if (scanResult) {
|
|
10339
|
+
const resultMutation = {
|
|
10340
|
+
status: "completed",
|
|
10341
|
+
runnerId,
|
|
10342
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
|
|
10343
|
+
result: scanResult,
|
|
10344
|
+
tool: toolName,
|
|
10345
|
+
durationMs,
|
|
10346
|
+
...sessionTelemetry,
|
|
10347
|
+
message: `${toolName} returned a test quality scan.`
|
|
10348
|
+
};
|
|
10349
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
10350
|
+
accountId: workItem.accountId,
|
|
10351
|
+
projectId,
|
|
10352
|
+
repositoryLinkId,
|
|
10353
|
+
runnerId,
|
|
10354
|
+
workItemId: workItem.workItemId,
|
|
10355
|
+
workKind: "testQualityScan",
|
|
10356
|
+
resultKind: "testQualityScanResult",
|
|
10357
|
+
attempt: workItem.attempt,
|
|
10358
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
10359
|
+
result: resultMutation
|
|
10360
|
+
});
|
|
10361
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
10362
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
10363
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
10364
|
+
status: scanResult.findings.some((finding) => finding.severity === "critical" || finding.severity === "high") ? "warning" : "completed",
|
|
10365
|
+
summary: scanResult.summary,
|
|
10366
|
+
idempotencyKey: `runner_milestone_test_quality_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
|
|
10367
|
+
metadata: { tool: toolName, durationMs, findingCount: scanResult.findings.length, commandCount: scanResult.commandSummaries.length, verificationSummary: scanResult.verificationPlan.join(" | ") }
|
|
10368
|
+
});
|
|
10369
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
10370
|
+
console.log("Test quality scan returned for review.");
|
|
10371
|
+
return { status: "completed", exitCode: 0 };
|
|
10372
|
+
}
|
|
10373
|
+
const failedMutation = {
|
|
10374
|
+
status: "failed",
|
|
10375
|
+
runnerId,
|
|
10376
|
+
idempotencyKey: `test_quality_${workItem.workItemId}_${randomUUID()}`,
|
|
10377
|
+
tool: toolName,
|
|
10378
|
+
durationMs,
|
|
10379
|
+
...sessionTelemetry,
|
|
10380
|
+
message: `${toolName} did not produce a valid test quality scan.`,
|
|
10381
|
+
...scanError ? { error: scanError } : {}
|
|
10382
|
+
};
|
|
10383
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
10384
|
+
accountId: workItem.accountId,
|
|
10385
|
+
projectId,
|
|
10386
|
+
repositoryLinkId,
|
|
10387
|
+
runnerId,
|
|
10388
|
+
workItemId: workItem.workItemId,
|
|
10389
|
+
workKind: "testQualityScan",
|
|
10390
|
+
resultKind: "testQualityScanResult",
|
|
10391
|
+
attempt: workItem.attempt,
|
|
10392
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10393
|
+
result: failedMutation
|
|
10394
|
+
});
|
|
10395
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10396
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
10397
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
10398
|
+
status: "failed",
|
|
10399
|
+
summary: scanError ?? `${toolName} did not produce a valid test quality scan.`,
|
|
10400
|
+
idempotencyKey: `runner_milestone_test_quality_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
|
|
10401
|
+
metadata: { tool: toolName, durationMs, verificationSummary: "Test quality output did not include valid structured JSON." }
|
|
10402
|
+
});
|
|
10403
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
10404
|
+
console.error(scanError ?? "Local runner test quality scan failed.");
|
|
10405
|
+
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
10406
|
+
}
|
|
10407
|
+
async function finalizeImplementationTestGateWork({
|
|
10408
|
+
apiClient,
|
|
10409
|
+
durationMs,
|
|
10410
|
+
projectId,
|
|
10411
|
+
repositoryLinkId,
|
|
10412
|
+
runnerId,
|
|
10413
|
+
sessionContext,
|
|
10414
|
+
toolConfig,
|
|
10415
|
+
toolName,
|
|
10416
|
+
toolResult,
|
|
10417
|
+
workItem
|
|
10418
|
+
}) {
|
|
10419
|
+
let gateResult = void 0;
|
|
10420
|
+
let gateError;
|
|
10421
|
+
if (toolResult.exitCode === 0) {
|
|
10422
|
+
try {
|
|
10423
|
+
gateResult = parseImplementationTestGateResult(`${toolResult.stdout}
|
|
10424
|
+
${toolResult.stderr}`);
|
|
10425
|
+
} catch (error) {
|
|
10426
|
+
gateError = errorMessage3(error);
|
|
10427
|
+
}
|
|
10428
|
+
} else {
|
|
10429
|
+
gateError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
10430
|
+
}
|
|
10431
|
+
const finalStatus = gateResult ? "completed" : "failed";
|
|
10432
|
+
const sessionTelemetry = pendingSessionTelemetry(sessionContext);
|
|
10433
|
+
if (gateResult) {
|
|
10434
|
+
const resultMutation = {
|
|
10435
|
+
status: "completed",
|
|
10436
|
+
runnerId,
|
|
10437
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
|
|
10438
|
+
result: gateResult,
|
|
10439
|
+
tool: toolName,
|
|
10440
|
+
durationMs,
|
|
10441
|
+
...sessionTelemetry,
|
|
10442
|
+
message: `${toolName} returned an implementation test gate result.`
|
|
10443
|
+
};
|
|
10444
|
+
const result = await submitPrimaryRunnerResult(apiClient, {
|
|
10445
|
+
accountId: workItem.accountId,
|
|
10446
|
+
projectId,
|
|
10447
|
+
repositoryLinkId,
|
|
10448
|
+
runnerId,
|
|
10449
|
+
workItemId: workItem.workItemId,
|
|
10450
|
+
workKind: "implementationTestGate",
|
|
10451
|
+
resultKind: "implementationTestGateResult",
|
|
10452
|
+
attempt: workItem.attempt,
|
|
10453
|
+
idempotencyKey: resultMutation.idempotencyKey,
|
|
10454
|
+
result: resultMutation
|
|
10455
|
+
});
|
|
10456
|
+
if (!result) return { status: "failed", exitCode: 1 };
|
|
10457
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
10458
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
10459
|
+
status: gateResult.outcome === "passed" ? "completed" : gateResult.outcome === "blocked" ? "blocked" : "failed",
|
|
10460
|
+
summary: gateResult.summary,
|
|
10461
|
+
idempotencyKey: `runner_milestone_implementation_test_gate_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
|
|
10462
|
+
metadata: { tool: toolName, durationMs, outcome: gateResult.outcome, findingCount: gateResult.findings.length, commandCount: gateResult.commandSummaries.length, verificationSummary: gateResult.verificationPlan.join(" | ") }
|
|
10463
|
+
});
|
|
10464
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
10465
|
+
console.log("Implementation test gate returned for review.");
|
|
10466
|
+
return { status: gateResult.outcome === "passed" ? "completed" : "failed", exitCode: gateResult.outcome === "passed" ? 0 : 1 };
|
|
10467
|
+
}
|
|
10468
|
+
const failedMutation = {
|
|
10469
|
+
status: "failed",
|
|
10470
|
+
runnerId,
|
|
10471
|
+
idempotencyKey: `implementation_test_gate_${workItem.workItemId}_${randomUUID()}`,
|
|
10472
|
+
tool: toolName,
|
|
10473
|
+
durationMs,
|
|
10474
|
+
...sessionTelemetry,
|
|
10475
|
+
message: `${toolName} did not produce a valid implementation test gate result.`,
|
|
10476
|
+
...gateError ? { error: gateError } : {}
|
|
10477
|
+
};
|
|
10478
|
+
const failedResult = await submitPrimaryRunnerResult(apiClient, {
|
|
10479
|
+
accountId: workItem.accountId,
|
|
10480
|
+
projectId,
|
|
10481
|
+
repositoryLinkId,
|
|
10482
|
+
runnerId,
|
|
10483
|
+
workItemId: workItem.workItemId,
|
|
10484
|
+
workKind: "implementationTestGate",
|
|
10485
|
+
resultKind: "implementationTestGateResult",
|
|
10486
|
+
attempt: workItem.attempt,
|
|
10487
|
+
idempotencyKey: failedMutation.idempotencyKey,
|
|
10488
|
+
result: failedMutation
|
|
10489
|
+
});
|
|
10490
|
+
if (!failedResult) return { status: "failed", exitCode: 1 };
|
|
10491
|
+
await finalizeToolSessionBestEffort({ apiClient, projectId, runnerId, sessionContext, status: finalStatus, toolResult, workItemId: workItem.workItemId });
|
|
10492
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
10493
|
+
status: "failed",
|
|
10494
|
+
summary: gateError ?? `${toolName} did not produce a valid implementation test gate result.`,
|
|
10495
|
+
idempotencyKey: `runner_milestone_implementation_test_gate_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
|
|
10496
|
+
metadata: { tool: toolName, durationMs, verificationSummary: "Implementation test gate output did not include valid structured JSON." }
|
|
10497
|
+
});
|
|
10498
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
10499
|
+
console.error(gateError ?? "Local runner implementation test gate failed.");
|
|
10500
|
+
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
10501
|
+
}
|
|
8745
10502
|
async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
8746
10503
|
if (workItem.workKind === "assistantQuestion") {
|
|
8747
10504
|
const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
|
|
@@ -8798,6 +10555,15 @@ async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
|
8798
10555
|
appEvaluationScan: { documents: documents2 }
|
|
8799
10556
|
});
|
|
8800
10557
|
}
|
|
10558
|
+
if (workItem.workKind === "testQualityScan") {
|
|
10559
|
+
const { documents: documents2 } = await apiClient.listBrainDocuments(projectId);
|
|
10560
|
+
return createWorkExecutionPrompt(workItem, {
|
|
10561
|
+
testQualityScan: { documents: documents2 }
|
|
10562
|
+
});
|
|
10563
|
+
}
|
|
10564
|
+
if (workItem.workKind === "implementationTestGate") {
|
|
10565
|
+
return createWorkExecutionPrompt(workItem);
|
|
10566
|
+
}
|
|
8801
10567
|
if (workItem.workKind === "projectContextRefresh") {
|
|
8802
10568
|
const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
|
|
8803
10569
|
const [{ documents: documents2 }, context] = await Promise.all([
|
|
@@ -9285,15 +11051,17 @@ function toRunnerToolCapabilities(tools) {
|
|
|
9285
11051
|
supportsGitWorktreeIsolation: true
|
|
9286
11052
|
}));
|
|
9287
11053
|
}
|
|
9288
|
-
function runnerIsolationCapabilityMetadata() {
|
|
11054
|
+
function runnerIsolationCapabilityMetadata(laneMetadata = {}) {
|
|
9289
11055
|
return {
|
|
9290
11056
|
machineId: runnerMachineId(),
|
|
9291
11057
|
supportedWorkKinds: runnerSupportedWorkKinds,
|
|
9292
11058
|
supportsBranchIsolation: true,
|
|
9293
|
-
supportsGitWorktreeIsolation: true
|
|
11059
|
+
supportsGitWorktreeIsolation: true,
|
|
11060
|
+
...laneMetadata.claimLaneId ? { claimLaneId: laneMetadata.claimLaneId } : {},
|
|
11061
|
+
...laneMetadata.maxConcurrentWork !== void 0 ? { maxConcurrentWork: laneMetadata.maxConcurrentWork } : {}
|
|
9294
11062
|
};
|
|
9295
11063
|
}
|
|
9296
|
-
function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
|
|
11064
|
+
function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode(), concurrencyMetadata = { maxConcurrentWork: 1, activeClaimLaneIds: ["default"] }) {
|
|
9297
11065
|
const modelConfig = toolConfig ? toolConfigModelOptions(toolConfig) : {};
|
|
9298
11066
|
const effectiveModelConfig = toolConfig?.ready ? modelConfig : {};
|
|
9299
11067
|
return {
|
|
@@ -9301,6 +11069,8 @@ function runnerHeartbeatMetadata(toolConfig, mode = currentRunnerMode()) {
|
|
|
9301
11069
|
mode,
|
|
9302
11070
|
hostname: os8.hostname(),
|
|
9303
11071
|
...runnerIsolationCapabilityMetadata(),
|
|
11072
|
+
maxConcurrentWork: concurrencyMetadata.maxConcurrentWork,
|
|
11073
|
+
activeClaimLaneIds: concurrencyMetadata.activeClaimLaneIds,
|
|
9304
11074
|
resourceUsage: sampleCurrentRunnerResourceUsage(),
|
|
9305
11075
|
...toolConfig?.capabilities ? { capabilities: toolConfig.capabilities } : {},
|
|
9306
11076
|
...toolConfig?.requestedTool ? { requestedTool: toolConfig.requestedTool } : {},
|