@octavus/docs 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,7 +23,7 @@ var docs_default = [
23
23
  section: "server-sdk",
24
24
  title: "Overview",
25
25
  description: "Introduction to the Octavus Server SDK for backend integration.",
26
- content: "\n# Server SDK Overview\n\nThe `@octavus/server-sdk` package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation loop.\n\n**Current version:** `2.8.0`\n\n## Installation\n\n```bash\nnpm install @octavus/server-sdk\n```\n\nFor agent management (sync, validate), install the CLI as a dev dependency:\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Basic Usage\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n```\n\n## Key Features\n\n### Agent Management\n\nAgent definitions are managed via the CLI. See the [CLI documentation](/docs/server-sdk/cli) for details.\n\n```bash\n# Sync agent from local files\noctavus sync ./agents/support-chat\n\n# Output: Created: support-chat\n# Agent ID: clxyz123abc456\n```\n\n### Session Management\n\nCreate and manage agent sessions using the agent ID:\n\n```typescript\n// Create a new session (use agent ID from CLI sync)\nconst sessionId = await client.agentSessions.create('clxyz123abc456', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n});\n\n// Get UI-ready session messages (for session restore)\nconst session = await client.agentSessions.getMessages(sessionId);\n```\n\n### Tool Handlers\n\nTools run on your server with your data:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n 'get-user-account': async (args) => {\n // Access your database, APIs, etc.\n return await db.users.findById(args.userId);\n },\n },\n});\n```\n\n### Streaming\n\nAll responses stream in real-time:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of events\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n## API Reference\n\n### OctavusClient\n\nThe main entry point for interacting with Octavus.\n\n```typescript\ninterface OctavusClientConfig {\n baseUrl: string; // Octavus API URL\n apiKey?: string; // Your API key\n}\n\nclass OctavusClient {\n readonly agents: AgentsApi;\n readonly agentSessions: AgentSessionsApi;\n readonly workers: WorkersApi;\n readonly files: FilesApi;\n\n constructor(config: OctavusClientConfig);\n}\n```\n\n### AgentSessionsApi\n\nManages agent sessions.\n\n```typescript\nclass AgentSessionsApi {\n // Create a new session\n async create(agentId: string, input?: Record<string, unknown>): Promise<string>;\n\n // Get full session state (for debugging/internal use)\n async get(sessionId: string): Promise<SessionState>;\n\n // Get UI-ready messages (for client display)\n async getMessages(sessionId: string): Promise<UISessionState>;\n\n // Attach to a session for triggering\n attach(sessionId: string, options?: SessionAttachOptions): AgentSession;\n}\n\n// Full session state (internal format)\ninterface SessionState {\n id: string;\n agentId: string;\n input: Record<string, unknown>;\n variables: Record<string, unknown>;\n resources: Record<string, unknown>;\n messages: ChatMessage[]; // Internal message format\n createdAt: string;\n updatedAt: string;\n}\n\n// UI-ready session state\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready messages for frontend\n}\n```\n\n### AgentSession\n\nHandles request execution and streaming for a specific session.\n\n```typescript\nclass AgentSession {\n // Execute a request and stream parsed events\n execute(request: SessionRequest, options?: TriggerOptions): AsyncGenerator<StreamEvent>;\n\n // Get the session ID\n getSessionId(): string;\n}\n\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n\n// Helper to convert events to SSE stream\nfunction toSSEStream(events: AsyncIterable<StreamEvent>): ReadableStream<Uint8Array>;\n```\n\n### FilesApi\n\nHandles file uploads for sessions.\n\n```typescript\nclass FilesApi {\n // Get presigned URLs for file uploads\n async getUploadUrls(sessionId: string, files: FileUploadRequest[]): Promise<UploadUrlsResponse>;\n}\n\ninterface FileUploadRequest {\n filename: string;\n mediaType: string;\n size: number;\n}\n\ninterface UploadUrlsResponse {\n files: {\n id: string; // File ID for references\n uploadUrl: string; // PUT to this URL\n downloadUrl: string; // GET URL after upload\n }[];\n}\n```\n\nThe client uploads files directly to S3 using the presigned upload URL. See [File Uploads](/docs/client-sdk/file-uploads) for the full integration pattern.\n\n## Next Steps\n\n- [Sessions](/docs/server-sdk/sessions) \u2014 Deep dive into session management\n- [Tools](/docs/server-sdk/tools) \u2014 Implementing tool handlers\n- [Streaming](/docs/server-sdk/streaming) \u2014 Understanding stream events\n- [Workers](/docs/server-sdk/workers) \u2014 Executing worker agents\n",
26
+ content: "\n# Server SDK Overview\n\nThe `@octavus/server-sdk` package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation loop.\n\n**Current version:** `2.9.0`\n\n## Installation\n\n```bash\nnpm install @octavus/server-sdk\n```\n\nFor agent management (sync, validate), install the CLI as a dev dependency:\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Basic Usage\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n```\n\n## Key Features\n\n### Agent Management\n\nAgent definitions are managed via the CLI. See the [CLI documentation](/docs/server-sdk/cli) for details.\n\n```bash\n# Sync agent from local files\noctavus sync ./agents/support-chat\n\n# Output: Created: support-chat\n# Agent ID: clxyz123abc456\n```\n\n### Session Management\n\nCreate and manage agent sessions using the agent ID:\n\n```typescript\n// Create a new session (use agent ID from CLI sync)\nconst sessionId = await client.agentSessions.create('clxyz123abc456', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n});\n\n// Get UI-ready session messages (for session restore)\nconst session = await client.agentSessions.getMessages(sessionId);\n```\n\n### Tool Handlers\n\nTools run on your server with your data:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n 'get-user-account': async (args) => {\n // Access your database, APIs, etc.\n return await db.users.findById(args.userId);\n },\n },\n});\n```\n\n### Streaming\n\nAll responses stream in real-time:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of events\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n## API Reference\n\n### OctavusClient\n\nThe main entry point for interacting with Octavus.\n\n```typescript\ninterface OctavusClientConfig {\n baseUrl: string; // Octavus API URL\n apiKey?: string; // Your API key\n traceModelRequests?: boolean; // Enable model request tracing (default: false)\n}\n\nclass OctavusClient {\n readonly agents: AgentsApi;\n readonly agentSessions: AgentSessionsApi;\n readonly workers: WorkersApi;\n readonly files: FilesApi;\n\n constructor(config: OctavusClientConfig);\n}\n```\n\n### AgentSessionsApi\n\nManages agent sessions.\n\n```typescript\nclass AgentSessionsApi {\n // Create a new session\n async create(agentId: string, input?: Record<string, unknown>): Promise<string>;\n\n // Get full session state (for debugging/internal use)\n async get(sessionId: string): Promise<SessionState>;\n\n // Get UI-ready messages (for client display)\n async getMessages(sessionId: string): Promise<UISessionState>;\n\n // Attach to a session for triggering\n attach(sessionId: string, options?: SessionAttachOptions): AgentSession;\n}\n\n// Full session state (internal format)\ninterface SessionState {\n id: string;\n agentId: string;\n input: Record<string, unknown>;\n variables: Record<string, unknown>;\n resources: Record<string, unknown>;\n messages: ChatMessage[]; // Internal message format\n createdAt: string;\n updatedAt: string;\n}\n\n// UI-ready session state\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready messages for frontend\n}\n```\n\n### AgentSession\n\nHandles request execution and streaming for a specific session.\n\n```typescript\nclass AgentSession {\n // Execute a request and stream parsed events\n execute(request: SessionRequest, options?: TriggerOptions): AsyncGenerator<StreamEvent>;\n\n // Get the session ID\n getSessionId(): string;\n}\n\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n\n// Helper to convert events to SSE stream\nfunction toSSEStream(events: AsyncIterable<StreamEvent>): ReadableStream<Uint8Array>;\n```\n\n### FilesApi\n\nHandles file uploads for sessions.\n\n```typescript\nclass FilesApi {\n // Get presigned URLs for file uploads\n async getUploadUrls(sessionId: string, files: FileUploadRequest[]): Promise<UploadUrlsResponse>;\n}\n\ninterface FileUploadRequest {\n filename: string;\n mediaType: string;\n size: number;\n}\n\ninterface UploadUrlsResponse {\n files: {\n id: string; // File ID for references\n uploadUrl: string; // PUT to this URL\n downloadUrl: string; // GET URL after upload\n }[];\n}\n```\n\nThe client uploads files directly to S3 using the presigned upload URL. See [File Uploads](/docs/client-sdk/file-uploads) for the full integration pattern.\n\n## Next Steps\n\n- [Sessions](/docs/server-sdk/sessions) \u2014 Deep dive into session management\n- [Tools](/docs/server-sdk/tools) \u2014 Implementing tool handlers\n- [Streaming](/docs/server-sdk/streaming) \u2014 Understanding stream events\n- [Workers](/docs/server-sdk/workers) \u2014 Executing worker agents\n- [Debugging](/docs/server-sdk/debugging) \u2014 Model request tracing and debugging\n",
27
27
  excerpt: "Server SDK Overview The package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation...",
28
28
  order: 1
29
29
  },
@@ -32,7 +32,7 @@ var docs_default = [
32
32
  section: "server-sdk",
33
33
  title: "Sessions",
34
34
  description: "Managing agent sessions with the Server SDK.",
35
- content: "\n# Sessions\n\nSessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions.\n\n## Creating Sessions\n\nCreate a session by specifying the agent ID and initial input variables:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\n// Create a session with the support-chat agent\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n USER_ID: 'user-123', // Optional inputs\n});\n\nconsole.log('Session created:', sessionId);\n```\n\n## Getting Session Messages\n\nTo restore a conversation on page load, use `getMessages()` to retrieve UI-ready messages:\n\n```typescript\nconst session = await client.agentSessions.getMessages(sessionId);\n\nconsole.log({\n sessionId: session.sessionId,\n agentId: session.agentId,\n messages: session.messages.length, // UIMessage[] ready for frontend\n});\n```\n\nThe returned messages can be passed directly to the client SDK's `initialMessages` option.\n\n### UISessionState Interface\n\n```typescript\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready conversation history\n}\n```\n\n## Full Session State (Debug)\n\nFor debugging or internal use, you can retrieve the complete session state including all variables and internal message format:\n\n```typescript\nconst state = await client.agentSessions.get(sessionId);\n\nconsole.log({\n id: state.id,\n agentId: state.agentId,\n messages: state.messages.length, // ChatMessage[] (internal format)\n resources: state.resources,\n variables: state.variables,\n createdAt: state.createdAt,\n updatedAt: state.updatedAt,\n});\n```\n\n> **Note**: Use `getMessages()` for client-facing code. The `get()` method returns internal message format that includes hidden content not intended for end users.\n\n## Attaching to Sessions\n\nTo trigger actions on a session, you need to attach to it first:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n // Tool handlers (see Tools documentation)\n },\n resources: [\n // Resource watchers (optional)\n ],\n});\n```\n\n## Executing Requests\n\nOnce attached, execute requests on the session using `execute()`:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() handles both triggers and client tool continuations\nconst events = session.execute(\n { type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello!' } },\n { signal: request.signal },\n);\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n### Request Types\n\nThe `execute()` method accepts a discriminated union:\n\n```typescript\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\n// Start a new conversation turn\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\n// Continue after client-side tool handling\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n```\n\nThis makes it easy to pass requests through from the client:\n\n```typescript\n// Simple passthrough from HTTP request body\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n /* ... */\n },\n });\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events));\n}\n```\n\n### Stop Support\n\nPass an abort signal to allow clients to stop generation:\n\n```typescript\nconst events = session.execute(request, {\n signal: request.signal, // Forward the client's abort signal\n});\n```\n\nWhen the client aborts the request, the signal propagates through to the LLM provider, stopping generation immediately. Any partial content is preserved.\n\n## WebSocket Handling\n\nFor WebSocket integrations, use `handleSocketMessage()` which manages abort controller lifecycle internally:\n\n```typescript\nimport type { SocketMessage } from '@octavus/server-sdk';\n\n// In your socket handler\nconn.on('data', async (rawData: string) => {\n const msg = JSON.parse(rawData);\n\n if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {\n await session.handleSocketMessage(msg as SocketMessage, {\n onEvent: (event) => conn.write(JSON.stringify(event)),\n onFinish: async () => {\n // Fetch and persist messages to your database for restoration\n },\n });\n }\n});\n```\n\nThe `handleSocketMessage()` method:\n\n- Handles `trigger`, `continue`, and `stop` messages\n- Automatically aborts previous requests when a new one arrives\n- Streams events via the `onEvent` callback\n- Calls `onFinish` after streaming completes (not called if aborted)\n\nSee [Socket Chat Example](/docs/examples/socket-chat) for a complete implementation.\n\n## Session Lifecycle\n\n```mermaid\nflowchart TD\n A[1. CREATE] --> B[2. ATTACH]\n B --> C[3. TRIGGER]\n C --> C\n C --> D[4. RETRIEVE]\n D --> C\n C --> E[5. EXPIRE]\n E --> F{6. RESTORE?}\n F -->|Yes| C\n F -->|No| A\n\n A -.- A1[\"`**client.agentSessions.create()**\n Returns sessionId\n Initializes state`\"]\n\n B -.- B1[\"`**client.agentSessions.attach()**\n Configure tool handlers\n Configure resource watchers`\"]\n\n C -.- C1[\"`**session.execute()**\n Execute request\n Stream events\n Update state`\"]\n\n D -.- D1[\"`**client.agentSessions.getMessages()**\n Get UI-ready messages\n Check session status`\"]\n\n E -.- E1[\"`Sessions expire after\n 24 hours (configurable)`\"]\n\n F -.- F1[\"`**client.agentSessions.restore()**\n Restore from stored messages\n Or create new session`\"]\n```\n\n## Session Expiration\n\nSessions expire after a period of inactivity (default: 24 hours). When you call `getMessages()` or `get()`, the response includes a `status` field:\n\n```typescript\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'expired') {\n // Session has expired - restore or create new\n console.log('Session expired:', result.sessionId);\n} else {\n // Session is active\n console.log('Messages:', result.messages.length);\n}\n```\n\n### Response Types\n\n| Status | Type | Description |\n| --------- | --------------------- | ------------------------------------------------------------- |\n| `active` | `UISessionState` | Session is active, includes `messages` array |\n| `expired` | `ExpiredSessionState` | Session expired, includes `sessionId`, `agentId`, `createdAt` |\n\n## Persisting Chat History\n\nTo enable session restoration, store the chat messages in your own database after each interaction:\n\n```typescript\n// After each trigger completes, save messages\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'active') {\n // Store in your database\n await db.chats.update({\n where: { id: chatId },\n data: {\n sessionId: result.sessionId,\n messages: result.messages, // Store UIMessage[] as JSON\n },\n });\n}\n```\n\n> **Best Practice**: Store the full `UIMessage[]` array. This preserves all message parts (text, tool calls, files, etc.) needed for accurate restoration.\n\n## Restoring Sessions\n\nWhen a user returns to your app:\n\n```typescript\n// 1. Load stored data from your database\nconst chat = await db.chats.findUnique({ where: { id: chatId } });\n\n// 2. Check if session is still active\nconst result = await client.agentSessions.getMessages(chat.sessionId);\n\nif (result.status === 'active') {\n // Session is active - use it directly\n return {\n sessionId: result.sessionId,\n messages: result.messages,\n };\n}\n\n// 3. Session expired - restore from stored messages\nif (chat.messages && chat.messages.length > 0) {\n const restored = await client.agentSessions.restore(\n chat.sessionId,\n chat.messages,\n { COMPANY_NAME: 'Acme Corp' }, // Optional: same input as create()\n );\n\n if (restored.restored) {\n // Session restored successfully\n return {\n sessionId: restored.sessionId,\n messages: chat.messages,\n };\n }\n}\n\n// 4. Cannot restore - create new session\nconst newSessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n});\n\nreturn {\n sessionId: newSessionId,\n messages: [],\n};\n```\n\n### Restore Response\n\n```typescript\ninterface RestoreSessionResult {\n sessionId: string;\n restored: boolean; // true if restored, false if session was already active\n}\n```\n\n## Complete Example\n\nHere's a complete session management flow:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\nasync function getOrCreateSession(chatId: string, agentId: string, input: Record<string, unknown>) {\n // Load existing chat data\n const chat = await db.chats.findUnique({ where: { id: chatId } });\n\n if (chat?.sessionId) {\n // Check session status\n const result = await client.agentSessions.getMessages(chat.sessionId);\n\n if (result.status === 'active') {\n return { sessionId: result.sessionId, messages: result.messages };\n }\n\n // Try to restore expired session\n if (chat.messages?.length > 0) {\n const restored = await client.agentSessions.restore(chat.sessionId, chat.messages, input);\n if (restored.restored) {\n return { sessionId: restored.sessionId, messages: chat.messages };\n }\n }\n }\n\n // Create new session\n const sessionId = await client.agentSessions.create(agentId, input);\n\n // Save to database\n await db.chats.upsert({\n where: { id: chatId },\n create: { id: chatId, sessionId, messages: [] },\n update: { sessionId, messages: [] },\n });\n\n return { sessionId, messages: [] };\n}\n```\n\n## Error Handling\n\n```typescript\nimport { ApiError } from '@octavus/server-sdk';\n\ntry {\n const session = await client.agentSessions.getMessages(sessionId);\n} catch (error) {\n if (error instanceof ApiError) {\n if (error.status === 404) {\n // Session not found or expired\n console.log('Session expired, create a new one');\n } else {\n console.error('API Error:', error.message);\n }\n }\n throw error;\n}\n```\n",
35
+ content: "\n# Sessions\n\nSessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions.\n\n## Creating Sessions\n\nCreate a session by specifying the agent ID and initial input variables:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\n// Create a session with the support-chat agent\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n USER_ID: 'user-123', // Optional inputs\n});\n\nconsole.log('Session created:', sessionId);\n```\n\n## Getting Session Messages\n\nTo restore a conversation on page load, use `getMessages()` to retrieve UI-ready messages:\n\n```typescript\nconst session = await client.agentSessions.getMessages(sessionId);\n\nconsole.log({\n sessionId: session.sessionId,\n agentId: session.agentId,\n messages: session.messages.length, // UIMessage[] ready for frontend\n});\n```\n\nThe returned messages can be passed directly to the client SDK's `initialMessages` option.\n\n### UISessionState Interface\n\n```typescript\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready conversation history\n}\n```\n\n## Full Session State (Debug)\n\nFor debugging or internal use, you can retrieve the complete session state including all variables and internal message format:\n\n```typescript\nconst state = await client.agentSessions.get(sessionId);\n\nconsole.log({\n id: state.id,\n agentId: state.agentId,\n messages: state.messages.length, // ChatMessage[] (internal format)\n resources: state.resources,\n variables: state.variables,\n createdAt: state.createdAt,\n updatedAt: state.updatedAt,\n});\n```\n\n> **Note**: Use `getMessages()` for client-facing code. The `get()` method returns internal message format that includes hidden content not intended for end users.\n\n## Attaching to Sessions\n\nTo trigger actions on a session, you need to attach to it first:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n // Tool handlers (see Tools documentation)\n },\n resources: [\n // Resource watchers (optional)\n ],\n});\n```\n\n## Executing Requests\n\nOnce attached, execute requests on the session using `execute()`:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() handles both triggers and client tool continuations\nconst events = session.execute(\n { type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello!' } },\n { signal: request.signal },\n);\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n### Request Types\n\nThe `execute()` method accepts a discriminated union:\n\n```typescript\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\n// Start a new conversation turn\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\n// Continue after client-side tool handling\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n```\n\nThis makes it easy to pass requests through from the client:\n\n```typescript\n// Simple passthrough from HTTP request body\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n /* ... */\n },\n });\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events));\n}\n```\n\n### Stop Support\n\nPass an abort signal to allow clients to stop generation:\n\n```typescript\nconst events = session.execute(request, {\n signal: request.signal, // Forward the client's abort signal\n});\n```\n\nWhen the client aborts the request, the signal propagates through to the LLM provider, stopping generation immediately. Any partial content is preserved.\n\n## WebSocket Handling\n\nFor WebSocket integrations, use `handleSocketMessage()` which manages abort controller lifecycle internally:\n\n```typescript\nimport type { SocketMessage } from '@octavus/server-sdk';\n\n// In your socket handler\nconn.on('data', async (rawData: string) => {\n const msg = JSON.parse(rawData);\n\n if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {\n await session.handleSocketMessage(msg as SocketMessage, {\n onEvent: (event) => conn.write(JSON.stringify(event)),\n onFinish: async () => {\n // Fetch and persist messages to your database for restoration\n },\n });\n }\n});\n```\n\nThe `handleSocketMessage()` method:\n\n- Handles `trigger`, `continue`, and `stop` messages\n- Automatically aborts previous requests when a new one arrives\n- Streams events via the `onEvent` callback\n- Calls `onFinish` after streaming completes (not called if aborted)\n\nSee [Socket Chat Example](/docs/examples/socket-chat) for a complete implementation.\n\n## Session Lifecycle\n\n```mermaid\nflowchart TD\n A[1. CREATE] --> B[2. ATTACH]\n B --> C[3. TRIGGER]\n C --> C\n C --> D[4. RETRIEVE]\n D --> C\n C --> E[5. EXPIRE]\n C --> G[5b. CLEAR]\n G --> F\n E --> F{6. RESTORE?}\n F -->|Yes| C\n F -->|No| A\n\n A -.- A1[\"`**client.agentSessions.create()**\n Returns sessionId\n Initializes state`\"]\n\n B -.- B1[\"`**client.agentSessions.attach()**\n Configure tool handlers\n Configure resource watchers`\"]\n\n C -.- C1[\"`**session.execute()**\n Execute request\n Stream events\n Update state`\"]\n\n D -.- D1[\"`**client.agentSessions.getMessages()**\n Get UI-ready messages\n Check session status`\"]\n\n E -.- E1[\"`Sessions expire after\n 24 hours (configurable)`\"]\n\n G -.- G1[\"`**client.agentSessions.clear()**\n Programmatically clear state\n Session becomes expired`\"]\n\n F -.- F1[\"`**client.agentSessions.restore()**\n Restore from stored messages\n Or create new session`\"]\n```\n\n## Session Expiration\n\nSessions expire after a period of inactivity (default: 24 hours). When you call `getMessages()` or `get()`, the response includes a `status` field:\n\n```typescript\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'expired') {\n // Session has expired - restore or create new\n console.log('Session expired:', result.sessionId);\n} else {\n // Session is active\n console.log('Messages:', result.messages.length);\n}\n```\n\n### Response Types\n\n| Status | Type | Description |\n| --------- | --------------------- | ------------------------------------------------------------- |\n| `active` | `UISessionState` | Session is active, includes `messages` array |\n| `expired` | `ExpiredSessionState` | Session expired, includes `sessionId`, `agentId`, `createdAt` |\n\n## Persisting Chat History\n\nTo enable session restoration, store the chat messages in your own database after each interaction:\n\n```typescript\n// After each trigger completes, save messages\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'active') {\n // Store in your database\n await db.chats.update({\n where: { id: chatId },\n data: {\n sessionId: result.sessionId,\n messages: result.messages, // Store UIMessage[] as JSON\n },\n });\n}\n```\n\n> **Best Practice**: Store the full `UIMessage[]` array. This preserves all message parts (text, tool calls, files, etc.) needed for accurate restoration.\n\n## Restoring Sessions\n\nWhen a user returns to your app:\n\n```typescript\n// 1. Load stored data from your database\nconst chat = await db.chats.findUnique({ where: { id: chatId } });\n\n// 2. Check if session is still active\nconst result = await client.agentSessions.getMessages(chat.sessionId);\n\nif (result.status === 'active') {\n // Session is active - use it directly\n return {\n sessionId: result.sessionId,\n messages: result.messages,\n };\n}\n\n// 3. Session expired - restore from stored messages\nif (chat.messages && chat.messages.length > 0) {\n const restored = await client.agentSessions.restore(\n chat.sessionId,\n chat.messages,\n { COMPANY_NAME: 'Acme Corp' }, // Optional: same input as create()\n );\n\n if (restored.restored) {\n // Session restored successfully\n return {\n sessionId: restored.sessionId,\n messages: chat.messages,\n };\n }\n}\n\n// 4. Cannot restore - create new session\nconst newSessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n});\n\nreturn {\n sessionId: newSessionId,\n messages: [],\n};\n```\n\n### Restore Response\n\n```typescript\ninterface RestoreSessionResult {\n sessionId: string;\n restored: boolean; // true if restored, false if session was already active\n}\n```\n\n## Complete Example\n\nHere's a complete session management flow:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\nasync function getOrCreateSession(chatId: string, agentId: string, input: Record<string, unknown>) {\n // Load existing chat data\n const chat = await db.chats.findUnique({ where: { id: chatId } });\n\n if (chat?.sessionId) {\n // Check session status\n const result = await client.agentSessions.getMessages(chat.sessionId);\n\n if (result.status === 'active') {\n return { sessionId: result.sessionId, messages: result.messages };\n }\n\n // Try to restore expired session\n if (chat.messages?.length > 0) {\n const restored = await client.agentSessions.restore(chat.sessionId, chat.messages, input);\n if (restored.restored) {\n return { sessionId: restored.sessionId, messages: chat.messages };\n }\n }\n }\n\n // Create new session\n const sessionId = await client.agentSessions.create(agentId, input);\n\n // Save to database\n await db.chats.upsert({\n where: { id: chatId },\n create: { id: chatId, sessionId, messages: [] },\n update: { sessionId, messages: [] },\n });\n\n return { sessionId, messages: [] };\n}\n```\n\n## Clearing Sessions\n\nTo programmatically clear a session's state (e.g., for testing reset/restore flows), use `clear()`:\n\n```typescript\nconst result = await client.agentSessions.clear(sessionId);\nconsole.log(result.cleared); // true\n```\n\nAfter clearing, the session transitions to `expired` status. You can then restore it with `restore()` or create a new session.\n\n```typescript\ninterface ClearSessionResult {\n sessionId: string;\n cleared: boolean;\n}\n```\n\nThis is idempotent \u2014 calling `clear()` on an already expired session succeeds without error.\n\n## Error Handling\n\n```typescript\nimport { ApiError } from '@octavus/server-sdk';\n\ntry {\n const session = await client.agentSessions.getMessages(sessionId);\n} catch (error) {\n if (error instanceof ApiError) {\n if (error.status === 404) {\n // Session not found or expired\n console.log('Session expired, create a new one');\n } else {\n console.error('API Error:', error.message);\n }\n }\n throw error;\n}\n```\n",
36
36
  excerpt: "Sessions Sessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions. Creating Sessions Create a session by...",
37
37
  order: 2
38
38
  },
