@kestrel-agents/next handles request parsing, authenticated context, correlation headers, and SSE formatting inside Next.js route handlers.
Install
pnpm add @kestrel-agents/next@0.7.0 @kestrel-agents/sdk@0.7.0Route helpers
| Helper | Best for | Key responsibility |
|---|---|---|
createJsonRunRouteHandler() | request-response routes | parse body, resolve context, return JSON terminal event |
createStreamRunRouteHandler() | live browser progress | parse body, resolve context, stream SSE, propagate cancellation |
createWebhookRunRouteHandler() | external or background payloads | map arbitrary payload into a Kestrel turn |
Two integration shapes
You usually choose between two patterns:
| Pattern | Best fit | Why choose it |
|---|---|---|
| route-helper integration | Next.js app wants typed Kestrel turns | clean server-only route code, built-in correlation handling, easy transition into SDK features |
| OpenAI-compatible proxying | app already speaks OpenAI chat or responses | preserves the existing client request format while routing through Kestrel |
Use compatibility mode to preserve an existing client. Move to route helpers when the application needs typed sessions, memory, and operator behavior.
Route example
export const POST = createStreamRunRouteHandler({
agent,
resolveContext,
});OpenAI-compatible proxy example
For chat-first products, keep the browser pointed at your Next.js route and forward to /v1/chat/completions or /v1/responses from the server:
export async function POST(request: Request) {
const body = await request.text();
const user = await requireAuthenticatedUser(request);
const upstream = await fetch(
`${process.env.KESTREL_RUNNER_SERVICE_URL}/v1/chat/completions`,
{
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${process.env.KESTREL_RUNNER_SERVICE_TOKEN}`,
"x-kestrel-actor-id": user.id,
"x-kestrel-actor-type": "end_user",
"x-kestrel-actor-name": user.name,
"x-kestrel-tenant-id": user.tenantId,
},
body,
},
);
return new Response(upstream.body, {
status: upstream.status,
headers: upstream.headers,
});
}Correlation headers
The route helpers read:
x-request-idx-correlation-iduser-agentx-forwarded-for
If those values are missing, the helper generates request and correlation IDs and returns them as x-kestrel-request-id and x-kestrel-correlation-id.
Those two headers connect the application request to later inspection. When you proxy the OpenAI-compatible runner endpoints instead, preserve their separate upstream session, run, thread, and model headers as well.
Common failure cases
| Failure | Usually means |
|---|---|
route body missing sessionId | the caller sent an incomplete request |
| stream closes immediately | upstream cancellation or runner outage |
| actor context missing | server route did not resolve context correctly |
| webhook payload maps badly | mapPayload() does not match the incoming payload |
| OpenAI-compatible request works but control-plane routes do not exist | you stayed on compat mode too long for the product needs |
When to choose this package
Use it when your product is already in Next.js and you want to keep:
- browser traffic pointed at your app
- runner credentials on the server
- Kestrel request correlation explicit
- route code small and repeatable
Move deeper into the SDK and custom protocol when the app needs operator inbox/thread/control, task graph, project snapshot, or filtered /events/stream subscriptions. That is the line between "proxying Kestrel" and "building directly on Kestrel."