Deployment modes
Deployment modes
The shared ChatDefinition is unchanged across deployment modes. Only the
adapter, trusted host context, storage, and native renderer vary.
Web-standard server — recommended
Mount createChatHandler from @agentskit/chat/server in a Node, serverless,
or edge route that supports Web Request, Response, ReadableStream, and
AbortSignal. Authenticate before resolving a definition or parsing untrusted
input. Keep provider keys, authorization, tenant context, durable storage, and
audit sinks on the server.
Host adapter recipes
createChatHandler already speaks the Web Fetch API. Host frameworks only need
to forward the request and return the response.
Next.js App Router
// app/api/chat/route.ts
import { createChatHandler } from '@agentskit/chat/server'
import { verifyBearer } from '@/lib/auth'
import { chats } from '@/lib/chats'
import { sessions } from '@/lib/session-storage'
const handler = createChatHandler({
authenticate: async request => {
const identity = await verifyBearer(request)
return identity
? { ok: true, context: identity }
: { ok: false, response: new Response('Unauthorized', { status: 401 }) }
},
resolveDefinition: context => chats.forTenant(context?.tenantId),
sessionStorage: context => sessions.forTenant(context?.tenantId),
})
export const POST = (request: Request) => handler(request)
Hono
import { Hono } from 'hono'
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* authenticate, resolveDefinition, sessionStorage */ })
const app = new Hono()
app.post('/api/chat', c => chat(c.req.raw))
export default app
Express (Node 18+)
import express from 'express'
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* … */ })
const app = express()
app.post('/api/chat', async (req, res) => {
const disconnected = new AbortController()
const abort = () => { if (!res.writableEnded) disconnected.abort() }
req.once('aborted', abort)
res.once('close', abort)
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined
try {
const headers = new Headers()
for (const [key, value] of Object.entries(req.headers)) {
if (typeof value === 'string') headers.set(key, value)
else if (Array.isArray(value)) value.forEach(item => headers.append(key, item))
}
const request = new Request(`http://${req.headers.host}${req.url}`, {
method: 'POST',
headers,
body: req,
duplex: 'half',
signal: disconnected.signal,
} as RequestInit)
const response = await chat(request)
reader = response.body?.getReader()
res.status(response.status)
response.headers.forEach((value, key) => res.setHeader(key, value))
if (!reader) return res.end()
while (true) {
const result = await reader.read()
if (result.done) break
res.write(result.value)
}
res.end()
} finally {
if (disconnected.signal.aborted) await reader?.cancel().catch(() => undefined)
req.removeListener('aborted', abort)
res.removeListener('close', abort)
}
})
Cloudflare Worker
import { createChatHandler } from '@agentskit/chat/server'
const chat = createChatHandler({ /* … */ })
export default {
fetch(request: Request): Promise<Response> {
const url = new URL(request.url)
if (request.method === 'POST' && url.pathname === '/api/chat') return chat(request)
return new Response('Not found', { status: 404 })
},
}
Mount createAskServiceHandler beside it when deterministic misses need cited
semantic retrieval. Hosted routes and self-hosted Node bridges import the same
factory; only the thin HTTP bridge varies. See the
Ask backend deployment guide.
Direct trusted runtime
A server process, desktop main process, or controlled terminal may inject an
AgentsKit adapter directly into defineChat. Browser and mobile clients must
not receive provider secrets. Use a server adapter or the shared Ask adapter
for those clients.
Browser and SSR hosts
React, Vue, Svelte, Solid, and Angular use their native renderer package. Keep
the definition in a framework-neutral module. SSR may render the shell, but a
chat session starts only where the selected AgentsKit binding and transport are
available. Persist messages through an upstream ChatMemory; AgentsKit Chat
session storage contains application metadata, not a second message history.
DOM parity demos for React, Vue, Svelte, and Solid live under apps/example-*
and share @agentskit/chat-example-shared.
React Native / Expo
Use @agentskit/chat/react-native and call a trusted backend. Storage,
navigation, safe-area layout, and platform permissions stay host-owned. The
release gate exercises native-mobile accessibility contracts and Expo web/iOS
production bundles.
Ink / terminal
Use @agentskit/chat/ink in an interactive TTY. Unsupported visual components
render their validated semantic fallback. The PTY gate covers keyboard submit,
choice, confirmation, lifecycle, stop, focus, and exit behavior.
Deterministic and degraded operation
Deterministic routes and verified local answer artifacts may resolve without a model. Unknown input delegates to the injected AgentsKit adapter when policy allows. Configure explicit offline/escalation behavior; never silently invent an answer when a required backend is unavailable.
See server details, Ask backend, sessions, and the security policy.