@@ -59,7 +59,7 @@ var docs_default = [
59
59
  section: "server-sdk",
60
60
  title: "CLI",
61
61
  description: "Command-line interface for validating and syncing agent definitions.",
62
- content: '\n# Octavus CLI\n\nThe `@octavus/cli` package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform.\n\n**Current version:** `2.8.0`\n\n## Installation\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Configuration\n\nThe CLI requires an API key with the **Agents** permission.\n\n### Environment Variables\n\n| Variable | Description |\n| --------------------- | ---------------------------------------------- |\n| `OCTAVUS_CLI_API_KEY` | API key with "Agents" permission (recommended) |\n| `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |\n| `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |\n\n### Two-Key Strategy (Recommended)\n\nFor production deployments, use separate API keys with minimal permissions:\n\n```bash\n# CI/CD or .env.local (not committed)\nOCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only\n\n# Production .env\nOCTAVUS_API_KEY=oct_sk_... # "Sessions" permission only\n```\n\nThis ensures production servers only have session permissions (smaller blast radius if leaked), while agent management is restricted to development/CI environments.\n\n### Multiple Environments\n\nUse separate Octavus projects for staging and production, each with their own API keys. The `--env` flag lets you load different environment files:\n\n```bash\n# Local development (default: .env)\noctavus sync ./agents/my-agent\n\n# Staging project\noctavus --env .env.staging sync ./agents/my-agent\n\n# Production project\noctavus --env .env.production sync ./agents/my-agent\n```\n\nExample environment files:\n\n```bash\n# .env.staging (syncs to your staging project)\nOCTAVUS_CLI_API_KEY=oct_sk_staging_project_key...\n\n# .env.production (syncs to your production project)\nOCTAVUS_CLI_API_KEY=oct_sk_production_project_key...\n```\n\nEach project has its own agents, so you\'ll get different agent IDs per environment.\n\n## Global Options\n\n| Option | Description |\n| -------------- | ------------------------------------------------------- |\n| `--env <file>` | Load environment from a specific file (default: `.env`) |\n| `--help` | Show help |\n| `--version` | Show version |\n\n## Commands\n\n### `octavus sync <path>`\n\nSync an agent definition to the platform. Creates the agent if it doesn\'t exist, or updates it if it does.\n\n```bash\noctavus sync ./agents/my-agent\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Reading agent from ./agents/my-agent...\n\u2139 Syncing support-chat...\n\u2713 Created: support-chat\n Agent ID: clxyz123abc456\n```\n\n### `octavus validate <path>`\n\nValidate an agent definition without saving. Useful for CI/CD pipelines.\n\n```bash\noctavus validate ./agents/my-agent\n```\n\n**Exit codes:**\n\n- `0` \u2014 Validation passed\n- `1` \u2014 Validation errors\n- `2` \u2014 Configuration errors (missing API key, etc.)\n\n### `octavus list`\n\nList all agents in your project.\n\n```bash\noctavus list\n```\n\n**Example output:**\n\n```\nSLUG NAME FORMAT ID\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nsupport-chat Support Chat Agent interactive clxyz123abc456\n\n1 agent(s)\n```\n\n### `octavus get <slug>`\n\nGet details about a specific agent by its slug.\n\n```bash\noctavus get support-chat\n```\n\n## Agent Directory Structure\n\nThe CLI expects agent definitions in a specific directory structure:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json # Required: Agent metadata\n\u251C\u2500\u2500 protocol.yaml # Required: Agent protocol\n\u2514\u2500\u2500 prompts/ # Optional: Prompt templates\n \u251C\u2500\u2500 system.md\n \u2514\u2500\u2500 user-message.md\n```\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "A helpful assistant",\n "format": "interactive"\n}\n```\n\n### protocol.yaml\n\nSee the [Protocol documentation](/docs/protocol/overview) for details on protocol syntax.\n\n## CI/CD Integration\n\n### GitHub Actions\n\n```yaml\nname: Validate and Sync Agents\n\non:\n push:\n branches: [main]\n paths:\n - \'agents/**\'\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions/setup-node@v4\n with:\n node-version: \'22\'\n\n - run: npm install\n\n - name: Validate agent\n run: npx octavus validate ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n\n - name: Sync agent\n run: npx octavus sync ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n```\n\n### Package.json Scripts\n\nAdd sync scripts to your `package.json`:\n\n```json\n{\n "scripts": {\n "agents:validate": "octavus validate ./agents/my-agent",\n "agents:sync": "octavus sync ./agents/my-agent"\n },\n "devDependencies": {\n "@octavus/cli": "^0.1.0"\n }\n}\n```\n\n## Workflow\n\nThe recommended workflow for managing agents:\n\n1. **Define agent locally** \u2014 Create `settings.json`, `protocol.yaml`, and prompts\n2. **Validate** \u2014 Run `octavus validate ./my-agent` to check for errors\n3. **Sync** \u2014 Run `octavus sync ./my-agent` to push to platform\n4. **Store agent ID** \u2014 Save the output ID in an environment variable\n5. **Use in app** \u2014 Read the ID from env and pass to `client.agentSessions.create()`\n\n```bash\n# After syncing: octavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n\n# Add to your .env file\nOCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456\n```\n\n```typescript\nconst agentId = process.env.OCTAVUS_SUPPORT_AGENT_ID;\n\nconst sessionId = await client.agentSessions.create(agentId, {\n COMPANY_NAME: \'Acme Corp\',\n});\n```\n',
62
+ content: '\n# Octavus CLI\n\nThe `@octavus/cli` package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform.\n\n**Current version:** `2.9.0`\n\n## Installation\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Configuration\n\nThe CLI requires an API key with the **Agents** permission.\n\n### Environment Variables\n\n| Variable | Description |\n| --------------------- | ---------------------------------------------- |\n| `OCTAVUS_CLI_API_KEY` | API key with "Agents" permission (recommended) |\n| `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |\n| `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |\n\n### Two-Key Strategy (Recommended)\n\nFor production deployments, use separate API keys with minimal permissions:\n\n```bash\n# CI/CD or .env.local (not committed)\nOCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only\n\n# Production .env\nOCTAVUS_API_KEY=oct_sk_... # "Sessions" permission only\n```\n\nThis ensures production servers only have session permissions (smaller blast radius if leaked), while agent management is restricted to development/CI environments.\n\n### Multiple Environments\n\nUse separate Octavus projects for staging and production, each with their own API keys. The `--env` flag lets you load different environment files:\n\n```bash\n# Local development (default: .env)\noctavus sync ./agents/my-agent\n\n# Staging project\noctavus --env .env.staging sync ./agents/my-agent\n\n# Production project\noctavus --env .env.production sync ./agents/my-agent\n```\n\nExample environment files:\n\n```bash\n# .env.staging (syncs to your staging project)\nOCTAVUS_CLI_API_KEY=oct_sk_staging_project_key...\n\n# .env.production (syncs to your production project)\nOCTAVUS_CLI_API_KEY=oct_sk_production_project_key...\n```\n\nEach project has its own agents, so you\'ll get different agent IDs per environment.\n\n## Global Options\n\n| Option | Description |\n| -------------- | ------------------------------------------------------- |\n| `--env <file>` | Load environment from a specific file (default: `.env`) |\n| `--help` | Show help |\n| `--version` | Show version |\n\n## Commands\n\n### `octavus sync <path>`\n\nSync an agent definition to the platform. Creates the agent if it doesn\'t exist, or updates it if it does.\n\n```bash\noctavus sync ./agents/my-agent\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Reading agent from ./agents/my-agent...\n\u2139 Syncing support-chat...\n\u2713 Created: support-chat\n Agent ID: clxyz123abc456\n```\n\n### `octavus validate <path>`\n\nValidate an agent definition without saving. Useful for CI/CD pipelines.\n\n```bash\noctavus validate ./agents/my-agent\n```\n\n**Exit codes:**\n\n- `0` \u2014 Validation passed\n- `1` \u2014 Validation errors\n- `2` \u2014 Configuration errors (missing API key, etc.)\n\n### `octavus list`\n\nList all agents in your project.\n\n```bash\noctavus list\n```\n\n**Example output:**\n\n```\nSLUG NAME FORMAT ID\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nsupport-chat Support Chat Agent interactive clxyz123abc456\n\n1 agent(s)\n```\n\n### `octavus get <slug>`\n\nGet details about a specific agent by its slug.\n\n```bash\noctavus get support-chat\n```\n\n### `octavus archive <slug>`\n\nArchive an agent by slug (soft delete). Archived agents are removed from the active agent list and their slug is freed for reuse.\n\n```bash\noctavus archive support-chat\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Archiving support-chat...\n\u2713 Archived: support-chat\n Agent ID: clxyz123abc456\n```\n\n## Agent Directory Structure\n\nThe CLI expects agent definitions in a specific directory structure:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json # Required: Agent metadata\n\u251C\u2500\u2500 protocol.yaml # Required: Agent protocol\n\u2514\u2500\u2500 prompts/ # Optional: Prompt templates\n \u251C\u2500\u2500 system.md\n \u2514\u2500\u2500 user-message.md\n```\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "A helpful assistant",\n "format": "interactive"\n}\n```\n\n### protocol.yaml\n\nSee the [Protocol documentation](/docs/protocol/overview) for details on protocol syntax.\n\n## CI/CD Integration\n\n### GitHub Actions\n\n```yaml\nname: Validate and Sync Agents\n\non:\n push:\n branches: [main]\n paths:\n - \'agents/**\'\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions/setup-node@v4\n with:\n node-version: \'22\'\n\n - run: npm install\n\n - name: Validate agent\n run: npx octavus validate ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n\n - name: Sync agent\n run: npx octavus sync ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n```\n\n### Package.json Scripts\n\nAdd sync scripts to your `package.json`:\n\n```json\n{\n "scripts": {\n "agents:validate": "octavus validate ./agents/my-agent",\n "agents:sync": "octavus sync ./agents/my-agent"\n },\n "devDependencies": {\n "@octavus/cli": "^0.1.0"\n }\n}\n```\n\n## Workflow\n\nThe recommended workflow for managing agents:\n\n1. **Define agent locally** \u2014 Create `settings.json`, `protocol.yaml`, and prompts\n2. **Validate** \u2014 Run `octavus validate ./my-agent` to check for errors\n3. **Sync** \u2014 Run `octavus sync ./my-agent` to push to platform\n4. **Store agent ID** \u2014 Save the output ID in an environment variable\n5. **Use in app** \u2014 Read the ID from env and pass to `client.agentSessions.create()`\n\n```bash\n# After syncing: octavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n\n# Add to your .env file\nOCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456\n```\n\n```typescript\nconst agentId = process.env.OCTAVUS_SUPPORT_AGENT_ID;\n\nconst sessionId = await client.agentSessions.create(agentId, {\n COMPANY_NAME: \'Acme Corp\',\n});\n```\n',
63
63
  excerpt: "Octavus CLI The package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform. Current version: Installation ...",
64
64
  order: 5
65
65
  },
@@ -72,12 +72,21 @@ var docs_default = [
72
72
  excerpt: "Workers API The enables executing worker agents from your server. Workers are task-based agents that run steps sequentially and return an output value. Basic Usage WorkersApi Reference execute()...",
73
73
  order: 6
74
74
  },
75
+ {
76
+ slug: "server-sdk/debugging",
77
+ section: "server-sdk",
78
+ title: "Debugging",
79
+ description: "Model request tracing and debugging tools for Octavus agents.",
80
+ content: "\n# Debugging\n\n## Model Request Tracing\n\nModel request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014 system prompts, messages, tool definitions, and provider options \u2014 making it easier to debug agent behavior.\n\n### Enabling Tracing\n\nEnable tracing by setting `traceModelRequests: true` in the client config:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: true,\n});\n```\n\nWhen enabled, the SDK sends an `X-Octavus-Trace: true` header with every request. The platform captures the full model request payload before each provider call and stores it in the execution logs.\n\nYou can also drive this from an environment variable for per-environment control:\n\n```typescript\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: process.env.TRACE_MODEL_REQUESTS === 'true',\n});\n```\n\n### What Gets Captured\n\n**LLM requests** include:\n\n- Full system prompt\n- All messages in AI SDK format (post-conversion)\n- Tool names, descriptions, and JSON schemas\n- Provider-specific options (thinking budgets, etc.)\n- Temperature, max steps, and thinking configuration\n\n**Image generation requests** include:\n\n- Image generation prompt\n- Requested size\n- Whether reference images were provided\n\n### Where Traces Appear\n\nTraces appear as **Model Request** entries in the execution log timeline, alongside existing entries like triggers, tool calls, and responses. Each trace is linked to the block that made the model call.\n\nIn the Octavus dashboard:\n\n- **Session debug view** \u2014 Full execution log with expandable model request entries\n- **Agent preview** \u2014 Activity panel shows model requests in the execution steps\n\nEach entry shows the raw JSON payload with a copy button for easy inspection.\n\n### Storage\n\nTraces are stored in Redis alongside other execution log entries with a 24-hour TTL. They are not permanently stored. A typical LLM trace with 10 messages and 5 tools is 10\u201350KB. Image traces are smaller (just prompt and metadata).\n\n### Recommendations\n\n| Environment | Recommendation |\n| ----------- | ---------------------------------------------------------- |\n| Development | Enable \u2014 helps debug agent behavior during development |\n| Staging | Enable \u2014 useful for pre-production testing |\n| Production | Disable (default) \u2014 saves storage for high-volume sessions |\n\n### Preview Sessions\n\nModel request tracing is always enabled for preview sessions in the Octavus dashboard. No configuration needed \u2014 the platform automatically traces all model requests when using the agent preview.\n",
81
+ excerpt: "Debugging Model Request Tracing Model request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014...",
82
+ order: 7
83
+ },
75
84
  {
76
85
  slug: "client-sdk/overview",
77
86
  section: "client-sdk",
78
87
  title: "Overview",
79
88
  description: "Introduction to the Octavus Client SDKs for building chat interfaces.",
80
- content: "\n# Client SDK Overview\n\nOctavus provides two packages for frontend integration:\n\n| Package | Purpose | Use When |\n| --------------------- | ------------------------ | ----------------------------------------------------- |\n| `@octavus/react` | React hooks and bindings | Building React applications |\n| `@octavus/client-sdk` | Framework-agnostic core | Using Vue, Svelte, vanilla JS, or custom integrations |\n\n**Most users should install `@octavus/react`** \u2014 it includes everything from `@octavus/client-sdk` plus React-specific hooks.\n\n## Installation\n\n### React Applications\n\n```bash\nnpm install @octavus/react\n```\n\n**Current version:** `2.8.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.8.0`\n\n## Transport Pattern\n\nThe Client SDK uses a **transport abstraction** to handle communication with your backend. This gives you flexibility in how events are delivered:\n\n| Transport | Use Case | Docs |\n| ----------------------- | -------------------------------------------- | ----------------------------------------------------- |\n| `createHttpTransport` | HTTP/SSE (Next.js, Express, etc.) | [HTTP Transport](/docs/client-sdk/http-transport) |\n| `createSocketTransport` | WebSocket, SockJS, or other socket protocols | [Socket Transport](/docs/client-sdk/socket-transport) |\n\nWhen the transport changes (e.g., when `sessionId` changes), the `useOctavusChat` hook automatically reinitializes with the new transport.\n\n> **Recommendation**: Use HTTP transport unless you specifically need WebSocket features (custom real-time events, Meteor/Phoenix, etc.).\n\n## React Usage\n\nThe `useOctavusChat` hook provides state management and streaming for React applications:\n\n```tsx\nimport { useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\n // Create a stable transport instance (memoized on sessionId)\n const transport = useMemo(\n () =>\n createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n }),\n [sessionId],\n );\n\n const { messages, status, send } = useOctavusChat({ transport });\n\n const sendMessage = async (text: string) => {\n await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n };\n\n return (\n <div>\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n return null;\n })}\n </div>\n );\n}\n```\n\n## Framework-Agnostic Usage\n\nThe `OctavusChat` class can be used with any framework or vanilla JavaScript:\n\n```typescript\nimport { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n\nconst chat = new OctavusChat({ transport });\n\n// Subscribe to state changes\nconst unsubscribe = chat.subscribe(() => {\n console.log('Messages:', chat.messages);\n console.log('Status:', chat.status);\n // Update your UI here\n});\n\n// Send a message\nawait chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });\n\n// Cleanup when done\nunsubscribe();\n```\n\n## Key Features\n\n### Unified Send Function\n\nThe `send` function handles both user message display and agent triggering in one call:\n\n```tsx\nconst { send } = useOctavusChat({ transport });\n\n// Add user message to UI and trigger agent\nawait send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n\n// Trigger without adding a user message (e.g., button click)\nawait send('request-human');\n```\n\n### Message Parts\n\nMessages contain ordered `parts` for rich content:\n\n```tsx\nconst { messages } = useOctavusChat({ transport });\n\n// Each message has typed parts\nmessage.parts.map((part) => {\n switch (part.type) {\n case 'text': // Text content\n case 'reasoning': // Extended reasoning/thinking\n case 'tool-call': // Tool execution\n case 'operation': // Internal operations (set-resource, etc.)\n }\n});\n```\n\n### Status Tracking\n\n```tsx\nconst { status } = useOctavusChat({ transport });\n\n// status: 'idle' | 'streaming' | 'error' | 'awaiting-input'\n// 'awaiting-input' occurs when interactive client tools need user action\n```\n\n### Stop Streaming\n\n```tsx\nconst { stop } = useOctavusChat({ transport });\n\n// Stop current stream and finalize message\nstop();\n```\n\n## Hook Reference (React)\n\n### useOctavusChat\n\n```typescript\nfunction useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn;\n\ninterface OctavusChatOptions {\n // Required: Transport for streaming events\n transport: Transport;\n\n // Optional: Function to request upload URLs for file uploads\n requestUploadUrls?: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n // Optional: Client-side tool handlers\n // - Function: executes automatically and returns result\n // - 'interactive': appears in pendingClientTools for user input\n clientTools?: Record<string, ClientToolHandler>;\n\n // Optional: Pre-populate with existing messages (session restore)\n initialMessages?: UIMessage[];\n\n // Optional: Callbacks\n onError?: (error: OctavusError) => void; // Structured error with type, source, retryable\n onFinish?: () => void;\n onStop?: () => void; // Called when user stops generation\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\ninterface UseOctavusChatReturn {\n // State\n messages: UIMessage[];\n status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n error: OctavusError | null; // Structured error with type, source, retryable\n\n // Connection (socket transport only - undefined for HTTP)\n connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'\n connectionError: Error | undefined;\n\n // Client tools (interactive tools awaiting user input)\n pendingClientTools: Record<string, InteractiveTool[]>; // Keyed by tool name\n\n // Actions\n send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n stop: () => void;\n\n // Connection management (socket transport only - undefined for HTTP)\n connect: (() => Promise<void>) | undefined;\n disconnect: (() => void) | undefined;\n\n // File uploads (requires requestUploadUrls)\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\ninterface UserMessageInput {\n content?: string;\n files?: FileList | File[] | FileReference[];\n}\n```\n\n## Transport Reference\n\n### createHttpTransport\n\nCreates an HTTP/SSE transport using native `fetch()`:\n\n```typescript\nimport { createHttpTransport } from '@octavus/react';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n```\n\n### createSocketTransport\n\nCreates a WebSocket/SockJS transport for real-time connections:\n\n```typescript\nimport { createSocketTransport } from '@octavus/react';\n\nconst transport = createSocketTransport({\n connect: () =>\n new Promise((resolve, reject) => {\n const ws = new WebSocket(`wss://api.example.com/stream?sessionId=${sessionId}`);\n ws.onopen = () => resolve(ws);\n ws.onerror = () => reject(new Error('Connection failed'));\n }),\n});\n```\n\nSocket transport provides additional connection management:\n\n```typescript\n// Access connection state directly\ntransport.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'\n\n// Subscribe to state changes\ntransport.onConnectionStateChange((state, error) => {\n /* ... */\n});\n\n// Eager connection (instead of lazy on first send)\nawait transport.connect();\n\n// Manual disconnect\ntransport.disconnect();\n```\n\nFor detailed WebSocket/SockJS usage including custom events, reconnection patterns, and server-side implementation, see [Socket Transport](/docs/client-sdk/socket-transport).\n\n## Class Reference (Framework-Agnostic)\n\n### OctavusChat\n\n```typescript\nclass OctavusChat {\n constructor(options: OctavusChatOptions);\n\n // State (read-only)\n readonly messages: UIMessage[];\n readonly status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n readonly error: OctavusError | null; // Structured error\n readonly pendingClientTools: Record<string, InteractiveTool[]>; // Interactive tools\n\n // Actions\n send(\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ): Promise<void>;\n stop(): void;\n\n // Subscription\n subscribe(callback: () => void): () => void; // Returns unsubscribe function\n}\n```\n\n## Next Steps\n\n- [HTTP Transport](/docs/client-sdk/http-transport) \u2014 HTTP/SSE integration (recommended)\n- [Socket Transport](/docs/client-sdk/socket-transport) \u2014 WebSocket and SockJS integration\n- [Messages](/docs/client-sdk/messages) \u2014 Working with message state\n- [Streaming](/docs/client-sdk/streaming) \u2014 Building streaming UIs\n- [Client Tools](/docs/client-sdk/client-tools) \u2014 Interactive browser-side tool handling\n- [Operations](/docs/client-sdk/execution-blocks) \u2014 Showing agent progress\n- [Error Handling](/docs/client-sdk/error-handling) \u2014 Handling errors with type guards\n- [File Uploads](/docs/client-sdk/file-uploads) \u2014 Uploading images and documents\n- [Examples](/docs/examples/overview) \u2014 Complete working examples\n",
89
+ content: "\n# Client SDK Overview\n\nOctavus provides two packages for frontend integration:\n\n| Package | Purpose | Use When |\n| --------------------- | ------------------------ | ----------------------------------------------------- |\n| `@octavus/react` | React hooks and bindings | Building React applications |\n| `@octavus/client-sdk` | Framework-agnostic core | Using Vue, Svelte, vanilla JS, or custom integrations |\n\n**Most users should install `@octavus/react`** \u2014 it includes everything from `@octavus/client-sdk` plus React-specific hooks.\n\n## Installation\n\n### React Applications\n\n```bash\nnpm install @octavus/react\n```\n\n**Current version:** `2.9.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.9.0`\n\n## Transport Pattern\n\nThe Client SDK uses a **transport abstraction** to handle communication with your backend. This gives you flexibility in how events are delivered:\n\n| Transport | Use Case | Docs |\n| ----------------------- | -------------------------------------------- | ----------------------------------------------------- |\n| `createHttpTransport` | HTTP/SSE (Next.js, Express, etc.) | [HTTP Transport](/docs/client-sdk/http-transport) |\n| `createSocketTransport` | WebSocket, SockJS, or other socket protocols | [Socket Transport](/docs/client-sdk/socket-transport) |\n\nWhen the transport changes (e.g., when `sessionId` changes), the `useOctavusChat` hook automatically reinitializes with the new transport.\n\n> **Recommendation**: Use HTTP transport unless you specifically need WebSocket features (custom real-time events, Meteor/Phoenix, etc.).\n\n## React Usage\n\nThe `useOctavusChat` hook provides state management and streaming for React applications:\n\n```tsx\nimport { useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\n // Create a stable transport instance (memoized on sessionId)\n const transport = useMemo(\n () =>\n createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n }),\n [sessionId],\n );\n\n const { messages, status, send } = useOctavusChat({ transport });\n\n const sendMessage = async (text: string) => {\n await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n };\n\n return (\n <div>\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n return null;\n })}\n </div>\n );\n}\n```\n\n## Framework-Agnostic Usage\n\nThe `OctavusChat` class can be used with any framework or vanilla JavaScript:\n\n```typescript\nimport { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n\nconst chat = new OctavusChat({ transport });\n\n// Subscribe to state changes\nconst unsubscribe = chat.subscribe(() => {\n console.log('Messages:', chat.messages);\n console.log('Status:', chat.status);\n // Update your UI here\n});\n\n// Send a message\nawait chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });\n\n// Cleanup when done\nunsubscribe();\n```\n\n## Key Features\n\n### Unified Send Function\n\nThe `send` function handles both user message display and agent triggering in one call:\n\n```tsx\nconst { send } = useOctavusChat({ transport });\n\n// Add user message to UI and trigger agent\nawait send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n\n// Trigger without adding a user message (e.g., button click)\nawait send('request-human');\n```\n\n### Message Parts\n\nMessages contain ordered `parts` for rich content:\n\n```tsx\nconst { messages } = useOctavusChat({ transport });\n\n// Each message has typed parts\nmessage.parts.map((part) => {\n switch (part.type) {\n case 'text': // Text content\n case 'reasoning': // Extended reasoning/thinking\n case 'tool-call': // Tool execution\n case 'operation': // Internal operations (set-resource, etc.)\n }\n});\n```\n\n### Status Tracking\n\n```tsx\nconst { status } = useOctavusChat({ transport });\n\n// status: 'idle' | 'streaming' | 'error' | 'awaiting-input'\n// 'awaiting-input' occurs when interactive client tools need user action\n```\n\n### Stop Streaming\n\n```tsx\nconst { stop } = useOctavusChat({ transport });\n\n// Stop current stream and finalize message\nstop();\n```\n\n## Hook Reference (React)\n\n### useOctavusChat\n\n```typescript\nfunction useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn;\n\ninterface OctavusChatOptions {\n // Required: Transport for streaming events\n transport: Transport;\n\n // Optional: Function to request upload URLs for file uploads\n requestUploadUrls?: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n // Optional: Client-side tool handlers\n // - Function: executes automatically and returns result\n // - 'interactive': appears in pendingClientTools for user input\n clientTools?: Record<string, ClientToolHandler>;\n\n // Optional: Pre-populate with existing messages (session restore)\n initialMessages?: UIMessage[];\n\n // Optional: Callbacks\n onError?: (error: OctavusError) => void; // Structured error with type, source, retryable\n onFinish?: () => void;\n onStop?: () => void; // Called when user stops generation\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\ninterface UseOctavusChatReturn {\n // State\n messages: UIMessage[];\n status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n error: OctavusError | null; // Structured error with type, source, retryable\n\n // Connection (socket transport only - undefined for HTTP)\n connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'\n connectionError: Error | undefined;\n\n // Client tools (interactive tools awaiting user input)\n pendingClientTools: Record<string, InteractiveTool[]>; // Keyed by tool name\n\n // Actions\n send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n stop: () => void;\n\n // Connection management (socket transport only - undefined for HTTP)\n connect: (() => Promise<void>) | undefined;\n disconnect: (() => void) | undefined;\n\n // File uploads (requires requestUploadUrls)\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\ninterface UserMessageInput {\n content?: string;\n files?: FileList | File[] | FileReference[];\n}\n```\n\n## Transport Reference\n\n### createHttpTransport\n\nCreates an HTTP/SSE transport using native `fetch()`:\n\n```typescript\nimport { createHttpTransport } from '@octavus/react';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n```\n\n### createSocketTransport\n\nCreates a WebSocket/SockJS transport for real-time connections:\n\n```typescript\nimport { createSocketTransport } from '@octavus/react';\n\nconst transport = createSocketTransport({\n connect: () =>\n new Promise((resolve, reject) => {\n const ws = new WebSocket(`wss://api.example.com/stream?sessionId=${sessionId}`);\n ws.onopen = () => resolve(ws);\n ws.onerror = () => reject(new Error('Connection failed'));\n }),\n});\n```\n\nSocket transport provides additional connection management:\n\n```typescript\n// Access connection state directly\ntransport.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'\n\n// Subscribe to state changes\ntransport.onConnectionStateChange((state, error) => {\n /* ... */\n});\n\n// Eager connection (instead of lazy on first send)\nawait transport.connect();\n\n// Manual disconnect\ntransport.disconnect();\n```\n\nFor detailed WebSocket/SockJS usage including custom events, reconnection patterns, and server-side implementation, see [Socket Transport](/docs/client-sdk/socket-transport).\n\n## Class Reference (Framework-Agnostic)\n\n### OctavusChat\n\n```typescript\nclass OctavusChat {\n constructor(options: OctavusChatOptions);\n\n // State (read-only)\n readonly messages: UIMessage[];\n readonly status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n readonly error: OctavusError | null; // Structured error\n readonly pendingClientTools: Record<string, InteractiveTool[]>; // Interactive tools\n\n // Actions\n send(\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ): Promise<void>;\n stop(): void;\n\n // Subscription\n subscribe(callback: () => void): () => void; // Returns unsubscribe function\n}\n```\n\n## Next Steps\n\n- [HTTP Transport](/docs/client-sdk/http-transport) \u2014 HTTP/SSE integration (recommended)\n- [Socket Transport](/docs/client-sdk/socket-transport) \u2014 WebSocket and SockJS integration\n- [Messages](/docs/client-sdk/messages) \u2014 Working with message state\n- [Streaming](/docs/client-sdk/streaming) \u2014 Building streaming UIs\n- [Client Tools](/docs/client-sdk/client-tools) \u2014 Interactive browser-side tool handling\n- [Operations](/docs/client-sdk/execution-blocks) \u2014 Showing agent progress\n- [Error Handling](/docs/client-sdk/error-handling) \u2014 Handling errors with type guards\n- [File Uploads](/docs/client-sdk/file-uploads) \u2014 Uploading images and documents\n- [Examples](/docs/examples/overview) \u2014 Complete working examples\n",
81
90
  excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
82
91
  order: 1
83
92
  },
@@ -540,7 +549,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
540
549
  section: "protocol",
541
550
  title: "Input & Resources",
542
551
  description: "Defining agent inputs and persistent resources.",
