Upgrading from v1 to v2? v2 replaces the old generated entry flow with an explicit app.ts, supports Hono-native routers, and moves build config into standard vite.config.ts. This guide focuses on the changes the current SDK actually requires and what the migration tool will rewrite for you.
What's Changed in v2
v2 introduces seven fundamental architectural changes:
1. Agents are Declarative
v1: Agents were auto-discovered from src/agent/*/agent.ts files at runtime.
v2: Import agents explicitly into createApp(). This keeps the registered agents visible in one place and ensures they are included for runtime registration and agent-to-agent calls:
// app.ts
import { createApp } from '@agentuity/runtime';
import agents from './src/agent';
export default await createApp({
agents,
});Type safety for ctx.invoke() comes from direct imports. Listing agents in createApp() controls what the app registers and bundles.
2. No More setup/shutdown Hooks
v1: Lifecycle hooks in createApp():
createApp({
setup: async (ctx) => { /* initialize */ },
shutdown: async (ctx) => { /* cleanup */ }
});v2: Use standard patterns:
- Initialization: Module-level code for env-independent setup, agent
setup()for env-dependent SDK clients - Cleanup:
registerShutdownHook()or Bun process hooks
3. Explicit Router for Typed Mounts
v1: File-based auto-discovery — routes in src/api/*.ts were automatically mounted.
v2: If you want an exported router type for hc<ApiRouter>(), create a root router in src/api/index.ts and pass it to createApp():
// app.ts
import { createApp } from '@agentuity/runtime';
import api from './src/api/index';
import agents from './src/agent';
export default await createApp({
router: { path: '/api', router: api },
agents,
});Compose route modules into a single exported router type when you want to import that type from a frontend or another app.
4. Use a Router Shape That Preserves Types
v1: createRouter() was a wrapper around Hono:
import { createRouter } from '@agentuity/runtime';
const router = createRouter();
router.get('/hello', async (c) => c.json({ msg: 'hi' }));v2: Both createRouter() and new Hono<Env>() work. The key change is to keep the root router in a chained expression when you want to export its type for Hono RPC:
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
const router = new Hono<Env>()
.get('/hello', async (c) => c.json({ msg: 'hi' }));Use chained methods (.get().post()) instead of mutating style (router.get()). Only chained methods preserve types for hc<ApiRouter>().
5. Legacy Transport Helpers Removed
v1: Older frontend code often relied on Agentuity-specific transport helpers and generated client types.
v2: Replace those legacy client patterns with Hono's native RPC client or your preferred data fetching library. If you're migrating older frontend code, look for imports from @agentuity/react that construct API clients or wrap route calls:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api';
const client = hc<ApiRouter>('/api');
// Direct usage
const res = await client.hello.$get();
const data = await res.json();v2 doesn't prescribe a data fetching approach. Use whatever works for your app:
- TanStack Query — caching, background updates, optimistic updates
- SWR — stale-while-revalidate pattern
- RTK Query — if you're already using Redux Toolkit
- Hono client directly — simple cases without extra dependencies
6. Standard vite.config.ts
v1: Vite config lived inside agentuity.config.ts:
// agentuity.config.ts
export default {
workbench: { route: '/workbench' },
// Vite plugins were added here too
};v2: Use standard vite.config.ts and move runtime settings to createApp():
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()], // Add plugins for your frontend framework
});// app.ts
import { createApp } from '@agentuity/runtime';
export default await createApp({
analytics: true, // or { trackClicks: false, ... }
});The CLI creates a React fallback config only when vite.config.ts is missing. Existing config files are passed to Vite directly, so include the plugin for your frontend framework:
- React:
@vitejs/plugin-react - Vue:
@vitejs/plugin-vue - Svelte:
@sveltejs/vite-plugin-svelte - Solid:
vite-plugin-solid
7. Move Env-Dependent Agent Clients into setup()
v1: Agent files often constructed SDK clients at module scope.
v2: The build system imports agent files to read metadata. Keep clients that need environment variables inside agent setup() so build-time discovery does not require those variables:
import OpenAI from 'openai';
import { createAgent } from '@agentuity/runtime';
import { s } from '@agentuity/schema';
export default createAgent('chat', {
schema: {
input: s.object({ prompt: s.string() }),
output: s.object({ answer: s.string() }),
},
setup: async () => ({
client: new OpenAI(),
}),
handler: async (ctx, input) => {
const completion = await ctx.config.client.chat.completions.create({
model: 'gpt-5.4-nano',
messages: [{ role: 'user', content: input.prompt }],
});
return {
answer: completion.choices[0]?.message?.content ?? '',
};
},
});These changes make v2 more explicit and predictable:
- Type safety: Hono's RPC client (
hc<ApiRouter>()) gets full type inference - Control: You decide what's registered and in what order
- Simplicity: No hidden magic, standard Hono/Bun patterns work everywhere
Usage
npx @agentuity/migrate [project-dir] [options]Run in your project root (or pass a path). The tool checks that your git worktree is clean before touching anything, so you can always git diff to review changes or git checkout . to roll back.
Options
| Flag | Description |
|---|---|
--yes, -y | Skip interactive confirmation |
--dry-run | Print the migration report without modifying files |
--help, -h | Show help |
What it migrates
Auto-fixable (fully automated)
| Finding | Action |
|---|---|
v1-generated files under src/generated/ | Deleted. Run agentuity build afterward so the current v2 build recreates any generated files it still needs |
bootstrapRuntimeEnv() call in app.ts | Removed (createApp handles it) |
v1 createRouter() + mutating .get()/.post() route files | Rewritten to new Hono<Env>() chained style |
Missing src/api/index.ts barrel | Generated from discovered route files |
Missing src/agent/index.ts barrel | Generated from discovered agent files |
Guided (applied with your review)
| Finding | What happens |
|---|---|
setup in createApp() | Migration comment added — move init to module level |
shutdown in createApp() | Guidance to move cleanup into registerShutdownHook() or Bun process hooks |
No router/agents in createApp() | Guidance shown — wire up the generated barrels |
agentuity.config.ts has Vite keys | Guidance to create vite.config.ts with plugins/define/render |
agentuity.config.ts has runtime settings | Remove — keep them in createApp() |
agentuity.config.ts empty | Can be deleted |
Manual (instructions only, no auto-transform)
| Finding | Guidance |
|---|---|
| Frontend files using removed transport helpers or generated client registry types | Replace with hc<ApiRouter>() from hono/client or your preferred data fetching library |
Step-by-Step Migration
Step 1: Run the Migration Tool
npx @agentuity/migrateThis will analyze your project and present a migration report. Use --dry-run first to preview changes without modifying files.
Step 2: Review Auto-fixes
The tool will automatically:
- Delete old v1-generated files under
src/generated/ - Remove
bootstrapRuntimeEnv()calls (handled bycreateApp()now) - Rewrite route files from mutating style to chained Hono style
After the migration finishes, run agentuity build so the current v2 build can recreate any generated files it still uses.
Before (v1 mutating style):
import { createRouter } from '@agentuity/runtime';
const router = createRouter();
router.get('/hello', async (c) => {
return c.json({ message: 'Hello' });
});
router.post('/echo', async (c) => {
const body = await c.req.json();
return c.json(body);
});
export default router;After (v2 chained style):
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
const router = new Hono<Env>()
.get('/hello', async (c) => {
return c.json({ message: 'Hello' });
})
.post('/echo', async (c) => {
const body = await c.req.json();
return c.json(body);
});
export default router;The chained style lets hc<ApiRouter>() from hono/client infer route types in your frontend:
import { hc } from 'hono/client';
import type { ApiRouter } from './api';
const client = hc<ApiRouter>('/api');
const res = await client.hello.$get();Step 3: Handle Guided Migrations
Review and apply the guided migrations:
Setup/Shutdown Lifecycle
v2 no longer expects the old app-level setup/shutdown flow from v1. Move startup work to modules or agent-level setup, and move cleanup into runtime or Bun shutdown hooks:
- Initialization: Run at module level (executes when the file loads)
- Cleanup: Use
registerShutdownHook()or Bun's process hooks
Before (v1):
// app.ts
import { createApp } from '@agentuity/runtime';
export default createApp({
setup: async (ctx) => {
// Initialize database and connections
ctx.app.db = await connectDB();
},
shutdown: async (ctx) => {
// Cleanup
await ctx.app.db.close();
}
});After (v2):
// db.ts - Initialize at module level
export const db = await connectDB();
// app.ts
import { createApp, registerShutdownHook } from '@agentuity/runtime';
import { db } from './db';
registerShutdownHook(async () => {
await db.close();
});
export default await createApp({
// No app-level setup/shutdown hooks
});Configuration Consolidation
Move all configuration from agentuity.config.ts to either:
createApp()inapp.tsfor runtime config (analytics, cors, compression)vite.config.tsfor build config (plugins, define, render)
Before (v1):
// agentuity.config.ts
export default {
workbench: { route: '/workbench' },
// plugins and other vite config
};After (v2):
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
define: { 'process.env': {} }
});
// app.ts
import { createApp } from '@agentuity/runtime';
export default await createApp({
analytics: true
});Step 4: Update API Client
If your frontend still uses a v1-style client, remove the old Agentuity transport layer and switch to Hono's RPC client directly:
After (v2):
import { hc } from 'hono/client';
import type { ApiRouter } from '../api';
const client = hc<ApiRouter>('/api');
const result = await client.hello.$get();Step 5: Wire Up Barrels
The migration tool generates barrel files:
src/api/index.ts:
import helloRouter from './hello/route';
import echoRouter from './echo/route';
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
const api = new Hono<Env>()
.route('/hello', helloRouter)
.route('/echo', echoRouter);
export default api;
export type ApiRouter = typeof api;src/agent/index.ts:
import helloAgent from './hello/agent';
import echoAgent from './echo/agent';
export default [helloAgent, echoAgent];Wire these into your app.ts:
import { createApp } from '@agentuity/runtime';
import api from './src/api/index';
import agents from './src/agent';
export default await createApp({
router: { path: '/api', router: api },
agents
});Step 6: Update Cron Routes
v2 changes the cron() signature and introduces platform-managed scheduling.
Updated cron() Signature
The 2-argument form cron(schedule, handler) is deprecated. Use the 3-argument form with an explicit auth option:
Before (v1):
import { cron } from '@agentuity/runtime';
router.post('/daily-cleanup', cron('0 0 * * *', (c) => {
c.var.logger.info('Running daily cleanup');
return { status: 'done' };
}));After (v2):
import { cron } from '@agentuity/runtime';
router.post('/daily-cleanup', cron('0 0 * * *', { auth: true }, (c) => {
c.var.logger.info('Running daily cleanup');
return { status: 'done' };
}));The deprecated form still works but defaults to auth: false. Set auth: true to require signed requests (recommended for deployed apps). See Scheduling Cron Jobs for the full cron route API.
Platform-Managed Schedules
v2 also adds @agentuity/schedule for platform-managed scheduling with delivery tracking. See Schedules for the complete API:
import { ScheduleClient } from '@agentuity/schedule';
const client = new ScheduleClient();
await client.create({
name: 'Hourly Sync',
expression: '0 * * * *',
destinations: [{ type: 'url', config: { url: 'https://example.com/sync' } }],
});- Cron routes (
cron()from@agentuity/runtime): in-process route handlers that run on a schedule with route services onc.var.* - Platform schedules (
ScheduleClientfrom@agentuity/schedule): managed by Agentuity with delivery tracking and multi-destination support
These coexist and serve different use cases.
Troubleshooting
Issue 1: "Cannot find module '@agentuity/react' client exports"
Cause: v2 removed the old transport helpers from @agentuity/react.
Solution: Replace with hc() from hono/client:
import { hc } from 'hono/client';
import type { ApiRouter } from '../api';
const client = hc<ApiRouter>('/api');Issue 2: Exporting a Router Type for hc<...>()
Cause: Hono RPC works best when your root router is composed in a single chained expression and exported from src/api/index.ts.
Solution: Export a single root router type. Both createRouter() and new Hono<Env>() work:
import { Hono } from 'hono';
import type { Env } from '@agentuity/runtime';
const router = new Hono<Env>();Issue 3: "ctx.app is empty" or "ctx.app state not available"
Cause: v1 apps often populated shared dependencies through ctx.app. New v2 code should prefer module-level dependencies or explicit services instead of relying on that old pattern.
Solution: Initialize shared dependencies in modules and import them where needed:
// Before: setup() populated ctx.app
// createApp({ setup: async (ctx) => { ctx.app.db = await connectDB(); } })
// After: initialize at module level and import directly
// db.ts
export const db = await connectDB();
// agent handler
import { db } from '../db';
// use db directly instead of ctx.app.dbGetting Help
If you encounter issues not covered in this guide:
- Check the Documentation: Visit the SDK Reference for detailed API docs on agents, routes, storage, and context
- Review Examples: Browse the Cookbook for working patterns like chat with history, cron with storage, and webhook handlers
- Community Support: Join our Discord community for help
- Report Issues: Open an issue on GitHub if you find bugs