Using SvelteKit with Agentuity

Add Agentuity service clients, local development, and deploy validation to a SvelteKit app

Start with a normal SvelteKit app. Agentuity adds service clients, local development support, and deploy validation around the file-based routing and build output you already use.

Create a Starter

npm create agentuity -- --name my-sveltekit-app --framework sveltekit
cd my-sveltekit-app

The starter calls sv create under the hood, then adds @agentuity/cli, installs @sveltejs/adapter-node, adds a deploy script, and overlays an AI example using +page.server.ts form actions.

Key files in the AI example:

FilePurpose
src/routes/+page.sveltePage UI with Svelte 5 runes
src/routes/+page.server.tsServer actions that call a model
svelte.config.jsAdapter, asset path, and compiler config
package.jsondev, build, and deploy scripts

The starter sets kit.paths.relative to false, so SvelteKit emits root-relative build asset URLs during server rendering unless you configure an external asset path.

Import an Existing App

For an existing SvelteKit app, add the CLI and register the project:

npm install -D @agentuity/cli
npx agentuity project import --validate-only
npx agentuity project import

npm install -D installs the CLI as a project-local dev dependency, not a global install. Use npx to run that local CLI. For Bun, pnpm, and Yarn equivalents, see Local versus global CLI.

agentuity project import writes the local project metadata that agentuity deploy expects. Run it once before the first deploy. After that, agentuity dev runs the SvelteKit dev script with the Agentuity environment values your app needs.

What Agentuity adds

AreaSvelteKit keepsAgentuity adds
routing+server.ts, +page.server.ts, and normal file routingservice clients imported in server-only modules
local devVite and SvelteKit dev serveragentuity dev supplies service credentials
buildSvelteKit adapter outputagentuity build runs the build and packages launch metadata
deploythe adapter server entryagentuity deploy ships the packaged app

Add an API Route

SvelteKit +server.ts files define pure JSON endpoints. Put Agentuity service clients here; never import them in client-side <script> blocks.

The route below uses AIGatewayClient for the model call. The project SDK key authenticates the request, and the model ID is app configuration or request input.

npm install @agentuity/aigateway @agentuity/keyvalue valibot
typescriptsrc/routes/api/translate/+server.ts
import { AIGatewayClient } from '@agentuity/aigateway';
import { KeyValueClient } from '@agentuity/keyvalue';
import * as v from 'valibot';
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
 
const DEFAULT_MODEL = 'openai/gpt-5.4-mini';
const HISTORY_NAMESPACE = 'translation-history';
const SESSION_COOKIE = 'agentuity_session';
const SESSION_TTL_SECONDS = 60 * 60 * 24 * 30;
 
interface HistoryEntry {
  readonly model: string;
  readonly sessionId: string;
  readonly text: string;
  readonly timestamp: string;
  readonly toLanguage: string;
  readonly translation: string;
}
 
interface HistoryState {
  readonly history: readonly HistoryEntry[];
  readonly translationCount: number;
}
 
const requestSchema = v.object({
  model: v.optional(v.string()),
  text: v.string(),
  toLanguage: v.string(),
});
 
// Instantiated once at module level; reads AGENTUITY_SDK_KEY from env
const kv = new KeyValueClient(); 
const gateway = new AIGatewayClient(); 
 
export const POST: RequestHandler = async ({ cookies, request }) => {
  const body: unknown = await request.json();
  const parsed = v.safeParse(requestSchema, body);
 
  if (!parsed.success) {
    return json({ error: 'text and toLanguage are required' }, { status: 400 });
  }
 
  const input = parsed.output;
  const model = input.model ?? DEFAULT_MODEL;
 
  // Read or create a session cookie to scope history per browser session
  const sessionId = cookies.get(SESSION_COOKIE) ?? crypto.randomUUID();
  cookies.set(SESSION_COOKIE, sessionId, {
    httpOnly: true,
    path: '/',
    sameSite: 'lax',
    maxAge: SESSION_TTL_SECONDS,
  });
 
  const result = await gateway.completeText({ 
    model,
    messages: [
      {
        role: 'user',
        content: `Translate to ${input.toLanguage}:\n\n${input.text}`,
      },
    ],
  });
 
  if (!result.hasText) {
    return json({ error: 'model returned no text' }, { status: 502 });
  }
 
  const translation = result.text;
 
  // Append to the last 5 entries stored for this session
  const previous = await kv.get<HistoryState>(HISTORY_NAMESPACE, sessionId);
  const history = [
    ...(previous.exists ? previous.data.history : []),
    {
      model,
      sessionId,
      text: input.text,
      timestamp: new Date().toISOString(),
      toLanguage: input.toLanguage,
      translation,
    },
  ].slice(-5);
  const translationCount = (previous.exists ? previous.data.translationCount : 0) + 1;
  await kv.set(HISTORY_NAMESPACE, sessionId, { history, translationCount }, { ttl: SESSION_TTL_SECONDS }); 
 
  return json({ translation, history, model, translationCount });
};