543
- content: "\n# Input & Resources\n\nInputs are provided when creating a session. Resources are persistent state the agent can read and write.\n\n## Input Variables\n\nDefine inputs that consumers must (or may) provide:\n\n```yaml\ninput:\n # Required input\n COMPANY_NAME:\n type: string\n description: The company name to use in responses\n\n # Required input with description\n PRODUCT_NAME:\n type: string\n description: Product being supported\n\n # Optional input (defaults to \"NONE\")\n SUPPORT_POLICIES:\n type: string\n description: Company policies for support\n optional: true\n\n # Optional input with custom default\n USER_ID:\n type: string\n description: Current user's ID\n optional: true\n default: ''\n```\n\n### Input Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this input is for |\n| `optional` | No | If true, consumer doesn't have to provide it |\n| `default` | No | Default value if not provided (defaults to `\"NONE\"`) |\n\n### Using Inputs\n\nWhen creating a session, pass input values:\n\n```typescript\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n SUPPORT_POLICIES: 'Refunds within 30 days...',\n // USER_ID is optional, not provided\n});\n```\n\nInputs can also be used for [dynamic model selection](/docs/protocol/agent-config#dynamic-model-selection):\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n```\n\nIn prompts, reference with `{{INPUT_NAME}}`:\n\n```markdown\nYou are a support agent for {{COMPANY_NAME}}.\n```\n\n> **Note:** Variables must be `UPPER_SNAKE_CASE`. Nested properties (dot notation like `{{VAR.property}}`) are not supported. Objects are serialized as JSON when interpolated.\n\n## Resources\n\nResources are persistent state that:\n\n- Survive across triggers\n- Can be read and written by the agent\n- Are synced to the consumer's application\n\n```yaml\nresources:\n # String resource with default\n CONVERSATION_SUMMARY:\n type: string\n description: Running summary of the conversation\n default: ''\n\n # Resource with unknown type (for complex data)\n USER_CONTEXT:\n type: unknown\n description: Cached user information\n default: {}\n\n # Read-only resource (agent can read but not write)\n SYSTEM_CONFIG:\n type: unknown\n description: System configuration\n readonly: true\n default:\n maxRetries: 3\n timeout: 30000\n```\n\n### Resource Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes the resource purpose |\n| `default` | No | Initial value |\n| `readonly` | No | If true, agent cannot write to it |\n\n### Writing Resources\n\nUse the `set-resource` block in handlers:\n\n```yaml\nhandlers:\n request-human:\n # ... generate summary ...\n\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable containing the value\n```\n\n### Resource Events\n\nWhen a resource is updated, the client SDK receives a `resource-update` event:\n\n```typescript\nuseOctavusChat({\n onResourceUpdate: (name, value) => {\n if (name === 'CONVERSATION_SUMMARY') {\n console.log('Summary updated:', value);\n }\n },\n});\n```\n\n## Variables\n\nVariables are internal state managed by block outputs. They persist across triggers but are not synced to the consumer (unlike resources).\n\n```yaml\nvariables:\n SUMMARY:\n type: string\n description: Generated summary text\n TICKET:\n type: unknown\n description: Ticket creation result\n CONVERSATION_TEXT:\n type: string\n description: Serialized conversation\n```\n\n### Variable Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this variable stores |\n| `default` | No | Initial value |\n\n### Using Variables\n\nSet variables as output from blocks:\n\n```yaml\nhandlers:\n request-human:\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT # Stores result in variable\n\n Generate summary:\n block: next-message\n output: SUMMARY # LLM output stored in variable\n\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Use variable as input\n output: TICKET\n```\n\n## Scoping\n\n| Type | Scope | Persistence | Synced to Consumer |\n| ----------- | ------- | ------------------------ | ------------------- |\n| `input` | Session | Immutable | Yes (at creation) |\n| `resources` | Session | Persists across triggers | Yes (via callbacks) |\n| `variables` | Session | Persists across triggers | No (internal only) |\n",
552
+ content: "\n# Input & Resources\n\nInputs are provided when creating a session. Resources are persistent state the agent can read and write.\n\n## Input Variables\n\nDefine inputs that consumers must (or may) provide:\n\n```yaml\ninput:\n # Required input\n COMPANY_NAME:\n type: string\n description: The company name to use in responses\n\n # Required input with description\n PRODUCT_NAME:\n type: string\n description: Product being supported\n\n # Optional input (defaults to \"NONE\")\n SUPPORT_POLICIES:\n type: string\n description: Company policies for support\n optional: true\n\n # Optional input with custom default\n USER_ID:\n type: string\n description: Current user's ID\n optional: true\n default: ''\n```\n\n### Input Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this input is for |\n| `optional` | No | If true, consumer doesn't have to provide it |\n| `default` | No | Default value if not provided (defaults to `\"NONE\"`) |\n\n### Using Inputs\n\nWhen creating a session, pass input values:\n\n```typescript\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n SUPPORT_POLICIES: 'Refunds within 30 days...',\n // USER_ID is optional, not provided\n});\n```\n\nInputs can also be used for [dynamic model selection](/docs/protocol/agent-config#dynamic-model-selection):\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n```\n\nIn prompts, reference variables with `{{VARIABLE_NAME}}`:\n\n```markdown\nYou are a support agent for {{COMPANY_NAME}}.\n```\n\nTo use a variable in a prompt, pass it through the `input` mapping on the [agent config](/docs/protocol/agent-config#system-prompt) or [block](/docs/protocol/handlers#block-input-mapping). Variables not listed in the `input` mapping won't be interpolated \u2014 the `{{VARIABLE}}` placeholder will be preserved as-is.\n\n> **Note:** Variables must be `UPPER_SNAKE_CASE`. Nested properties (dot notation like `{{VAR.property}}`) are not supported. Objects are serialized as JSON when interpolated.\n\n## Resources\n\nResources are persistent state that:\n\n- Survive across triggers\n- Can be read and written by the agent\n- Are synced to the consumer's application\n\n```yaml\nresources:\n # String resource with default\n CONVERSATION_SUMMARY:\n type: string\n description: Running summary of the conversation\n default: ''\n\n # Resource with unknown type (for complex data)\n USER_CONTEXT:\n type: unknown\n description: Cached user information\n default: {}\n\n # Read-only resource (agent can read but not write)\n SYSTEM_CONFIG:\n type: unknown\n description: System configuration\n readonly: true\n default:\n maxRetries: 3\n timeout: 30000\n```\n\n### Resource Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes the resource purpose |\n| `default` | No | Initial value |\n| `readonly` | No | If true, agent cannot write to it |\n\n### Writing Resources\n\nUse the `set-resource` block in handlers:\n\n```yaml\nhandlers:\n request-human:\n # ... generate summary ...\n\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable containing the value\n```\n\n### Resource Events\n\nWhen a resource is updated, the client SDK receives a `resource-update` event:\n\n```typescript\nuseOctavusChat({\n onResourceUpdate: (name, value) => {\n if (name === 'CONVERSATION_SUMMARY') {\n console.log('Summary updated:', value);\n }\n },\n});\n```\n\n## Variables\n\nVariables are internal state managed by block outputs. They persist across triggers but are not synced to the consumer (unlike resources).\n\n```yaml\nvariables:\n SUMMARY:\n type: string\n description: Generated summary text\n TICKET:\n type: unknown\n description: Ticket creation result\n CONVERSATION_TEXT:\n type: string\n description: Serialized conversation\n```\n\n### Variable Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this variable stores |\n| `default` | No | Initial value |\n\n### Using Variables\n\nSet variables as output from blocks:\n\n```yaml\nhandlers:\n request-human:\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT # Stores result in variable\n\n Generate summary:\n block: next-message\n output: SUMMARY # LLM output stored in variable\n\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Use variable as input\n output: TICKET\n```\n\n## Scoping\n\n| Type | Scope | Persistence | Synced to Consumer |\n| ----------- | ------- | ------------------------ | ------------------- |\n| `input` | Session | Immutable | Yes (at creation) |\n| `resources` | Session | Persists across triggers | Yes (via callbacks) |\n| `variables` | Session | Persists across triggers | No (internal only) |\n",
544
553
  excerpt: "Input & Resources Inputs are provided when creating a session. Resources are persistent state the agent can read and write. Input Variables Define inputs that consumers must (or may) provide: Input...",
545
554
  order: 2
546
555
  },
@@ -576,7 +585,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
576
585
  section: "protocol",
577
586
  title: "Handlers",
578
587
  description: "Defining execution handlers with blocks.",
579
- content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n```\n\nThe `model` field can also reference a variable for dynamic model selection:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nMap variables to block inputs:\n\n```yaml\n# Simple list (variable name = prompt variable)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Mapping (different names)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # CONVERSATION in prompt = CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
588
+ content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n```\n\nThe `model` field can also reference a variable for dynamic model selection:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nThe `input` field on blocks controls which variables are passed to the prompt. Only variables listed in `input` are available for interpolation.\n\nVariables can come from `protocol.input`, `protocol.resources`, `protocol.variables`, `trigger.input`, or outputs from prior blocks.\n\n```yaml\n# Array format (same name)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Array format (rename)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # Prompt sees CONVERSATION, value comes from CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n\n# Object format (rename)\ninput:\n CONVERSATION: CONVERSATION_TEXT\n TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
580
589
  excerpt: "Handlers Handlers define what happens when a trigger fires. They contain execution blocks that run in sequence. Handler Structure Each block has a human-readable name (shown in debug UI) and a ...",
581
590
  order: 6
582
591
  },
@@ -585,7 +594,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
585
594
  section: "protocol",
586
595
  title: "Agent Config",
587
596
  description: "Configuring the agent model and behavior.",
588
- content: "\n# Agent Config\n\nThe `agent` section configures the LLM model, system prompt, tools, and behavior.\n\n## Basic Configuration\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account] # Available tools\n skills: [qr-code] # Available skills\n```\n\n## Configuration Options\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------- |\n| `model` | Yes | Model identifier or variable reference |\n| `system` | Yes | System prompt filename (without .md) |\n| `input` | No | Variables to interpolate in system prompt |\n| `tools` | No | List of tools the LLM can call |\n| `skills` | No | List of Octavus skills the LLM can use |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `agentic` | No | Allow multiple tool call cycles |\n| `maxSteps` | No | Maximum agentic steps (default: 10) |\n| `temperature` | No | Model temperature (0-2) |\n| `thinking` | No | Extended reasoning level |\n| `anthropic` | No | Anthropic-specific options (tools, skills) |\n\n## Models\n\nSpecify models in `provider/model-id` format. Any model supported by the provider's SDK will work.\n\n### Supported Providers\n\n| Provider | Format | Examples |\n| --------- | ---------------------- | -------------------------------------------------------------------- |\n| Anthropic | `anthropic/{model-id}` | `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |\n| Google | `google/{model-id}` | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash` |\n| OpenAI | `openai/{model-id}` | `gpt-5`, `gpt-4o`, `o4-mini`, `o3`, `o3-mini`, `o1` |\n\n### Examples\n\n```yaml\n# Anthropic Claude 4.5\nagent:\n model: anthropic/claude-sonnet-4-5\n\n# Google Gemini 3\nagent:\n model: google/gemini-3-flash-preview\n\n# OpenAI GPT-5\nagent:\n model: openai/gpt-5\n\n# OpenAI reasoning models\nagent:\n model: openai/o3-mini\n```\n\n> **Note**: Model IDs are passed directly to the provider SDK. Check the provider's documentation for the latest available models.\n\n### Dynamic Model Selection\n\nThe model field can also reference an input variable, allowing consumers to choose the model when creating a session:\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n system: system\n```\n\nWhen creating a session, pass the model:\n\n```typescript\nconst sessionId = await client.agentSessions.create('my-agent', {\n MODEL: 'anthropic/claude-sonnet-4-5',\n});\n```\n\nThis enables:\n\n- **Multi-provider support** \u2014 Same agent works with different providers\n- **A/B testing** \u2014 Test different models without protocol changes\n- **User preferences** \u2014 Let users choose their preferred model\n\nThe model value is validated at runtime to ensure it's in the correct `provider/model-id` format.\n\n> **Note**: When using dynamic models, provider-specific options (like `anthropic:`) may not apply if the model resolves to a different provider.\n\n## System Prompt\n\nThe system prompt sets the agent's persona and instructions:\n\n```yaml\nagent:\n system: system # Uses prompts/system.md\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n```\n\nExample `prompts/system.md`:\n\n```markdown\nYou are a friendly support agent for {{COMPANY_NAME}}.\n\n## Your Role\n\nHelp users with questions about {{PRODUCT_NAME}}.\n\n## Guidelines\n\n- Be helpful and professional\n- If you can't help, offer to escalate\n- Never share internal information\n```\n\n## Agentic Mode\n\nEnable multi-step tool calling:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [get-user-account, search-docs, create-ticket]\n agentic: true # LLM can call multiple tools\n maxSteps: 10 # Limit cycles to prevent runaway\n```\n\n**How it works:**\n\n1. LLM receives user message\n2. LLM decides to call a tool\n3. Tool executes, result returned to LLM\n4. LLM decides if more tools needed\n5. Repeat until LLM responds or maxSteps reached\n\n## Extended Thinking\n\nEnable extended reasoning for complex tasks:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n thinking: medium # low | medium | high\n```\n\n| Level | Token Budget | Use Case |\n| -------- | ------------ | ------------------- |\n| `low` | ~5,000 | Simple reasoning |\n| `medium` | ~10,000 | Moderate complexity |\n| `high` | ~20,000 | Complex analysis |\n\nThinking content streams to the UI and can be displayed to users.\n\n## Skills\n\nEnable Octavus skills for code execution and file generation:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code] # Enable skills\n agentic: true\n```\n\nSkills provide provider-agnostic code execution in isolated sandboxes. When enabled, the LLM can execute Python/Bash code, run skill scripts, and generate files.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Image Generation\n\nEnable the LLM to generate images autonomously:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n imageModel: google/gemini-2.5-flash-image\n agentic: true\n```\n\nWhen `imageModel` is configured, the `octavus_generate_image` tool becomes available. The LLM can decide when to generate images based on user requests. The tool supports both text-to-image generation and image editing/transformation using reference images.\n\n### Supported Image Providers\n\n| Provider | Model Types | Examples |\n| -------- | --------------------------------------- | --------------------------------------------------------- |\n| OpenAI | Dedicated image models | `gpt-image-1` |\n| Google | Gemini native (contains \"image\") | `gemini-2.5-flash-image`, `gemini-3-flash-image-generate` |\n| Google | Imagen dedicated (starts with \"imagen\") | `imagen-4.0-generate-001` |\n\n> **Note**: Google has two image generation approaches. Gemini \"native\" models (containing \"image\" in the ID) generate images using the language model API with `responseModalities`. Imagen models (starting with \"imagen\") use a dedicated image generation API.\n\n### Image Sizes\n\nThe tool supports three image sizes:\n\n- `1024x1024` (default) \u2014 Square\n- `1792x1024` \u2014 Landscape (16:9)\n- `1024x1792` \u2014 Portrait (9:16)\n\n### Image Editing with Reference Images\n\nBoth the agentic tool and the `generate-image` block support reference images for editing and transformation. When reference images are provided, the prompt describes how to modify or use those images.\n\n| Provider | Models | Reference Image Support |\n| -------- | -------------------------------- | ----------------------- |\n| OpenAI | `gpt-image-1` | Yes |\n| Google | Gemini native (`gemini-*-image`) | Yes |\n| Google | Imagen (`imagen-*`) | No |\n\n### Agentic vs Deterministic\n\nUse `imageModel` in agent config when:\n\n- The LLM should decide when to generate or edit images\n- Users ask for images in natural language\n\nUse `generate-image` block (see [Handlers](/docs/protocol/handlers#generate-image)) when:\n\n- You want explicit control over image generation or editing\n- Building prompt engineering pipelines\n- Images are generated at specific handler steps\n\n## Temperature\n\nControl response randomness:\n\n```yaml\nagent:\n model: openai/gpt-4o\n temperature: 0.7 # 0 = deterministic, 2 = creative\n```\n\n**Guidelines:**\n\n- `0 - 0.3`: Factual, consistent responses\n- `0.4 - 0.7`: Balanced (good default)\n- `0.8 - 1.2`: Creative, varied responses\n- `> 1.2`: Very creative (may be inconsistent)\n\n## Provider Options\n\nEnable provider-specific features like Anthropic's built-in tools and skills:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n```\n\nProvider options are validated against the model\u2014using `anthropic:` with a non-Anthropic model will fail validation.\n\nSee [Provider Options](/docs/protocol/provider-options) for full documentation.\n\n## Thread-Specific Config\n\nOverride config for named threads:\n\n```yaml\nhandlers:\n request-human:\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5 # Different model\n thinking: low # Different thinking\n maxSteps: 1 # Limit tool calls\n system: escalation-summary # Different prompt\n```\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n PRODUCT_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\nresources:\n CONVERSATION_SUMMARY:\n type: string\n default: ''\n\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { type: string }\n\n search-docs:\n description: Search help documentation\n parameters:\n query: { type: string }\n\n create-support-ticket:\n description: Create a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string } # low, medium, high\n\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n tools:\n - get-user-account\n - search-docs\n - create-support-ticket\n skills: [qr-code] # Octavus skills\n agentic: true\n maxSteps: 10\n thinking: medium\n # Anthropic-specific options\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n Respond:\n block: next-message\n```\n",
597
+ content: "\n# Agent Config\n\nThe `agent` section configures the LLM model, system prompt, tools, and behavior.\n\n## Basic Configuration\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account] # Available tools\n skills: [qr-code] # Available skills\n```\n\n## Configuration Options\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------- |\n| `model` | Yes | Model identifier or variable reference |\n| `system` | Yes | System prompt filename (without .md) |\n| `input` | No | Variables to pass to the system prompt |\n| `tools` | No | List of tools the LLM can call |\n| `skills` | No | List of Octavus skills the LLM can use |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `agentic` | No | Allow multiple tool call cycles |\n| `maxSteps` | No | Maximum agentic steps (default: 10) |\n| `temperature` | No | Model temperature (0-2) |\n| `thinking` | No | Extended reasoning level |\n| `anthropic` | No | Anthropic-specific options (tools, skills) |\n\n## Models\n\nSpecify models in `provider/model-id` format. Any model supported by the provider's SDK will work.\n\n### Supported Providers\n\n| Provider | Format | Examples |\n| --------- | ---------------------- | -------------------------------------------------------------------- |\n| Anthropic | `anthropic/{model-id}` | `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |\n| Google | `google/{model-id}` | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash` |\n| OpenAI | `openai/{model-id}` | `gpt-5`, `gpt-4o`, `o4-mini`, `o3`, `o3-mini`, `o1` |\n\n### Examples\n\n```yaml\n# Anthropic Claude 4.5\nagent:\n model: anthropic/claude-sonnet-4-5\n\n# Google Gemini 3\nagent:\n model: google/gemini-3-flash-preview\n\n# OpenAI GPT-5\nagent:\n model: openai/gpt-5\n\n# OpenAI reasoning models\nagent:\n model: openai/o3-mini\n```\n\n> **Note**: Model IDs are passed directly to the provider SDK. Check the provider's documentation for the latest available models.\n\n### Dynamic Model Selection\n\nThe model field can also reference an input variable, allowing consumers to choose the model when creating a session:\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n system: system\n```\n\nWhen creating a session, pass the model:\n\n```typescript\nconst sessionId = await client.agentSessions.create('my-agent', {\n MODEL: 'anthropic/claude-sonnet-4-5',\n});\n```\n\nThis enables:\n\n- **Multi-provider support** \u2014 Same agent works with different providers\n- **A/B testing** \u2014 Test different models without protocol changes\n- **User preferences** \u2014 Let users choose their preferred model\n\nThe model value is validated at runtime to ensure it's in the correct `provider/model-id` format.\n\n> **Note**: When using dynamic models, provider-specific options (like `anthropic:`) may not apply if the model resolves to a different provider.\n\n## System Prompt\n\nThe system prompt sets the agent's persona and instructions. The `input` field controls which variables are available to the prompt \u2014 only variables listed in `input` are interpolated.\n\n```yaml\nagent:\n system: system # Uses prompts/system.md\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n```\n\nVariables in `input` can come from `protocol.input`, `protocol.resources`, or `protocol.variables`.\n\n### Input Mapping Formats\n\n```yaml\n# Array format (same name)\ninput:\n - COMPANY_NAME\n - PRODUCT_NAME\n\n# Array format (rename)\ninput:\n - CONTEXT: CONVERSATION_SUMMARY # Prompt sees CONTEXT, value comes from CONVERSATION_SUMMARY\n\n# Object format (rename)\ninput:\n CONTEXT: CONVERSATION_SUMMARY\n```\n\nThe left side (label) is what the prompt sees. The right side (source) is where the value comes from.\n\n### Example\n\n`prompts/system.md`:\n\n```markdown\nYou are a friendly support agent for {{COMPANY_NAME}}.\n\n## Your Role\n\nHelp users with questions about {{PRODUCT_NAME}}.\n\n## Guidelines\n\n- Be helpful and professional\n- If you can't help, offer to escalate\n- Never share internal information\n```\n\n## Agentic Mode\n\nEnable multi-step tool calling:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [get-user-account, search-docs, create-ticket]\n agentic: true # LLM can call multiple tools\n maxSteps: 10 # Limit cycles to prevent runaway\n```\n\n**How it works:**\n\n1. LLM receives user message\n2. LLM decides to call a tool\n3. Tool executes, result returned to LLM\n4. LLM decides if more tools needed\n5. Repeat until LLM responds or maxSteps reached\n\n## Extended Thinking\n\nEnable extended reasoning for complex tasks:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n thinking: medium # low | medium | high\n```\n\n| Level | Token Budget | Use Case |\n| -------- | ------------ | ------------------- |\n| `low` | ~5,000 | Simple reasoning |\n| `medium` | ~10,000 | Moderate complexity |\n| `high` | ~20,000 | Complex analysis |\n\nThinking content streams to the UI and can be displayed to users.\n\n## Skills\n\nEnable Octavus skills for code execution and file generation:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code] # Enable skills\n agentic: true\n```\n\nSkills provide provider-agnostic code execution in isolated sandboxes. When enabled, the LLM can execute Python/Bash code, run skill scripts, and generate files.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Image Generation\n\nEnable the LLM to generate images autonomously:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n imageModel: google/gemini-2.5-flash-image\n agentic: true\n```\n\nWhen `imageModel` is configured, the `octavus_generate_image` tool becomes available. The LLM can decide when to generate images based on user requests. The tool supports both text-to-image generation and image editing/transformation using reference images.\n\n### Supported Image Providers\n\n| Provider | Model Types | Examples |\n| -------- | --------------------------------------- | --------------------------------------------------------- |\n| OpenAI | Dedicated image models | `gpt-image-1` |\n| Google | Gemini native (contains \"image\") | `gemini-2.5-flash-image`, `gemini-3-flash-image-generate` |\n| Google | Imagen dedicated (starts with \"imagen\") | `imagen-4.0-generate-001` |\n\n> **Note**: Google has two image generation approaches. Gemini \"native\" models (containing \"image\" in the ID) generate images using the language model API with `responseModalities`. Imagen models (starting with \"imagen\") use a dedicated image generation API.\n\n### Image Sizes\n\nThe tool supports three image sizes:\n\n- `1024x1024` (default) \u2014 Square\n- `1792x1024` \u2014 Landscape (16:9)\n- `1024x1792` \u2014 Portrait (9:16)\n\n### Image Editing with Reference Images\n\nBoth the agentic tool and the `generate-image` block support reference images for editing and transformation. When reference images are provided, the prompt describes how to modify or use those images.\n\n| Provider | Models | Reference Image Support |\n| -------- | -------------------------------- | ----------------------- |\n| OpenAI | `gpt-image-1` | Yes |\n| Google | Gemini native (`gemini-*-image`) | Yes |\n| Google | Imagen (`imagen-*`) | No |\n\n### Agentic vs Deterministic\n\nUse `imageModel` in agent config when:\n\n- The LLM should decide when to generate or edit images\n- Users ask for images in natural language\n\nUse `generate-image` block (see [Handlers](/docs/protocol/handlers#generate-image)) when:\n\n- You want explicit control over image generation or editing\n- Building prompt engineering pipelines\n- Images are generated at specific handler steps\n\n## Temperature\n\nControl response randomness:\n\n```yaml\nagent:\n model: openai/gpt-4o\n temperature: 0.7 # 0 = deterministic, 2 = creative\n```\n\n**Guidelines:**\n\n- `0 - 0.3`: Factual, consistent responses\n- `0.4 - 0.7`: Balanced (good default)\n- `0.8 - 1.2`: Creative, varied responses\n- `> 1.2`: Very creative (may be inconsistent)\n\n## Provider Options\n\nEnable provider-specific features like Anthropic's built-in tools and skills:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n```\n\nProvider options are validated against the model\u2014using `anthropic:` with a non-Anthropic model will fail validation.\n\nSee [Provider Options](/docs/protocol/provider-options) for full documentation.\n\n## Thread-Specific Config\n\nOverride config for named threads:\n\n```yaml\nhandlers:\n request-human:\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5 # Different model\n thinking: low # Different thinking\n maxSteps: 1 # Limit tool calls\n system: escalation-summary # Different prompt\n```\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n PRODUCT_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\nresources:\n CONVERSATION_SUMMARY:\n type: string\n default: ''\n\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { type: string }\n\n search-docs:\n description: Search help documentation\n parameters:\n query: { type: string }\n\n create-support-ticket:\n description: Create a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string } # low, medium, high\n\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n tools:\n - get-user-account\n - search-docs\n - create-support-ticket\n skills: [qr-code] # Octavus skills\n agentic: true\n maxSteps: 10\n thinking: medium\n # Anthropic-specific options\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n Respond:\n block: next-message\n```\n",
589
598
  excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
590
599
  order: 7
591
600
  },
@@ -639,7 +648,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
639
648
  section: "api-reference",
640
649
  title: "Sessions",
641
650
  description: "Session management API endpoints.",
