Repository navigation
Expand file tree
/
Copy pathhandleCodingAgentCallback.ts
More file actions
87 lines (74 loc) · 2.78 KB
/
Copy pathhandleCodingAgentCallback.ts
File metadata and controls
87 lines (74 loc) · 2.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { NextResponse } from "next/server";
import { getCorsHeaders } from "@/lib/networking/getCorsHeaders";
import { validateCodingAgentCallback } from "./validateCodingAgentCallback";
import { getThread } from "@/lib/agents/getThread";
import { handlePRCreated } from "./handlePRCreated";
import { buildPRCard } from "./buildPRCard";
import { setCodingAgentPRState } from "./prState";
import type { CodingAgentThreadState } from "./types";
/**
* Handles coding agent task callback from Trigger.dev.
* Verifies the shared secret and dispatches based on callback status.
*
* @param request - The incoming callback request
* @returns A NextResponse
*/
export async function handleCodingAgentCallback(request: Request): Promise<NextResponse> {
const secret = request.headers.get("x-callback-secret");
const expectedSecret = process.env.CODING_AGENT_CALLBACK_SECRET;
if (!secret || secret !== expectedSecret) {
return NextResponse.json(
{ status: "error", error: "Unauthorized" },
{ status: 401, headers: getCorsHeaders() },
);
}
let body: unknown;
try {
body = await request.json();
} catch {
return NextResponse.json(
{ status: "error", error: "Invalid JSON body" },
{ status: 400, headers: getCorsHeaders() },
);
}
const validated = validateCodingAgentCallback(body);
if (validated instanceof NextResponse) {
return validated;
}
const thread = getThread<CodingAgentThreadState>(validated.threadId);
// Post agent stdout to the thread so users see the full agent response
if (validated.stdout?.trim()) {
await thread.post(validated.stdout.trim());
}
switch (validated.status) {
case "pr_created":
await handlePRCreated(validated.threadId, validated);
break;
case "no_changes":
await thread.setState({ status: "no_changes" });
await thread.post("No changes were detected. The agent didn't modify any files.");
break;
case "failed":
await thread.setState({ status: "failed" });
await thread.post(`Agent failed: ${validated.message ?? "Unknown error"}`);
break;
case "updated": {
const state = (await thread.state) as CodingAgentThreadState | null;
await thread.setState({ status: "pr_created", snapshotId: validated.snapshotId });
const prs = state?.prs ?? [];
const card = buildPRCard("PRs Updated", prs);
await thread.post({ card });
if (state?.branch && state?.prs?.length) {
await setCodingAgentPRState(state.prs[0].repo, state.branch, {
status: "pr_created",
snapshotId: validated.snapshotId,
branch: state.branch,
repo: state.prs[0].repo,
prs: state.prs,
});
}
break;
}
}
return NextResponse.json({ status: "ok" }, { headers: getCorsHeaders() });
}