@copilotkit/runtime 1.71.0 → 1.71.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.cjs +1 -1
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts.map +1 -1
- package/dist/agent/index.d.mts.map +1 -1
- package/dist/agent/index.mjs +1 -1
- package/dist/agent/index.mjs.map +1 -1
- package/dist/package.cjs +3 -3
- package/dist/package.mjs +3 -3
- package/package.json +4 -5
- package/skills/runtime/SKILL.md +0 -98
- package/skills/runtime/references/agent-runners-custom.md +0 -161
- package/skills/runtime/references/agent-runners-in-memory.md +0 -79
- package/skills/runtime/references/agent-runners-sqlite.md +0 -90
- package/skills/runtime/references/agent-runners.md +0 -336
- package/skills/runtime/references/built-in-agent-factory-modes.md +0 -232
- package/skills/runtime/references/built-in-agent-helper-utilities.md +0 -123
- package/skills/runtime/references/built-in-agent-model-identifiers.md +0 -58
- package/skills/runtime/references/built-in-agent.md +0 -523
- package/skills/runtime/references/intelligence-mode.md +0 -364
- package/skills/runtime/references/middleware.md +0 -376
- package/skills/runtime/references/server-side-tools.md +0 -414
- package/skills/runtime/references/setup-endpoint.md +0 -503
- package/skills/runtime/references/transcription.md +0 -287
- package/skills/runtime/references/wiring-a2a.md +0 -40
- package/skills/runtime/references/wiring-adk.md +0 -45
- package/skills/runtime/references/wiring-ag2.md +0 -41
- package/skills/runtime/references/wiring-agno.md +0 -40
- package/skills/runtime/references/wiring-aws-strands.md +0 -59
- package/skills/runtime/references/wiring-crewai-crews.md +0 -51
- package/skills/runtime/references/wiring-crewai-flows.md +0 -45
- package/skills/runtime/references/wiring-external-agents.md +0 -348
- package/skills/runtime/references/wiring-langgraph.md +0 -49
- package/skills/runtime/references/wiring-llamaindex.md +0 -39
- package/skills/runtime/references/wiring-mastra.md +0 -70
- package/skills/runtime/references/wiring-mcp-apps-middleware.md +0 -73
- package/skills/runtime/references/wiring-ms-agent-framework.md +0 -41
- package/skills/runtime/references/wiring-pydantic-ai.md +0 -45
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
# CopilotKit Runtime Middleware
|
|
2
|
-
|
|
3
|
-
Two coexisting middleware surfaces:
|
|
4
|
-
|
|
5
|
-
- **`hooks`** (preferred, newer) — pass to `createCopilotRuntimeHandler({ hooks })`.
|
|
6
|
-
Route-aware via `onBeforeHandler({ route })`. Throw a `Response` to short-circuit.
|
|
7
|
-
- **`beforeRequestMiddleware` / `afterRequestMiddleware`** (legacy) — pass to
|
|
8
|
-
`new CopilotRuntime({ ... })`. Runs **after `hooks.onRequest` but before routing** (see
|
|
9
|
-
`fetch-handler.ts:136-147` for exact order). Pre-routing only.
|
|
10
|
-
|
|
11
|
-
Use **hooks** for new code.
|
|
12
|
-
|
|
13
|
-
## Setup
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import {
|
|
17
|
-
CopilotRuntime,
|
|
18
|
-
createCopilotRuntimeHandler,
|
|
19
|
-
} from "@copilotkit/runtime/v2";
|
|
20
|
-
|
|
21
|
-
const runtime = new CopilotRuntime({
|
|
22
|
-
agents: {
|
|
23
|
-
/* ... */
|
|
24
|
-
} as any,
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const handler = createCopilotRuntimeHandler({
|
|
28
|
-
runtime,
|
|
29
|
-
basePath: "/api/copilotkit",
|
|
30
|
-
hooks: {
|
|
31
|
-
onRequest: async ({ request }) => {
|
|
32
|
-
const token = request.headers.get("authorization");
|
|
33
|
-
if (!token) throw new Response("Unauthorized", { status: 401 });
|
|
34
|
-
},
|
|
35
|
-
onBeforeHandler: async ({ route, request }) => {
|
|
36
|
-
if (route.method === "agent/run" && route.agentId === "admin") {
|
|
37
|
-
const user = await verifyAdminToken(
|
|
38
|
-
request.headers.get("authorization"),
|
|
39
|
-
);
|
|
40
|
-
if (!user) throw new Response("Forbidden", { status: 403 });
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
onResponse: async ({ response }) => {
|
|
44
|
-
const headers = new Headers(response.headers);
|
|
45
|
-
headers.set("x-copilot-version", "2.0");
|
|
46
|
-
return new Response(response.body, {
|
|
47
|
-
status: response.status,
|
|
48
|
-
statusText: response.statusText,
|
|
49
|
-
headers,
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
onError: async ({ error, route }) => {
|
|
53
|
-
console.error("[copilotkit]", route?.method, error);
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
async function verifyAdminToken(
|
|
59
|
-
header: string | null,
|
|
60
|
-
): Promise<{ id: string } | null> {
|
|
61
|
-
if (!header) return null;
|
|
62
|
-
// delegate to your auth lib
|
|
63
|
-
return { id: "admin" };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export default { fetch: handler };
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Core Patterns
|
|
70
|
-
|
|
71
|
-
### Reject unauthenticated requests at the runtime boundary
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
createCopilotRuntimeHandler({
|
|
75
|
-
runtime,
|
|
76
|
-
basePath: "/api/copilotkit",
|
|
77
|
-
hooks: {
|
|
78
|
-
onRequest: ({ request }) => {
|
|
79
|
-
const token = request.headers.get("authorization");
|
|
80
|
-
if (!token?.startsWith("Bearer ")) {
|
|
81
|
-
throw new Response(JSON.stringify({ error: "unauthorized" }), {
|
|
82
|
-
status: 401,
|
|
83
|
-
headers: { "content-type": "application/json" },
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
### Route-aware authorization
|
|
92
|
-
|
|
93
|
-
Use `onBeforeHandler` — the `route` object carries `method`, `agentId`, and (for thread/stop
|
|
94
|
-
methods) `threadId`.
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
createCopilotRuntimeHandler({
|
|
98
|
-
runtime,
|
|
99
|
-
basePath: "/api/copilotkit",
|
|
100
|
-
hooks: {
|
|
101
|
-
onBeforeHandler: async ({ route, request }) => {
|
|
102
|
-
if (route.method === "agent/run" && route.agentId === "billing") {
|
|
103
|
-
const ok = await canAccessBilling(request);
|
|
104
|
-
if (!ok) throw new Response("Forbidden", { status: 403 });
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
async function canAccessBilling(request: Request): Promise<boolean> {
|
|
111
|
-
// delegate to your policy engine
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### Rate-limit by calling an external limiter from the hook
|
|
117
|
-
|
|
118
|
-
Delegate to a dedicated lib — do not implement a rate limiter inline.
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import { Ratelimit } from "@upstash/ratelimit";
|
|
122
|
-
import { Redis } from "@upstash/redis";
|
|
123
|
-
|
|
124
|
-
const ratelimit = new Ratelimit({
|
|
125
|
-
redis: Redis.fromEnv(),
|
|
126
|
-
limiter: Ratelimit.slidingWindow(60, "1 m"),
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
createCopilotRuntimeHandler({
|
|
130
|
-
runtime,
|
|
131
|
-
basePath: "/api/copilotkit",
|
|
132
|
-
hooks: {
|
|
133
|
-
onRequest: async ({ request }) => {
|
|
134
|
-
const userId = request.headers.get("x-user-id") ?? "anon";
|
|
135
|
-
const { success } = await ratelimit.limit(userId);
|
|
136
|
-
if (!success) throw new Response("Too Many Requests", { status: 429 });
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
### Non-blocking telemetry on response
|
|
143
|
-
|
|
144
|
-
`afterRequestMiddleware` runs non-blocking (errors inside only log). Do not await heavy
|
|
145
|
-
work that the user's response waits on.
|
|
146
|
-
|
|
147
|
-
```typescript
|
|
148
|
-
import { CopilotRuntime } from "@copilotkit/runtime/v2";
|
|
149
|
-
|
|
150
|
-
const runtime = new CopilotRuntime({
|
|
151
|
-
agents: {
|
|
152
|
-
/* ... */
|
|
153
|
-
} as any,
|
|
154
|
-
afterRequestMiddleware: async ({ threadId, messages }) => {
|
|
155
|
-
// fire-and-forget; do not await heavy work that blocks response
|
|
156
|
-
void queue.enqueue({ type: "chat", threadId, messages });
|
|
157
|
-
},
|
|
158
|
-
});
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
## Common Mistakes
|
|
162
|
-
|
|
163
|
-
### HIGH Returning a Response instead of throwing
|
|
164
|
-
|
|
165
|
-
Wrong:
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
new CopilotRuntime({
|
|
169
|
-
agents,
|
|
170
|
-
beforeRequestMiddleware: async () =>
|
|
171
|
-
new Response("Unauthorized", { status: 401 }),
|
|
172
|
-
});
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
Correct:
|
|
176
|
-
|
|
177
|
-
```typescript
|
|
178
|
-
new CopilotRuntime({
|
|
179
|
-
agents,
|
|
180
|
-
beforeRequestMiddleware: async ({ request }) => {
|
|
181
|
-
if (!request.headers.get("authorization")) {
|
|
182
|
-
throw new Response("Unauthorized", { status: 401 });
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
The middleware contract returns `Request | void`. Returning a Response corrupts the
|
|
189
|
-
request object — `fetch-handler.ts:140-147` assigns any truthy return value back to
|
|
190
|
-
`request`, so the router then tries to read `request.method` / `request.headers.get(...)`
|
|
191
|
-
from the Response and downstream handling blows up. Always `throw` a Response to
|
|
192
|
-
short-circuit; never return one.
|
|
193
|
-
|
|
194
|
-
Source: `packages/runtime/src/v2/runtime/core/fetch-handler.ts:140-156`.
|
|
195
|
-
|
|
196
|
-
### MEDIUM Defaulting to beforeRequestMiddleware when hooks are preferred
|
|
197
|
-
|
|
198
|
-
Wrong:
|
|
199
|
-
|
|
200
|
-
```typescript
|
|
201
|
-
new CopilotRuntime({
|
|
202
|
-
agents,
|
|
203
|
-
beforeRequestMiddleware: async ({ request, path }) => {
|
|
204
|
-
if (path.includes("/agent/admin/")) {
|
|
205
|
-
/* check admin auth */
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
});
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
Correct:
|
|
212
|
-
|
|
213
|
-
```typescript
|
|
214
|
-
const runtime = new CopilotRuntime({ agents });
|
|
215
|
-
const handler = createCopilotRuntimeHandler({
|
|
216
|
-
runtime,
|
|
217
|
-
basePath: "/api/copilotkit",
|
|
218
|
-
hooks: {
|
|
219
|
-
onBeforeHandler: ({ route, request }) => {
|
|
220
|
-
if (route.method === "agent/run" && route.agentId === "admin") {
|
|
221
|
-
/* ... */
|
|
222
|
-
}
|
|
223
|
-
},
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Both surfaces coexist. For new code the hook API on `createCopilotRuntimeHandler` is
|
|
229
|
-
preferred — `onBeforeHandler` receives typed `route` info, so you don't string-match paths.
|
|
230
|
-
|
|
231
|
-
Source: `packages/runtime/src/v2/runtime/core/hooks.ts:84-117`; maintainer Phase 4c.
|
|
232
|
-
|
|
233
|
-
### MEDIUM Route-specific auth in global beforeRequestMiddleware
|
|
234
|
-
|
|
235
|
-
Wrong:
|
|
236
|
-
|
|
237
|
-
```typescript
|
|
238
|
-
new CopilotRuntime({
|
|
239
|
-
agents,
|
|
240
|
-
beforeRequestMiddleware: async ({ path, request }) => {
|
|
241
|
-
if (path.includes("/agent/admin/")) {
|
|
242
|
-
/* ... */
|
|
243
|
-
}
|
|
244
|
-
},
|
|
245
|
-
});
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
Correct:
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
createCopilotRuntimeHandler({
|
|
252
|
-
runtime,
|
|
253
|
-
basePath: "/api/copilotkit",
|
|
254
|
-
hooks: {
|
|
255
|
-
onBeforeHandler: ({ route, request }) => {
|
|
256
|
-
if (route.method === "agent/run" && route.agentId === "admin") {
|
|
257
|
-
/* ... */
|
|
258
|
-
}
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
`beforeRequestMiddleware` fires before routing, so no route info exists yet — string-matching
|
|
265
|
-
paths is fragile. `onBeforeHandler` fires after routing with typed `route.method`, `route.agentId`.
|
|
266
|
-
|
|
267
|
-
Source: `packages/runtime/src/v2/runtime/core/hooks.ts:94-103`.
|
|
268
|
-
|
|
269
|
-
### MEDIUM Blocking on afterRequestMiddleware
|
|
270
|
-
|
|
271
|
-
Wrong:
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
new CopilotRuntime({
|
|
275
|
-
agents,
|
|
276
|
-
afterRequestMiddleware: async ({ response, threadId, messages }) => {
|
|
277
|
-
await heavyAnalytics(response, threadId, messages);
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
Correct:
|
|
283
|
-
|
|
284
|
-
```typescript
|
|
285
|
-
new CopilotRuntime({
|
|
286
|
-
agents,
|
|
287
|
-
afterRequestMiddleware: async ({ response, threadId, messages }) => {
|
|
288
|
-
void queue.enqueue({ type: "chat", threadId, messages, response });
|
|
289
|
-
},
|
|
290
|
-
});
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
The `afterRequestMiddleware` callback receives
|
|
294
|
-
`{ runtime, response, path, messages?, threadId?, runId? }` — all these fields are always
|
|
295
|
-
available (`messages`/`threadId`/`runId` are populated from the SSE stream when present,
|
|
296
|
-
undefined otherwise). The hook runs non-blocking via `.catch()` so errors only log and any
|
|
297
|
-
heavy awaited work can be lost on process exit — fire-and-forget is the intended shape.
|
|
298
|
-
|
|
299
|
-
Source: `packages/runtime/src/v2/runtime/core/fetch-handler.ts:225-234`.
|
|
300
|
-
|
|
301
|
-
### MEDIUM Passing a webhook URL string as middleware
|
|
302
|
-
|
|
303
|
-
Wrong:
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
new CopilotRuntime({
|
|
307
|
-
agents,
|
|
308
|
-
beforeRequestMiddleware: "https://hooks.example/auth" as any,
|
|
309
|
-
});
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Correct:
|
|
313
|
-
|
|
314
|
-
```typescript
|
|
315
|
-
new CopilotRuntime({
|
|
316
|
-
agents,
|
|
317
|
-
beforeRequestMiddleware: async ({ request }) => {
|
|
318
|
-
await fetch("https://hooks.example/auth", {
|
|
319
|
-
method: "POST",
|
|
320
|
-
body: request.headers.get("authorization") ?? "",
|
|
321
|
-
});
|
|
322
|
-
},
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
Webhook-URL middleware is dead code in v2 — the runtime logs
|
|
327
|
-
`"Unsupported beforeRequestMiddleware value – skipped"` and does nothing. Only function
|
|
328
|
-
middleware is wired.
|
|
329
|
-
|
|
330
|
-
Source: `packages/runtime/src/v2/runtime/core/middleware.ts:72-87`.
|
|
331
|
-
|
|
332
|
-
### HIGH Implementing auth / rate-limit inside CopilotKit middleware
|
|
333
|
-
|
|
334
|
-
Wrong:
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
new CopilotRuntime({
|
|
338
|
-
agents,
|
|
339
|
-
beforeRequestMiddleware: async ({ request }) => {
|
|
340
|
-
// hand-rolling a token-bucket rate limiter inline with Redis calls...
|
|
341
|
-
},
|
|
342
|
-
});
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
Correct:
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
import { Ratelimit } from "@upstash/ratelimit";
|
|
349
|
-
import { Redis } from "@upstash/redis";
|
|
350
|
-
|
|
351
|
-
const ratelimit = new Ratelimit({
|
|
352
|
-
redis: Redis.fromEnv(),
|
|
353
|
-
limiter: Ratelimit.slidingWindow(60, "1 m"),
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
new CopilotRuntime({
|
|
357
|
-
agents,
|
|
358
|
-
beforeRequestMiddleware: async ({ request }) => {
|
|
359
|
-
const { success } = await ratelimit.limit(
|
|
360
|
-
request.headers.get("x-user-id") ?? "anon",
|
|
361
|
-
);
|
|
362
|
-
if (!success) throw new Response("Too Many Requests", { status: 429 });
|
|
363
|
-
},
|
|
364
|
-
});
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
Auth, rate-limiting, and observability are server-framework concerns. CopilotKit middleware
|
|
368
|
-
is the hook to invoke them, not a replacement.
|
|
369
|
-
|
|
370
|
-
Source: maintainer interview (Phase 2c).
|
|
371
|
-
|
|
372
|
-
## See also
|
|
373
|
-
|
|
374
|
-
- `copilotkit/setup-endpoint` — `hooks` are passed to `createCopilotRuntimeHandler`
|
|
375
|
-
- `copilotkit/go-to-production` — production checklist lists auth/rate-limit wiring
|
|
376
|
-
- `copilotkit/debug-and-troubleshoot` — `onError` telemetry pattern
|