642
- content: '\n# Sessions API\n\nSessions represent conversations with agents. They store conversation history, resources, and variables.\n\nAll session endpoints require an API key with the **Sessions** permission.\n\n## Create Session\n\nCreate a new agent session.\n\n```\nPOST /api/agent-sessions\n```\n\n### Request Body\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro",\n "USER_ID": "user-123"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| --------- | ------ | -------- | ------------------------------------- |\n| `agentId` | string | Yes | Agent ID (the `id` field, not `slug`) |\n| `input` | object | No | Input variables for the agent |\n\n> **Getting the agent ID:** Copy the ID from the agent URL in the [platform](https://octavus.ai) (e.g., `octavus.ai/agents/clxyz123`), or use the [CLI](/docs/server-sdk/cli) (`octavus sync ./agents/my-agent`) for local development workflows.\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n }\n }\'\n```\n\n## Get Session\n\nRetrieve session state. Returns UI-ready messages for active sessions, or expiration info for expired sessions.\n\n```\nGET /api/agent-sessions/:sessionId\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n| --------- | ------ | ---------------------------------------------------- |\n| `format` | string | Optional. Use `format=ui` for UI-ready messages only |\n\n### Response (Active Session)\n\nWhen the session is active, the response includes `UIMessage` objects:\n\n```json\n{\n "id": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "status": "active",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n },\n "variables": {},\n "resources": {\n "CONVERSATION_SUMMARY": ""\n },\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n },\n {\n "id": "1702345805000-def456b",\n "role": "assistant",\n "parts": [\n { "type": "text", "text": "I can help you reset your password...", "status": "done" }\n ],\n "status": "done",\n "createdAt": "2024-01-15T10:30:05.000Z"\n }\n ],\n "createdAt": "2024-01-15T10:30:00Z",\n "updatedAt": "2024-01-15T10:30:05Z"\n}\n```\n\n### Response (Expired Session)\n\nWhen the session has expired, the response indicates the expiration status:\n\n```json\n{\n "status": "expired",\n "sessionId": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "createdAt": "2024-01-15T10:30:00Z"\n}\n```\n\nUse the [Restore Session](#restore-session) endpoint to restore an expired session from stored messages.\n\n````\n\n### UIMessage Parts\n\nMessages contain typed `parts` that preserve content ordering:\n\n| Part Type | Description |\n|-----------|-------------|\n| `text` | Text content with `text` and `status` fields |\n| `reasoning` | Extended reasoning with `text` and `status` fields |\n| `tool-call` | Tool execution with `toolCallId`, `toolName`, `displayName`, `args`, `result`, `status` |\n| `operation` | Internal operations with `operationId`, `name`, `operationType`, `status` |\n| `file` | File attachment with `id`, `mediaType`, `url`, `filename`, `size` |\n| `source` | Source reference with `sourceType`, `id`, `url`, `title` |\n| `object` | Structured output with `id`, `typeName`, `object`, `status` |\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n````\n\n## Restore Session\n\nRestore an expired session from stored messages. This allows you to continue a conversation after the server-side state has expired.\n\n```\nPOST /api/agent-sessions/:sessionId/restore\n```\n\n### Request Body\n\n```json\n{\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n }\n ],\n "input": {\n "COMPANY_NAME": "Acme Corp"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| ---------- | ----------- | -------- | -------------------------------------------------------------- |\n| `messages` | UIMessage[] | Yes | Previously stored chat history |\n| `input` | object | No | Session input for system prompt interpolation (same as create) |\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "restored": true\n}\n```\n\n| Field | Type | Description |\n| ----------- | ------- | ----------------------------------------------------------------------- |\n| `sessionId` | string | The session ID |\n| `restored` | boolean | `true` if restored from messages, `false` if session was already active |\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions/:sessionId/restore \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "messages": [...],\n "input": { "COMPANY_NAME": "Acme Corp" }\n }\'\n```\n\n> **Note**: Store the `UIMessage[]` array after each interaction to enable restoration. The restore endpoint reconstructs the conversation state from these messages.\n\n## Trigger Session\n\nExecute a trigger on a session. Returns a Server-Sent Events stream.\n\n```\nPOST /api/agent-sessions/:sessionId/trigger\n```\n\n### Request Body\n\n```json\n{\n "triggerName": "user-message",\n "input": {\n "USER_MESSAGE": "How do I reset my password?"\n },\n "toolResults": []\n}\n```\n\n| Field | Type | Required | Description |\n| ------------- | ------ | -------- | ---------------------------------------------- |\n| `triggerName` | string | Yes | Name of the trigger to execute |\n| `input` | object | No | Input variables for the trigger |\n| `toolResults` | array | No | Tool results for continuation (handled by SDK) |\n\n### Response\n\nReturns `text/event-stream` with SSE events:\n\n```\ndata: {"type":"start","messageId":"msg-123"}\n\ndata: {"type":"block-start","blockId":"b1","blockName":"Add user message","blockType":"add-message","display":"hidden"}\n\ndata: {"type":"block-end","blockId":"b1"}\n\ndata: {"type":"block-start","blockId":"b2","blockName":"Respond to user","blockType":"next-message","display":"stream","outputToChat":true}\n\ndata: {"type":"text-start","id":"t1"}\n\ndata: {"type":"text-delta","id":"t1","delta":"I"}\n\ndata: {"type":"text-delta","id":"t1","delta":" can"}\n\ndata: {"type":"text-delta","id":"t1","delta":" help"}\n\ndata: {"type":"text-delta","id":"t1","delta":" you"}\n\ndata: {"type":"text-delta","id":"t1","delta":" reset"}\n\ndata: {"type":"text-delta","id":"t1","delta":" your"}\n\ndata: {"type":"text-delta","id":"t1","delta":" password"}\n\ndata: {"type":"text-delta","id":"t1","delta":"!"}\n\ndata: {"type":"text-end","id":"t1"}\n\ndata: {"type":"block-end","blockId":"b2"}\n\ndata: {"type":"finish","finishReason":"stop"}\n\ndata: [DONE]\n```\n\n### Event Types\n\n| Event | Description |\n| ----------------------- | ---------------------------------- |\n| `start` | Stream started |\n| `finish` | Execution complete |\n| `error` | Error occurred |\n| `block-start` | Execution block started |\n| `block-end` | Execution block completed |\n| `text-start` | Text generation started |\n| `text-delta` | Incremental text content |\n| `text-end` | Text generation ended |\n| `reasoning-start` | Extended reasoning started |\n| `reasoning-delta` | Reasoning content |\n| `reasoning-end` | Extended reasoning ended |\n| `tool-input-start` | Tool call initiated |\n| `tool-input-delta` | Tool arguments streaming |\n| `tool-input-end` | Tool arguments streaming ended |\n| `tool-input-available` | Tool input complete |\n| `tool-output-available` | Tool completed with result |\n| `tool-output-error` | Tool failed |\n| `tool-request` | Platform requesting tool execution |\n| `file-available` | File ready for display/download |\n| `resource-update` | Resource value changed |\n\n### Example\n\n```bash\ncurl -N -X POST https://octavus.ai/api/agent-sessions/:sessionId/trigger \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "How do I reset my password?" }\n }\'\n```\n\n## Tool Continuation\n\nWhen the agent calls external tools, you\'ll receive a `tool-request` event. Execute the tools and send results back:\n\n```json\n{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "..." },\n "toolResults": [\n {\n "toolCallId": "tc_123",\n "toolName": "get-user-account",\n "result": {\n "name": "Demo User",\n "email": "demo@example.com"\n }\n }\n ]\n}\n```\n\nThe Server SDK handles this continuation pattern automatically.\n\n## Upload URLs\n\nGet presigned URLs for file uploads. Files are uploaded directly to S3.\n\n```\nPOST /api/files/upload-urls\n```\n\n### Request Body\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "files": [\n {\n "filename": "photo.jpg",\n "mediaType": "image/jpeg",\n "size": 102400\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ------------------- | ------ | -------- | ----------------------------------- |\n| `sessionId` | string | Yes | Session ID to associate files with |\n| `files` | array | Yes | Array of file metadata (1-20 files) |\n| `files[].filename` | string | Yes | Original filename |\n| `files[].mediaType` | string | Yes | MIME type (e.g., `image/png`) |\n| `files[].size` | number | Yes | File size in bytes |\n\n### Response\n\n```json\n{\n "files": [\n {\n "id": "file-abc123",\n "uploadUrl": "https://s3.amazonaws.com/bucket/key?...",\n "downloadUrl": "https://s3.amazonaws.com/bucket/key?..."\n }\n ]\n}\n```\n\n### Upload Flow\n\n1. Request upload URLs from the platform\n2. PUT file content to `uploadUrl` with `Content-Type` header\n3. Use `downloadUrl` as the `url` in `FileReference`\n4. Include `FileReference` in trigger input\n\n### Supported Types\n\n| Category | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n### Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 10 MB |\n| Max total per request | 50 MB |\n| Max files per request | 20 |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n',
651
+ content: '\n# Sessions API\n\nSessions represent conversations with agents. They store conversation history, resources, and variables.\n\nAll session endpoints require an API key with the **Sessions** permission.\n\n## Create Session\n\nCreate a new agent session.\n\n```\nPOST /api/agent-sessions\n```\n\n### Request Body\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro",\n "USER_ID": "user-123"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| --------- | ------ | -------- | ------------------------------------- |\n| `agentId` | string | Yes | Agent ID (the `id` field, not `slug`) |\n| `input` | object | No | Input variables for the agent |\n\n> **Getting the agent ID:** Copy the ID from the agent URL in the [platform](https://octavus.ai) (e.g., `octavus.ai/agents/clxyz123`), or use the [CLI](/docs/server-sdk/cli) (`octavus sync ./agents/my-agent`) for local development workflows.\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n }\n }\'\n```\n\n## Get Session\n\nRetrieve session state. Returns UI-ready messages for active sessions, or expiration info for expired sessions.\n\n```\nGET /api/agent-sessions/:sessionId\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n| --------- | ------ | ---------------------------------------------------- |\n| `format` | string | Optional. Use `format=ui` for UI-ready messages only |\n\n### Response (Active Session)\n\nWhen the session is active, the response includes `UIMessage` objects:\n\n```json\n{\n "id": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "status": "active",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n },\n "variables": {},\n "resources": {\n "CONVERSATION_SUMMARY": ""\n },\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n },\n {\n "id": "1702345805000-def456b",\n "role": "assistant",\n "parts": [\n { "type": "text", "text": "I can help you reset your password...", "status": "done" }\n ],\n "status": "done",\n "createdAt": "2024-01-15T10:30:05.000Z"\n }\n ],\n "createdAt": "2024-01-15T10:30:00Z",\n "updatedAt": "2024-01-15T10:30:05Z"\n}\n```\n\n### Response (Expired Session)\n\nWhen the session has expired, the response indicates the expiration status:\n\n```json\n{\n "status": "expired",\n "sessionId": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "createdAt": "2024-01-15T10:30:00Z"\n}\n```\n\nUse the [Restore Session](#restore-session) endpoint to restore an expired session from stored messages.\n\n````\n\n### UIMessage Parts\n\nMessages contain typed `parts` that preserve content ordering:\n\n| Part Type | Description |\n|-----------|-------------|\n| `text` | Text content with `text` and `status` fields |\n| `reasoning` | Extended reasoning with `text` and `status` fields |\n| `tool-call` | Tool execution with `toolCallId`, `toolName`, `displayName`, `args`, `result`, `status` |\n| `operation` | Internal operations with `operationId`, `name`, `operationType`, `status` |\n| `file` | File attachment with `id`, `mediaType`, `url`, `filename`, `size` |\n| `source` | Source reference with `sourceType`, `id`, `url`, `title` |\n| `object` | Structured output with `id`, `typeName`, `object`, `status` |\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n````\n\n## Restore Session\n\nRestore an expired session from stored messages. This allows you to continue a conversation after the server-side state has expired.\n\n```\nPOST /api/agent-sessions/:sessionId/restore\n```\n\n### Request Body\n\n```json\n{\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n }\n ],\n "input": {\n "COMPANY_NAME": "Acme Corp"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| ---------- | ----------- | -------- | -------------------------------------------------------------- |\n| `messages` | UIMessage[] | Yes | Previously stored chat history |\n| `input` | object | No | Session input for system prompt interpolation (same as create) |\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "restored": true\n}\n```\n\n| Field | Type | Description |\n| ----------- | ------- | ----------------------------------------------------------------------- |\n| `sessionId` | string | The session ID |\n| `restored` | boolean | `true` if restored from messages, `false` if session was already active |\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions/:sessionId/restore \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "messages": [...],\n "input": { "COMPANY_NAME": "Acme Corp" }\n }\'\n```\n\n> **Note**: Store the `UIMessage[]` array after each interaction to enable restoration. The restore endpoint reconstructs the conversation state from these messages.\n\n## Clear Session\n\nClear session state, transitioning it to `expired` status. The session can be restored afterwards with the [Restore Session](#restore-session) endpoint.\n\nThis is idempotent \u2014 clearing an already expired session succeeds without error.\n\n```\nDELETE /api/agent-sessions/:sessionId\n```\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "cleared": true\n}\n```\n\n### Example\n\n```bash\ncurl -X DELETE https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Trigger Session\n\nExecute a trigger on a session. Returns a Server-Sent Events stream.\n\n```\nPOST /api/agent-sessions/:sessionId/trigger\n```\n\n### Request Body\n\n```json\n{\n "triggerName": "user-message",\n "input": {\n "USER_MESSAGE": "How do I reset my password?"\n },\n "toolResults": []\n}\n```\n\n| Field | Type | Required | Description |\n| ------------- | ------ | -------- | ---------------------------------------------- |\n| `triggerName` | string | Yes | Name of the trigger to execute |\n| `input` | object | No | Input variables for the trigger |\n| `toolResults` | array | No | Tool results for continuation (handled by SDK) |\n\n### Response\n\nReturns `text/event-stream` with SSE events:\n\n```\ndata: {"type":"start","messageId":"msg-123"}\n\ndata: {"type":"block-start","blockId":"b1","blockName":"Add user message","blockType":"add-message","display":"hidden"}\n\ndata: {"type":"block-end","blockId":"b1"}\n\ndata: {"type":"block-start","blockId":"b2","blockName":"Respond to user","blockType":"next-message","display":"stream","outputToChat":true}\n\ndata: {"type":"text-start","id":"t1"}\n\ndata: {"type":"text-delta","id":"t1","delta":"I"}\n\ndata: {"type":"text-delta","id":"t1","delta":" can"}\n\ndata: {"type":"text-delta","id":"t1","delta":" help"}\n\ndata: {"type":"text-delta","id":"t1","delta":" you"}\n\ndata: {"type":"text-delta","id":"t1","delta":" reset"}\n\ndata: {"type":"text-delta","id":"t1","delta":" your"}\n\ndata: {"type":"text-delta","id":"t1","delta":" password"}\n\ndata: {"type":"text-delta","id":"t1","delta":"!"}\n\ndata: {"type":"text-end","id":"t1"}\n\ndata: {"type":"block-end","blockId":"b2"}\n\ndata: {"type":"finish","finishReason":"stop"}\n\ndata: [DONE]\n```\n\n### Event Types\n\n| Event | Description |\n| ----------------------- | ---------------------------------- |\n| `start` | Stream started |\n| `finish` | Execution complete |\n| `error` | Error occurred |\n| `block-start` | Execution block started |\n| `block-end` | Execution block completed |\n| `text-start` | Text generation started |\n| `text-delta` | Incremental text content |\n| `text-end` | Text generation ended |\n| `reasoning-start` | Extended reasoning started |\n| `reasoning-delta` | Reasoning content |\n| `reasoning-end` | Extended reasoning ended |\n| `tool-input-start` | Tool call initiated |\n| `tool-input-delta` | Tool arguments streaming |\n| `tool-input-end` | Tool arguments streaming ended |\n| `tool-input-available` | Tool input complete |\n| `tool-output-available` | Tool completed with result |\n| `tool-output-error` | Tool failed |\n| `tool-request` | Platform requesting tool execution |\n| `file-available` | File ready for display/download |\n| `resource-update` | Resource value changed |\n\n### Example\n\n```bash\ncurl -N -X POST https://octavus.ai/api/agent-sessions/:sessionId/trigger \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "How do I reset my password?" }\n }\'\n```\n\n## Tool Continuation\n\nWhen the agent calls external tools, you\'ll receive a `tool-request` event. Execute the tools and send results back:\n\n```json\n{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "..." },\n "toolResults": [\n {\n "toolCallId": "tc_123",\n "toolName": "get-user-account",\n "result": {\n "name": "Demo User",\n "email": "demo@example.com"\n }\n }\n ]\n}\n```\n\nThe Server SDK handles this continuation pattern automatically.\n\n## Upload URLs\n\nGet presigned URLs for file uploads. Files are uploaded directly to S3.\n\n```\nPOST /api/files/upload-urls\n```\n\n### Request Body\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "files": [\n {\n "filename": "photo.jpg",\n "mediaType": "image/jpeg",\n "size": 102400\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ------------------- | ------ | -------- | ----------------------------------- |\n| `sessionId` | string | Yes | Session ID to associate files with |\n| `files` | array | Yes | Array of file metadata (1-20 files) |\n| `files[].filename` | string | Yes | Original filename |\n| `files[].mediaType` | string | Yes | MIME type (e.g., `image/png`) |\n| `files[].size` | number | Yes | File size in bytes |\n\n### Response\n\n```json\n{\n "files": [\n {\n "id": "file-abc123",\n "uploadUrl": "https://s3.amazonaws.com/bucket/key?...",\n "downloadUrl": "https://s3.amazonaws.com/bucket/key?..."\n }\n ]\n}\n```\n\n### Upload Flow\n\n1. Request upload URLs from the platform\n2. PUT file content to `uploadUrl` with `Content-Type` header\n3. Use `downloadUrl` as the `url` in `FileReference`\n4. Include `FileReference` in trigger input\n\n### Supported Types\n\n| Category | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n### Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 10 MB |\n| Max total per request | 50 MB |\n| Max files per request | 20 |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n',
643
652
  excerpt: "Sessions API Sessions represent conversations with agents. They store conversation history, resources, and variables. All session endpoints require an API key with the Sessions permission. Create...",
644
653
  order: 2
645
654
  },
@@ -648,7 +657,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
648
657
  section: "api-reference",
649
658
  title: "Agents",
650
659
  description: "Agent management API endpoints.",
651
- content: '\n# Agents API\n\nManage agent definitions including protocols and prompts.\n\n## Permissions\n\n| Endpoint | Method | Permission Required |\n| ---------------------- | ------ | ------------------- |\n| `/api/agents` | GET | Agents OR Sessions |\n| `/api/agents/:id` | GET | Agents OR Sessions |\n| `/api/agents` | POST | Agents |\n| `/api/agents/:id` | PATCH | Agents |\n| `/api/agents/validate` | POST | Agents |\n\nRead endpoints work with either permission since both the CLI (for sync) and Server SDK (for sessions) need to read agent definitions.\n\n## List Agents\n\nGet all agents in the project.\n\n```\nGET /api/agents\n```\n\n### Response\n\n```json\n{\n "agents": [\n {\n "id": "cm5xvz7k80001abcd",\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive",\n "createdAt": "2024-01-10T08:00:00Z",\n "updatedAt": "2024-01-15T10:00:00Z"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Get Agent\n\nGet a single agent by ID.\n\n```\nGET /api/agents/:id\n```\n\n### Response\n\n```json\n{\n "id": "cm5xvz7k80001abcd",\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent for {{COMPANY_NAME}}..."\n },\n {\n "name": "user-message",\n "content": "{{USER_MESSAGE}}"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n> **Tip:** You can also view and edit agents directly in the [platform](https://octavus.ai), or use the [CLI](/docs/server-sdk/cli) (`octavus list`) for local workflows.\n\n## Create Agent\n\nCreate a new agent.\n\n```\nPOST /api/agents\n```\n\n### Request Body\n\n```json\n{\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent..."\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ---------------------- | ------ | -------- | ------------------------- |\n| `settings.slug` | string | Yes | URL-safe identifier |\n| `settings.name` | string | Yes | Display name |\n| `settings.description` | string | No | Agent description |\n| `settings.format` | string | Yes | `interactive` or `worker` |\n| `protocol` | string | Yes | YAML protocol definition |\n| `prompts` | array | Yes | Prompt files |\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent created successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "settings": {\n "slug": "my-agent",\n "name": "My Agent",\n "format": "interactive"\n },\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system",\n "prompts": [\n { "name": "system", "content": "You are a helpful assistant." }\n ]\n }\'\n```\n\n## Update Agent\n\nUpdate an existing agent.\n\n```\nPATCH /api/agents/:id\n```\n\n### Request Body\n\n```json\n{\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "Updated system prompt..."\n }\n ]\n}\n```\n\nAll fields are optional. Only provided fields are updated.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent updated successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X PATCH https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system\\n thinking: high"\n }\'\n```\n\n## Creating and Managing Agents\n\nThere are two ways to manage agents:\n\n### Platform UI\n\nCreate and edit agents directly at [octavus.ai](https://octavus.ai). The web editor provides real-time validation and is the easiest way to get started. Copy the agent ID from the URL to use in your application.\n\n### CLI (Local Development)\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\noctavus sync ./agents/support-chat\n```\n\nThis creates the agent if it doesn\'t exist, or updates it if it does. The CLI outputs the agent ID which you should store in an environment variable.\n\nFor CI/CD integration, see the [CLI documentation](/docs/server-sdk/cli#cicd-integration).\n',
660
+ content: '\n# Agents API\n\nManage agent definitions including protocols and prompts.\n\n## Permissions\n\n| Endpoint | Method | Permission Required |\n| ---------------------- | ------ | ------------------- |\n| `/api/agents` | GET | Agents OR Sessions |\n| `/api/agents/:id` | GET | Agents OR Sessions |\n| `/api/agents` | POST | Agents |\n| `/api/agents/:id` | PATCH | Agents |\n| `/api/agents/:id` | DELETE | Agents |\n| `/api/agents/validate` | POST | Agents |\n\nRead endpoints work with either permission since both the CLI (for sync) and Server SDK (for sessions) need to read agent definitions.\n\n## List Agents\n\nGet all agents in the project.\n\n```\nGET /api/agents\n```\n\n### Response\n\n```json\n{\n "agents": [\n {\n "id": "cm5xvz7k80001abcd",\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive",\n "createdAt": "2024-01-10T08:00:00Z",\n "updatedAt": "2024-01-15T10:00:00Z"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Get Agent\n\nGet a single agent by ID.\n\n```\nGET /api/agents/:id\n```\n\n### Response\n\n```json\n{\n "id": "cm5xvz7k80001abcd",\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent for {{COMPANY_NAME}}..."\n },\n {\n "name": "user-message",\n "content": "{{USER_MESSAGE}}"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n> **Tip:** You can also view and edit agents directly in the [platform](https://octavus.ai), or use the [CLI](/docs/server-sdk/cli) (`octavus list`) for local workflows.\n\n## Create Agent\n\nCreate a new agent.\n\n```\nPOST /api/agents\n```\n\n### Request Body\n\n```json\n{\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent..."\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ---------------------- | ------ | -------- | ------------------------- |\n| `settings.slug` | string | Yes | URL-safe identifier |\n| `settings.name` | string | Yes | Display name |\n| `settings.description` | string | No | Agent description |\n| `settings.format` | string | Yes | `interactive` or `worker` |\n| `protocol` | string | Yes | YAML protocol definition |\n| `prompts` | array | Yes | Prompt files |\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent created successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "settings": {\n "slug": "my-agent",\n "name": "My Agent",\n "format": "interactive"\n },\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system",\n "prompts": [\n { "name": "system", "content": "You are a helpful assistant." }\n ]\n }\'\n```\n\n## Update Agent\n\nUpdate an existing agent.\n\n```\nPATCH /api/agents/:id\n```\n\n### Request Body\n\n```json\n{\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "Updated system prompt..."\n }\n ]\n}\n```\n\nAll fields are optional. Only provided fields are updated.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent updated successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X PATCH https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system\\n thinking: high"\n }\'\n```\n\n## Archive Agent\n\nArchive an agent (soft delete). The agent is removed from the active agent list and its slug is freed for reuse. Session history is preserved.\n\n```\nDELETE /api/agents/:id\n```\n\nSupports `?by=slug` query parameter to look up by slug instead of ID.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent archived successfully"\n}\n```\n\n### Example\n\n```bash\n# Archive by ID\ncurl -X DELETE https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n\n# Archive by slug\ncurl -X DELETE https://octavus.ai/api/agents/support-chat?by=slug \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Creating and Managing Agents\n\nThere are two ways to manage agents:\n\n### Platform UI\n\nCreate and edit agents directly at [octavus.ai](https://octavus.ai). The web editor provides real-time validation and is the easiest way to get started. Copy the agent ID from the URL to use in your application.\n\n### CLI (Local Development)\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\noctavus sync ./agents/support-chat\n```\n\nThis creates the agent if it doesn\'t exist, or updates it if it does. The CLI outputs the agent ID which you should store in an environment variable.\n\nFor CI/CD integration, see the [CLI documentation](/docs/server-sdk/cli#cicd-integration).\n',
652
661
  excerpt: "Agents API Manage agent definitions including protocols and prompts. Permissions | Endpoint | Method | Permission Required | | ---------------------- | ------ | ------------------- | |...",
653
662
  order: 3
654
663
  },
@@ -729,7 +738,7 @@ var sections_default = [
729
738
  section: "server-sdk",
730
739
  title: "Overview",
731
740
  description: "Introduction to the Octavus Server SDK for backend integration.",
732
- content: "\n# Server SDK Overview\n\nThe `@octavus/server-sdk` package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation loop.\n\n**Current version:** `2.8.0`\n\n## Installation\n\n```bash\nnpm install @octavus/server-sdk\n```\n\nFor agent management (sync, validate), install the CLI as a dev dependency:\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Basic Usage\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n```\n\n## Key Features\n\n### Agent Management\n\nAgent definitions are managed via the CLI. See the [CLI documentation](/docs/server-sdk/cli) for details.\n\n```bash\n# Sync agent from local files\noctavus sync ./agents/support-chat\n\n# Output: Created: support-chat\n# Agent ID: clxyz123abc456\n```\n\n### Session Management\n\nCreate and manage agent sessions using the agent ID:\n\n```typescript\n// Create a new session (use agent ID from CLI sync)\nconst sessionId = await client.agentSessions.create('clxyz123abc456', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n});\n\n// Get UI-ready session messages (for session restore)\nconst session = await client.agentSessions.getMessages(sessionId);\n```\n\n### Tool Handlers\n\nTools run on your server with your data:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n 'get-user-account': async (args) => {\n // Access your database, APIs, etc.\n return await db.users.findById(args.userId);\n },\n },\n});\n```\n\n### Streaming\n\nAll responses stream in real-time:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of events\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n## API Reference\n\n### OctavusClient\n\nThe main entry point for interacting with Octavus.\n\n```typescript\ninterface OctavusClientConfig {\n baseUrl: string; // Octavus API URL\n apiKey?: string; // Your API key\n}\n\nclass OctavusClient {\n readonly agents: AgentsApi;\n readonly agentSessions: AgentSessionsApi;\n readonly workers: WorkersApi;\n readonly files: FilesApi;\n\n constructor(config: OctavusClientConfig);\n}\n```\n\n### AgentSessionsApi\n\nManages agent sessions.\n\n```typescript\nclass AgentSessionsApi {\n // Create a new session\n async create(agentId: string, input?: Record<string, unknown>): Promise<string>;\n\n // Get full session state (for debugging/internal use)\n async get(sessionId: string): Promise<SessionState>;\n\n // Get UI-ready messages (for client display)\n async getMessages(sessionId: string): Promise<UISessionState>;\n\n // Attach to a session for triggering\n attach(sessionId: string, options?: SessionAttachOptions): AgentSession;\n}\n\n// Full session state (internal format)\ninterface SessionState {\n id: string;\n agentId: string;\n input: Record<string, unknown>;\n variables: Record<string, unknown>;\n resources: Record<string, unknown>;\n messages: ChatMessage[]; // Internal message format\n createdAt: string;\n updatedAt: string;\n}\n\n// UI-ready session state\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready messages for frontend\n}\n```\n\n### AgentSession\n\nHandles request execution and streaming for a specific session.\n\n```typescript\nclass AgentSession {\n // Execute a request and stream parsed events\n execute(request: SessionRequest, options?: TriggerOptions): AsyncGenerator<StreamEvent>;\n\n // Get the session ID\n getSessionId(): string;\n}\n\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n\n// Helper to convert events to SSE stream\nfunction toSSEStream(events: AsyncIterable<StreamEvent>): ReadableStream<Uint8Array>;\n```\n\n### FilesApi\n\nHandles file uploads for sessions.\n\n```typescript\nclass FilesApi {\n // Get presigned URLs for file uploads\n async getUploadUrls(sessionId: string, files: FileUploadRequest[]): Promise<UploadUrlsResponse>;\n}\n\ninterface FileUploadRequest {\n filename: string;\n mediaType: string;\n size: number;\n}\n\ninterface UploadUrlsResponse {\n files: {\n id: string; // File ID for references\n uploadUrl: string; // PUT to this URL\n downloadUrl: string; // GET URL after upload\n }[];\n}\n```\n\nThe client uploads files directly to S3 using the presigned upload URL. See [File Uploads](/docs/client-sdk/file-uploads) for the full integration pattern.\n\n## Next Steps\n\n- [Sessions](/docs/server-sdk/sessions) \u2014 Deep dive into session management\n- [Tools](/docs/server-sdk/tools) \u2014 Implementing tool handlers\n- [Streaming](/docs/server-sdk/streaming) \u2014 Understanding stream events\n- [Workers](/docs/server-sdk/workers) \u2014 Executing worker agents\n",
741
+ content: "\n# Server SDK Overview\n\nThe `@octavus/server-sdk` package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation loop.\n\n**Current version:** `2.9.0`\n\n## Installation\n\n```bash\nnpm install @octavus/server-sdk\n```\n\nFor agent management (sync, validate), install the CLI as a dev dependency:\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Basic Usage\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n```\n\n## Key Features\n\n### Agent Management\n\nAgent definitions are managed via the CLI. See the [CLI documentation](/docs/server-sdk/cli) for details.\n\n```bash\n# Sync agent from local files\noctavus sync ./agents/support-chat\n\n# Output: Created: support-chat\n# Agent ID: clxyz123abc456\n```\n\n### Session Management\n\nCreate and manage agent sessions using the agent ID:\n\n```typescript\n// Create a new session (use agent ID from CLI sync)\nconst sessionId = await client.agentSessions.create('clxyz123abc456', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n});\n\n// Get UI-ready session messages (for session restore)\nconst session = await client.agentSessions.getMessages(sessionId);\n```\n\n### Tool Handlers\n\nTools run on your server with your data:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n 'get-user-account': async (args) => {\n // Access your database, APIs, etc.\n return await db.users.findById(args.userId);\n },\n },\n});\n```\n\n### Streaming\n\nAll responses stream in real-time:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of events\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n## API Reference\n\n### OctavusClient\n\nThe main entry point for interacting with Octavus.\n\n```typescript\ninterface OctavusClientConfig {\n baseUrl: string; // Octavus API URL\n apiKey?: string; // Your API key\n traceModelRequests?: boolean; // Enable model request tracing (default: false)\n}\n\nclass OctavusClient {\n readonly agents: AgentsApi;\n readonly agentSessions: AgentSessionsApi;\n readonly workers: WorkersApi;\n readonly files: FilesApi;\n\n constructor(config: OctavusClientConfig);\n}\n```\n\n### AgentSessionsApi\n\nManages agent sessions.\n\n```typescript\nclass AgentSessionsApi {\n // Create a new session\n async create(agentId: string, input?: Record<string, unknown>): Promise<string>;\n\n // Get full session state (for debugging/internal use)\n async get(sessionId: string): Promise<SessionState>;\n\n // Get UI-ready messages (for client display)\n async getMessages(sessionId: string): Promise<UISessionState>;\n\n // Attach to a session for triggering\n attach(sessionId: string, options?: SessionAttachOptions): AgentSession;\n}\n\n// Full session state (internal format)\ninterface SessionState {\n id: string;\n agentId: string;\n input: Record<string, unknown>;\n variables: Record<string, unknown>;\n resources: Record<string, unknown>;\n messages: ChatMessage[]; // Internal message format\n createdAt: string;\n updatedAt: string;\n}\n\n// UI-ready session state\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready messages for frontend\n}\n```\n\n### AgentSession\n\nHandles request execution and streaming for a specific session.\n\n```typescript\nclass AgentSession {\n // Execute a request and stream parsed events\n execute(request: SessionRequest, options?: TriggerOptions): AsyncGenerator<StreamEvent>;\n\n // Get the session ID\n getSessionId(): string;\n}\n\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n\n// Helper to convert events to SSE stream\nfunction toSSEStream(events: AsyncIterable<StreamEvent>): ReadableStream<Uint8Array>;\n```\n\n### FilesApi\n\nHandles file uploads for sessions.\n\n```typescript\nclass FilesApi {\n // Get presigned URLs for file uploads\n async getUploadUrls(sessionId: string, files: FileUploadRequest[]): Promise<UploadUrlsResponse>;\n}\n\ninterface FileUploadRequest {\n filename: string;\n mediaType: string;\n size: number;\n}\n\ninterface UploadUrlsResponse {\n files: {\n id: string; // File ID for references\n uploadUrl: string; // PUT to this URL\n downloadUrl: string; // GET URL after upload\n }[];\n}\n```\n\nThe client uploads files directly to S3 using the presigned upload URL. See [File Uploads](/docs/client-sdk/file-uploads) for the full integration pattern.\n\n## Next Steps\n\n- [Sessions](/docs/server-sdk/sessions) \u2014 Deep dive into session management\n- [Tools](/docs/server-sdk/tools) \u2014 Implementing tool handlers\n- [Streaming](/docs/server-sdk/streaming) \u2014 Understanding stream events\n- [Workers](/docs/server-sdk/workers) \u2014 Executing worker agents\n- [Debugging](/docs/server-sdk/debugging) \u2014 Model request tracing and debugging\n",
733
742
  excerpt: "Server SDK Overview The package provides a Node.js SDK for integrating Octavus agents into your backend application. It handles session management, streaming, and the tool execution continuation...",
734
743
  order: 1
735
744
  },
