Show the selected bot profile and add OpenRouter fallback (#18)
Uses Codex first, then Claude, then OpenCode through OpenRouter when quota is exhausted. Every generated comment and review now identifies the selected profile and model in a final quote. The dependent action interface remains one master key. Verified formatting, lint, type checks, encrypted profile decryption, current OpenCode CLI flags, and 45 tests. Reviewed-on: #18 Reviewed-by: bot <noreply@capsulizers.com> Co-authored-by: Danny Kim <temeddix@gmail.com> Co-committed-by: Danny Kim <temeddix@gmail.com>
This commit was merged in pull request #18.
This commit is contained in:
+60
-4
@@ -29,6 +29,7 @@ async function fixture(
|
||||
const outcome: RuntimeOutcome = source.ok
|
||||
? {
|
||||
...source,
|
||||
profile: { type: "codex", model: "gpt-test" },
|
||||
reviewPath: `${profileHome}/review.md`,
|
||||
anchorsPath: `${profileHome}/anchors.json`,
|
||||
}
|
||||
@@ -96,7 +97,9 @@ Deno.test("posts a successful issue result and removes runtime credentials", asy
|
||||
requests[0].url,
|
||||
`${context.apiUrl}/repos/owner/repo/issues/7/comments`,
|
||||
);
|
||||
assertEquals(JSON.parse(String(requests[0].init?.body)), { body: "done" });
|
||||
assertEquals(JSON.parse(String(requests[0].init?.body)), {
|
||||
body: "done\n\n> Profile: `codex` · `gpt-test`",
|
||||
});
|
||||
assertEquals(requests[0].init?.headers, {
|
||||
Authorization: "token reviewer-secret",
|
||||
"Content-Type": "application/json",
|
||||
@@ -123,7 +126,15 @@ Deno.test("retries a rejected inline review without anchors", async () => {
|
||||
});
|
||||
|
||||
assertEquals((bodies[0] as { comments: unknown[] }).comments.length, 1);
|
||||
assertEquals(
|
||||
(bodies[0] as { comments: Array<{ body: string }> }).comments[0].body,
|
||||
"Finding\n\n> Profile: `codex` · `gpt-test`",
|
||||
);
|
||||
assertEquals((bodies[1] as { comments: unknown[] }).comments, []);
|
||||
assertEquals(
|
||||
(bodies[1] as { body: string }).body,
|
||||
"✅ Approved\nReady\n\n> Profile: `codex` · `gpt-test`",
|
||||
);
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
@@ -141,7 +152,9 @@ Deno.test("reports a generic agent failure and keeps the action failed", async (
|
||||
"agent phase failed",
|
||||
);
|
||||
|
||||
assertEquals(bodies, [{ body: "AI bot failed." }]);
|
||||
assertEquals(bodies, [{
|
||||
body: "AI bot failed.\n\n> Profile: none · all profiles failed",
|
||||
}]);
|
||||
assert(!JSON.stringify(bodies).includes("reviewer-secret"));
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
@@ -161,7 +174,9 @@ Deno.test("reports a generic failure when the runner wrote no outcome", async ()
|
||||
"agent phase failed",
|
||||
);
|
||||
|
||||
assertEquals(bodies, [{ body: "AI bot failed." }]);
|
||||
assertEquals(bodies, [{
|
||||
body: "AI bot failed.\n\n> Profile: none · all profiles failed",
|
||||
}]);
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
@@ -181,6 +196,24 @@ Deno.test("removes runtime credentials when posting fails", async () => {
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
Deno.test("preserves a Gitea transport failure without retrying a comment", async () => {
|
||||
const result = await fixture({ ok: true, body: "done" });
|
||||
let requests = 0;
|
||||
|
||||
await assertRejects(
|
||||
() =>
|
||||
postRuntime(result.manifestPath, context, () => {
|
||||
requests++;
|
||||
return Promise.reject(new Error(`network failure ${requests}`));
|
||||
}),
|
||||
Error,
|
||||
"network failure 1",
|
||||
);
|
||||
|
||||
assertEquals(requests, 1);
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
Deno.test("refuses to follow an agent artifact symlink", async () => {
|
||||
if (Deno.build.os === "windows") return;
|
||||
const result = await fixture({ ok: true, body: "done" });
|
||||
@@ -197,6 +230,29 @@ Deno.test("refuses to follow an agent artifact symlink", async () => {
|
||||
"regular private file",
|
||||
);
|
||||
|
||||
assertEquals(requests, 0);
|
||||
assertEquals(requests, 1);
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
Deno.test("reports an attributed failure when a pull request review is missing", async () => {
|
||||
const result = await fixture({ ok: true, body: "done" });
|
||||
const bodies: unknown[] = [];
|
||||
|
||||
await assertRejects(
|
||||
() =>
|
||||
postRuntime(result.manifestPath, {
|
||||
...context,
|
||||
eventName: "pull_request",
|
||||
}, (_url, init) => {
|
||||
bodies.push(JSON.parse(String(init?.body)));
|
||||
return Promise.resolve(new Response(null, { status: 201 }));
|
||||
}),
|
||||
Error,
|
||||
"did not write",
|
||||
);
|
||||
|
||||
assertEquals(bodies, [{
|
||||
body: "AI bot failed.\n\n> Profile: none · all profiles failed",
|
||||
}]);
|
||||
assert(await missing(result.runtimeDir));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user