Skip to content

Deployment

Kuralle agents run anywhere that runs Node.js, Bun, or Cloudflare Workers. The runtime has no external process dependencies — serve it with whatever HTTP framework you prefer.

@kuralle-agents/hono-server mounts a full set of endpoints onto a Hono app with one call.

Terminal window
npm install @kuralle-agents/hono-server hono @hono/node-server @hono/node-ws
server.ts
import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { createNodeWebSocket } from '@hono/node-ws';
import { createRuntime, defineAgent } from '@kuralle-agents/core';
import { createKuralleChatRouter } from '@kuralle-agents/hono-server';
import { openai } from '@ai-sdk/openai';
const agent = defineAgent({
id: 'support',
instructions: 'You are a helpful support agent.',
model: openai('gpt-4o-mini'),
});
const runtime = createRuntime({ agents: [agent], defaultAgentId: 'support' });
const app = new Hono();
const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app });
app.route('/', createKuralleChatRouter({ runtime, upgradeWebSocket }));
const server = serve({ fetch: app.fetch, port: 3000 });
injectWebSocket(server);

Bun doesn’t need @hono/node-server or createNodeWebSocket. Use Bun’s built-in WebSocket upgrade instead:

import { upgradeWebSocket } from 'hono/bun';
const app = new Hono();
app.route('/', createKuralleChatRouter({ runtime, upgradeWebSocket }));
export default app;

createKuralleChatRouter mounts these routes:

| Method | Path | Description | |---|---|---| | POST | /api/chat | Single-turn JSON response | | POST | /api/chat/sse | SSE streaming response | | POST | /api/chat/stream | Chunked text stream | | GET | /agents/chat/:sessionId | WebSocket widget endpoint | | GET | /ws/:sessionId | WebSocket turn endpoint | | GET | /api/session/:id | Fetch session | | DELETE | /api/session/:id | Delete session | | GET | /health | Health check |

@kuralle-agents/cf-agent runs Kuralle agents on Cloudflare Workers with Durable Objects. Subclass KuralleAgent, implement two methods, and Cloudflare handles SQLite persistence, multi-client sync, and stream resumability.

Terminal window
npm install @kuralle-agents/cf-agent
import { KuralleAgent } from '@kuralle-agents/cf-agent';
import { defineAgent } from '@kuralle-agents/core';
import { createOpenAI } from '@ai-sdk/openai';
interface Env {
OPENAI_API_KEY: string;
}
export class SupportAgent extends KuralleAgent<Env> {
protected getAgents() {
const openai = createOpenAI({ apiKey: this.env.OPENAI_API_KEY });
return [
defineAgent({
id: 'support',
instructions: 'You are a helpful support agent.',
model: openai('gpt-4o-mini'),
}),
];
}
protected getDefaultAgentId() {
return 'support';
}
}
export default SupportAgent;

Declare the Durable Object in wrangler.toml:

[[durable_objects.bindings]]
name = "SUPPORT_AGENT"
class_name = "SupportAgent"
[[migrations]]
tag = "v1"
new_sqlite_classes = ["SupportAgent"]

Flows, tools, and structured routing all work identically on Cloudflare — the same defineAgent primitive, no runtime differences.

Use handle.toResponseStream('sse') to pipe a TurnHandle directly to an HTTP response without createKuralleChatRouter:

app.post('/chat', async (c) => {
const { input, sessionId } = await c.req.json();
const handle = runtime.run({ input, sessionId });
return handle.toResponseStream('sse');
});

createKuralleChatRouter uses this internally and also wires sessions, WebSocket, and the full endpoint set.