@amistio/cli 0.1.17 → 0.1.19
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 +6 -2
- package/dist/index.js +344 -9
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Runner lifecycle controls in the web app, such as update, restart, and remove, a
|
|
|
15
15
|
|
|
16
16
|
Runner Update installs the official `@amistio/cli` package and then refreshes the runner runtime. Background runners attempt a replacement restart so the next heartbeat reports the new CLI version. If replacement restart metadata is missing or restart fails after a successful install, the old runner still stops and reports manual restart guidance instead of continuing to heartbeat the stale runtime. Foreground `amistio run --watch` sessions stop after a successful install with restart guidance; start the command again to load the updated package.
|
|
17
17
|
|
|
18
|
-
Current runners advertise the work kinds they can claim. Older runners that do not send this capability can continue legacy brain generation, implementation, and plan revision work, but they will skip source-aware assistant, impact-preview, project-context refresh, issue-diagnosis, app-evaluation,
|
|
18
|
+
Current runners advertise the work kinds they can claim. Older runners that do not send this capability can continue legacy brain generation, implementation, and plan revision work, but they will skip source-aware assistant, impact-preview, project-context refresh, issue-diagnosis, app-evaluation, security-posture, and implementation-verification work until updated.
|
|
19
19
|
|
|
20
20
|
Repository brain auto-sync is disabled until the repository link option is enabled in the app. After pairing, run `amistio sync watch` from the paired checkout to push recognized external brain Markdown/MDX files and HTML companions under `docs/html/<area>/`, including local ADRs, plans, prompts, workflows, memory, context, architecture, and feature docs, to the app for review. `amistio run --watch` also runs the same cycle between work polls when the option is enabled. The CLI skips templates, unsupported paths, oversized files, unchanged managed docs, and conflicts instead of silently overwriting web state.
|
|
21
21
|
|
|
@@ -43,14 +43,18 @@ When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until
|
|
|
43
43
|
|
|
44
44
|
`amistio runner status` reports local background runner state, latest heartbeat, and bounded resource usage when available. Resource usage is latest-sample runner process memory/CPU plus safe aggregate system memory/load signals; it does not include source files, environment variables, command lines, process lists, credentials, or arbitrary local paths.
|
|
45
45
|
|
|
46
|
-
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only; source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
|
|
46
|
+
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only. Current runners can claim read-only `implementationVerification` jobs from Tasks to prove whether completed implementation work actually landed; verification submits bounded acceptance-criteria evidence, checks, gaps, outcome, and recommendation without mutating source. Source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, or security remediation plan in the app.
|
|
47
47
|
|
|
48
48
|
Approved implementation work uses Git as the handoff boundary. After the local tool completes successfully, the runner commits the isolated worktree, pushes an `amistio/work/...` branch to the linked GitHub remote, opens or reuses a pull request with the locally authenticated `gh` CLI, reports only safe PR metadata to Amistio, and removes the local worktree after the PR URL is durable. Prepare the runner machine with Git commit identity, push permission to the linked remote, and `gh auth status`. If commit, push, or PR creation fails, the work item is blocked and the branch/worktree stay on disk for manual recovery; source files and patches are not uploaded to Amistio.
|
|
49
49
|
|
|
50
|
+
Failed or stale work can be requeued from the web Tasks panel. Requeue creates a new linked work attempt and preserves the original terminal attempt for audit history; it is blocked while equivalent work is already active or when the paired runner does not advertise the needed work kind. Completed implementation status is separate from proof: queue `implementationVerification` from Tasks when a plan needs source-aware evidence before cleanup or implementation status decisions.
|
|
51
|
+
|
|
50
52
|
Runner setup and local-tool execution use bounded failure controls. `amistio run --watch` retries Git worktree preflight failures by releasing the claim for another attempt, then fails the work item after `--max-preflight-attempts` attempts, defaulting to 3. Active local-tool runs renew the work lease, and `--tool-timeout-seconds` caps tool execution, defaulting to 1800 seconds.
|
|
51
53
|
|
|
52
54
|
Watch mode prints a completed-work success once per work item, keeps fresh completion visible briefly, and returns old completed work to the ready state when no queued, running, blocked, failed, review, or runner-readiness action needs attention.
|
|
53
55
|
|
|
56
|
+
Known validation failures such as `unsafe_context_path` are printed with attention-needed next steps. For project-context refresh path-safety failures, deploy the latest web/API fix, update and restart the runner when applicable, retry the refresh, and capture only bounded non-secret output if it repeats.
|
|
57
|
+
|
|
54
58
|
For headless startup after login on supported user-level service managers:
|
|
55
59
|
|
|
56
60
|
```sh
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ var itemTypeSchema = z.enum([
|
|
|
26
26
|
"securityPostureSnapshot",
|
|
27
27
|
"appEvaluationScan",
|
|
28
28
|
"appEvaluationFinding",
|
|
29
|
+
"implementationVerification",
|
|
29
30
|
"projectContextMap",
|
|
30
31
|
"projectContextRefresh",
|
|
31
32
|
"projectSystemDiagram",
|
|
@@ -78,9 +79,12 @@ var workStatusSchema = z.enum([
|
|
|
78
79
|
]);
|
|
79
80
|
var sourceSchema = z.enum(["web", "repo", "generated", "runner"]);
|
|
80
81
|
var executionModeSchema = z.enum(["localRunner", "cloudSandbox"]);
|
|
81
|
-
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh"]);
|
|
82
|
+
var workKindSchema = z.enum(["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"]);
|
|
82
83
|
var implementationHandoffStatusSchema = z.enum(["notStarted", "noChanges", "prReady", "blocked", "failed"]);
|
|
83
84
|
var implementationHandoffCleanupStatusSchema = z.enum(["notApplicable", "pending", "completed", "failed"]);
|
|
85
|
+
var implementationVerificationOutcomeSchema = z.enum(["verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked"]);
|
|
86
|
+
var implementationVerificationStatusSchema = z.enum(["queued", "running", "completed", "failed", "blocked", "stale"]);
|
|
87
|
+
var implementationProofStatusSchema = z.enum(["unverified", "verificationQueued", "verificationRunning", "verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked", "humanOverride"]);
|
|
84
88
|
var implementationHandoffSchema = z.object({
|
|
85
89
|
provider: z.string().trim().min(1).max(80).optional(),
|
|
86
90
|
status: implementationHandoffStatusSchema,
|
|
@@ -105,7 +109,7 @@ var issueStatusSchema = z.enum(["queued", "diagnosing", "analysisReady", "approv
|
|
|
105
109
|
var issueApprovalStateSchema = z.enum(["proposed", "approved", "changesRequested", "rejected", "implemented", "blocked"]);
|
|
106
110
|
var securityScanStatusSchema = z.enum(["queued", "running", "completed", "failed", "stale", "skipped"]);
|
|
107
111
|
var securityScanSourceSchema = z.enum(["nightly", "manual", "runner"]);
|
|
108
|
-
var securityFindingCategorySchema = z.enum(["owasp", "dependency", "supplyChain", "secretHygiene", "authentication", "authorization", "tenantIsolation", "headers", "redirects", "csrf", "cors", "ciCd", "logging", "runnerBoundary", "other"]);
|
|
112
|
+
var securityFindingCategorySchema = z.enum(["owasp", "dependency", "supplyChain", "secretHygiene", "authentication", "authorization", "tenantIsolation", "headers", "redirects", "csrf", "cors", "ciCd", "logging", "rateLimiting", "runnerBoundary", "other"]);
|
|
109
113
|
var securityFindingSeveritySchema = z.enum(["info", "low", "medium", "high", "critical"]);
|
|
110
114
|
var securityFindingConfidenceSchema = z.enum(["low", "medium", "high"]);
|
|
111
115
|
var securityFindingStatusSchema = z.enum(["open", "planReady", "approved", "changesRequested", "dismissed", "acceptedRisk", "remediationQueued", "resolved", "failed"]);
|
|
@@ -156,6 +160,14 @@ var activityEventTypeSchema = z.enum([
|
|
|
156
160
|
"appEvaluationFindingReviewed",
|
|
157
161
|
"appEvaluationPlanCreated",
|
|
158
162
|
"appEvaluationImplementationQueued",
|
|
163
|
+
"workRequeueRequested",
|
|
164
|
+
"workRequeueQueued",
|
|
165
|
+
"workRequeueBlocked",
|
|
166
|
+
"implementationVerificationQueued",
|
|
167
|
+
"implementationVerificationStarted",
|
|
168
|
+
"implementationVerificationCompleted",
|
|
169
|
+
"implementationVerificationBlocked",
|
|
170
|
+
"implementationVerificationOverride",
|
|
159
171
|
"projectContextRefreshQueued",
|
|
160
172
|
"projectContextRefreshStarted",
|
|
161
173
|
"projectContextRefreshCompleted",
|
|
@@ -423,8 +435,20 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
423
435
|
securityFindingId: z.string().min(1).optional(),
|
|
424
436
|
appEvaluationScanId: z.string().min(1).optional(),
|
|
425
437
|
appEvaluationFindingId: z.string().min(1).optional(),
|
|
438
|
+
implementationVerificationId: z.string().min(1).optional(),
|
|
426
439
|
projectContextRefreshId: z.string().min(1).optional(),
|
|
427
440
|
contextMissId: z.string().min(1).optional(),
|
|
441
|
+
sourceWorkItemId: z.string().min(1).optional(),
|
|
442
|
+
requeueReason: z.string().trim().min(1).max(600).optional(),
|
|
443
|
+
requeuedByUserId: z.string().min(1).optional(),
|
|
444
|
+
requeuedAt: isoDateTimeSchema.optional(),
|
|
445
|
+
sourceAttempt: z.number().int().nonnegative().optional(),
|
|
446
|
+
sourceTerminalStatus: workStatusSchema.optional(),
|
|
447
|
+
sourceFailureSummary: z.string().trim().min(1).max(1e3).optional(),
|
|
448
|
+
implementationProofStatus: implementationProofStatusSchema.optional(),
|
|
449
|
+
implementationVerificationOutcome: implementationVerificationOutcomeSchema.optional(),
|
|
450
|
+
implementationVerificationResultId: z.string().min(1).optional(),
|
|
451
|
+
humanImplementationOverrideReason: z.string().trim().min(1).max(1e3).optional(),
|
|
428
452
|
repositoryLinkId: z.string().min(1).optional(),
|
|
429
453
|
reviewThreadId: z.string().min(1).optional(),
|
|
430
454
|
reviewDocumentId: z.string().min(1).optional(),
|
|
@@ -965,6 +989,59 @@ var projectContextPackSchema = z.object({
|
|
|
965
989
|
tokenEstimate: z.number().int().nonnegative().default(0),
|
|
966
990
|
generatedAt: isoDateTimeSchema
|
|
967
991
|
});
|
|
992
|
+
var implementationVerificationCheckStatusSchema = z.enum(["passed", "failed", "blocked", "skipped"]);
|
|
993
|
+
var implementationVerificationRecommendationSchema = z.enum(["none", "requeue", "createFollowUpPlan", "requestHumanReview", "markBlocked"]);
|
|
994
|
+
var implementationVerificationEvidenceSchema = z.object({
|
|
995
|
+
acceptanceCriterion: z.string().trim().min(1).max(500),
|
|
996
|
+
status: z.enum(["satisfied", "partial", "missing", "unknown"]),
|
|
997
|
+
summary: z.string().trim().min(1).max(1200),
|
|
998
|
+
citations: z.array(projectContextCitationSchema).default([])
|
|
999
|
+
});
|
|
1000
|
+
var implementationVerificationCheckSchema = z.object({
|
|
1001
|
+
name: z.string().trim().min(1).max(200),
|
|
1002
|
+
status: implementationVerificationCheckStatusSchema,
|
|
1003
|
+
summary: z.string().trim().min(1).max(1200),
|
|
1004
|
+
safePaths: z.array(projectContextRepoPathSchema).default([])
|
|
1005
|
+
});
|
|
1006
|
+
var implementationVerificationResultSchema = z.object({
|
|
1007
|
+
outcome: implementationVerificationOutcomeSchema,
|
|
1008
|
+
summary: z.string().trim().min(1).max(2e3),
|
|
1009
|
+
evidence: z.array(implementationVerificationEvidenceSchema).min(1),
|
|
1010
|
+
checks: z.array(implementationVerificationCheckSchema).default([]),
|
|
1011
|
+
gaps: z.array(z.string().trim().min(1).max(600)).default([]),
|
|
1012
|
+
branch: z.string().trim().min(1).max(200).optional(),
|
|
1013
|
+
pullRequestUrl: z.string().trim().url().max(500).optional(),
|
|
1014
|
+
worktreeKey: z.string().trim().min(1).max(300).optional(),
|
|
1015
|
+
recommendation: implementationVerificationRecommendationSchema.default("none"),
|
|
1016
|
+
verificationPlan: z.array(z.string().trim().min(1).max(300)).default([]),
|
|
1017
|
+
warnings: z.array(z.string().trim().min(1).max(600)).default([])
|
|
1018
|
+
});
|
|
1019
|
+
var implementationVerificationItemSchema = baseItemSchema.extend({
|
|
1020
|
+
type: z.literal("implementationVerification"),
|
|
1021
|
+
projectId: z.string().min(1),
|
|
1022
|
+
implementationVerificationId: z.string().min(1),
|
|
1023
|
+
status: implementationVerificationStatusSchema,
|
|
1024
|
+
source: z.enum(["manual", "runner", "appEvaluation", "planCleanup", "system"]).default("manual"),
|
|
1025
|
+
planDocumentId: z.string().min(1).optional(),
|
|
1026
|
+
planDocumentRevision: z.number().int().nonnegative().optional(),
|
|
1027
|
+
sourceWorkItemId: z.string().min(1).optional(),
|
|
1028
|
+
verificationWorkItemId: z.string().min(1).optional(),
|
|
1029
|
+
repositoryLinkId: z.string().min(1).optional(),
|
|
1030
|
+
runnerId: z.string().min(1).optional(),
|
|
1031
|
+
sourceRevision: z.string().trim().min(1).max(160).optional(),
|
|
1032
|
+
brainRevision: z.number().int().nonnegative().optional(),
|
|
1033
|
+
contextPackId: z.string().min(1).optional(),
|
|
1034
|
+
outcome: implementationVerificationOutcomeSchema.optional(),
|
|
1035
|
+
result: implementationVerificationResultSchema.optional(),
|
|
1036
|
+
summary: z.string().trim().min(1).max(2e3).optional(),
|
|
1037
|
+
startedAt: isoDateTimeSchema.optional(),
|
|
1038
|
+
completedAt: isoDateTimeSchema.optional(),
|
|
1039
|
+
failedAt: isoDateTimeSchema.optional(),
|
|
1040
|
+
overriddenByUserId: z.string().min(1).optional(),
|
|
1041
|
+
overrideReason: z.string().trim().min(1).max(1e3).optional(),
|
|
1042
|
+
overriddenAt: isoDateTimeSchema.optional(),
|
|
1043
|
+
error: z.string().max(1e3).optional()
|
|
1044
|
+
});
|
|
968
1045
|
var contextMissItemSchema = baseItemSchema.extend({
|
|
969
1046
|
type: z.literal("contextMiss"),
|
|
970
1047
|
projectId: z.string().min(1),
|
|
@@ -1275,6 +1352,7 @@ var activityEventItemSchema = baseItemSchema.extend({
|
|
|
1275
1352
|
relatedSecurityFindingId: z.string().min(1).optional(),
|
|
1276
1353
|
relatedAppEvaluationScanId: z.string().min(1).optional(),
|
|
1277
1354
|
relatedAppEvaluationFindingId: z.string().min(1).optional(),
|
|
1355
|
+
relatedImplementationVerificationId: z.string().min(1).optional(),
|
|
1278
1356
|
relatedProjectContextRefreshId: z.string().min(1).optional(),
|
|
1279
1357
|
relatedProjectSystemDiagramId: z.string().min(1).optional(),
|
|
1280
1358
|
relatedContextMissId: z.string().min(1).optional(),
|
|
@@ -1340,6 +1418,7 @@ var projectItemUnionSchema = z.discriminatedUnion("type", [
|
|
|
1340
1418
|
securityPostureSnapshotItemSchema,
|
|
1341
1419
|
appEvaluationScanItemSchema,
|
|
1342
1420
|
appEvaluationFindingItemSchema,
|
|
1421
|
+
implementationVerificationItemSchema,
|
|
1343
1422
|
projectContextMapItemSchema,
|
|
1344
1423
|
projectContextRefreshItemSchema,
|
|
1345
1424
|
projectSystemDiagramItemSchema,
|
|
@@ -1583,6 +1662,73 @@ function decideSyncState(state) {
|
|
|
1583
1662
|
}
|
|
1584
1663
|
|
|
1585
1664
|
// ../shared/src/next-action.ts
|
|
1665
|
+
var workFailureGuidanceCodes = ["unsafe_context_path", "unsafe_context_result", "invalid_context_result", "context_map_too_large", "context_map_store_failed"];
|
|
1666
|
+
var projectContextFailureGuidance = {
|
|
1667
|
+
unsafe_context_path: {
|
|
1668
|
+
code: "unsafe_context_path",
|
|
1669
|
+
title: "Attention needed",
|
|
1670
|
+
message: "Project context refresh was rejected because a returned path looked unsafe.",
|
|
1671
|
+
steps: [
|
|
1672
|
+
"Deploy the latest web/API fix and update the local runner if this started after a path-safety release.",
|
|
1673
|
+
"Retry the project context refresh.",
|
|
1674
|
+
"If it fails again, capture the failing repo-relative path or bounded runner output without sharing secrets."
|
|
1675
|
+
]
|
|
1676
|
+
},
|
|
1677
|
+
unsafe_context_result: {
|
|
1678
|
+
code: "unsafe_context_result",
|
|
1679
|
+
title: "Attention needed",
|
|
1680
|
+
message: "Project context refresh was rejected because the result looked like it contained unsafe evidence.",
|
|
1681
|
+
steps: [
|
|
1682
|
+
"Review the local runner output for accidental secret-looking evidence without pasting secrets into Amistio.",
|
|
1683
|
+
"Retry the project context refresh after removing or narrowing unsafe evidence.",
|
|
1684
|
+
"If it repeats, capture a bounded redacted summary of the runner output."
|
|
1685
|
+
]
|
|
1686
|
+
},
|
|
1687
|
+
invalid_context_result: {
|
|
1688
|
+
code: "invalid_context_result",
|
|
1689
|
+
title: "Attention needed",
|
|
1690
|
+
message: "Project context refresh returned structured data the server could not accept.",
|
|
1691
|
+
steps: [
|
|
1692
|
+
"Update and restart the local runner so it uses the latest context refresh schema.",
|
|
1693
|
+
"Retry the project context refresh.",
|
|
1694
|
+
"If it fails again, capture the bounded marker-delimited result without source dumps or secrets."
|
|
1695
|
+
]
|
|
1696
|
+
},
|
|
1697
|
+
context_map_too_large: {
|
|
1698
|
+
code: "context_map_too_large",
|
|
1699
|
+
title: "Attention needed",
|
|
1700
|
+
message: "Project context refresh produced a map too large to store safely.",
|
|
1701
|
+
steps: [
|
|
1702
|
+
"Retry the refresh after narrowing the context scope or waiting for smaller incremental refresh support.",
|
|
1703
|
+
"Keep raw source and large dumps out of the context result.",
|
|
1704
|
+
"If this repeats on normal output, split the context-map storage model before retrying large repositories."
|
|
1705
|
+
]
|
|
1706
|
+
},
|
|
1707
|
+
context_map_store_failed: {
|
|
1708
|
+
code: "context_map_store_failed",
|
|
1709
|
+
title: "Attention needed",
|
|
1710
|
+
message: "Project context refresh could not be stored safely after validation.",
|
|
1711
|
+
steps: [
|
|
1712
|
+
"Retry the project context refresh once.",
|
|
1713
|
+
"If it repeats, check the web/API storage logs for the safe rejection reason.",
|
|
1714
|
+
"Keep the runner output bounded and avoid sharing raw source or secrets."
|
|
1715
|
+
]
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
function getWorkFailureGuidance(input) {
|
|
1719
|
+
const normalizedMessage = (input.message ?? "").toLowerCase();
|
|
1720
|
+
const code = workFailureGuidanceCodes.find((candidate) => normalizedMessage.includes(candidate));
|
|
1721
|
+
if (!code) {
|
|
1722
|
+
return void 0;
|
|
1723
|
+
}
|
|
1724
|
+
if (input.workKind && input.workKind !== "projectContextRefresh" && !normalizedMessage.includes("project context")) {
|
|
1725
|
+
return void 0;
|
|
1726
|
+
}
|
|
1727
|
+
return projectContextFailureGuidance[code];
|
|
1728
|
+
}
|
|
1729
|
+
function formatWorkFailureGuidanceDetail(guidance) {
|
|
1730
|
+
return `Next steps: ${guidance.steps.join(" ")}`;
|
|
1731
|
+
}
|
|
1586
1732
|
var nextActionRunnerHeartbeatFreshMs = 15 * 60 * 1e3;
|
|
1587
1733
|
var nextActionCompletedWorkFreshMs = 60 * 60 * 1e3;
|
|
1588
1734
|
function computeProjectNextAction(input) {
|
|
@@ -1647,11 +1793,11 @@ function computeProjectNextAction(input) {
|
|
|
1647
1793
|
}
|
|
1648
1794
|
const failedBrainGeneration = latestWorkItem(input.workItems.filter((item) => item.workKind === "brainGeneration" && item.status === "failed"));
|
|
1649
1795
|
if (failedBrainGeneration) {
|
|
1650
|
-
return
|
|
1796
|
+
return failedWorkAction(failedBrainGeneration, "brainGenerationFailed", "Brain generation failed", failedBrainGeneration.lastStatusMessage ?? "Retry generation after checking the runner output.");
|
|
1651
1797
|
}
|
|
1652
1798
|
const failedPlanRevision = latestWorkItem(input.workItems.filter((item) => item.workKind === "planRevision" && item.status === "failed"));
|
|
1653
1799
|
if (failedPlanRevision) {
|
|
1654
|
-
return
|
|
1800
|
+
return failedWorkAction(failedPlanRevision, "planRevisionFailed", "Plan revision failed", failedPlanRevision.lastStatusMessage ?? "Review the conversation and request another revision if needed.");
|
|
1655
1801
|
}
|
|
1656
1802
|
const blockedWork = latestWorkItem(input.workItems.filter((item) => item.status === "blocked" || item.status === "changesRequested"));
|
|
1657
1803
|
if (blockedWork) {
|
|
@@ -1659,7 +1805,7 @@ function computeProjectNextAction(input) {
|
|
|
1659
1805
|
}
|
|
1660
1806
|
const failedWork = latestWorkItem(input.workItems.filter((item) => item.status === "failed"));
|
|
1661
1807
|
if (failedWork) {
|
|
1662
|
-
return
|
|
1808
|
+
return failedWorkAction(failedWork, "workFailed", "Work failed", failedWork.lastStatusMessage ?? "Review the failure and retry or update the plan.");
|
|
1663
1809
|
}
|
|
1664
1810
|
const queuedBrainGeneration = latestWorkItem(input.workItems.filter((item) => item.workKind === "brainGeneration" && item.status === "approved"));
|
|
1665
1811
|
const queuedPlanRevision = latestWorkItem(input.workItems.filter((item) => item.workKind === "planRevision" && item.status === "approved"));
|
|
@@ -1698,7 +1844,7 @@ function computeProjectNextAction(input) {
|
|
|
1698
1844
|
};
|
|
1699
1845
|
}
|
|
1700
1846
|
function formatProjectNextAction(action) {
|
|
1701
|
-
return `${action.title}: ${action.message}`;
|
|
1847
|
+
return `${action.title}: ${action.message}${action.detail ? ` ${action.detail}` : ""}`;
|
|
1702
1848
|
}
|
|
1703
1849
|
function runnerWaitAction(readiness, fallbackTitle) {
|
|
1704
1850
|
const repositoryName = readiness.repositoryLink?.repoName ?? "the paired repository";
|
|
@@ -1761,13 +1907,21 @@ function isGeneratedDocument(document) {
|
|
|
1761
1907
|
const generatedDraftId = document.frontmatter.generatedDraftId;
|
|
1762
1908
|
return typeof generatedDraftId === "string" && generatedDraftId.length > 0;
|
|
1763
1909
|
}
|
|
1764
|
-
function
|
|
1910
|
+
function failedWorkAction(workItem, kind, fallbackTitle, fallbackMessage) {
|
|
1911
|
+
const guidance = getWorkFailureGuidance({ message: workItem.lastStatusMessage, workKind: workItem.workKind });
|
|
1912
|
+
if (guidance) {
|
|
1913
|
+
return workAction(workItem, kind, "user", "danger", guidance.title, guidance.message, formatWorkFailureGuidanceDetail(guidance));
|
|
1914
|
+
}
|
|
1915
|
+
return workAction(workItem, kind, "user", "danger", fallbackTitle, fallbackMessage);
|
|
1916
|
+
}
|
|
1917
|
+
function workAction(workItem, kind, actor, tone, title, message, detail) {
|
|
1765
1918
|
return {
|
|
1766
1919
|
kind,
|
|
1767
1920
|
actor,
|
|
1768
1921
|
tone,
|
|
1769
1922
|
title,
|
|
1770
1923
|
message,
|
|
1924
|
+
...detail ? { detail } : {},
|
|
1771
1925
|
workItemId: workItem.workItemId,
|
|
1772
1926
|
...workItem.claimedByRunnerId ? { runnerId: workItem.claimedByRunnerId } : {},
|
|
1773
1927
|
updatedAt: workItem.lastStatusAt
|
|
@@ -2339,6 +2493,16 @@ var ApiClient = class {
|
|
|
2339
2493
|
}
|
|
2340
2494
|
);
|
|
2341
2495
|
}
|
|
2496
|
+
async submitImplementationVerificationResult(projectId, workItemId, result) {
|
|
2497
|
+
return this.request(
|
|
2498
|
+
`/projects/${projectId}/work-items/${workItemId}/implementation-verification-result`,
|
|
2499
|
+
z3.object({ verification: implementationVerificationItemSchema, workItem: workItemSchema, sourceWorkItem: workItemSchema.optional() }),
|
|
2500
|
+
{
|
|
2501
|
+
method: "POST",
|
|
2502
|
+
body: JSON.stringify(result)
|
|
2503
|
+
}
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2342
2506
|
async request(urlPath, schema, init) {
|
|
2343
2507
|
const response = await fetch(resolveApiUrl(this.options.apiUrl, urlPath), {
|
|
2344
2508
|
...init,
|
|
@@ -4392,9 +4556,60 @@ var appEvaluationStart = "AMISTIO_APP_EVALUATION_START";
|
|
|
4392
4556
|
var appEvaluationEnd = "AMISTIO_APP_EVALUATION_END";
|
|
4393
4557
|
var projectContextRefreshStart = "AMISTIO_PROJECT_CONTEXT_REFRESH_START";
|
|
4394
4558
|
var projectContextRefreshEnd = "AMISTIO_PROJECT_CONTEXT_REFRESH_END";
|
|
4559
|
+
var implementationVerificationStart = "AMISTIO_IMPLEMENTATION_VERIFICATION_START";
|
|
4560
|
+
var implementationVerificationEnd = "AMISTIO_IMPLEMENTATION_VERIFICATION_END";
|
|
4395
4561
|
var validProjectContextSliceKinds = /* @__PURE__ */ new Set(["overview", "architecture", "domain", "data", "api", "frontend", "backend", "cli", "workflow", "operations", "security", "testing", "unknown"]);
|
|
4396
4562
|
var validProjectContextEntityTypes = /* @__PURE__ */ new Set(["project", "system", "component", "domain", "tool", "decision", "feature", "risk", "team", "workflow", "unknown"]);
|
|
4397
4563
|
var validProjectContextRelationTypes = /* @__PURE__ */ new Set(["uses", "depends_on", "decides", "supersedes", "touches", "blocks", "implements", "mentions"]);
|
|
4564
|
+
function createImplementationVerificationPrompt(workItem) {
|
|
4565
|
+
return [
|
|
4566
|
+
"# Amistio Implementation Verification",
|
|
4567
|
+
"",
|
|
4568
|
+
"You are running locally through the Amistio CLI inside the user's repository.",
|
|
4569
|
+
"Verify whether the linked implementation work was actually completed. This is a read-only proof pass.",
|
|
4570
|
+
"Do not modify files, create branches, commit, install packages, run implementation prompts, or make destructive changes.",
|
|
4571
|
+
"Use local repository inspection and focused verification commands only when they are safe for this checkout.",
|
|
4572
|
+
"",
|
|
4573
|
+
"## Work Item",
|
|
4574
|
+
"",
|
|
4575
|
+
`Title: ${workItem.title}`,
|
|
4576
|
+
`Work item ID: ${workItem.workItemId}`,
|
|
4577
|
+
`Project ID: ${workItem.projectId}`,
|
|
4578
|
+
`Implementation verification ID: ${workItem.implementationVerificationId ?? "unknown"}`,
|
|
4579
|
+
`Source work item ID: ${workItem.sourceWorkItemId ?? "unknown"}`,
|
|
4580
|
+
"",
|
|
4581
|
+
"## Verification Request",
|
|
4582
|
+
"",
|
|
4583
|
+
workItem.sourceWish ?? "Verify that the linked implementation plan was completed in the local repository.",
|
|
4584
|
+
"",
|
|
4585
|
+
"## Verification Requirements",
|
|
4586
|
+
"",
|
|
4587
|
+
"- Compare the approved plan or prompt requirements against the current local repository state.",
|
|
4588
|
+
"- Look beyond runner status. Use source evidence, generated handoff details, PR/branch/worktree metadata when present, and targeted checks.",
|
|
4589
|
+
"- Treat missing acceptance criteria as an explicit gap instead of guessing.",
|
|
4590
|
+
"- Record evidence for at least one acceptance criterion with safe repository-relative citations.",
|
|
4591
|
+
"- Use outcome verifiedImplemented only when the implementation is materially present and checks support it.",
|
|
4592
|
+
"- Use partiallyImplemented, notImplemented, inconclusive, or verificationBlocked when evidence is incomplete, absent, contradictory, or checks cannot run.",
|
|
4593
|
+
"",
|
|
4594
|
+
"## Data Safety",
|
|
4595
|
+
"",
|
|
4596
|
+
"- Persist summaries, safe paths, short citation excerpts, and verification status only.",
|
|
4597
|
+
"- Do not include raw source dumps, secrets, env vars, process lists, absolute local paths, credential values, tokens, provider sessions, or destructive shell output.",
|
|
4598
|
+
"- Citations and safePaths must be repository-relative and must not use ../ traversal.",
|
|
4599
|
+
"",
|
|
4600
|
+
"## Output Contract",
|
|
4601
|
+
"",
|
|
4602
|
+
"Print exactly one JSON object between the markers below. The CLI will submit only this structured verification result back to Amistio.",
|
|
4603
|
+
"Accepted outcome values: verifiedImplemented, partiallyImplemented, notImplemented, inconclusive, verificationBlocked.",
|
|
4604
|
+
"Accepted recommendation values: none, requeue, createFollowUpPlan, requestHumanReview, markBlocked.",
|
|
4605
|
+
"",
|
|
4606
|
+
implementationVerificationStart,
|
|
4607
|
+
'{"outcome":"partiallyImplemented","summary":"The implementation is present in the workspace, but one acceptance check could not be confirmed.","evidence":[{"acceptanceCriterion":"The requested behavior is wired through the runner lifecycle.","status":"partial","summary":"The route and UI wiring exist, but the runner result submission still needs validation.","citations":[{"source":"localSource","repoPath":"src/apps/web/components/workspace-client.tsx","excerpt":"Implementation verification queued for the local runner."}]}],"checks":[{"name":"Focused typecheck","status":"passed","summary":"The touched package typechecked successfully.","safePaths":["src/apps/web"]}],"gaps":["Runner result submission was not exercised end to end."],"recommendation":"requestHumanReview","verificationPlan":["Run focused CLI tests","Review the verification evidence in Amistio"],"warnings":[]}',
|
|
4608
|
+
implementationVerificationEnd,
|
|
4609
|
+
"",
|
|
4610
|
+
"Do not put Markdown fences around the markers. Do not implement or fix anything during this verification pass."
|
|
4611
|
+
].join("\n");
|
|
4612
|
+
}
|
|
4398
4613
|
function createWorkExecutionPrompt(workItem, context) {
|
|
4399
4614
|
if (workItem.workKind === "brainGeneration") {
|
|
4400
4615
|
return createBrainGenerationPrompt(workItem);
|
|
@@ -4420,6 +4635,9 @@ function createWorkExecutionPrompt(workItem, context) {
|
|
|
4420
4635
|
if (workItem.workKind === "projectContextRefresh") {
|
|
4421
4636
|
return createProjectContextRefreshPrompt(workItem, context?.projectContextRefresh);
|
|
4422
4637
|
}
|
|
4638
|
+
if (workItem.workKind === "implementationVerification") {
|
|
4639
|
+
return createImplementationVerificationPrompt(workItem);
|
|
4640
|
+
}
|
|
4423
4641
|
return [
|
|
4424
4642
|
"# Amistio Work Execution",
|
|
4425
4643
|
"",
|
|
@@ -4570,6 +4788,7 @@ function createSecurityPostureScanPrompt(workItem, context) {
|
|
|
4570
4788
|
"## Output Contract",
|
|
4571
4789
|
"",
|
|
4572
4790
|
"Print exactly one JSON object between the markers below. The CLI will submit only this structured scan result back to Amistio.",
|
|
4791
|
+
"Accepted category values: owasp, dependency, supplyChain, secretHygiene, authentication, authorization, tenantIsolation, headers, redirects, csrf, cors, ciCd, logging, rateLimiting, runnerBoundary, other.",
|
|
4573
4792
|
"",
|
|
4574
4793
|
securityPostureStart,
|
|
4575
4794
|
'{"summary":"Security posture is mostly healthy, but dependency advisory review is not automated.","baselineVersion":"amistio-security-baseline-v1","postureScore":82,"postureGrade":"B","categorySummaries":[{"category":"dependency","status":"warning","summary":"Dependency audit automation is incomplete."}],"findings":[{"title":"Dependency audit is not enforced in CI","category":"dependency","severity":"medium","confidence":"high","summary":"The repository has a lockfile, but CI does not appear to fail on known vulnerable dependencies.","standardReferences":[{"standard":"OWASP ASVS","control":"V14.2 Dependency"}],"affectedSurfaces":["CI verification"],"evidence":["No dependency audit step was found in the CI verification summary."],"recommendedRemediation":"Add a dependency advisory check to the existing verification pipeline and document how failures are handled.","verificationPlan":["Run the updated CI verification command locally","Confirm a known advisory fails the check in a controlled test"],"safePaths":["package.json","pnpm-lock.yaml"]}],"verificationPlan":["Run focused dependency and CI checks","Review Security panel findings for approval"]}',
|
|
@@ -4970,11 +5189,23 @@ function parseProjectContextRefreshResult(output, options = {}) {
|
|
|
4970
5189
|
const normalized = normalizeProjectContextRefreshPaths(normalizeProjectContextRefreshEnums(parsed), options);
|
|
4971
5190
|
return projectContextRefreshResultSchema.parse(normalized);
|
|
4972
5191
|
}
|
|
5192
|
+
function parseImplementationVerificationResult(output) {
|
|
5193
|
+
const start = output.indexOf(implementationVerificationStart);
|
|
5194
|
+
const end = output.indexOf(implementationVerificationEnd, start + implementationVerificationStart.length);
|
|
5195
|
+
if (start === -1 || end === -1 || end <= start) {
|
|
5196
|
+
throw new Error("Local AI verification did not return an Amistio implementation verification block.");
|
|
5197
|
+
}
|
|
5198
|
+
const payload = output.slice(start + implementationVerificationStart.length, end).trim();
|
|
5199
|
+
const parsed = JSON.parse(stripJsonFence(payload));
|
|
5200
|
+
return implementationVerificationResultSchema.parse(parsed);
|
|
5201
|
+
}
|
|
4973
5202
|
function projectContextRefreshSubmissionFailureSummary(result) {
|
|
4974
5203
|
if (result.refresh.status !== "failed" && result.workItem.status !== "failed") {
|
|
4975
5204
|
return void 0;
|
|
4976
5205
|
}
|
|
4977
|
-
|
|
5206
|
+
const summary = result.refresh.error ?? result.workItem.lastStatusMessage ?? "Server rejected the project context refresh result.";
|
|
5207
|
+
const guidance = getWorkFailureGuidance({ message: summary, workKind: "projectContextRefresh" });
|
|
5208
|
+
return guidance ? `${summary} ${formatWorkFailureGuidanceDetail(guidance)}` : summary;
|
|
4978
5209
|
}
|
|
4979
5210
|
function createBrainGenerationPrompt(workItem) {
|
|
4980
5211
|
const wish = workItem.sourceWish ?? workItem.title;
|
|
@@ -6041,7 +6272,7 @@ var DEFAULT_MAX_PREFLIGHT_ATTEMPTS = 3;
|
|
|
6041
6272
|
var DEFAULT_TOOL_TIMEOUT_SECONDS = 30 * 60;
|
|
6042
6273
|
var RUNNER_WORK_LEASE_SECONDS = 300;
|
|
6043
6274
|
var RUNNER_WORK_LEASE_RENEWAL_MS = 12e4;
|
|
6044
|
-
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh"];
|
|
6275
|
+
var runnerSupportedWorkKinds = ["brainGeneration", "implementation", "planRevision", "assistantQuestion", "impactPreview", "issueDiagnosis", "securityPostureScan", "appEvaluationScan", "projectContextRefresh", "implementationVerification"];
|
|
6045
6276
|
program.name("amistio").description("Amistio project brain CLI").version(CLI_VERSION);
|
|
6046
6277
|
program.command("init").description("Create Amistio control-plane folders for a new project").option("--root <path>", "Repository root", defaultRoot).action(async (options) => {
|
|
6047
6278
|
const created = await initControlPlane(options.root);
|
|
@@ -7077,6 +7308,24 @@ async function runNextWorkItem({
|
|
|
7077
7308
|
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
7078
7309
|
}
|
|
7079
7310
|
}
|
|
7311
|
+
if (result.workItem.workKind === "implementationVerification") {
|
|
7312
|
+
try {
|
|
7313
|
+
return await finalizeImplementationVerificationWork({
|
|
7314
|
+
apiClient,
|
|
7315
|
+
durationMs: Date.now() - startedAt,
|
|
7316
|
+
projectId,
|
|
7317
|
+
repositoryLinkId,
|
|
7318
|
+
runnerId,
|
|
7319
|
+
sessionContext,
|
|
7320
|
+
toolConfig,
|
|
7321
|
+
toolName: preview.toolName,
|
|
7322
|
+
toolResult,
|
|
7323
|
+
workItem: result.workItem
|
|
7324
|
+
});
|
|
7325
|
+
} catch (error) {
|
|
7326
|
+
return recordFinalizationFailure({ apiClient, error, isolationTelemetry, projectId, repositoryLinkId, runnerId, sessionContext, toolConfig, toolName: preview.toolName, workItem: result.workItem, durationMs: Date.now() - startedAt });
|
|
7327
|
+
}
|
|
7328
|
+
}
|
|
7080
7329
|
let finalStatus = toolResult.exitCode === 0 ? "completed" : "failed";
|
|
7081
7330
|
const durationMs = Date.now() - startedAt;
|
|
7082
7331
|
const failureExcerpt = toolResult.exitCode === 0 ? void 0 : truncateLogExcerpt(toolResult.stderr || toolResult.stdout);
|
|
@@ -8014,6 +8263,92 @@ ${toolResult.stderr}`, { repositoryRoot: executionRoot });
|
|
|
8014
8263
|
console.error(refreshError ?? "Local runner project context refresh failed.");
|
|
8015
8264
|
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
8016
8265
|
}
|
|
8266
|
+
async function finalizeImplementationVerificationWork({
|
|
8267
|
+
apiClient,
|
|
8268
|
+
durationMs,
|
|
8269
|
+
projectId,
|
|
8270
|
+
repositoryLinkId,
|
|
8271
|
+
runnerId,
|
|
8272
|
+
sessionContext,
|
|
8273
|
+
toolConfig,
|
|
8274
|
+
toolName,
|
|
8275
|
+
toolResult,
|
|
8276
|
+
workItem
|
|
8277
|
+
}) {
|
|
8278
|
+
let verificationResult = void 0;
|
|
8279
|
+
let verificationError;
|
|
8280
|
+
if (toolResult.exitCode === 0) {
|
|
8281
|
+
try {
|
|
8282
|
+
verificationResult = parseImplementationVerificationResult(`${toolResult.stdout}
|
|
8283
|
+
${toolResult.stderr}`);
|
|
8284
|
+
} catch (error) {
|
|
8285
|
+
verificationError = errorMessage3(error);
|
|
8286
|
+
}
|
|
8287
|
+
} else {
|
|
8288
|
+
verificationError = truncateLogExcerpt(toolResult.stderr || toolResult.stdout) || `${toolName} exited with code ${toolResult.exitCode}.`;
|
|
8289
|
+
}
|
|
8290
|
+
const finalStatus = verificationResult ? "completed" : "failed";
|
|
8291
|
+
const updatedToolSession = await finalizeToolSession({
|
|
8292
|
+
apiClient,
|
|
8293
|
+
projectId,
|
|
8294
|
+
status: finalStatus,
|
|
8295
|
+
runnerId,
|
|
8296
|
+
workItemId: workItem.workItemId,
|
|
8297
|
+
stdout: toolResult.stdout,
|
|
8298
|
+
...sessionContext.toolSession ? { session: sessionContext.toolSession } : {},
|
|
8299
|
+
...toolResult.messageCount !== void 0 ? { messageCount: toolResult.messageCount } : {},
|
|
8300
|
+
...toolResult.tokensIn !== void 0 ? { tokensIn: toolResult.tokensIn } : {},
|
|
8301
|
+
...toolResult.tokensOut !== void 0 ? { tokensOut: toolResult.tokensOut } : {},
|
|
8302
|
+
...toolResult.costUsd !== void 0 ? { costUsd: toolResult.costUsd } : {}
|
|
8303
|
+
});
|
|
8304
|
+
const sessionTelemetry = {
|
|
8305
|
+
sessionPolicy: sessionContext.policy,
|
|
8306
|
+
sessionDecision: sessionContext.decision,
|
|
8307
|
+
sessionDecisionReason: sessionContext.reason,
|
|
8308
|
+
...updatedToolSession ? { toolSessionId: updatedToolSession.toolSessionId } : {},
|
|
8309
|
+
...updatedToolSession?.sessionGroupKey ? { sessionGroupKey: updatedToolSession.sessionGroupKey } : {}
|
|
8310
|
+
};
|
|
8311
|
+
if (verificationResult) {
|
|
8312
|
+
const result = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
|
|
8313
|
+
status: "completed",
|
|
8314
|
+
runnerId,
|
|
8315
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
|
|
8316
|
+
result: verificationResult,
|
|
8317
|
+
tool: toolName,
|
|
8318
|
+
durationMs,
|
|
8319
|
+
...sessionTelemetry,
|
|
8320
|
+
message: `${toolName} returned implementation verification proof.`
|
|
8321
|
+
});
|
|
8322
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8323
|
+
status: verificationResult.outcome === "verifiedImplemented" ? "completed" : verificationResult.outcome === "verificationBlocked" ? "blocked" : "warning",
|
|
8324
|
+
summary: verificationResult.summary,
|
|
8325
|
+
idempotencyKey: `runner_milestone_implementation_verification_completed_${workItem.workItemId}_${result.workItem.idempotencyKey}`,
|
|
8326
|
+
metadata: { tool: toolName, durationMs, outcome: verificationResult.outcome, recommendation: verificationResult.recommendation, gapCount: verificationResult.gaps.length, verificationSummary: verificationResult.verificationPlan.join(" | ") }
|
|
8327
|
+
});
|
|
8328
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
8329
|
+
console.log("Implementation verification returned for review.");
|
|
8330
|
+
return { status: "completed", exitCode: 0 };
|
|
8331
|
+
}
|
|
8332
|
+
const failedResult = await apiClient.submitImplementationVerificationResult(projectId, workItem.workItemId, {
|
|
8333
|
+
status: "failed",
|
|
8334
|
+
runnerId,
|
|
8335
|
+
idempotencyKey: `implementation_verification_${workItem.workItemId}_${randomUUID()}`,
|
|
8336
|
+
tool: toolName,
|
|
8337
|
+
durationMs,
|
|
8338
|
+
...sessionTelemetry,
|
|
8339
|
+
message: `${toolName} did not produce valid implementation verification proof.`,
|
|
8340
|
+
...verificationError ? { error: verificationError } : {}
|
|
8341
|
+
});
|
|
8342
|
+
await recordRunnerMilestone(apiClient, projectId, workItem, runnerId, repositoryLinkId, {
|
|
8343
|
+
status: "failed",
|
|
8344
|
+
summary: verificationError ?? `${toolName} did not produce valid implementation verification proof.`,
|
|
8345
|
+
idempotencyKey: `runner_milestone_implementation_verification_failed_${workItem.workItemId}_${failedResult.workItem.idempotencyKey}`,
|
|
8346
|
+
metadata: { tool: toolName, durationMs, verificationSummary: "Implementation verification output did not include valid structured JSON." }
|
|
8347
|
+
});
|
|
8348
|
+
await apiClient.sendRunnerHeartbeat(projectId, runnerId, repositoryLinkId, "online", runnerHeartbeatMetadata(toolConfig));
|
|
8349
|
+
console.error(verificationError ?? "Local runner implementation verification failed.");
|
|
8350
|
+
return { status: "failed", exitCode: toolResult.exitCode || 1 };
|
|
8351
|
+
}
|
|
8017
8352
|
async function createRunnerWorkPrompt(apiClient, projectId, workItem) {
|
|
8018
8353
|
if (workItem.workKind === "assistantQuestion") {
|
|
8019
8354
|
const emptyProjectContext = { maps: [], refreshes: [], misses: [] };
|