Skip to content

Commit 59db540

Browse files
committed
fix(showcase): add bare /api/copilotkit + /api/smoke routes for built-in-agent
The catch-all [[...slug]] route doesn't handle the bare /api/copilotkit path, returning 404. The smoke probe interprets 404 as "route not wired" and marks agent:built-in-agent red, blocking the depth ladder at D2.
1 parent c645e2e commit 59db540

2 files changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { GET, POST, OPTIONS } from "./[[...slug]]/route";
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { NextResponse } from "next/server";
2+
3+
const INTEGRATION_SLUG = "built-in-agent";
4+
5+
export const dynamic = "force-dynamic";
6+
export const maxDuration = 60;
7+
8+
export async function GET() {
9+
const start = Date.now();
10+
const baseUrl =
11+
process.env.NEXT_PUBLIC_BASE_URL ||
12+
`http://localhost:${process.env.PORT || 3000}`;
13+
14+
try {
15+
const res = await fetch(`${baseUrl}/api/copilotkit`, {
16+
method: "POST",
17+
headers: { "Content-Type": "application/json" },
18+
body: JSON.stringify({
19+
method: "agent/run",
20+
params: { agentId: "default" },
21+
body: {
22+
threadId: `smoke-${Date.now()}`,
23+
runId: `smoke-run-${Date.now()}`,
24+
state: {},
25+
messages: [
26+
{
27+
id: `smoke-msg-${Date.now()}`,
28+
role: "user",
29+
content: "Respond with exactly: OK",
30+
},
31+
],
32+
tools: [],
33+
context: [],
34+
forwardedProps: {},
35+
},
36+
}),
37+
signal: AbortSignal.timeout(45000),
38+
});
39+
40+
const latency = Date.now() - start;
41+
42+
if (!res.ok) {
43+
const errBody = await res.text().catch(() => "");
44+
return NextResponse.json(
45+
{
46+
status: "error",
47+
integration: INTEGRATION_SLUG,
48+
stage: "runtime_response",
49+
error: `Runtime returned ${res.status}: ${errBody.slice(0, 200)}`,
50+
latency_ms: latency,
51+
timestamp: new Date().toISOString(),
52+
},
53+
{ status: 502 },
54+
);
55+
}
56+
57+
const reader = res.body?.getReader();
58+
if (!reader) {
59+
return NextResponse.json(
60+
{
61+
status: "error",
62+
integration: INTEGRATION_SLUG,
63+
stage: "response_empty",
64+
error: "Runtime returned no readable body",
65+
latency_ms: latency,
66+
timestamp: new Date().toISOString(),
67+
},
68+
{ status: 502 },
69+
);
70+
}
71+
const { value, done } = await reader.read();
72+
reader.cancel();
73+
if (done || !value || value.length === 0) {
74+
return NextResponse.json(
75+
{
76+
status: "error",
77+
integration: INTEGRATION_SLUG,
78+
stage: "response_empty",
79+
error: "Runtime returned empty response body",
80+
latency_ms: latency,
81+
timestamp: new Date().toISOString(),
82+
},
83+
{ status: 502 },
84+
);
85+
}
86+
87+
return NextResponse.json({
88+
status: "ok",
89+
integration: INTEGRATION_SLUG,
90+
latency_ms: latency,
91+
timestamp: new Date().toISOString(),
92+
});
93+
} catch (e: unknown) {
94+
const err = e instanceof Error ? e : new Error(String(e));
95+
const latency = Date.now() - start;
96+
97+
let stage = "unknown";
98+
if (err.name === "AbortError" || err.message.includes("timeout"))
99+
stage = "timeout";
100+
else if (
101+
err.message.includes("fetch") ||
102+
err.message.includes("ECONNREFUSED")
103+
)
104+
stage = "agent_unreachable";
105+
else stage = "pipeline_error";
106+
107+
return NextResponse.json(
108+
{
109+
status: "error",
110+
integration: INTEGRATION_SLUG,
111+
stage,
112+
error: err.message,
113+
latency_ms: latency,
114+
timestamp: new Date().toISOString(),
115+
},
116+
{ status: 502 },
117+
);
118+
}
119+
}

0 commit comments

Comments
 (0)