@@ -738,7 +747,7 @@ var sections_default = [
738
747
  section: "server-sdk",
739
748
  title: "Sessions",
740
749
  description: "Managing agent sessions with the Server SDK.",
741
- content: "\n# Sessions\n\nSessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions.\n\n## Creating Sessions\n\nCreate a session by specifying the agent ID and initial input variables:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\n// Create a session with the support-chat agent\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n USER_ID: 'user-123', // Optional inputs\n});\n\nconsole.log('Session created:', sessionId);\n```\n\n## Getting Session Messages\n\nTo restore a conversation on page load, use `getMessages()` to retrieve UI-ready messages:\n\n```typescript\nconst session = await client.agentSessions.getMessages(sessionId);\n\nconsole.log({\n sessionId: session.sessionId,\n agentId: session.agentId,\n messages: session.messages.length, // UIMessage[] ready for frontend\n});\n```\n\nThe returned messages can be passed directly to the client SDK's `initialMessages` option.\n\n### UISessionState Interface\n\n```typescript\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready conversation history\n}\n```\n\n## Full Session State (Debug)\n\nFor debugging or internal use, you can retrieve the complete session state including all variables and internal message format:\n\n```typescript\nconst state = await client.agentSessions.get(sessionId);\n\nconsole.log({\n id: state.id,\n agentId: state.agentId,\n messages: state.messages.length, // ChatMessage[] (internal format)\n resources: state.resources,\n variables: state.variables,\n createdAt: state.createdAt,\n updatedAt: state.updatedAt,\n});\n```\n\n> **Note**: Use `getMessages()` for client-facing code. The `get()` method returns internal message format that includes hidden content not intended for end users.\n\n## Attaching to Sessions\n\nTo trigger actions on a session, you need to attach to it first:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n // Tool handlers (see Tools documentation)\n },\n resources: [\n // Resource watchers (optional)\n ],\n});\n```\n\n## Executing Requests\n\nOnce attached, execute requests on the session using `execute()`:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() handles both triggers and client tool continuations\nconst events = session.execute(\n { type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello!' } },\n { signal: request.signal },\n);\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n### Request Types\n\nThe `execute()` method accepts a discriminated union:\n\n```typescript\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\n// Start a new conversation turn\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\n// Continue after client-side tool handling\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n```\n\nThis makes it easy to pass requests through from the client:\n\n```typescript\n// Simple passthrough from HTTP request body\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n /* ... */\n },\n });\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events));\n}\n```\n\n### Stop Support\n\nPass an abort signal to allow clients to stop generation:\n\n```typescript\nconst events = session.execute(request, {\n signal: request.signal, // Forward the client's abort signal\n});\n```\n\nWhen the client aborts the request, the signal propagates through to the LLM provider, stopping generation immediately. Any partial content is preserved.\n\n## WebSocket Handling\n\nFor WebSocket integrations, use `handleSocketMessage()` which manages abort controller lifecycle internally:\n\n```typescript\nimport type { SocketMessage } from '@octavus/server-sdk';\n\n// In your socket handler\nconn.on('data', async (rawData: string) => {\n const msg = JSON.parse(rawData);\n\n if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {\n await session.handleSocketMessage(msg as SocketMessage, {\n onEvent: (event) => conn.write(JSON.stringify(event)),\n onFinish: async () => {\n // Fetch and persist messages to your database for restoration\n },\n });\n }\n});\n```\n\nThe `handleSocketMessage()` method:\n\n- Handles `trigger`, `continue`, and `stop` messages\n- Automatically aborts previous requests when a new one arrives\n- Streams events via the `onEvent` callback\n- Calls `onFinish` after streaming completes (not called if aborted)\n\nSee [Socket Chat Example](/docs/examples/socket-chat) for a complete implementation.\n\n## Session Lifecycle\n\n```mermaid\nflowchart TD\n A[1. CREATE] --> B[2. ATTACH]\n B --> C[3. TRIGGER]\n C --> C\n C --> D[4. RETRIEVE]\n D --> C\n C --> E[5. EXPIRE]\n E --> F{6. RESTORE?}\n F -->|Yes| C\n F -->|No| A\n\n A -.- A1[\"`**client.agentSessions.create()**\n Returns sessionId\n Initializes state`\"]\n\n B -.- B1[\"`**client.agentSessions.attach()**\n Configure tool handlers\n Configure resource watchers`\"]\n\n C -.- C1[\"`**session.execute()**\n Execute request\n Stream events\n Update state`\"]\n\n D -.- D1[\"`**client.agentSessions.getMessages()**\n Get UI-ready messages\n Check session status`\"]\n\n E -.- E1[\"`Sessions expire after\n 24 hours (configurable)`\"]\n\n F -.- F1[\"`**client.agentSessions.restore()**\n Restore from stored messages\n Or create new session`\"]\n```\n\n## Session Expiration\n\nSessions expire after a period of inactivity (default: 24 hours). When you call `getMessages()` or `get()`, the response includes a `status` field:\n\n```typescript\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'expired') {\n // Session has expired - restore or create new\n console.log('Session expired:', result.sessionId);\n} else {\n // Session is active\n console.log('Messages:', result.messages.length);\n}\n```\n\n### Response Types\n\n| Status | Type | Description |\n| --------- | --------------------- | ------------------------------------------------------------- |\n| `active` | `UISessionState` | Session is active, includes `messages` array |\n| `expired` | `ExpiredSessionState` | Session expired, includes `sessionId`, `agentId`, `createdAt` |\n\n## Persisting Chat History\n\nTo enable session restoration, store the chat messages in your own database after each interaction:\n\n```typescript\n// After each trigger completes, save messages\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'active') {\n // Store in your database\n await db.chats.update({\n where: { id: chatId },\n data: {\n sessionId: result.sessionId,\n messages: result.messages, // Store UIMessage[] as JSON\n },\n });\n}\n```\n\n> **Best Practice**: Store the full `UIMessage[]` array. This preserves all message parts (text, tool calls, files, etc.) needed for accurate restoration.\n\n## Restoring Sessions\n\nWhen a user returns to your app:\n\n```typescript\n// 1. Load stored data from your database\nconst chat = await db.chats.findUnique({ where: { id: chatId } });\n\n// 2. Check if session is still active\nconst result = await client.agentSessions.getMessages(chat.sessionId);\n\nif (result.status === 'active') {\n // Session is active - use it directly\n return {\n sessionId: result.sessionId,\n messages: result.messages,\n };\n}\n\n// 3. Session expired - restore from stored messages\nif (chat.messages && chat.messages.length > 0) {\n const restored = await client.agentSessions.restore(\n chat.sessionId,\n chat.messages,\n { COMPANY_NAME: 'Acme Corp' }, // Optional: same input as create()\n );\n\n if (restored.restored) {\n // Session restored successfully\n return {\n sessionId: restored.sessionId,\n messages: chat.messages,\n };\n }\n}\n\n// 4. Cannot restore - create new session\nconst newSessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n});\n\nreturn {\n sessionId: newSessionId,\n messages: [],\n};\n```\n\n### Restore Response\n\n```typescript\ninterface RestoreSessionResult {\n sessionId: string;\n restored: boolean; // true if restored, false if session was already active\n}\n```\n\n## Complete Example\n\nHere's a complete session management flow:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\nasync function getOrCreateSession(chatId: string, agentId: string, input: Record<string, unknown>) {\n // Load existing chat data\n const chat = await db.chats.findUnique({ where: { id: chatId } });\n\n if (chat?.sessionId) {\n // Check session status\n const result = await client.agentSessions.getMessages(chat.sessionId);\n\n if (result.status === 'active') {\n return { sessionId: result.sessionId, messages: result.messages };\n }\n\n // Try to restore expired session\n if (chat.messages?.length > 0) {\n const restored = await client.agentSessions.restore(chat.sessionId, chat.messages, input);\n if (restored.restored) {\n return { sessionId: restored.sessionId, messages: chat.messages };\n }\n }\n }\n\n // Create new session\n const sessionId = await client.agentSessions.create(agentId, input);\n\n // Save to database\n await db.chats.upsert({\n where: { id: chatId },\n create: { id: chatId, sessionId, messages: [] },\n update: { sessionId, messages: [] },\n });\n\n return { sessionId, messages: [] };\n}\n```\n\n## Error Handling\n\n```typescript\nimport { ApiError } from '@octavus/server-sdk';\n\ntry {\n const session = await client.agentSessions.getMessages(sessionId);\n} catch (error) {\n if (error instanceof ApiError) {\n if (error.status === 404) {\n // Session not found or expired\n console.log('Session expired, create a new one');\n } else {\n console.error('API Error:', error.message);\n }\n }\n throw error;\n}\n```\n",
750
+ content: "\n# Sessions\n\nSessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions.\n\n## Creating Sessions\n\nCreate a session by specifying the agent ID and initial input variables:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\n// Create a session with the support-chat agent\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n USER_ID: 'user-123', // Optional inputs\n});\n\nconsole.log('Session created:', sessionId);\n```\n\n## Getting Session Messages\n\nTo restore a conversation on page load, use `getMessages()` to retrieve UI-ready messages:\n\n```typescript\nconst session = await client.agentSessions.getMessages(sessionId);\n\nconsole.log({\n sessionId: session.sessionId,\n agentId: session.agentId,\n messages: session.messages.length, // UIMessage[] ready for frontend\n});\n```\n\nThe returned messages can be passed directly to the client SDK's `initialMessages` option.\n\n### UISessionState Interface\n\n```typescript\ninterface UISessionState {\n sessionId: string;\n agentId: string;\n messages: UIMessage[]; // UI-ready conversation history\n}\n```\n\n## Full Session State (Debug)\n\nFor debugging or internal use, you can retrieve the complete session state including all variables and internal message format:\n\n```typescript\nconst state = await client.agentSessions.get(sessionId);\n\nconsole.log({\n id: state.id,\n agentId: state.agentId,\n messages: state.messages.length, // ChatMessage[] (internal format)\n resources: state.resources,\n variables: state.variables,\n createdAt: state.createdAt,\n updatedAt: state.updatedAt,\n});\n```\n\n> **Note**: Use `getMessages()` for client-facing code. The `get()` method returns internal message format that includes hidden content not intended for end users.\n\n## Attaching to Sessions\n\nTo trigger actions on a session, you need to attach to it first:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n // Tool handlers (see Tools documentation)\n },\n resources: [\n // Resource watchers (optional)\n ],\n});\n```\n\n## Executing Requests\n\nOnce attached, execute requests on the session using `execute()`:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() handles both triggers and client tool continuations\nconst events = session.execute(\n { type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello!' } },\n { signal: request.signal },\n);\n\n// Convert to SSE stream for HTTP responses\nreturn new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n});\n```\n\n### Request Types\n\nThe `execute()` method accepts a discriminated union:\n\n```typescript\ntype SessionRequest = TriggerRequest | ContinueRequest;\n\n// Start a new conversation turn\ninterface TriggerRequest {\n type: 'trigger';\n triggerName: string;\n input?: Record<string, unknown>;\n}\n\n// Continue after client-side tool handling\ninterface ContinueRequest {\n type: 'continue';\n executionId: string;\n toolResults: ToolResult[];\n}\n```\n\nThis makes it easy to pass requests through from the client:\n\n```typescript\n// Simple passthrough from HTTP request body\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n /* ... */\n },\n });\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events));\n}\n```\n\n### Stop Support\n\nPass an abort signal to allow clients to stop generation:\n\n```typescript\nconst events = session.execute(request, {\n signal: request.signal, // Forward the client's abort signal\n});\n```\n\nWhen the client aborts the request, the signal propagates through to the LLM provider, stopping generation immediately. Any partial content is preserved.\n\n## WebSocket Handling\n\nFor WebSocket integrations, use `handleSocketMessage()` which manages abort controller lifecycle internally:\n\n```typescript\nimport type { SocketMessage } from '@octavus/server-sdk';\n\n// In your socket handler\nconn.on('data', async (rawData: string) => {\n const msg = JSON.parse(rawData);\n\n if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {\n await session.handleSocketMessage(msg as SocketMessage, {\n onEvent: (event) => conn.write(JSON.stringify(event)),\n onFinish: async () => {\n // Fetch and persist messages to your database for restoration\n },\n });\n }\n});\n```\n\nThe `handleSocketMessage()` method:\n\n- Handles `trigger`, `continue`, and `stop` messages\n- Automatically aborts previous requests when a new one arrives\n- Streams events via the `onEvent` callback\n- Calls `onFinish` after streaming completes (not called if aborted)\n\nSee [Socket Chat Example](/docs/examples/socket-chat) for a complete implementation.\n\n## Session Lifecycle\n\n```mermaid\nflowchart TD\n A[1. CREATE] --> B[2. ATTACH]\n B --> C[3. TRIGGER]\n C --> C\n C --> D[4. RETRIEVE]\n D --> C\n C --> E[5. EXPIRE]\n C --> G[5b. CLEAR]\n G --> F\n E --> F{6. RESTORE?}\n F -->|Yes| C\n F -->|No| A\n\n A -.- A1[\"`**client.agentSessions.create()**\n Returns sessionId\n Initializes state`\"]\n\n B -.- B1[\"`**client.agentSessions.attach()**\n Configure tool handlers\n Configure resource watchers`\"]\n\n C -.- C1[\"`**session.execute()**\n Execute request\n Stream events\n Update state`\"]\n\n D -.- D1[\"`**client.agentSessions.getMessages()**\n Get UI-ready messages\n Check session status`\"]\n\n E -.- E1[\"`Sessions expire after\n 24 hours (configurable)`\"]\n\n G -.- G1[\"`**client.agentSessions.clear()**\n Programmatically clear state\n Session becomes expired`\"]\n\n F -.- F1[\"`**client.agentSessions.restore()**\n Restore from stored messages\n Or create new session`\"]\n```\n\n## Session Expiration\n\nSessions expire after a period of inactivity (default: 24 hours). When you call `getMessages()` or `get()`, the response includes a `status` field:\n\n```typescript\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'expired') {\n // Session has expired - restore or create new\n console.log('Session expired:', result.sessionId);\n} else {\n // Session is active\n console.log('Messages:', result.messages.length);\n}\n```\n\n### Response Types\n\n| Status | Type | Description |\n| --------- | --------------------- | ------------------------------------------------------------- |\n| `active` | `UISessionState` | Session is active, includes `messages` array |\n| `expired` | `ExpiredSessionState` | Session expired, includes `sessionId`, `agentId`, `createdAt` |\n\n## Persisting Chat History\n\nTo enable session restoration, store the chat messages in your own database after each interaction:\n\n```typescript\n// After each trigger completes, save messages\nconst result = await client.agentSessions.getMessages(sessionId);\n\nif (result.status === 'active') {\n // Store in your database\n await db.chats.update({\n where: { id: chatId },\n data: {\n sessionId: result.sessionId,\n messages: result.messages, // Store UIMessage[] as JSON\n },\n });\n}\n```\n\n> **Best Practice**: Store the full `UIMessage[]` array. This preserves all message parts (text, tool calls, files, etc.) needed for accurate restoration.\n\n## Restoring Sessions\n\nWhen a user returns to your app:\n\n```typescript\n// 1. Load stored data from your database\nconst chat = await db.chats.findUnique({ where: { id: chatId } });\n\n// 2. Check if session is still active\nconst result = await client.agentSessions.getMessages(chat.sessionId);\n\nif (result.status === 'active') {\n // Session is active - use it directly\n return {\n sessionId: result.sessionId,\n messages: result.messages,\n };\n}\n\n// 3. Session expired - restore from stored messages\nif (chat.messages && chat.messages.length > 0) {\n const restored = await client.agentSessions.restore(\n chat.sessionId,\n chat.messages,\n { COMPANY_NAME: 'Acme Corp' }, // Optional: same input as create()\n );\n\n if (restored.restored) {\n // Session restored successfully\n return {\n sessionId: restored.sessionId,\n messages: chat.messages,\n };\n }\n}\n\n// 4. Cannot restore - create new session\nconst newSessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n});\n\nreturn {\n sessionId: newSessionId,\n messages: [],\n};\n```\n\n### Restore Response\n\n```typescript\ninterface RestoreSessionResult {\n sessionId: string;\n restored: boolean; // true if restored, false if session was already active\n}\n```\n\n## Complete Example\n\nHere's a complete session management flow:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\nasync function getOrCreateSession(chatId: string, agentId: string, input: Record<string, unknown>) {\n // Load existing chat data\n const chat = await db.chats.findUnique({ where: { id: chatId } });\n\n if (chat?.sessionId) {\n // Check session status\n const result = await client.agentSessions.getMessages(chat.sessionId);\n\n if (result.status === 'active') {\n return { sessionId: result.sessionId, messages: result.messages };\n }\n\n // Try to restore expired session\n if (chat.messages?.length > 0) {\n const restored = await client.agentSessions.restore(chat.sessionId, chat.messages, input);\n if (restored.restored) {\n return { sessionId: restored.sessionId, messages: chat.messages };\n }\n }\n }\n\n // Create new session\n const sessionId = await client.agentSessions.create(agentId, input);\n\n // Save to database\n await db.chats.upsert({\n where: { id: chatId },\n create: { id: chatId, sessionId, messages: [] },\n update: { sessionId, messages: [] },\n });\n\n return { sessionId, messages: [] };\n}\n```\n\n## Clearing Sessions\n\nTo programmatically clear a session's state (e.g., for testing reset/restore flows), use `clear()`:\n\n```typescript\nconst result = await client.agentSessions.clear(sessionId);\nconsole.log(result.cleared); // true\n```\n\nAfter clearing, the session transitions to `expired` status. You can then restore it with `restore()` or create a new session.\n\n```typescript\ninterface ClearSessionResult {\n sessionId: string;\n cleared: boolean;\n}\n```\n\nThis is idempotent \u2014 calling `clear()` on an already expired session succeeds without error.\n\n## Error Handling\n\n```typescript\nimport { ApiError } from '@octavus/server-sdk';\n\ntry {\n const session = await client.agentSessions.getMessages(sessionId);\n} catch (error) {\n if (error instanceof ApiError) {\n if (error.status === 404) {\n // Session not found or expired\n console.log('Session expired, create a new one');\n } else {\n console.error('API Error:', error.message);\n }\n }\n throw error;\n}\n```\n",
742
751
  excerpt: "Sessions Sessions represent conversations with an agent. They store conversation history, track resources and variables, and enable stateful interactions. Creating Sessions Create a session by...",
743
752
  order: 2
744
753
  },
@@ -765,7 +774,7 @@ var sections_default = [
765
774
  section: "server-sdk",
766
775
  title: "CLI",
767
776
  description: "Command-line interface for validating and syncing agent definitions.",
768
- content: '\n# Octavus CLI\n\nThe `@octavus/cli` package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform.\n\n**Current version:** `2.8.0`\n\n## Installation\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Configuration\n\nThe CLI requires an API key with the **Agents** permission.\n\n### Environment Variables\n\n| Variable | Description |\n| --------------------- | ---------------------------------------------- |\n| `OCTAVUS_CLI_API_KEY` | API key with "Agents" permission (recommended) |\n| `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |\n| `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |\n\n### Two-Key Strategy (Recommended)\n\nFor production deployments, use separate API keys with minimal permissions:\n\n```bash\n# CI/CD or .env.local (not committed)\nOCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only\n\n# Production .env\nOCTAVUS_API_KEY=oct_sk_... # "Sessions" permission only\n```\n\nThis ensures production servers only have session permissions (smaller blast radius if leaked), while agent management is restricted to development/CI environments.\n\n### Multiple Environments\n\nUse separate Octavus projects for staging and production, each with their own API keys. The `--env` flag lets you load different environment files:\n\n```bash\n# Local development (default: .env)\noctavus sync ./agents/my-agent\n\n# Staging project\noctavus --env .env.staging sync ./agents/my-agent\n\n# Production project\noctavus --env .env.production sync ./agents/my-agent\n```\n\nExample environment files:\n\n```bash\n# .env.staging (syncs to your staging project)\nOCTAVUS_CLI_API_KEY=oct_sk_staging_project_key...\n\n# .env.production (syncs to your production project)\nOCTAVUS_CLI_API_KEY=oct_sk_production_project_key...\n```\n\nEach project has its own agents, so you\'ll get different agent IDs per environment.\n\n## Global Options\n\n| Option | Description |\n| -------------- | ------------------------------------------------------- |\n| `--env <file>` | Load environment from a specific file (default: `.env`) |\n| `--help` | Show help |\n| `--version` | Show version |\n\n## Commands\n\n### `octavus sync <path>`\n\nSync an agent definition to the platform. Creates the agent if it doesn\'t exist, or updates it if it does.\n\n```bash\noctavus sync ./agents/my-agent\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Reading agent from ./agents/my-agent...\n\u2139 Syncing support-chat...\n\u2713 Created: support-chat\n Agent ID: clxyz123abc456\n```\n\n### `octavus validate <path>`\n\nValidate an agent definition without saving. Useful for CI/CD pipelines.\n\n```bash\noctavus validate ./agents/my-agent\n```\n\n**Exit codes:**\n\n- `0` \u2014 Validation passed\n- `1` \u2014 Validation errors\n- `2` \u2014 Configuration errors (missing API key, etc.)\n\n### `octavus list`\n\nList all agents in your project.\n\n```bash\noctavus list\n```\n\n**Example output:**\n\n```\nSLUG NAME FORMAT ID\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nsupport-chat Support Chat Agent interactive clxyz123abc456\n\n1 agent(s)\n```\n\n### `octavus get <slug>`\n\nGet details about a specific agent by its slug.\n\n```bash\noctavus get support-chat\n```\n\n## Agent Directory Structure\n\nThe CLI expects agent definitions in a specific directory structure:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json # Required: Agent metadata\n\u251C\u2500\u2500 protocol.yaml # Required: Agent protocol\n\u2514\u2500\u2500 prompts/ # Optional: Prompt templates\n \u251C\u2500\u2500 system.md\n \u2514\u2500\u2500 user-message.md\n```\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "A helpful assistant",\n "format": "interactive"\n}\n```\n\n### protocol.yaml\n\nSee the [Protocol documentation](/docs/protocol/overview) for details on protocol syntax.\n\n## CI/CD Integration\n\n### GitHub Actions\n\n```yaml\nname: Validate and Sync Agents\n\non:\n push:\n branches: [main]\n paths:\n - \'agents/**\'\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions/setup-node@v4\n with:\n node-version: \'22\'\n\n - run: npm install\n\n - name: Validate agent\n run: npx octavus validate ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n\n - name: Sync agent\n run: npx octavus sync ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n```\n\n### Package.json Scripts\n\nAdd sync scripts to your `package.json`:\n\n```json\n{\n "scripts": {\n "agents:validate": "octavus validate ./agents/my-agent",\n "agents:sync": "octavus sync ./agents/my-agent"\n },\n "devDependencies": {\n "@octavus/cli": "^0.1.0"\n }\n}\n```\n\n## Workflow\n\nThe recommended workflow for managing agents:\n\n1. **Define agent locally** \u2014 Create `settings.json`, `protocol.yaml`, and prompts\n2. **Validate** \u2014 Run `octavus validate ./my-agent` to check for errors\n3. **Sync** \u2014 Run `octavus sync ./my-agent` to push to platform\n4. **Store agent ID** \u2014 Save the output ID in an environment variable\n5. **Use in app** \u2014 Read the ID from env and pass to `client.agentSessions.create()`\n\n```bash\n# After syncing: octavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n\n# Add to your .env file\nOCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456\n```\n\n```typescript\nconst agentId = process.env.OCTAVUS_SUPPORT_AGENT_ID;\n\nconst sessionId = await client.agentSessions.create(agentId, {\n COMPANY_NAME: \'Acme Corp\',\n});\n```\n',
777
+ content: '\n# Octavus CLI\n\nThe `@octavus/cli` package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform.\n\n**Current version:** `2.9.0`\n\n## Installation\n\n```bash\nnpm install --save-dev @octavus/cli\n```\n\n## Configuration\n\nThe CLI requires an API key with the **Agents** permission.\n\n### Environment Variables\n\n| Variable | Description |\n| --------------------- | ---------------------------------------------- |\n| `OCTAVUS_CLI_API_KEY` | API key with "Agents" permission (recommended) |\n| `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |\n| `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |\n\n### Two-Key Strategy (Recommended)\n\nFor production deployments, use separate API keys with minimal permissions:\n\n```bash\n# CI/CD or .env.local (not committed)\nOCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only\n\n# Production .env\nOCTAVUS_API_KEY=oct_sk_... # "Sessions" permission only\n```\n\nThis ensures production servers only have session permissions (smaller blast radius if leaked), while agent management is restricted to development/CI environments.\n\n### Multiple Environments\n\nUse separate Octavus projects for staging and production, each with their own API keys. The `--env` flag lets you load different environment files:\n\n```bash\n# Local development (default: .env)\noctavus sync ./agents/my-agent\n\n# Staging project\noctavus --env .env.staging sync ./agents/my-agent\n\n# Production project\noctavus --env .env.production sync ./agents/my-agent\n```\n\nExample environment files:\n\n```bash\n# .env.staging (syncs to your staging project)\nOCTAVUS_CLI_API_KEY=oct_sk_staging_project_key...\n\n# .env.production (syncs to your production project)\nOCTAVUS_CLI_API_KEY=oct_sk_production_project_key...\n```\n\nEach project has its own agents, so you\'ll get different agent IDs per environment.\n\n## Global Options\n\n| Option | Description |\n| -------------- | ------------------------------------------------------- |\n| `--env <file>` | Load environment from a specific file (default: `.env`) |\n| `--help` | Show help |\n| `--version` | Show version |\n\n## Commands\n\n### `octavus sync <path>`\n\nSync an agent definition to the platform. Creates the agent if it doesn\'t exist, or updates it if it does.\n\n```bash\noctavus sync ./agents/my-agent\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Reading agent from ./agents/my-agent...\n\u2139 Syncing support-chat...\n\u2713 Created: support-chat\n Agent ID: clxyz123abc456\n```\n\n### `octavus validate <path>`\n\nValidate an agent definition without saving. Useful for CI/CD pipelines.\n\n```bash\noctavus validate ./agents/my-agent\n```\n\n**Exit codes:**\n\n- `0` \u2014 Validation passed\n- `1` \u2014 Validation errors\n- `2` \u2014 Configuration errors (missing API key, etc.)\n\n### `octavus list`\n\nList all agents in your project.\n\n```bash\noctavus list\n```\n\n**Example output:**\n\n```\nSLUG NAME FORMAT ID\n\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nsupport-chat Support Chat Agent interactive clxyz123abc456\n\n1 agent(s)\n```\n\n### `octavus get <slug>`\n\nGet details about a specific agent by its slug.\n\n```bash\noctavus get support-chat\n```\n\n### `octavus archive <slug>`\n\nArchive an agent by slug (soft delete). Archived agents are removed from the active agent list and their slug is freed for reuse.\n\n```bash\noctavus archive support-chat\n```\n\n**Options:**\n\n- `--json` \u2014 Output as JSON (for CI/CD parsing)\n- `--quiet` \u2014 Suppress non-essential output\n\n**Example output:**\n\n```\n\u2139 Archiving support-chat...\n\u2713 Archived: support-chat\n Agent ID: clxyz123abc456\n```\n\n## Agent Directory Structure\n\nThe CLI expects agent definitions in a specific directory structure:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json # Required: Agent metadata\n\u251C\u2500\u2500 protocol.yaml # Required: Agent protocol\n\u2514\u2500\u2500 prompts/ # Optional: Prompt templates\n \u251C\u2500\u2500 system.md\n \u2514\u2500\u2500 user-message.md\n```\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "A helpful assistant",\n "format": "interactive"\n}\n```\n\n### protocol.yaml\n\nSee the [Protocol documentation](/docs/protocol/overview) for details on protocol syntax.\n\n## CI/CD Integration\n\n### GitHub Actions\n\n```yaml\nname: Validate and Sync Agents\n\non:\n push:\n branches: [main]\n paths:\n - \'agents/**\'\n\njobs:\n sync:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n\n - uses: actions/setup-node@v4\n with:\n node-version: \'22\'\n\n - run: npm install\n\n - name: Validate agent\n run: npx octavus validate ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n\n - name: Sync agent\n run: npx octavus sync ./agents/support-chat\n env:\n OCTAVUS_CLI_API_KEY: ${{ secrets.OCTAVUS_CLI_API_KEY }}\n```\n\n### Package.json Scripts\n\nAdd sync scripts to your `package.json`:\n\n```json\n{\n "scripts": {\n "agents:validate": "octavus validate ./agents/my-agent",\n "agents:sync": "octavus sync ./agents/my-agent"\n },\n "devDependencies": {\n "@octavus/cli": "^0.1.0"\n }\n}\n```\n\n## Workflow\n\nThe recommended workflow for managing agents:\n\n1. **Define agent locally** \u2014 Create `settings.json`, `protocol.yaml`, and prompts\n2. **Validate** \u2014 Run `octavus validate ./my-agent` to check for errors\n3. **Sync** \u2014 Run `octavus sync ./my-agent` to push to platform\n4. **Store agent ID** \u2014 Save the output ID in an environment variable\n5. **Use in app** \u2014 Read the ID from env and pass to `client.agentSessions.create()`\n\n```bash\n# After syncing: octavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n\n# Add to your .env file\nOCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456\n```\n\n```typescript\nconst agentId = process.env.OCTAVUS_SUPPORT_AGENT_ID;\n\nconst sessionId = await client.agentSessions.create(agentId, {\n COMPANY_NAME: \'Acme Corp\',\n});\n```\n',
769
778
  excerpt: "Octavus CLI The package provides a command-line interface for validating and syncing agent definitions from your local filesystem to the Octavus platform. Current version: Installation ...",
770
779
  order: 5
771
780
  },
