SvelteKit
Use Better Agent in a SvelteKit app with the Svelte createAgentChat store.
Server
// src/lib/better-agent/server.ts
import { env } from "$env/dynamic/private";
import { betterAgent, defineAgent } from "@better-agent/core";
import { createOpenAI } from "@better-agent/openai";
const openai = createOpenAI({
apiKey: env.OPENAI_API_KEY,
});
const supportAgent = defineAgent({
name: "support",
model: openai("gpt-5.5"),
instruction: "You help customers.",
});
const app = betterAgent({
agents: [supportAgent],
basePath: "/api/agents",
});
export default app;Route
Create a rest endpoint and forward every method to app.handler.
// src/routes/api/agents/[...path]/+server.ts
import app from "$lib/better-agent/server";
import type { RequestHandler } from "./$types";
const handle: RequestHandler = ({ request }) => app.handler(request);
export const GET = handle;
export const POST = handle;
export const PUT = handle;
export const PATCH = handle;
export const DELETE = handle;
export const OPTIONS = handle;
export const HEAD = handle;Client
// src/lib/better-agent/client.ts
import { createClient } from "@better-agent/client";
import type app from "$lib/better-agent/server";
export const client = createClient<typeof app>({
baseURL: "/api/agents",
});Keep basePath, the route path, and baseURL aligned.
Basic chat
createAgentChat gives you a Svelte store plus actions.
<script lang="ts">
import { createAgentChat } from "@better-agent/client/svelte";
import { client } from "$lib/better-agent/client";
const chat = createAgentChat(client.agent("support"), {
threadId: "main",
});
let input = $state("");
</script>
<form
onsubmit={(event) => {
event.preventDefault();
const message = input.trim();
if (!message) return;
input = "";
chat.sendMessage(message);
}}
>
{#each $chat.messages as message (message.id)}
<p>{message.role}</p>
{/each}
{#if $chat.error}
<p>{$chat.error.message}</p>
{/if}
<input bind:value={input} />
<button disabled={$chat.isRunning}>Send</button>
<button type="button" onclick={() => chat.stop()} disabled={!$chat.isRunning}>
Stop
</button>
</form>Threads
Pass threadId to continue a saved conversation. Memory needs storage to
survive reloads and deploys.
<script lang="ts">
const chat = createAgentChat(client.agent("support"), {
threadId: "customer-123",
});
</script>Load messages
Memory-enabled agents expose helpers for loading and switching threads.
<button onclick={() => chat.loadMessages()}>Reload</button>
<button onclick={() => chat.selectThread("customer-456")}>
Switch thread
</button>
<button onclick={() => chat.clearThread()}>New thread</button>Context
Use context for stable per-chat values. You can also pass context per
message.
<script lang="ts">
const chat = createAgentChat(client.agent("support"), {
context: {
userId: "user_123",
plan: "pro",
},
});
async function reviewPlan() {
await chat.sendMessage("Review my plan.", {
context: {
userId: "user_123",
plan: "enterprise",
},
});
}
</script>Client tools
Register handlers for tools that run in the browser. Matching client tools resolve and resume automatically.
<script lang="ts">
const chat = createAgentChat(client.agent("support"), {
toolHandlers: {
confirm_address: async (input) => {
const confirmed = window.confirm(input.address);
return { confirmed };
},
},
});
</script>When no handler exists, use pendingClientTools to render your own UI.
{#each $chat.pendingClientTools as tool (tool.interruptId)}
<p>{tool.toolName}</p>
{/each}Approvals
When a server tool waits for approval, render pendingToolApprovals.
{#each $chat.pendingToolApprovals as approval (approval.interruptId)}
<div>
<p>{approval.toolName}</p>
<button onclick={() => chat.approveToolCall(approval.interruptId)}>
Approve
</button>
<button onclick={() => chat.rejectToolCall(approval.interruptId)}>
Reject
</button>
</div>
{/each}Events
Use onEvent for progress UI, logging, analytics, or custom event handling.
<script lang="ts">
import { EventType } from "@better-agent/core";
const chat = createAgentChat(client.agent("support"), {
onEvent(event) {
if (event.type === EventType.TEXT_MESSAGE_CONTENT) {
console.log(event.delta);
}
},
});
</script>Finish and errors
Use lifecycle callbacks for final messages, interrupts, aborts, and errors.
<script lang="ts">
const chat = createAgentChat(client.agent("support"), {
onFinish(finish) {
console.log(finish.runId, finish.generatedMessages);
},
onError(error) {
console.error(error.code, error.message);
},
});
</script>Resume
Use resume when you have a saved run cursor.
<script lang="ts">
const chat = createAgentChat(client.agent("support"), {
resume: {
runId: "run_123",
afterSequence: 42,
},
});
async function reconnect() {
await chat.resume();
}
</script>Next
See Client, Tools, Human in the Loop, Memory, Events, and Storage.