Recognize sign-in failures for every agent and fall through (#19)

Co-authored-by: Danny Kim <temeddix@gmail.com>
Co-committed-by: Danny Kim <temeddix@gmail.com>
This commit was merged in pull request #19.
This commit is contained in:
Danny Kim
2026-10-12 02:51:28 +00:00
committed by temeddix
parent ea20f6d629
commit f001a71c61
11 changed files with 413 additions and 36 deletions
+37 -6
View File
@@ -4,7 +4,12 @@ import {
type RuntimeManifest,
type RuntimeOutcome,
} from "./runtime.ts";
import { withProfile } from "./profiles.ts";
import {
failureFooter,
type FailureReason,
type ProfileFailure,
withProfile,
} from "./profiles.ts";
export type PostContext = {
apiUrl: string;
@@ -26,7 +31,10 @@ const VERDICTS: Record<string, [event: string, mark: string]> = {
Approved: ["APPROVED", "✅"],
"Changes requested": ["REQUEST_CHANGES", "🛑"],
};
const FAILURE = "AI bot failed.\n\n> Profile: none · all profiles failed";
const PROFILE_TYPES = ["claude", "codex", "opencode"];
const FAILURE_REASONS = ["sign-in failed", "usage exhausted", "failed"];
const failureComment = (failures: ProfileFailure[]): string =>
`AI bot failed.\n\n${failureFooter(failures)}`;
class GiteaRequestError extends Error {}
@@ -61,7 +69,28 @@ async function readOutcome(path: string): Promise<RuntimeOutcome> {
throw new Error("invalid agent outcome");
}
const outcome = value as Record<string, unknown>;
if (outcome.ok === false) return { ok: false };
if (outcome.ok === false) {
// Only known labels reach the comment, never raw agent output.
const failures = Array.isArray(outcome.failures) ? outcome.failures : [];
return {
ok: false,
failures: failures.flatMap((entry: unknown) => {
const { type, model, reason } = (entry ?? {}) as Record<
string,
unknown
>;
return typeof type === "string" && PROFILE_TYPES.includes(type) &&
typeof model === "string" && typeof reason === "string" &&
FAILURE_REASONS.includes(reason)
? [{
type: type as ProfileFailure["type"],
model,
reason: reason as FailureReason,
}]
: [];
}),
};
}
if (
outcome.ok !== true || typeof outcome.body !== "string" ||
typeof outcome.reviewPath !== "string" ||
@@ -152,11 +181,11 @@ export async function postRuntime(
try {
outcome = await readOutcome(manifest.outcomeFile);
} catch (error) {
await postComment(FAILURE);
await postComment(failureComment([]));
throw new Error("agent phase failed", { cause: error });
}
if (!outcome.ok) {
await postComment(FAILURE);
await postComment(failureComment(outcome.failures));
throw new Error("agent phase failed");
}
@@ -208,7 +237,9 @@ export async function postRuntime(
});
} catch (error) {
if (error instanceof GiteaRequestError) throw error;
await postComment(FAILURE);
await postComment(
failureComment([{ ...outcome.profile, reason: "failed" }]),
);
throw error;
}
} finally {