@@ -777,6 +786,15 @@ var sections_default = [
777
786
  content: "\n# Workers API\n\nThe `WorkersApi` enables executing worker agents from your server. Workers are task-based agents that run steps sequentially and return an output value.\n\n## Basic Usage\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n\n// Execute a worker\nconst events = client.workers.execute(agentId, {\n TOPIC: 'AI safety',\n DEPTH: 'detailed',\n});\n\n// Process events\nfor await (const event of events) {\n if (event.type === 'worker-start') {\n console.log(`Worker ${event.workerSlug} started`);\n }\n if (event.type === 'text-delta') {\n process.stdout.write(event.delta);\n }\n if (event.type === 'worker-result') {\n console.log('Output:', event.output);\n }\n}\n```\n\n## WorkersApi Reference\n\n### execute()\n\nExecute a worker and stream the response.\n\n```typescript\nasync *execute(\n agentId: string,\n input: Record<string, unknown>,\n options?: WorkerExecuteOptions\n): AsyncGenerator<StreamEvent>\n```\n\n**Parameters:**\n\n| Parameter | Type | Description |\n| --------- | ------------------------- | --------------------------- |\n| `agentId` | `string` | The worker agent ID |\n| `input` | `Record<string, unknown>` | Input values for the worker |\n| `options` | `WorkerExecuteOptions` | Optional configuration |\n\n**Options:**\n\n```typescript\ninterface WorkerExecuteOptions {\n /** Tool handlers for server-side tool execution */\n tools?: ToolHandlers;\n /** Abort signal to cancel the execution */\n signal?: AbortSignal;\n}\n```\n\n### continue()\n\nContinue execution after client-side tool handling.\n\n```typescript\nasync *continue(\n agentId: string,\n executionId: string,\n toolResults: ToolResult[],\n options?: WorkerExecuteOptions\n): AsyncGenerator<StreamEvent>\n```\n\nUse this when the worker has tools without server-side handlers. The execution pauses with a `client-tool-request` event, you execute the tools, then call `continue()` to resume.\n\n## Tool Handlers\n\nProvide tool handlers to execute tools server-side:\n\n```typescript\nconst events = client.workers.execute(\n agentId,\n { TOPIC: 'AI safety' },\n {\n tools: {\n 'web-search': async (args) => {\n const results = await searchWeb(args.query);\n return results;\n },\n 'get-user-data': async (args) => {\n return await db.users.findById(args.userId);\n },\n },\n },\n);\n```\n\nTools defined in the worker protocol but not provided as handlers become client tools \u2014 the execution pauses and emits a `client-tool-request` event.\n\n## Stream Events\n\nWorkers emit standard stream events plus worker-specific events.\n\n### Worker Events\n\n```typescript\n// Worker started\n{\n type: 'worker-start',\n workerId: string, // Unique ID (also used as session ID for debug)\n workerSlug: string, // The worker's slug\n description?: string, // Display description for UI\n}\n\n// Worker completed\n{\n type: 'worker-result',\n workerId: string,\n output?: unknown, // The worker's output value\n error?: string, // Error message if worker failed\n}\n```\n\n### Common Events\n\n| Event | Description |\n| ----------------------- | --------------------------- |\n| `start` | Execution started |\n| `finish` | Execution completed |\n| `text-start` | Text generation started |\n| `text-delta` | Text chunk received |\n| `text-end` | Text generation ended |\n| `block-start` | Step started |\n| `block-end` | Step completed |\n| `tool-input-available` | Tool arguments ready |\n| `tool-output-available` | Tool result ready |\n| `client-tool-request` | Client tools need execution |\n| `error` | Error occurred |\n\n## Extracting Output\n\nTo get just the worker's output value:\n\n```typescript\nasync function executeWorker(\n client: OctavusClient,\n agentId: string,\n input: Record<string, unknown>,\n): Promise<unknown> {\n const events = client.workers.execute(agentId, input);\n\n for await (const event of events) {\n if (event.type === 'worker-result') {\n if (event.error) {\n throw new Error(event.error);\n }\n return event.output;\n }\n }\n\n return undefined;\n}\n\n// Usage\nconst analysis = await executeWorker(client, agentId, { TOPIC: 'AI' });\n```\n\n## Client Tool Continuation\n\nWhen workers have tools without handlers, execution pauses:\n\n```typescript\nfor await (const event of client.workers.execute(agentId, input)) {\n if (event.type === 'client-tool-request') {\n // Execute tools client-side\n const results = await executeClientTools(event.toolCalls);\n\n // Continue execution\n for await (const ev of client.workers.continue(agentId, event.executionId, results)) {\n // Handle remaining events\n }\n break;\n }\n}\n```\n\nThe `client-tool-request` event includes:\n\n```typescript\n{\n type: 'client-tool-request',\n executionId: string, // Pass to continue()\n toolCalls: [{\n toolCallId: string,\n toolName: string,\n args: Record<string, unknown>,\n }],\n}\n```\n\n## Streaming to HTTP Response\n\nConvert worker events to an SSE stream:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\nexport async function POST(request: Request) {\n const { agentId, input } = await request.json();\n\n const events = client.workers.execute(agentId, input, {\n tools: {\n search: async (args) => await search(args.query),\n },\n });\n\n return new Response(toSSEStream(events), {\n headers: { 'Content-Type': 'text/event-stream' },\n });\n}\n```\n\n## Cancellation\n\nUse an abort signal to cancel execution:\n\n```typescript\nconst controller = new AbortController();\n\n// Cancel after 30 seconds\nsetTimeout(() => controller.abort(), 30000);\n\nconst events = client.workers.execute(agentId, input, {\n signal: controller.signal,\n});\n\ntry {\n for await (const event of events) {\n // Process events\n }\n} catch (error) {\n if (error.name === 'AbortError') {\n console.log('Worker cancelled');\n }\n}\n```\n\n## Error Handling\n\nErrors can occur at different levels:\n\n```typescript\nfor await (const event of client.workers.execute(agentId, input)) {\n // Stream-level error event\n if (event.type === 'error') {\n console.error(`Error: ${event.message}`);\n console.error(`Type: ${event.errorType}`);\n console.error(`Retryable: ${event.retryable}`);\n }\n\n // Worker-level error in result\n if (event.type === 'worker-result' && event.error) {\n console.error(`Worker failed: ${event.error}`);\n }\n}\n```\n\nError types include:\n\n| Type | Description |\n| ------------------ | --------------------- |\n| `validation_error` | Invalid input |\n| `not_found_error` | Worker not found |\n| `provider_error` | LLM provider error |\n| `tool_error` | Tool execution failed |\n| `execution_error` | Worker step failed |\n\n## Full Example\n\n```typescript\nimport { OctavusClient, type StreamEvent } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n\nasync function runResearchWorker(topic: string) {\n console.log(`Researching: ${topic}\\n`);\n\n const events = client.workers.execute(\n 'research-assistant-id',\n {\n TOPIC: topic,\n DEPTH: 'detailed',\n },\n {\n tools: {\n 'web-search': async ({ query }) => {\n console.log(`Searching: ${query}`);\n return await performWebSearch(query);\n },\n },\n },\n );\n\n let output: unknown;\n\n for await (const event of events) {\n switch (event.type) {\n case 'worker-start':\n console.log(`Started: ${event.workerSlug}`);\n break;\n\n case 'block-start':\n console.log(`Step: ${event.blockName}`);\n break;\n\n case 'text-delta':\n process.stdout.write(event.delta);\n break;\n\n case 'worker-result':\n if (event.error) {\n throw new Error(event.error);\n }\n output = event.output;\n break;\n\n case 'error':\n throw new Error(event.message);\n }\n }\n\n console.log('\\n\\nResearch complete!');\n return output;\n}\n\n// Run the worker\nconst result = await runResearchWorker('AI safety best practices');\nconsole.log('Result:', result);\n```\n\n## Next Steps\n\n- [Workers Protocol](/docs/protocol/workers) \u2014 Worker protocol reference\n- [Streaming](/docs/server-sdk/streaming) \u2014 Understanding stream events\n- [Tools](/docs/server-sdk/tools) \u2014 Tool handler patterns\n",
778
787
  excerpt: "Workers API The enables executing worker agents from your server. Workers are task-based agents that run steps sequentially and return an output value. Basic Usage WorkersApi Reference execute()...",
779
788
  order: 6
789
+ },
790
+ {
791
+ slug: "server-sdk/debugging",
792
+ section: "server-sdk",
793
+ title: "Debugging",
794
+ description: "Model request tracing and debugging tools for Octavus agents.",
795
+ content: "\n# Debugging\n\n## Model Request Tracing\n\nModel request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014 system prompts, messages, tool definitions, and provider options \u2014 making it easier to debug agent behavior.\n\n### Enabling Tracing\n\nEnable tracing by setting `traceModelRequests: true` in the client config:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: true,\n});\n```\n\nWhen enabled, the SDK sends an `X-Octavus-Trace: true` header with every request. The platform captures the full model request payload before each provider call and stores it in the execution logs.\n\nYou can also drive this from an environment variable for per-environment control:\n\n```typescript\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: process.env.TRACE_MODEL_REQUESTS === 'true',\n});\n```\n\n### What Gets Captured\n\n**LLM requests** include:\n\n- Full system prompt\n- All messages in AI SDK format (post-conversion)\n- Tool names, descriptions, and JSON schemas\n- Provider-specific options (thinking budgets, etc.)\n- Temperature, max steps, and thinking configuration\n\n**Image generation requests** include:\n\n- Image generation prompt\n- Requested size\n- Whether reference images were provided\n\n### Where Traces Appear\n\nTraces appear as **Model Request** entries in the execution log timeline, alongside existing entries like triggers, tool calls, and responses. Each trace is linked to the block that made the model call.\n\nIn the Octavus dashboard:\n\n- **Session debug view** \u2014 Full execution log with expandable model request entries\n- **Agent preview** \u2014 Activity panel shows model requests in the execution steps\n\nEach entry shows the raw JSON payload with a copy button for easy inspection.\n\n### Storage\n\nTraces are stored in Redis alongside other execution log entries with a 24-hour TTL. They are not permanently stored. A typical LLM trace with 10 messages and 5 tools is 10\u201350KB. Image traces are smaller (just prompt and metadata).\n\n### Recommendations\n\n| Environment | Recommendation |\n| ----------- | ---------------------------------------------------------- |\n| Development | Enable \u2014 helps debug agent behavior during development |\n| Staging | Enable \u2014 useful for pre-production testing |\n| Production | Disable (default) \u2014 saves storage for high-volume sessions |\n\n### Preview Sessions\n\nModel request tracing is always enabled for preview sessions in the Octavus dashboard. No configuration needed \u2014 the platform automatically traces all model requests when using the agent preview.\n",
796
+ excerpt: "Debugging Model Request Tracing Model request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014...",
797
+ order: 7
780
798
  }
781
799
  ]
782
800
  },
@@ -791,7 +809,7 @@ var sections_default = [
791
809
  section: "client-sdk",
792
810
  title: "Overview",
793
811
  description: "Introduction to the Octavus Client SDKs for building chat interfaces.",
794
- content: "\n# Client SDK Overview\n\nOctavus provides two packages for frontend integration:\n\n| Package | Purpose | Use When |\n| --------------------- | ------------------------ | ----------------------------------------------------- |\n| `@octavus/react` | React hooks and bindings | Building React applications |\n| `@octavus/client-sdk` | Framework-agnostic core | Using Vue, Svelte, vanilla JS, or custom integrations |\n\n**Most users should install `@octavus/react`** \u2014 it includes everything from `@octavus/client-sdk` plus React-specific hooks.\n\n## Installation\n\n### React Applications\n\n```bash\nnpm install @octavus/react\n```\n\n**Current version:** `2.8.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.8.0`\n\n## Transport Pattern\n\nThe Client SDK uses a **transport abstraction** to handle communication with your backend. This gives you flexibility in how events are delivered:\n\n| Transport | Use Case | Docs |\n| ----------------------- | -------------------------------------------- | ----------------------------------------------------- |\n| `createHttpTransport` | HTTP/SSE (Next.js, Express, etc.) | [HTTP Transport](/docs/client-sdk/http-transport) |\n| `createSocketTransport` | WebSocket, SockJS, or other socket protocols | [Socket Transport](/docs/client-sdk/socket-transport) |\n\nWhen the transport changes (e.g., when `sessionId` changes), the `useOctavusChat` hook automatically reinitializes with the new transport.\n\n> **Recommendation**: Use HTTP transport unless you specifically need WebSocket features (custom real-time events, Meteor/Phoenix, etc.).\n\n## React Usage\n\nThe `useOctavusChat` hook provides state management and streaming for React applications:\n\n```tsx\nimport { useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\n // Create a stable transport instance (memoized on sessionId)\n const transport = useMemo(\n () =>\n createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n }),\n [sessionId],\n );\n\n const { messages, status, send } = useOctavusChat({ transport });\n\n const sendMessage = async (text: string) => {\n await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n };\n\n return (\n <div>\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n return null;\n })}\n </div>\n );\n}\n```\n\n## Framework-Agnostic Usage\n\nThe `OctavusChat` class can be used with any framework or vanilla JavaScript:\n\n```typescript\nimport { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n\nconst chat = new OctavusChat({ transport });\n\n// Subscribe to state changes\nconst unsubscribe = chat.subscribe(() => {\n console.log('Messages:', chat.messages);\n console.log('Status:', chat.status);\n // Update your UI here\n});\n\n// Send a message\nawait chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });\n\n// Cleanup when done\nunsubscribe();\n```\n\n## Key Features\n\n### Unified Send Function\n\nThe `send` function handles both user message display and agent triggering in one call:\n\n```tsx\nconst { send } = useOctavusChat({ transport });\n\n// Add user message to UI and trigger agent\nawait send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n\n// Trigger without adding a user message (e.g., button click)\nawait send('request-human');\n```\n\n### Message Parts\n\nMessages contain ordered `parts` for rich content:\n\n```tsx\nconst { messages } = useOctavusChat({ transport });\n\n// Each message has typed parts\nmessage.parts.map((part) => {\n switch (part.type) {\n case 'text': // Text content\n case 'reasoning': // Extended reasoning/thinking\n case 'tool-call': // Tool execution\n case 'operation': // Internal operations (set-resource, etc.)\n }\n});\n```\n\n### Status Tracking\n\n```tsx\nconst { status } = useOctavusChat({ transport });\n\n// status: 'idle' | 'streaming' | 'error' | 'awaiting-input'\n// 'awaiting-input' occurs when interactive client tools need user action\n```\n\n### Stop Streaming\n\n```tsx\nconst { stop } = useOctavusChat({ transport });\n\n// Stop current stream and finalize message\nstop();\n```\n\n## Hook Reference (React)\n\n### useOctavusChat\n\n```typescript\nfunction useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn;\n\ninterface OctavusChatOptions {\n // Required: Transport for streaming events\n transport: Transport;\n\n // Optional: Function to request upload URLs for file uploads\n requestUploadUrls?: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n // Optional: Client-side tool handlers\n // - Function: executes automatically and returns result\n // - 'interactive': appears in pendingClientTools for user input\n clientTools?: Record<string, ClientToolHandler>;\n\n // Optional: Pre-populate with existing messages (session restore)\n initialMessages?: UIMessage[];\n\n // Optional: Callbacks\n onError?: (error: OctavusError) => void; // Structured error with type, source, retryable\n onFinish?: () => void;\n onStop?: () => void; // Called when user stops generation\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\ninterface UseOctavusChatReturn {\n // State\n messages: UIMessage[];\n status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n error: OctavusError | null; // Structured error with type, source, retryable\n\n // Connection (socket transport only - undefined for HTTP)\n connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'\n connectionError: Error | undefined;\n\n // Client tools (interactive tools awaiting user input)\n pendingClientTools: Record<string, InteractiveTool[]>; // Keyed by tool name\n\n // Actions\n send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n stop: () => void;\n\n // Connection management (socket transport only - undefined for HTTP)\n connect: (() => Promise<void>) | undefined;\n disconnect: (() => void) | undefined;\n\n // File uploads (requires requestUploadUrls)\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\ninterface UserMessageInput {\n content?: string;\n files?: FileList | File[] | FileReference[];\n}\n```\n\n## Transport Reference\n\n### createHttpTransport\n\nCreates an HTTP/SSE transport using native `fetch()`:\n\n```typescript\nimport { createHttpTransport } from '@octavus/react';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n```\n\n### createSocketTransport\n\nCreates a WebSocket/SockJS transport for real-time connections:\n\n```typescript\nimport { createSocketTransport } from '@octavus/react';\n\nconst transport = createSocketTransport({\n connect: () =>\n new Promise((resolve, reject) => {\n const ws = new WebSocket(`wss://api.example.com/stream?sessionId=${sessionId}`);\n ws.onopen = () => resolve(ws);\n ws.onerror = () => reject(new Error('Connection failed'));\n }),\n});\n```\n\nSocket transport provides additional connection management:\n\n```typescript\n// Access connection state directly\ntransport.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'\n\n// Subscribe to state changes\ntransport.onConnectionStateChange((state, error) => {\n /* ... */\n});\n\n// Eager connection (instead of lazy on first send)\nawait transport.connect();\n\n// Manual disconnect\ntransport.disconnect();\n```\n\nFor detailed WebSocket/SockJS usage including custom events, reconnection patterns, and server-side implementation, see [Socket Transport](/docs/client-sdk/socket-transport).\n\n## Class Reference (Framework-Agnostic)\n\n### OctavusChat\n\n```typescript\nclass OctavusChat {\n constructor(options: OctavusChatOptions);\n\n // State (read-only)\n readonly messages: UIMessage[];\n readonly status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n readonly error: OctavusError | null; // Structured error\n readonly pendingClientTools: Record<string, InteractiveTool[]>; // Interactive tools\n\n // Actions\n send(\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ): Promise<void>;\n stop(): void;\n\n // Subscription\n subscribe(callback: () => void): () => void; // Returns unsubscribe function\n}\n```\n\n## Next Steps\n\n- [HTTP Transport](/docs/client-sdk/http-transport) \u2014 HTTP/SSE integration (recommended)\n- [Socket Transport](/docs/client-sdk/socket-transport) \u2014 WebSocket and SockJS integration\n- [Messages](/docs/client-sdk/messages) \u2014 Working with message state\n- [Streaming](/docs/client-sdk/streaming) \u2014 Building streaming UIs\n- [Client Tools](/docs/client-sdk/client-tools) \u2014 Interactive browser-side tool handling\n- [Operations](/docs/client-sdk/execution-blocks) \u2014 Showing agent progress\n- [Error Handling](/docs/client-sdk/error-handling) \u2014 Handling errors with type guards\n- [File Uploads](/docs/client-sdk/file-uploads) \u2014 Uploading images and documents\n- [Examples](/docs/examples/overview) \u2014 Complete working examples\n",
812
+ content: "\n# Client SDK Overview\n\nOctavus provides two packages for frontend integration:\n\n| Package | Purpose | Use When |\n| --------------------- | ------------------------ | ----------------------------------------------------- |\n| `@octavus/react` | React hooks and bindings | Building React applications |\n| `@octavus/client-sdk` | Framework-agnostic core | Using Vue, Svelte, vanilla JS, or custom integrations |\n\n**Most users should install `@octavus/react`** \u2014 it includes everything from `@octavus/client-sdk` plus React-specific hooks.\n\n## Installation\n\n### React Applications\n\n```bash\nnpm install @octavus/react\n```\n\n**Current version:** `2.9.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.9.0`\n\n## Transport Pattern\n\nThe Client SDK uses a **transport abstraction** to handle communication with your backend. This gives you flexibility in how events are delivered:\n\n| Transport | Use Case | Docs |\n| ----------------------- | -------------------------------------------- | ----------------------------------------------------- |\n| `createHttpTransport` | HTTP/SSE (Next.js, Express, etc.) | [HTTP Transport](/docs/client-sdk/http-transport) |\n| `createSocketTransport` | WebSocket, SockJS, or other socket protocols | [Socket Transport](/docs/client-sdk/socket-transport) |\n\nWhen the transport changes (e.g., when `sessionId` changes), the `useOctavusChat` hook automatically reinitializes with the new transport.\n\n> **Recommendation**: Use HTTP transport unless you specifically need WebSocket features (custom real-time events, Meteor/Phoenix, etc.).\n\n## React Usage\n\nThe `useOctavusChat` hook provides state management and streaming for React applications:\n\n```tsx\nimport { useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\n // Create a stable transport instance (memoized on sessionId)\n const transport = useMemo(\n () =>\n createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n }),\n [sessionId],\n );\n\n const { messages, status, send } = useOctavusChat({ transport });\n\n const sendMessage = async (text: string) => {\n await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n };\n\n return (\n <div>\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n return null;\n })}\n </div>\n );\n}\n```\n\n## Framework-Agnostic Usage\n\nThe `OctavusChat` class can be used with any framework or vanilla JavaScript:\n\n```typescript\nimport { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n\nconst chat = new OctavusChat({ transport });\n\n// Subscribe to state changes\nconst unsubscribe = chat.subscribe(() => {\n console.log('Messages:', chat.messages);\n console.log('Status:', chat.status);\n // Update your UI here\n});\n\n// Send a message\nawait chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });\n\n// Cleanup when done\nunsubscribe();\n```\n\n## Key Features\n\n### Unified Send Function\n\nThe `send` function handles both user message display and agent triggering in one call:\n\n```tsx\nconst { send } = useOctavusChat({ transport });\n\n// Add user message to UI and trigger agent\nawait send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });\n\n// Trigger without adding a user message (e.g., button click)\nawait send('request-human');\n```\n\n### Message Parts\n\nMessages contain ordered `parts` for rich content:\n\n```tsx\nconst { messages } = useOctavusChat({ transport });\n\n// Each message has typed parts\nmessage.parts.map((part) => {\n switch (part.type) {\n case 'text': // Text content\n case 'reasoning': // Extended reasoning/thinking\n case 'tool-call': // Tool execution\n case 'operation': // Internal operations (set-resource, etc.)\n }\n});\n```\n\n### Status Tracking\n\n```tsx\nconst { status } = useOctavusChat({ transport });\n\n// status: 'idle' | 'streaming' | 'error' | 'awaiting-input'\n// 'awaiting-input' occurs when interactive client tools need user action\n```\n\n### Stop Streaming\n\n```tsx\nconst { stop } = useOctavusChat({ transport });\n\n// Stop current stream and finalize message\nstop();\n```\n\n## Hook Reference (React)\n\n### useOctavusChat\n\n```typescript\nfunction useOctavusChat(options: OctavusChatOptions): UseOctavusChatReturn;\n\ninterface OctavusChatOptions {\n // Required: Transport for streaming events\n transport: Transport;\n\n // Optional: Function to request upload URLs for file uploads\n requestUploadUrls?: (\n files: { filename: string; mediaType: string; size: number }[],\n ) => Promise<UploadUrlsResponse>;\n\n // Optional: Client-side tool handlers\n // - Function: executes automatically and returns result\n // - 'interactive': appears in pendingClientTools for user input\n clientTools?: Record<string, ClientToolHandler>;\n\n // Optional: Pre-populate with existing messages (session restore)\n initialMessages?: UIMessage[];\n\n // Optional: Callbacks\n onError?: (error: OctavusError) => void; // Structured error with type, source, retryable\n onFinish?: () => void;\n onStop?: () => void; // Called when user stops generation\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\ninterface UseOctavusChatReturn {\n // State\n messages: UIMessage[];\n status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n error: OctavusError | null; // Structured error with type, source, retryable\n\n // Connection (socket transport only - undefined for HTTP)\n connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'\n connectionError: Error | undefined;\n\n // Client tools (interactive tools awaiting user input)\n pendingClientTools: Record<string, InteractiveTool[]>; // Keyed by tool name\n\n // Actions\n send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n stop: () => void;\n\n // Connection management (socket transport only - undefined for HTTP)\n connect: (() => Promise<void>) | undefined;\n disconnect: (() => void) | undefined;\n\n // File uploads (requires requestUploadUrls)\n uploadFiles: (\n files: FileList | File[],\n onProgress?: (fileIndex: number, progress: number) => void,\n ) => Promise<FileReference[]>;\n}\n\ninterface UserMessageInput {\n content?: string;\n files?: FileList | File[] | FileReference[];\n}\n```\n\n## Transport Reference\n\n### createHttpTransport\n\nCreates an HTTP/SSE transport using native `fetch()`:\n\n```typescript\nimport { createHttpTransport } from '@octavus/react';\n\nconst transport = createHttpTransport({\n request: (payload, options) =>\n fetch('/api/trigger', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, ...payload }),\n signal: options?.signal,\n }),\n});\n```\n\n### createSocketTransport\n\nCreates a WebSocket/SockJS transport for real-time connections:\n\n```typescript\nimport { createSocketTransport } from '@octavus/react';\n\nconst transport = createSocketTransport({\n connect: () =>\n new Promise((resolve, reject) => {\n const ws = new WebSocket(`wss://api.example.com/stream?sessionId=${sessionId}`);\n ws.onopen = () => resolve(ws);\n ws.onerror = () => reject(new Error('Connection failed'));\n }),\n});\n```\n\nSocket transport provides additional connection management:\n\n```typescript\n// Access connection state directly\ntransport.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'\n\n// Subscribe to state changes\ntransport.onConnectionStateChange((state, error) => {\n /* ... */\n});\n\n// Eager connection (instead of lazy on first send)\nawait transport.connect();\n\n// Manual disconnect\ntransport.disconnect();\n```\n\nFor detailed WebSocket/SockJS usage including custom events, reconnection patterns, and server-side implementation, see [Socket Transport](/docs/client-sdk/socket-transport).\n\n## Class Reference (Framework-Agnostic)\n\n### OctavusChat\n\n```typescript\nclass OctavusChat {\n constructor(options: OctavusChatOptions);\n\n // State (read-only)\n readonly messages: UIMessage[];\n readonly status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'\n readonly error: OctavusError | null; // Structured error\n readonly pendingClientTools: Record<string, InteractiveTool[]>; // Interactive tools\n\n // Actions\n send(\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ): Promise<void>;\n stop(): void;\n\n // Subscription\n subscribe(callback: () => void): () => void; // Returns unsubscribe function\n}\n```\n\n## Next Steps\n\n- [HTTP Transport](/docs/client-sdk/http-transport) \u2014 HTTP/SSE integration (recommended)\n- [Socket Transport](/docs/client-sdk/socket-transport) \u2014 WebSocket and SockJS integration\n- [Messages](/docs/client-sdk/messages) \u2014 Working with message state\n- [Streaming](/docs/client-sdk/streaming) \u2014 Building streaming UIs\n- [Client Tools](/docs/client-sdk/client-tools) \u2014 Interactive browser-side tool handling\n- [Operations](/docs/client-sdk/execution-blocks) \u2014 Showing agent progress\n- [Error Handling](/docs/client-sdk/error-handling) \u2014 Handling errors with type guards\n- [File Uploads](/docs/client-sdk/file-uploads) \u2014 Uploading images and documents\n- [Examples](/docs/examples/overview) \u2014 Complete working examples\n",
795
813
  excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
796
814
  order: 1
797
815
  },
