diff --git a/utils/runLifecycle.ts b/utils/runLifecycle.ts index e70fc65..4525209 100644 --- a/utils/runLifecycle.ts +++ b/utils/runLifecycle.ts @@ -27,13 +27,12 @@ import type { AgentResult } from "../agents/shared.ts"; import { deleteProgressComment } from "../mcp/comment.ts"; import type { ToolContext } from "../mcp/server.ts"; import type { ToolState } from "../toolState.ts"; -import { formatApiKeyErrorSummary, isApiKeyAuthError } from "./apiKeys.ts"; import { formatUsageSummary, log, writeSummary } from "./cli.ts"; import { reportErrorToComment } from "./errorReport.ts"; import { persistLearnings } from "./learnings.ts"; import { persistSummary } from "./prSummary.ts"; import { postReviewCleanup } from "./reviewCleanup.ts"; -import type { RenderedRunError } from "./runErrorRenderer.ts"; +import { type RenderedRunError, renderRunError } from "./runErrorRenderer.ts"; /** * Best-effort cleanup shared by both run-end paths: @@ -57,9 +56,12 @@ export async function persistRunArtifacts(toolContext: ToolContext): Promise { await persistRunArtifacts(input.toolContext); - if (!input.result.success && input.toolState.progressComment) { - const rawError = input.result.error || "agent run failed"; - const errorBody = isApiKeyAuthError(rawError) - ? formatApiKeyErrorSummary({ - owner: input.repo.owner, - name: input.repo.name, - raw: rawError, - }) - : rawError; - await reportErrorToComment({ toolState: input.toolState, error: errorBody }).catch((error) => { - log.debug(`failure error report failed: ${error}`); - }); + // shared rendering for the !success branch — same classifier as the + // outer catch path (BillingError reclassify → hang → api-key → generic), + // so a harness-returned `{success: false}` lands an actionable error + // block in the job summary alongside the matching body in the progress + // comment. hang and generic get the `### ❌ Pullfrog failed` H3 banner; + // BillingError and api-key render their own provider-specific framing + // (no banner). renders once; reused for both surfaces below. + const rendered = !input.result.success + ? renderRunError({ + errorMessage: input.result.error || "agent run failed", + repo: input.repo, + agentDiagnostic: input.toolState.agentDiagnostic, + }) + : null; + + if (rendered && input.toolState.progressComment) { + await reportErrorToComment({ toolState: input.toolState, error: rendered.comment }).catch( + (error) => { + log.debug(`failure error report failed: ${error}`); + } + ); } // create_pull_request_review owns its own deletion (see mcp/review.ts), so @@ -110,7 +121,7 @@ export async function finalizeSuccessRun(input: { try { const usageSummary = formatUsageSummary(input.toolState.usageEntries); const body = input.toolState.lastProgressBody || input.result.output; - const parts = [body, usageSummary].filter(Boolean); + const parts = [rendered?.summary, body, usageSummary].filter(Boolean); if (parts.length > 0) { await writeSummary(parts.join("\n\n")); }