Backend Setup
Backend Setup
Overview
Setting up an agent runtime backend involves:
- Configuring environment variables
- Implementing a PersistenceAdapter
- Creating the runtime with configuration
- Starting REST and WebSocket servers
Environment Variables
Required environment variables:
# Modal credentials (for sandbox creation)
MODAL_TOKEN_ID=your_modal_token_id
MODAL_TOKEN_SECRET=your_modal_token_secret
# Anthropic API key (for Claude agents)
ANTHROPIC_API_KEY=your_anthropic_api_key
Obtain Modal credentials from modal.com.
Minimal Server Example
import { createServer } from "http";
import { createAgentRuntime, type PersistenceAdapter } from "@hhopkins/agent-runtime";
// 1. Implement PersistenceAdapter (see references/types.md for full interface)
const persistence: PersistenceAdapter = {
// Session operations
listAllSessions: async () => [],
loadSession: async (sessionId) => null,
createSessionRecord: async (session) => {},
updateSessionRecord: async (sessionId, updates) => {},
// Storage operations
saveTranscript: async (sessionId, rawTranscript) => {},
saveWorkspaceFile: async (sessionId, file) => {},
deleteSessionFile: async (sessionId, path) => {},
// Agent profile operations
listAgentProfiles: async () => [{ id: "default", name: "Default Agent" }],
loadAgentProfile: async (agentProfileId) => ({
id: "default",
name: "Default Agent",
systemPrompt: "You are a helpful assistant.",
tools: ["Read", "Write", "Edit", "Bash"],
}),
};
async function main() {
// 2. Create runtime
const runtime = await createAgentRuntime({
persistence,
modal: {
tokenId: process.env.MODAL_TOKEN_ID!,
tokenSecret: process.env.MODAL_TOKEN_SECRET!,
appName: "my-agent-app",
},
idleTimeoutMs: 15 * 60 * 1000, // 15 minutes
syncIntervalMs: 30 * 1000, // 30 seconds
});
// 3. Start runtime (loads sessions, starts background jobs)
await runtime.start();
// 4. Create REST API server
const restApp = runtime.createRestServer({
apiKey: "your-api-key",
});
// 5. Create HTTP server and attach REST routes
const httpServer = createServer(async (req, res) => {
const response = await restApp.fetch(
new Request(`http://${req.headers.host}${req.url}`, {
method: req.method,
headers: req.headers as any,
body: req.method !== "GET" && req.method !== "HEAD"
? await getRequestBody(req)
: undefined,
})
);
res.statusCode = response.status;
response.headers.forEach((value, key) => res.setHeader(key, value));
res.end(await response.text());
});
// 6. Create WebSocket server on same HTTP server
const wsServer = runtime.createWebSocketServer(httpServer);
// 7. Start listening
httpServer.listen(3001, () => {
console.log("Server running on http://localhost:3001");
});
// 8. Graceful shutdown
process.on("SIGTERM", async () => {
httpServer.close();
wsServer.close();
await runtime.shutdown();
process.exit(0);
});
}
function getRequestBody(req: any): Promise<string> {
return new Promise((resolve, reject) => {
let body = "";
req.on("data", (chunk: any) => body += chunk.toString());
req.on("end", () => resolve(body));
req.on("error", reject);
});
}
main();
Runtime Configuration
const runtime = await createAgentRuntime({
// Required: persistence adapter implementation
persistence: PersistenceAdapter,
// Required: Modal credentials
modal: {
tokenId: string,
tokenSecret: string,
appName: string, // Modal app name for sandboxes
},
// Optional: idle timeout before session cleanup (default: 15 min)
idleTimeoutMs: number,
// Optional: sync interval for persisting state (default: 30 sec)
syncIntervalMs: number,
});
REST API Endpoints
The runtime creates these REST endpoints:
| Method | Endpoint | Description |
|---|---|---|
| POST | /sessions/create |
Create new session |
| GET | /sessions/:id |
Get session data |
| POST | /sessions/:id/message |
Send message to agent |
| GET | /sessions |
List all sessions |
| GET | /agent-profiles |
List available agent profiles |
| GET | /health |
Health check |
Lazy Sandbox Pattern
Sandboxes are created lazily - not when a session is created, but when the first message is sent. This optimizes resource usage:
POST /sessions/create- Creates session record, no sandbox yetPOST /sessions/:id/message- First message triggers sandbox creation- Subsequent messages reuse the running sandbox
- Idle timeout eventually terminates the sandbox
SessionManager Access
Access the session manager for advanced operations:
// Get all loaded sessions
const sessions = runtime.sessionManager.getLoadedSessions();
// Get specific session
const session = runtime.sessionManager.getSession(sessionId);
// Unload a session (terminates sandbox, syncs state)
await runtime.sessionManager.unloadSession(sessionId);
// Get session state
const state = session.getState();
WebSocket Events
The WebSocket server emits these events to connected clients:
Block streaming:
session:block:start- New block beginssession:block:delta- Incremental text updatesession:block:update- Block metadata changessession:block:complete- Block finishes
Session lifecycle:
session:status- Runtime state changessession:metadata:update- Token/cost updates
Files:
session:file:created- New file in workspacesession:file:modified- File changedsession:file:deleted- File removed
Subagents:
session:subagent:discovered- New subagent startedsession:subagent:completed- Subagent finished
Errors:
error- Error occurred
PersistenceAdapter
The PersistenceAdapter is the main integration point. Implement this interface to connect the runtime to your storage layer. See references/types.md for the full interface.
Common implementations:
- In-memory - For development/testing
- SQLite - For single-server deployments
- PostgreSQL/MySQL - For production
- Convex/Supabase - For serverless
Related Skills
- overview - Understanding the runtime architecture
- react-integration - Building React frontends
- agent-design - Configuring agent profiles
More from hhopkins95/ai-systems
opencode sdk development
This skill should be used when the user asks to "create an OpenCode tool", "build an OpenCode plugin", "write a custom tool for OpenCode", "use @opencode-ai/sdk", "use @opencode-ai/plugin", "integrate with OpenCode", "create OpenCode hooks", "define tool schema", "use tool.schema", "work with OpenCode sessions", or needs guidance on OpenCode SDK patterns, plugin development, or custom tool creation.
30project tracking
System for tracking work across AI sessions. Use when starting or ending work sessions, managing initiatives, capturing ideas or todos, or when needing to understand what work is in progress. Handles knowledge transfer between sessions.
1agent design
This skill should be used when the user asks to "configure agent profile", "add skills to agent", "set up MCP servers", "configure agent tools", "write system prompt", "create agent workflow", "define agent commands", "add subagents", or needs to define what capabilities an agent has and how to orchestrate complex workflows in the runtime.
1agent design 2
This skill should be used when the user asks to "configure agent profile", "add skills to agent", "set up MCP servers", "configure agent tools", "write system prompt", "create agent workflow", "define agent commands", "add subagents", or needs to define what capabilities an agent has and how to orchestrate complex workflows in the runtime.
1documentation system
Standards for documenting codebases. Use when writing documentation, deciding where docs should go, reviewing doc quality, or updating docs after code changes. Covers folder structure, content guidelines, and maintenance workflows.
1agent runtime overview
This skill should be used when the user asks "what is agent-runtime", "why use agent-runtime", "what does this repo do", "agent runtime architecture", "how does agent-runtime work", or needs to understand the purpose, value proposition, and high-level architecture of the @hhopkins/agent-runtime monorepo.
1