@@ -1262,7 +1280,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
1262
1280
  section: "protocol",
1263
1281
  title: "Input & Resources",
1264
1282
  description: "Defining agent inputs and persistent resources.",
1265
- content: "\n# Input & Resources\n\nInputs are provided when creating a session. Resources are persistent state the agent can read and write.\n\n## Input Variables\n\nDefine inputs that consumers must (or may) provide:\n\n```yaml\ninput:\n # Required input\n COMPANY_NAME:\n type: string\n description: The company name to use in responses\n\n # Required input with description\n PRODUCT_NAME:\n type: string\n description: Product being supported\n\n # Optional input (defaults to \"NONE\")\n SUPPORT_POLICIES:\n type: string\n description: Company policies for support\n optional: true\n\n # Optional input with custom default\n USER_ID:\n type: string\n description: Current user's ID\n optional: true\n default: ''\n```\n\n### Input Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this input is for |\n| `optional` | No | If true, consumer doesn't have to provide it |\n| `default` | No | Default value if not provided (defaults to `\"NONE\"`) |\n\n### Using Inputs\n\nWhen creating a session, pass input values:\n\n```typescript\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n SUPPORT_POLICIES: 'Refunds within 30 days...',\n // USER_ID is optional, not provided\n});\n```\n\nInputs can also be used for [dynamic model selection](/docs/protocol/agent-config#dynamic-model-selection):\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n```\n\nIn prompts, reference with `{{INPUT_NAME}}`:\n\n```markdown\nYou are a support agent for {{COMPANY_NAME}}.\n```\n\n> **Note:** Variables must be `UPPER_SNAKE_CASE`. Nested properties (dot notation like `{{VAR.property}}`) are not supported. Objects are serialized as JSON when interpolated.\n\n## Resources\n\nResources are persistent state that:\n\n- Survive across triggers\n- Can be read and written by the agent\n- Are synced to the consumer's application\n\n```yaml\nresources:\n # String resource with default\n CONVERSATION_SUMMARY:\n type: string\n description: Running summary of the conversation\n default: ''\n\n # Resource with unknown type (for complex data)\n USER_CONTEXT:\n type: unknown\n description: Cached user information\n default: {}\n\n # Read-only resource (agent can read but not write)\n SYSTEM_CONFIG:\n type: unknown\n description: System configuration\n readonly: true\n default:\n maxRetries: 3\n timeout: 30000\n```\n\n### Resource Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes the resource purpose |\n| `default` | No | Initial value |\n| `readonly` | No | If true, agent cannot write to it |\n\n### Writing Resources\n\nUse the `set-resource` block in handlers:\n\n```yaml\nhandlers:\n request-human:\n # ... generate summary ...\n\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable containing the value\n```\n\n### Resource Events\n\nWhen a resource is updated, the client SDK receives a `resource-update` event:\n\n```typescript\nuseOctavusChat({\n onResourceUpdate: (name, value) => {\n if (name === 'CONVERSATION_SUMMARY') {\n console.log('Summary updated:', value);\n }\n },\n});\n```\n\n## Variables\n\nVariables are internal state managed by block outputs. They persist across triggers but are not synced to the consumer (unlike resources).\n\n```yaml\nvariables:\n SUMMARY:\n type: string\n description: Generated summary text\n TICKET:\n type: unknown\n description: Ticket creation result\n CONVERSATION_TEXT:\n type: string\n description: Serialized conversation\n```\n\n### Variable Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this variable stores |\n| `default` | No | Initial value |\n\n### Using Variables\n\nSet variables as output from blocks:\n\n```yaml\nhandlers:\n request-human:\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT # Stores result in variable\n\n Generate summary:\n block: next-message\n output: SUMMARY # LLM output stored in variable\n\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Use variable as input\n output: TICKET\n```\n\n## Scoping\n\n| Type | Scope | Persistence | Synced to Consumer |\n| ----------- | ------- | ------------------------ | ------------------- |\n| `input` | Session | Immutable | Yes (at creation) |\n| `resources` | Session | Persists across triggers | Yes (via callbacks) |\n| `variables` | Session | Persists across triggers | No (internal only) |\n",
1283
+ content: "\n# Input & Resources\n\nInputs are provided when creating a session. Resources are persistent state the agent can read and write.\n\n## Input Variables\n\nDefine inputs that consumers must (or may) provide:\n\n```yaml\ninput:\n # Required input\n COMPANY_NAME:\n type: string\n description: The company name to use in responses\n\n # Required input with description\n PRODUCT_NAME:\n type: string\n description: Product being supported\n\n # Optional input (defaults to \"NONE\")\n SUPPORT_POLICIES:\n type: string\n description: Company policies for support\n optional: true\n\n # Optional input with custom default\n USER_ID:\n type: string\n description: Current user's ID\n optional: true\n default: ''\n```\n\n### Input Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this input is for |\n| `optional` | No | If true, consumer doesn't have to provide it |\n| `default` | No | Default value if not provided (defaults to `\"NONE\"`) |\n\n### Using Inputs\n\nWhen creating a session, pass input values:\n\n```typescript\nconst sessionId = await client.agentSessions.create('support-chat', {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n SUPPORT_POLICIES: 'Refunds within 30 days...',\n // USER_ID is optional, not provided\n});\n```\n\nInputs can also be used for [dynamic model selection](/docs/protocol/agent-config#dynamic-model-selection):\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n```\n\nIn prompts, reference variables with `{{VARIABLE_NAME}}`:\n\n```markdown\nYou are a support agent for {{COMPANY_NAME}}.\n```\n\nTo use a variable in a prompt, pass it through the `input` mapping on the [agent config](/docs/protocol/agent-config#system-prompt) or [block](/docs/protocol/handlers#block-input-mapping). Variables not listed in the `input` mapping won't be interpolated \u2014 the `{{VARIABLE}}` placeholder will be preserved as-is.\n\n> **Note:** Variables must be `UPPER_SNAKE_CASE`. Nested properties (dot notation like `{{VAR.property}}`) are not supported. Objects are serialized as JSON when interpolated.\n\n## Resources\n\nResources are persistent state that:\n\n- Survive across triggers\n- Can be read and written by the agent\n- Are synced to the consumer's application\n\n```yaml\nresources:\n # String resource with default\n CONVERSATION_SUMMARY:\n type: string\n description: Running summary of the conversation\n default: ''\n\n # Resource with unknown type (for complex data)\n USER_CONTEXT:\n type: unknown\n description: Cached user information\n default: {}\n\n # Read-only resource (agent can read but not write)\n SYSTEM_CONFIG:\n type: unknown\n description: System configuration\n readonly: true\n default:\n maxRetries: 3\n timeout: 30000\n```\n\n### Resource Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes the resource purpose |\n| `default` | No | Initial value |\n| `readonly` | No | If true, agent cannot write to it |\n\n### Writing Resources\n\nUse the `set-resource` block in handlers:\n\n```yaml\nhandlers:\n request-human:\n # ... generate summary ...\n\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable containing the value\n```\n\n### Resource Events\n\nWhen a resource is updated, the client SDK receives a `resource-update` event:\n\n```typescript\nuseOctavusChat({\n onResourceUpdate: (name, value) => {\n if (name === 'CONVERSATION_SUMMARY') {\n console.log('Summary updated:', value);\n }\n },\n});\n```\n\n## Variables\n\nVariables are internal state managed by block outputs. They persist across triggers but are not synced to the consumer (unlike resources).\n\n```yaml\nvariables:\n SUMMARY:\n type: string\n description: Generated summary text\n TICKET:\n type: unknown\n description: Ticket creation result\n CONVERSATION_TEXT:\n type: string\n description: Serialized conversation\n```\n\n### Variable Definition\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a [custom type](/docs/protocol/types) |\n| `description` | No | Describes what this variable stores |\n| `default` | No | Initial value |\n\n### Using Variables\n\nSet variables as output from blocks:\n\n```yaml\nhandlers:\n request-human:\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT # Stores result in variable\n\n Generate summary:\n block: next-message\n output: SUMMARY # LLM output stored in variable\n\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Use variable as input\n output: TICKET\n```\n\n## Scoping\n\n| Type | Scope | Persistence | Synced to Consumer |\n| ----------- | ------- | ------------------------ | ------------------- |\n| `input` | Session | Immutable | Yes (at creation) |\n| `resources` | Session | Persists across triggers | Yes (via callbacks) |\n| `variables` | Session | Persists across triggers | No (internal only) |\n",
1266
1284
  excerpt: "Input & Resources Inputs are provided when creating a session. Resources are persistent state the agent can read and write. Input Variables Define inputs that consumers must (or may) provide: Input...",
1267
1285
  order: 2
1268
1286
  },
@@ -1298,7 +1316,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
1298
1316
  section: "protocol",
1299
1317
  title: "Handlers",
1300
1318
  description: "Defining execution handlers with blocks.",
1301
- content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n```\n\nThe `model` field can also reference a variable for dynamic model selection:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nMap variables to block inputs:\n\n```yaml\n# Simple list (variable name = prompt variable)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Mapping (different names)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # CONVERSATION in prompt = CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
1319
+ content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n```\n\nThe `model` field can also reference a variable for dynamic model selection:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nThe `input` field on blocks controls which variables are passed to the prompt. Only variables listed in `input` are available for interpolation.\n\nVariables can come from `protocol.input`, `protocol.resources`, `protocol.variables`, `trigger.input`, or outputs from prior blocks.\n\n```yaml\n# Array format (same name)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Array format (rename)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # Prompt sees CONVERSATION, value comes from CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n\n# Object format (rename)\ninput:\n CONVERSATION: CONVERSATION_TEXT\n TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
1302
1320
  excerpt: "Handlers Handlers define what happens when a trigger fires. They contain execution blocks that run in sequence. Handler Structure Each block has a human-readable name (shown in debug UI) and a ...",
1303
1321
  order: 6
1304
1322
  },
@@ -1307,7 +1325,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
1307
1325
  section: "protocol",
1308
1326
  title: "Agent Config",
1309
1327
  description: "Configuring the agent model and behavior.",
1310
- content: "\n# Agent Config\n\nThe `agent` section configures the LLM model, system prompt, tools, and behavior.\n\n## Basic Configuration\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account] # Available tools\n skills: [qr-code] # Available skills\n```\n\n## Configuration Options\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------- |\n| `model` | Yes | Model identifier or variable reference |\n| `system` | Yes | System prompt filename (without .md) |\n| `input` | No | Variables to interpolate in system prompt |\n| `tools` | No | List of tools the LLM can call |\n| `skills` | No | List of Octavus skills the LLM can use |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `agentic` | No | Allow multiple tool call cycles |\n| `maxSteps` | No | Maximum agentic steps (default: 10) |\n| `temperature` | No | Model temperature (0-2) |\n| `thinking` | No | Extended reasoning level |\n| `anthropic` | No | Anthropic-specific options (tools, skills) |\n\n## Models\n\nSpecify models in `provider/model-id` format. Any model supported by the provider's SDK will work.\n\n### Supported Providers\n\n| Provider | Format | Examples |\n| --------- | ---------------------- | -------------------------------------------------------------------- |\n| Anthropic | `anthropic/{model-id}` | `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |\n| Google | `google/{model-id}` | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash` |\n| OpenAI | `openai/{model-id}` | `gpt-5`, `gpt-4o`, `o4-mini`, `o3`, `o3-mini`, `o1` |\n\n### Examples\n\n```yaml\n# Anthropic Claude 4.5\nagent:\n model: anthropic/claude-sonnet-4-5\n\n# Google Gemini 3\nagent:\n model: google/gemini-3-flash-preview\n\n# OpenAI GPT-5\nagent:\n model: openai/gpt-5\n\n# OpenAI reasoning models\nagent:\n model: openai/o3-mini\n```\n\n> **Note**: Model IDs are passed directly to the provider SDK. Check the provider's documentation for the latest available models.\n\n### Dynamic Model Selection\n\nThe model field can also reference an input variable, allowing consumers to choose the model when creating a session:\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n system: system\n```\n\nWhen creating a session, pass the model:\n\n```typescript\nconst sessionId = await client.agentSessions.create('my-agent', {\n MODEL: 'anthropic/claude-sonnet-4-5',\n});\n```\n\nThis enables:\n\n- **Multi-provider support** \u2014 Same agent works with different providers\n- **A/B testing** \u2014 Test different models without protocol changes\n- **User preferences** \u2014 Let users choose their preferred model\n\nThe model value is validated at runtime to ensure it's in the correct `provider/model-id` format.\n\n> **Note**: When using dynamic models, provider-specific options (like `anthropic:`) may not apply if the model resolves to a different provider.\n\n## System Prompt\n\nThe system prompt sets the agent's persona and instructions:\n\n```yaml\nagent:\n system: system # Uses prompts/system.md\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n```\n\nExample `prompts/system.md`:\n\n```markdown\nYou are a friendly support agent for {{COMPANY_NAME}}.\n\n## Your Role\n\nHelp users with questions about {{PRODUCT_NAME}}.\n\n## Guidelines\n\n- Be helpful and professional\n- If you can't help, offer to escalate\n- Never share internal information\n```\n\n## Agentic Mode\n\nEnable multi-step tool calling:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [get-user-account, search-docs, create-ticket]\n agentic: true # LLM can call multiple tools\n maxSteps: 10 # Limit cycles to prevent runaway\n```\n\n**How it works:**\n\n1. LLM receives user message\n2. LLM decides to call a tool\n3. Tool executes, result returned to LLM\n4. LLM decides if more tools needed\n5. Repeat until LLM responds or maxSteps reached\n\n## Extended Thinking\n\nEnable extended reasoning for complex tasks:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n thinking: medium # low | medium | high\n```\n\n| Level | Token Budget | Use Case |\n| -------- | ------------ | ------------------- |\n| `low` | ~5,000 | Simple reasoning |\n| `medium` | ~10,000 | Moderate complexity |\n| `high` | ~20,000 | Complex analysis |\n\nThinking content streams to the UI and can be displayed to users.\n\n## Skills\n\nEnable Octavus skills for code execution and file generation:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code] # Enable skills\n agentic: true\n```\n\nSkills provide provider-agnostic code execution in isolated sandboxes. When enabled, the LLM can execute Python/Bash code, run skill scripts, and generate files.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Image Generation\n\nEnable the LLM to generate images autonomously:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n imageModel: google/gemini-2.5-flash-image\n agentic: true\n```\n\nWhen `imageModel` is configured, the `octavus_generate_image` tool becomes available. The LLM can decide when to generate images based on user requests. The tool supports both text-to-image generation and image editing/transformation using reference images.\n\n### Supported Image Providers\n\n| Provider | Model Types | Examples |\n| -------- | --------------------------------------- | --------------------------------------------------------- |\n| OpenAI | Dedicated image models | `gpt-image-1` |\n| Google | Gemini native (contains \"image\") | `gemini-2.5-flash-image`, `gemini-3-flash-image-generate` |\n| Google | Imagen dedicated (starts with \"imagen\") | `imagen-4.0-generate-001` |\n\n> **Note**: Google has two image generation approaches. Gemini \"native\" models (containing \"image\" in the ID) generate images using the language model API with `responseModalities`. Imagen models (starting with \"imagen\") use a dedicated image generation API.\n\n### Image Sizes\n\nThe tool supports three image sizes:\n\n- `1024x1024` (default) \u2014 Square\n- `1792x1024` \u2014 Landscape (16:9)\n- `1024x1792` \u2014 Portrait (9:16)\n\n### Image Editing with Reference Images\n\nBoth the agentic tool and the `generate-image` block support reference images for editing and transformation. When reference images are provided, the prompt describes how to modify or use those images.\n\n| Provider | Models | Reference Image Support |\n| -------- | -------------------------------- | ----------------------- |\n| OpenAI | `gpt-image-1` | Yes |\n| Google | Gemini native (`gemini-*-image`) | Yes |\n| Google | Imagen (`imagen-*`) | No |\n\n### Agentic vs Deterministic\n\nUse `imageModel` in agent config when:\n\n- The LLM should decide when to generate or edit images\n- Users ask for images in natural language\n\nUse `generate-image` block (see [Handlers](/docs/protocol/handlers#generate-image)) when:\n\n- You want explicit control over image generation or editing\n- Building prompt engineering pipelines\n- Images are generated at specific handler steps\n\n## Temperature\n\nControl response randomness:\n\n```yaml\nagent:\n model: openai/gpt-4o\n temperature: 0.7 # 0 = deterministic, 2 = creative\n```\n\n**Guidelines:**\n\n- `0 - 0.3`: Factual, consistent responses\n- `0.4 - 0.7`: Balanced (good default)\n- `0.8 - 1.2`: Creative, varied responses\n- `> 1.2`: Very creative (may be inconsistent)\n\n## Provider Options\n\nEnable provider-specific features like Anthropic's built-in tools and skills:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n```\n\nProvider options are validated against the model\u2014using `anthropic:` with a non-Anthropic model will fail validation.\n\nSee [Provider Options](/docs/protocol/provider-options) for full documentation.\n\n## Thread-Specific Config\n\nOverride config for named threads:\n\n```yaml\nhandlers:\n request-human:\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5 # Different model\n thinking: low # Different thinking\n maxSteps: 1 # Limit tool calls\n system: escalation-summary # Different prompt\n```\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n PRODUCT_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\nresources:\n CONVERSATION_SUMMARY:\n type: string\n default: ''\n\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { type: string }\n\n search-docs:\n description: Search help documentation\n parameters:\n query: { type: string }\n\n create-support-ticket:\n description: Create a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string } # low, medium, high\n\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n tools:\n - get-user-account\n - search-docs\n - create-support-ticket\n skills: [qr-code] # Octavus skills\n agentic: true\n maxSteps: 10\n thinking: medium\n # Anthropic-specific options\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n Respond:\n block: next-message\n```\n",
1328
+ content: "\n# Agent Config\n\nThe `agent` section configures the LLM model, system prompt, tools, and behavior.\n\n## Basic Configuration\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account] # Available tools\n skills: [qr-code] # Available skills\n```\n\n## Configuration Options\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------- |\n| `model` | Yes | Model identifier or variable reference |\n| `system` | Yes | System prompt filename (without .md) |\n| `input` | No | Variables to pass to the system prompt |\n| `tools` | No | List of tools the LLM can call |\n| `skills` | No | List of Octavus skills the LLM can use |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `agentic` | No | Allow multiple tool call cycles |\n| `maxSteps` | No | Maximum agentic steps (default: 10) |\n| `temperature` | No | Model temperature (0-2) |\n| `thinking` | No | Extended reasoning level |\n| `anthropic` | No | Anthropic-specific options (tools, skills) |\n\n## Models\n\nSpecify models in `provider/model-id` format. Any model supported by the provider's SDK will work.\n\n### Supported Providers\n\n| Provider | Format | Examples |\n| --------- | ---------------------- | -------------------------------------------------------------------- |\n| Anthropic | `anthropic/{model-id}` | `claude-opus-4-5`, `claude-sonnet-4-5`, `claude-haiku-4-5` |\n| Google | `google/{model-id}` | `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-flash` |\n| OpenAI | `openai/{model-id}` | `gpt-5`, `gpt-4o`, `o4-mini`, `o3`, `o3-mini`, `o1` |\n\n### Examples\n\n```yaml\n# Anthropic Claude 4.5\nagent:\n model: anthropic/claude-sonnet-4-5\n\n# Google Gemini 3\nagent:\n model: google/gemini-3-flash-preview\n\n# OpenAI GPT-5\nagent:\n model: openai/gpt-5\n\n# OpenAI reasoning models\nagent:\n model: openai/o3-mini\n```\n\n> **Note**: Model IDs are passed directly to the provider SDK. Check the provider's documentation for the latest available models.\n\n### Dynamic Model Selection\n\nThe model field can also reference an input variable, allowing consumers to choose the model when creating a session:\n\n```yaml\ninput:\n MODEL:\n type: string\n description: The LLM model to use\n\nagent:\n model: MODEL # Resolved from session input\n system: system\n```\n\nWhen creating a session, pass the model:\n\n```typescript\nconst sessionId = await client.agentSessions.create('my-agent', {\n MODEL: 'anthropic/claude-sonnet-4-5',\n});\n```\n\nThis enables:\n\n- **Multi-provider support** \u2014 Same agent works with different providers\n- **A/B testing** \u2014 Test different models without protocol changes\n- **User preferences** \u2014 Let users choose their preferred model\n\nThe model value is validated at runtime to ensure it's in the correct `provider/model-id` format.\n\n> **Note**: When using dynamic models, provider-specific options (like `anthropic:`) may not apply if the model resolves to a different provider.\n\n## System Prompt\n\nThe system prompt sets the agent's persona and instructions. The `input` field controls which variables are available to the prompt \u2014 only variables listed in `input` are interpolated.\n\n```yaml\nagent:\n system: system # Uses prompts/system.md\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n```\n\nVariables in `input` can come from `protocol.input`, `protocol.resources`, or `protocol.variables`.\n\n### Input Mapping Formats\n\n```yaml\n# Array format (same name)\ninput:\n - COMPANY_NAME\n - PRODUCT_NAME\n\n# Array format (rename)\ninput:\n - CONTEXT: CONVERSATION_SUMMARY # Prompt sees CONTEXT, value comes from CONVERSATION_SUMMARY\n\n# Object format (rename)\ninput:\n CONTEXT: CONVERSATION_SUMMARY\n```\n\nThe left side (label) is what the prompt sees. The right side (source) is where the value comes from.\n\n### Example\n\n`prompts/system.md`:\n\n```markdown\nYou are a friendly support agent for {{COMPANY_NAME}}.\n\n## Your Role\n\nHelp users with questions about {{PRODUCT_NAME}}.\n\n## Guidelines\n\n- Be helpful and professional\n- If you can't help, offer to escalate\n- Never share internal information\n```\n\n## Agentic Mode\n\nEnable multi-step tool calling:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [get-user-account, search-docs, create-ticket]\n agentic: true # LLM can call multiple tools\n maxSteps: 10 # Limit cycles to prevent runaway\n```\n\n**How it works:**\n\n1. LLM receives user message\n2. LLM decides to call a tool\n3. Tool executes, result returned to LLM\n4. LLM decides if more tools needed\n5. Repeat until LLM responds or maxSteps reached\n\n## Extended Thinking\n\nEnable extended reasoning for complex tasks:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n thinking: medium # low | medium | high\n```\n\n| Level | Token Budget | Use Case |\n| -------- | ------------ | ------------------- |\n| `low` | ~5,000 | Simple reasoning |\n| `medium` | ~10,000 | Moderate complexity |\n| `high` | ~20,000 | Complex analysis |\n\nThinking content streams to the UI and can be displayed to users.\n\n## Skills\n\nEnable Octavus skills for code execution and file generation:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code] # Enable skills\n agentic: true\n```\n\nSkills provide provider-agnostic code execution in isolated sandboxes. When enabled, the LLM can execute Python/Bash code, run skill scripts, and generate files.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Image Generation\n\nEnable the LLM to generate images autonomously:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n imageModel: google/gemini-2.5-flash-image\n agentic: true\n```\n\nWhen `imageModel` is configured, the `octavus_generate_image` tool becomes available. The LLM can decide when to generate images based on user requests. The tool supports both text-to-image generation and image editing/transformation using reference images.\n\n### Supported Image Providers\n\n| Provider | Model Types | Examples |\n| -------- | --------------------------------------- | --------------------------------------------------------- |\n| OpenAI | Dedicated image models | `gpt-image-1` |\n| Google | Gemini native (contains \"image\") | `gemini-2.5-flash-image`, `gemini-3-flash-image-generate` |\n| Google | Imagen dedicated (starts with \"imagen\") | `imagen-4.0-generate-001` |\n\n> **Note**: Google has two image generation approaches. Gemini \"native\" models (containing \"image\" in the ID) generate images using the language model API with `responseModalities`. Imagen models (starting with \"imagen\") use a dedicated image generation API.\n\n### Image Sizes\n\nThe tool supports three image sizes:\n\n- `1024x1024` (default) \u2014 Square\n- `1792x1024` \u2014 Landscape (16:9)\n- `1024x1792` \u2014 Portrait (9:16)\n\n### Image Editing with Reference Images\n\nBoth the agentic tool and the `generate-image` block support reference images for editing and transformation. When reference images are provided, the prompt describes how to modify or use those images.\n\n| Provider | Models | Reference Image Support |\n| -------- | -------------------------------- | ----------------------- |\n| OpenAI | `gpt-image-1` | Yes |\n| Google | Gemini native (`gemini-*-image`) | Yes |\n| Google | Imagen (`imagen-*`) | No |\n\n### Agentic vs Deterministic\n\nUse `imageModel` in agent config when:\n\n- The LLM should decide when to generate or edit images\n- Users ask for images in natural language\n\nUse `generate-image` block (see [Handlers](/docs/protocol/handlers#generate-image)) when:\n\n- You want explicit control over image generation or editing\n- Building prompt engineering pipelines\n- Images are generated at specific handler steps\n\n## Temperature\n\nControl response randomness:\n\n```yaml\nagent:\n model: openai/gpt-4o\n temperature: 0.7 # 0 = deterministic, 2 = creative\n```\n\n**Guidelines:**\n\n- `0 - 0.3`: Factual, consistent responses\n- `0.4 - 0.7`: Balanced (good default)\n- `0.8 - 1.2`: Creative, varied responses\n- `> 1.2`: Very creative (may be inconsistent)\n\n## Provider Options\n\nEnable provider-specific features like Anthropic's built-in tools and skills:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n```\n\nProvider options are validated against the model\u2014using `anthropic:` with a non-Anthropic model will fail validation.\n\nSee [Provider Options](/docs/protocol/provider-options) for full documentation.\n\n## Thread-Specific Config\n\nOverride config for named threads:\n\n```yaml\nhandlers:\n request-human:\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5 # Different model\n thinking: low # Different thinking\n maxSteps: 1 # Limit tool calls\n system: escalation-summary # Different prompt\n```\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n PRODUCT_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\nresources:\n CONVERSATION_SUMMARY:\n type: string\n default: ''\n\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { type: string }\n\n search-docs:\n description: Search help documentation\n parameters:\n query: { type: string }\n\n create-support-ticket:\n description: Create a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string } # low, medium, high\n\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input:\n - COMPANY_NAME\n - PRODUCT_NAME\n tools:\n - get-user-account\n - search-docs\n - create-support-ticket\n skills: [qr-code] # Octavus skills\n agentic: true\n maxSteps: 10\n thinking: medium\n # Anthropic-specific options\n anthropic:\n tools:\n web-search:\n display: description\n description: Searching the web\n skills:\n pdf:\n type: anthropic\n description: Processing PDF\n\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n Respond:\n block: next-message\n```\n",
1311
1329
  excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