KeyValueClient reads AGENTUITY_SDK_KEY from the environment. Run the app via agentuity dev so the linked project key is available to server routes.

Use the model id exactly as it appears in the AI Gateway model catalog. Use a provider SDK directly only when the route depends on provider-specific APIs.

Call the Route from a Component

Svelte 5 uses rune-based reactivity ($state, $props). The component calls the server route directly:

sveltesrc/routes/+page.svelte
<script lang="ts">
  let text = $state('Welcome to Agentuity');
  let toLanguage = $state('Spanish');
  let translation = $state('');
  let history: Array<{ text: string; translation: string; toLanguage: string }> = $state([]);
  let loading = $state(false);
 
  async function translate() {
    loading = true;
    try {
      const response = await fetch('/api/translate', { 
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ text, toLanguage }),
      });
      const result = await response.json();
      translation = result.translation;
      history = result.history;
    } finally {
      loading = false;
    }
  }
</script>
 
<textarea bind:value={text} rows="4" placeholder="Text to translate"></textarea>
<button onclick={translate} disabled={loading}>
  {loading ? 'Translating…' : 'Translate'}
</button>
 
{#if translation}
  <p>{translation}</p>
{/if}

Run Locally

agentuity dev supplies AGENTUITY_SDK_KEY and a PORT value to the dev process. If you want npm run dev to go through Agentuity on a fixed port, wrap the framework command like this:

jsonpackage.json
{
  "scripts": {
    "dev": "agentuity dev --script dev:start",
    "dev:start": "vite dev --port 3000",
    "build": "vite build",
    "deploy": "agentuity deploy"
  }
}

Then run:

npm run dev

Smoke-test the JSON route on port 3000:

curl http://localhost:3000/api/translate \
  -H "content-type: application/json" \
  -d '{"text":"Hello","toLanguage":"Spanish"}'

The --port 3000 flag is required because Vite ignores the PORT env var. Frameworks like Next.js read PORT directly, while Nuxt's native nuxt dev flow does not need this wrapper pattern.

Deployed Environment Variables

Configure the variables your deployed app needs. Direct AIGatewayClient calls use AGENTUITY_SDK_KEY; the model ID in this example is normal app configuration or request input.

VariableUsed by
AGENTUITY_SDK_KEYAgentuity service clients and @agentuity/aigateway
DATABASE_URLdatabase server routes
AWS_*object storage server routes; see Object Storage for exact variables

Validate Before Deploy

Run the framework build when you want SvelteKit-only feedback, then run the Agentuity build. agentuity build runs the framework build during packaging and writes .agentuity/launch.json.

npm run build
npx agentuity build

After both succeed, inspect the launch metadata:

cat .agentuity/launch.json

SvelteKit requires a server adapter for SSR routes. The starter uses @sveltejs/adapter-node (one of the five official adapters), which produces a build/ directory with a index.js server entry. The launch.json command must start that entry. If it points at a static _serve.js helper, the packaged output is not serving your +server.ts routes.

Deploy only after the framework build, Agentuity build, and a local packaged validation of / and one API route pass:

agentuity deploy --confirm

Common Gotchas

SymptomCheck
Service client code appears in the browser bundleKeep KeyValueClient and AIGatewayClient in +server.ts or +page.server.ts, not in <script> blocks
Route works in dev but fails after agentuity buildInspect .agentuity/launch.json for the correct server entry path
Built assets 404 on nested routesConfirm kit.paths.relative is false; SvelteKit defaults to relative asset paths
Model call returns an auth errorVerify AGENTUITY_SDK_KEY is available to the server route. See AI Gateway
Cookies do not persist across requestsSet path: '/' when writing cookies from API routes
Route returns HTML instead of JSONAPI handlers go in +server.ts, not in +page.server.ts actions
Missing adapter error at buildConfirm svelte.config.js imports a server-capable adapter; @sveltejs/adapter-static has no server entry. See SvelteKit Adapters

Framework Docs

Next Steps

  • Key-Value Storage: store compact state from server routes with TTL
  • AI Gateway: route provider SDK calls through Agentuity for all supported models
  • App Configuration: configure agentuity.json, env vars, and scripts
  • Deployment: CLI deploy flags and lifecycle script behavior