1312
1330
  order: 7
1313
1331
  },
@@ -1369,7 +1387,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
1369
1387
  section: "api-reference",
1370
1388
  title: "Sessions",
1371
1389
  description: "Session management API endpoints.",
1372
- content: '\n# Sessions API\n\nSessions represent conversations with agents. They store conversation history, resources, and variables.\n\nAll session endpoints require an API key with the **Sessions** permission.\n\n## Create Session\n\nCreate a new agent session.\n\n```\nPOST /api/agent-sessions\n```\n\n### Request Body\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro",\n "USER_ID": "user-123"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| --------- | ------ | -------- | ------------------------------------- |\n| `agentId` | string | Yes | Agent ID (the `id` field, not `slug`) |\n| `input` | object | No | Input variables for the agent |\n\n> **Getting the agent ID:** Copy the ID from the agent URL in the [platform](https://octavus.ai) (e.g., `octavus.ai/agents/clxyz123`), or use the [CLI](/docs/server-sdk/cli) (`octavus sync ./agents/my-agent`) for local development workflows.\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n }\n }\'\n```\n\n## Get Session\n\nRetrieve session state. Returns UI-ready messages for active sessions, or expiration info for expired sessions.\n\n```\nGET /api/agent-sessions/:sessionId\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n| --------- | ------ | ---------------------------------------------------- |\n| `format` | string | Optional. Use `format=ui` for UI-ready messages only |\n\n### Response (Active Session)\n\nWhen the session is active, the response includes `UIMessage` objects:\n\n```json\n{\n "id": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "status": "active",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n },\n "variables": {},\n "resources": {\n "CONVERSATION_SUMMARY": ""\n },\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n },\n {\n "id": "1702345805000-def456b",\n "role": "assistant",\n "parts": [\n { "type": "text", "text": "I can help you reset your password...", "status": "done" }\n ],\n "status": "done",\n "createdAt": "2024-01-15T10:30:05.000Z"\n }\n ],\n "createdAt": "2024-01-15T10:30:00Z",\n "updatedAt": "2024-01-15T10:30:05Z"\n}\n```\n\n### Response (Expired Session)\n\nWhen the session has expired, the response indicates the expiration status:\n\n```json\n{\n "status": "expired",\n "sessionId": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "createdAt": "2024-01-15T10:30:00Z"\n}\n```\n\nUse the [Restore Session](#restore-session) endpoint to restore an expired session from stored messages.\n\n````\n\n### UIMessage Parts\n\nMessages contain typed `parts` that preserve content ordering:\n\n| Part Type | Description |\n|-----------|-------------|\n| `text` | Text content with `text` and `status` fields |\n| `reasoning` | Extended reasoning with `text` and `status` fields |\n| `tool-call` | Tool execution with `toolCallId`, `toolName`, `displayName`, `args`, `result`, `status` |\n| `operation` | Internal operations with `operationId`, `name`, `operationType`, `status` |\n| `file` | File attachment with `id`, `mediaType`, `url`, `filename`, `size` |\n| `source` | Source reference with `sourceType`, `id`, `url`, `title` |\n| `object` | Structured output with `id`, `typeName`, `object`, `status` |\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n````\n\n## Restore Session\n\nRestore an expired session from stored messages. This allows you to continue a conversation after the server-side state has expired.\n\n```\nPOST /api/agent-sessions/:sessionId/restore\n```\n\n### Request Body\n\n```json\n{\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n }\n ],\n "input": {\n "COMPANY_NAME": "Acme Corp"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| ---------- | ----------- | -------- | -------------------------------------------------------------- |\n| `messages` | UIMessage[] | Yes | Previously stored chat history |\n| `input` | object | No | Session input for system prompt interpolation (same as create) |\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "restored": true\n}\n```\n\n| Field | Type | Description |\n| ----------- | ------- | ----------------------------------------------------------------------- |\n| `sessionId` | string | The session ID |\n| `restored` | boolean | `true` if restored from messages, `false` if session was already active |\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions/:sessionId/restore \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "messages": [...],\n "input": { "COMPANY_NAME": "Acme Corp" }\n }\'\n```\n\n> **Note**: Store the `UIMessage[]` array after each interaction to enable restoration. The restore endpoint reconstructs the conversation state from these messages.\n\n## Trigger Session\n\nExecute a trigger on a session. Returns a Server-Sent Events stream.\n\n```\nPOST /api/agent-sessions/:sessionId/trigger\n```\n\n### Request Body\n\n```json\n{\n "triggerName": "user-message",\n "input": {\n "USER_MESSAGE": "How do I reset my password?"\n },\n "toolResults": []\n}\n```\n\n| Field | Type | Required | Description |\n| ------------- | ------ | -------- | ---------------------------------------------- |\n| `triggerName` | string | Yes | Name of the trigger to execute |\n| `input` | object | No | Input variables for the trigger |\n| `toolResults` | array | No | Tool results for continuation (handled by SDK) |\n\n### Response\n\nReturns `text/event-stream` with SSE events:\n\n```\ndata: {"type":"start","messageId":"msg-123"}\n\ndata: {"type":"block-start","blockId":"b1","blockName":"Add user message","blockType":"add-message","display":"hidden"}\n\ndata: {"type":"block-end","blockId":"b1"}\n\ndata: {"type":"block-start","blockId":"b2","blockName":"Respond to user","blockType":"next-message","display":"stream","outputToChat":true}\n\ndata: {"type":"text-start","id":"t1"}\n\ndata: {"type":"text-delta","id":"t1","delta":"I"}\n\ndata: {"type":"text-delta","id":"t1","delta":" can"}\n\ndata: {"type":"text-delta","id":"t1","delta":" help"}\n\ndata: {"type":"text-delta","id":"t1","delta":" you"}\n\ndata: {"type":"text-delta","id":"t1","delta":" reset"}\n\ndata: {"type":"text-delta","id":"t1","delta":" your"}\n\ndata: {"type":"text-delta","id":"t1","delta":" password"}\n\ndata: {"type":"text-delta","id":"t1","delta":"!"}\n\ndata: {"type":"text-end","id":"t1"}\n\ndata: {"type":"block-end","blockId":"b2"}\n\ndata: {"type":"finish","finishReason":"stop"}\n\ndata: [DONE]\n```\n\n### Event Types\n\n| Event | Description |\n| ----------------------- | ---------------------------------- |\n| `start` | Stream started |\n| `finish` | Execution complete |\n| `error` | Error occurred |\n| `block-start` | Execution block started |\n| `block-end` | Execution block completed |\n| `text-start` | Text generation started |\n| `text-delta` | Incremental text content |\n| `text-end` | Text generation ended |\n| `reasoning-start` | Extended reasoning started |\n| `reasoning-delta` | Reasoning content |\n| `reasoning-end` | Extended reasoning ended |\n| `tool-input-start` | Tool call initiated |\n| `tool-input-delta` | Tool arguments streaming |\n| `tool-input-end` | Tool arguments streaming ended |\n| `tool-input-available` | Tool input complete |\n| `tool-output-available` | Tool completed with result |\n| `tool-output-error` | Tool failed |\n| `tool-request` | Platform requesting tool execution |\n| `file-available` | File ready for display/download |\n| `resource-update` | Resource value changed |\n\n### Example\n\n```bash\ncurl -N -X POST https://octavus.ai/api/agent-sessions/:sessionId/trigger \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "How do I reset my password?" }\n }\'\n```\n\n## Tool Continuation\n\nWhen the agent calls external tools, you\'ll receive a `tool-request` event. Execute the tools and send results back:\n\n```json\n{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "..." },\n "toolResults": [\n {\n "toolCallId": "tc_123",\n "toolName": "get-user-account",\n "result": {\n "name": "Demo User",\n "email": "demo@example.com"\n }\n }\n ]\n}\n```\n\nThe Server SDK handles this continuation pattern automatically.\n\n## Upload URLs\n\nGet presigned URLs for file uploads. Files are uploaded directly to S3.\n\n```\nPOST /api/files/upload-urls\n```\n\n### Request Body\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "files": [\n {\n "filename": "photo.jpg",\n "mediaType": "image/jpeg",\n "size": 102400\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ------------------- | ------ | -------- | ----------------------------------- |\n| `sessionId` | string | Yes | Session ID to associate files with |\n| `files` | array | Yes | Array of file metadata (1-20 files) |\n| `files[].filename` | string | Yes | Original filename |\n| `files[].mediaType` | string | Yes | MIME type (e.g., `image/png`) |\n| `files[].size` | number | Yes | File size in bytes |\n\n### Response\n\n```json\n{\n "files": [\n {\n "id": "file-abc123",\n "uploadUrl": "https://s3.amazonaws.com/bucket/key?...",\n "downloadUrl": "https://s3.amazonaws.com/bucket/key?..."\n }\n ]\n}\n```\n\n### Upload Flow\n\n1. Request upload URLs from the platform\n2. PUT file content to `uploadUrl` with `Content-Type` header\n3. Use `downloadUrl` as the `url` in `FileReference`\n4. Include `FileReference` in trigger input\n\n### Supported Types\n\n| Category | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n### Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 10 MB |\n| Max total per request | 50 MB |\n| Max files per request | 20 |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n',
1390
+ content: '\n# Sessions API\n\nSessions represent conversations with agents. They store conversation history, resources, and variables.\n\nAll session endpoints require an API key with the **Sessions** permission.\n\n## Create Session\n\nCreate a new agent session.\n\n```\nPOST /api/agent-sessions\n```\n\n### Request Body\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro",\n "USER_ID": "user-123"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| --------- | ------ | -------- | ------------------------------------- |\n| `agentId` | string | Yes | Agent ID (the `id` field, not `slug`) |\n| `input` | object | No | Input variables for the agent |\n\n> **Getting the agent ID:** Copy the ID from the agent URL in the [platform](https://octavus.ai) (e.g., `octavus.ai/agents/clxyz123`), or use the [CLI](/docs/server-sdk/cli) (`octavus sync ./agents/my-agent`) for local development workflows.\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "agentId": "cm5xvz7k80001abcd",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n }\n }\'\n```\n\n## Get Session\n\nRetrieve session state. Returns UI-ready messages for active sessions, or expiration info for expired sessions.\n\n```\nGET /api/agent-sessions/:sessionId\n```\n\n### Query Parameters\n\n| Parameter | Type | Description |\n| --------- | ------ | ---------------------------------------------------- |\n| `format` | string | Optional. Use `format=ui` for UI-ready messages only |\n\n### Response (Active Session)\n\nWhen the session is active, the response includes `UIMessage` objects:\n\n```json\n{\n "id": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "status": "active",\n "input": {\n "COMPANY_NAME": "Acme Corp",\n "PRODUCT_NAME": "Widget Pro"\n },\n "variables": {},\n "resources": {\n "CONVERSATION_SUMMARY": ""\n },\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n },\n {\n "id": "1702345805000-def456b",\n "role": "assistant",\n "parts": [\n { "type": "text", "text": "I can help you reset your password...", "status": "done" }\n ],\n "status": "done",\n "createdAt": "2024-01-15T10:30:05.000Z"\n }\n ],\n "createdAt": "2024-01-15T10:30:00Z",\n "updatedAt": "2024-01-15T10:30:05Z"\n}\n```\n\n### Response (Expired Session)\n\nWhen the session has expired, the response indicates the expiration status:\n\n```json\n{\n "status": "expired",\n "sessionId": "cm5xyz123abc456def",\n "agentId": "cm5xvz7k80001abcd",\n "createdAt": "2024-01-15T10:30:00Z"\n}\n```\n\nUse the [Restore Session](#restore-session) endpoint to restore an expired session from stored messages.\n\n````\n\n### UIMessage Parts\n\nMessages contain typed `parts` that preserve content ordering:\n\n| Part Type | Description |\n|-----------|-------------|\n| `text` | Text content with `text` and `status` fields |\n| `reasoning` | Extended reasoning with `text` and `status` fields |\n| `tool-call` | Tool execution with `toolCallId`, `toolName`, `displayName`, `args`, `result`, `status` |\n| `operation` | Internal operations with `operationId`, `name`, `operationType`, `status` |\n| `file` | File attachment with `id`, `mediaType`, `url`, `filename`, `size` |\n| `source` | Source reference with `sourceType`, `id`, `url`, `title` |\n| `object` | Structured output with `id`, `typeName`, `object`, `status` |\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n````\n\n## Restore Session\n\nRestore an expired session from stored messages. This allows you to continue a conversation after the server-side state has expired.\n\n```\nPOST /api/agent-sessions/:sessionId/restore\n```\n\n### Request Body\n\n```json\n{\n "messages": [\n {\n "id": "1702345800000-xyz789a",\n "role": "user",\n "parts": [{ "type": "text", "text": "How do I reset my password?", "status": "done" }],\n "status": "done",\n "createdAt": "2024-01-15T10:30:00.000Z"\n }\n ],\n "input": {\n "COMPANY_NAME": "Acme Corp"\n }\n}\n```\n\n| Field | Type | Required | Description |\n| ---------- | ----------- | -------- | -------------------------------------------------------------- |\n| `messages` | UIMessage[] | Yes | Previously stored chat history |\n| `input` | object | No | Session input for system prompt interpolation (same as create) |\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "restored": true\n}\n```\n\n| Field | Type | Description |\n| ----------- | ------- | ----------------------------------------------------------------------- |\n| `sessionId` | string | The session ID |\n| `restored` | boolean | `true` if restored from messages, `false` if session was already active |\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agent-sessions/:sessionId/restore \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "messages": [...],\n "input": { "COMPANY_NAME": "Acme Corp" }\n }\'\n```\n\n> **Note**: Store the `UIMessage[]` array after each interaction to enable restoration. The restore endpoint reconstructs the conversation state from these messages.\n\n## Clear Session\n\nClear session state, transitioning it to `expired` status. The session can be restored afterwards with the [Restore Session](#restore-session) endpoint.\n\nThis is idempotent \u2014 clearing an already expired session succeeds without error.\n\n```\nDELETE /api/agent-sessions/:sessionId\n```\n\n### Response\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "cleared": true\n}\n```\n\n### Example\n\n```bash\ncurl -X DELETE https://octavus.ai/api/agent-sessions/:sessionId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Trigger Session\n\nExecute a trigger on a session. Returns a Server-Sent Events stream.\n\n```\nPOST /api/agent-sessions/:sessionId/trigger\n```\n\n### Request Body\n\n```json\n{\n "triggerName": "user-message",\n "input": {\n "USER_MESSAGE": "How do I reset my password?"\n },\n "toolResults": []\n}\n```\n\n| Field | Type | Required | Description |\n| ------------- | ------ | -------- | ---------------------------------------------- |\n| `triggerName` | string | Yes | Name of the trigger to execute |\n| `input` | object | No | Input variables for the trigger |\n| `toolResults` | array | No | Tool results for continuation (handled by SDK) |\n\n### Response\n\nReturns `text/event-stream` with SSE events:\n\n```\ndata: {"type":"start","messageId":"msg-123"}\n\ndata: {"type":"block-start","blockId":"b1","blockName":"Add user message","blockType":"add-message","display":"hidden"}\n\ndata: {"type":"block-end","blockId":"b1"}\n\ndata: {"type":"block-start","blockId":"b2","blockName":"Respond to user","blockType":"next-message","display":"stream","outputToChat":true}\n\ndata: {"type":"text-start","id":"t1"}\n\ndata: {"type":"text-delta","id":"t1","delta":"I"}\n\ndata: {"type":"text-delta","id":"t1","delta":" can"}\n\ndata: {"type":"text-delta","id":"t1","delta":" help"}\n\ndata: {"type":"text-delta","id":"t1","delta":" you"}\n\ndata: {"type":"text-delta","id":"t1","delta":" reset"}\n\ndata: {"type":"text-delta","id":"t1","delta":" your"}\n\ndata: {"type":"text-delta","id":"t1","delta":" password"}\n\ndata: {"type":"text-delta","id":"t1","delta":"!"}\n\ndata: {"type":"text-end","id":"t1"}\n\ndata: {"type":"block-end","blockId":"b2"}\n\ndata: {"type":"finish","finishReason":"stop"}\n\ndata: [DONE]\n```\n\n### Event Types\n\n| Event | Description |\n| ----------------------- | ---------------------------------- |\n| `start` | Stream started |\n| `finish` | Execution complete |\n| `error` | Error occurred |\n| `block-start` | Execution block started |\n| `block-end` | Execution block completed |\n| `text-start` | Text generation started |\n| `text-delta` | Incremental text content |\n| `text-end` | Text generation ended |\n| `reasoning-start` | Extended reasoning started |\n| `reasoning-delta` | Reasoning content |\n| `reasoning-end` | Extended reasoning ended |\n| `tool-input-start` | Tool call initiated |\n| `tool-input-delta` | Tool arguments streaming |\n| `tool-input-end` | Tool arguments streaming ended |\n| `tool-input-available` | Tool input complete |\n| `tool-output-available` | Tool completed with result |\n| `tool-output-error` | Tool failed |\n| `tool-request` | Platform requesting tool execution |\n| `file-available` | File ready for display/download |\n| `resource-update` | Resource value changed |\n\n### Example\n\n```bash\ncurl -N -X POST https://octavus.ai/api/agent-sessions/:sessionId/trigger \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "How do I reset my password?" }\n }\'\n```\n\n## Tool Continuation\n\nWhen the agent calls external tools, you\'ll receive a `tool-request` event. Execute the tools and send results back:\n\n```json\n{\n "triggerName": "user-message",\n "input": { "USER_MESSAGE": "..." },\n "toolResults": [\n {\n "toolCallId": "tc_123",\n "toolName": "get-user-account",\n "result": {\n "name": "Demo User",\n "email": "demo@example.com"\n }\n }\n ]\n}\n```\n\nThe Server SDK handles this continuation pattern automatically.\n\n## Upload URLs\n\nGet presigned URLs for file uploads. Files are uploaded directly to S3.\n\n```\nPOST /api/files/upload-urls\n```\n\n### Request Body\n\n```json\n{\n "sessionId": "cm5xyz123abc456def",\n "files": [\n {\n "filename": "photo.jpg",\n "mediaType": "image/jpeg",\n "size": 102400\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ------------------- | ------ | -------- | ----------------------------------- |\n| `sessionId` | string | Yes | Session ID to associate files with |\n| `files` | array | Yes | Array of file metadata (1-20 files) |\n| `files[].filename` | string | Yes | Original filename |\n| `files[].mediaType` | string | Yes | MIME type (e.g., `image/png`) |\n| `files[].size` | number | Yes | File size in bytes |\n\n### Response\n\n```json\n{\n "files": [\n {\n "id": "file-abc123",\n "uploadUrl": "https://s3.amazonaws.com/bucket/key?...",\n "downloadUrl": "https://s3.amazonaws.com/bucket/key?..."\n }\n ]\n}\n```\n\n### Upload Flow\n\n1. Request upload URLs from the platform\n2. PUT file content to `uploadUrl` with `Content-Type` header\n3. Use `downloadUrl` as the `url` in `FileReference`\n4. Include `FileReference` in trigger input\n\n### Supported Types\n\n| Category | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n### Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 10 MB |\n| Max total per request | 50 MB |\n| Max files per request | 20 |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n',
1373
1391
  excerpt: "Sessions API Sessions represent conversations with agents. They store conversation history, resources, and variables. All session endpoints require an API key with the Sessions permission. Create...",
1374
1392
  order: 2
1375
1393
  },
@@ -1378,7 +1396,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
1378
1396
  section: "api-reference",
1379
1397
  title: "Agents",
1380
1398
  description: "Agent management API endpoints.",
1381
- content: '\n# Agents API\n\nManage agent definitions including protocols and prompts.\n\n## Permissions\n\n| Endpoint | Method | Permission Required |\n| ---------------------- | ------ | ------------------- |\n| `/api/agents` | GET | Agents OR Sessions |\n| `/api/agents/:id` | GET | Agents OR Sessions |\n| `/api/agents` | POST | Agents |\n| `/api/agents/:id` | PATCH | Agents |\n| `/api/agents/validate` | POST | Agents |\n\nRead endpoints work with either permission since both the CLI (for sync) and Server SDK (for sessions) need to read agent definitions.\n\n## List Agents\n\nGet all agents in the project.\n\n```\nGET /api/agents\n```\n\n### Response\n\n```json\n{\n "agents": [\n {\n "id": "cm5xvz7k80001abcd",\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive",\n "createdAt": "2024-01-10T08:00:00Z",\n "updatedAt": "2024-01-15T10:00:00Z"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Get Agent\n\nGet a single agent by ID.\n\n```\nGET /api/agents/:id\n```\n\n### Response\n\n```json\n{\n "id": "cm5xvz7k80001abcd",\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent for {{COMPANY_NAME}}..."\n },\n {\n "name": "user-message",\n "content": "{{USER_MESSAGE}}"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n> **Tip:** You can also view and edit agents directly in the [platform](https://octavus.ai), or use the [CLI](/docs/server-sdk/cli) (`octavus list`) for local workflows.\n\n## Create Agent\n\nCreate a new agent.\n\n```\nPOST /api/agents\n```\n\n### Request Body\n\n```json\n{\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent..."\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ---------------------- | ------ | -------- | ------------------------- |\n| `settings.slug` | string | Yes | URL-safe identifier |\n| `settings.name` | string | Yes | Display name |\n| `settings.description` | string | No | Agent description |\n| `settings.format` | string | Yes | `interactive` or `worker` |\n| `protocol` | string | Yes | YAML protocol definition |\n| `prompts` | array | Yes | Prompt files |\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent created successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "settings": {\n "slug": "my-agent",\n "name": "My Agent",\n "format": "interactive"\n },\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system",\n "prompts": [\n { "name": "system", "content": "You are a helpful assistant." }\n ]\n }\'\n```\n\n## Update Agent\n\nUpdate an existing agent.\n\n```\nPATCH /api/agents/:id\n```\n\n### Request Body\n\n```json\n{\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "Updated system prompt..."\n }\n ]\n}\n```\n\nAll fields are optional. Only provided fields are updated.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent updated successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X PATCH https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system\\n thinking: high"\n }\'\n```\n\n## Creating and Managing Agents\n\nThere are two ways to manage agents:\n\n### Platform UI\n\nCreate and edit agents directly at [octavus.ai](https://octavus.ai). The web editor provides real-time validation and is the easiest way to get started. Copy the agent ID from the URL to use in your application.\n\n### CLI (Local Development)\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\noctavus sync ./agents/support-chat\n```\n\nThis creates the agent if it doesn\'t exist, or updates it if it does. The CLI outputs the agent ID which you should store in an environment variable.\n\nFor CI/CD integration, see the [CLI documentation](/docs/server-sdk/cli#cicd-integration).\n',
1399
+ content: '\n# Agents API\n\nManage agent definitions including protocols and prompts.\n\n## Permissions\n\n| Endpoint | Method | Permission Required |\n| ---------------------- | ------ | ------------------- |\n| `/api/agents` | GET | Agents OR Sessions |\n| `/api/agents/:id` | GET | Agents OR Sessions |\n| `/api/agents` | POST | Agents |\n| `/api/agents/:id` | PATCH | Agents |\n| `/api/agents/:id` | DELETE | Agents |\n| `/api/agents/validate` | POST | Agents |\n\nRead endpoints work with either permission since both the CLI (for sync) and Server SDK (for sessions) need to read agent definitions.\n\n## List Agents\n\nGet all agents in the project.\n\n```\nGET /api/agents\n```\n\n### Response\n\n```json\n{\n "agents": [\n {\n "id": "cm5xvz7k80001abcd",\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive",\n "createdAt": "2024-01-10T08:00:00Z",\n "updatedAt": "2024-01-15T10:00:00Z"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Get Agent\n\nGet a single agent by ID.\n\n```\nGET /api/agents/:id\n```\n\n### Response\n\n```json\n{\n "id": "cm5xvz7k80001abcd",\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent for {{COMPANY_NAME}}..."\n },\n {\n "name": "user-message",\n "content": "{{USER_MESSAGE}}"\n }\n ]\n}\n```\n\n### Example\n\n```bash\ncurl https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n> **Tip:** You can also view and edit agents directly in the [platform](https://octavus.ai), or use the [CLI](/docs/server-sdk/cli) (`octavus list`) for local workflows.\n\n## Create Agent\n\nCreate a new agent.\n\n```\nPOST /api/agents\n```\n\n### Request Body\n\n```json\n{\n "settings": {\n "slug": "support-chat",\n "name": "Support Chat",\n "description": "Customer support agent",\n "format": "interactive"\n },\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "You are a support agent..."\n }\n ]\n}\n```\n\n| Field | Type | Required | Description |\n| ---------------------- | ------ | -------- | ------------------------- |\n| `settings.slug` | string | Yes | URL-safe identifier |\n| `settings.name` | string | Yes | Display name |\n| `settings.description` | string | No | Agent description |\n| `settings.format` | string | Yes | `interactive` or `worker` |\n| `protocol` | string | Yes | YAML protocol definition |\n| `prompts` | array | Yes | Prompt files |\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent created successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X POST https://octavus.ai/api/agents \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "settings": {\n "slug": "my-agent",\n "name": "My Agent",\n "format": "interactive"\n },\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system",\n "prompts": [\n { "name": "system", "content": "You are a helpful assistant." }\n ]\n }\'\n```\n\n## Update Agent\n\nUpdate an existing agent.\n\n```\nPATCH /api/agents/:id\n```\n\n### Request Body\n\n```json\n{\n "protocol": "input:\\n COMPANY_NAME: { type: string }\\n...",\n "prompts": [\n {\n "name": "system",\n "content": "Updated system prompt..."\n }\n ]\n}\n```\n\nAll fields are optional. Only provided fields are updated.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent updated successfully"\n}\n```\n\n### Example\n\n```bash\ncurl -X PATCH https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY" \\\n -H "Content-Type: application/json" \\\n -d \'{\n "protocol": "agent:\\n model: anthropic/claude-sonnet-4-5\\n system: system\\n thinking: high"\n }\'\n```\n\n## Archive Agent\n\nArchive an agent (soft delete). The agent is removed from the active agent list and its slug is freed for reuse. Session history is preserved.\n\n```\nDELETE /api/agents/:id\n```\n\nSupports `?by=slug` query parameter to look up by slug instead of ID.\n\n### Response\n\n```json\n{\n "agentId": "cm5xvz7k80001abcd",\n "message": "Agent archived successfully"\n}\n```\n\n### Example\n\n```bash\n# Archive by ID\ncurl -X DELETE https://octavus.ai/api/agents/:agentId \\\n -H "Authorization: Bearer YOUR_API_KEY"\n\n# Archive by slug\ncurl -X DELETE https://octavus.ai/api/agents/support-chat?by=slug \\\n -H "Authorization: Bearer YOUR_API_KEY"\n```\n\n## Creating and Managing Agents\n\nThere are two ways to manage agents:\n\n### Platform UI\n\nCreate and edit agents directly at [octavus.ai](https://octavus.ai). The web editor provides real-time validation and is the easiest way to get started. Copy the agent ID from the URL to use in your application.\n\n### CLI (Local Development)\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\noctavus sync ./agents/support-chat\n```\n\nThis creates the agent if it doesn\'t exist, or updates it if it does. The CLI outputs the agent ID which you should store in an environment variable.\n\nFor CI/CD integration, see the [CLI documentation](/docs/server-sdk/cli#cicd-integration).\n',
1382
1400
  excerpt: "Agents API Manage agent definitions including protocols and prompts. Permissions | Endpoint | Method | Permission Required | | ---------------------- | ------ | ------------------- | |...",
1383
1401
  order: 3
1384
1402
  }
@@ -1468,4 +1486,4 @@ export {
1468
1486
  getDocSlugs,
1469
1487
  getSectionBySlug
1470
1488
  };
1471
- //# sourceMappingURL=chunk-7AGGVMRQ.js.map
1489
+ //# sourceMappingURL=chunk-RZZE5BMI.js.map