@octavus/docs 2.11.0 → 2.13.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.
- package/content/02-server-sdk/02-sessions.md +1 -0
- package/content/02-server-sdk/05-cli.md +9 -3
- package/content/03-client-sdk/01-overview.md +17 -0
- package/content/03-client-sdk/06-http-transport.md +1 -0
- package/content/03-client-sdk/08-file-uploads.md +57 -3
- package/content/03-client-sdk/09-error-handling.md +28 -4
- package/content/04-protocol/01-overview.md +12 -6
- package/content/04-protocol/04-tools.md +4 -3
- package/content/04-protocol/07-agent-config.md +46 -1
- package/content/04-protocol/08-provider-options.md +2 -7
- package/content/04-protocol/11-workers.md +4 -2
- package/content/04-protocol/12-references.md +189 -0
- package/content/05-api-reference/02-sessions.md +6 -5
- package/content/05-api-reference/03-agents.md +31 -9
- package/dist/{chunk-H6M6M3MY.js → chunk-PYLADDXH.js} +51 -31
- package/dist/chunk-PYLADDXH.js.map +1 -0
- package/dist/{chunk-NCTX3Y2J.js → chunk-SNBEHHFU.js} +43 -25
- package/dist/chunk-SNBEHHFU.js.map +1 -0
- package/dist/{chunk-EIUCL4CP.js → chunk-Z5E72EIS.js} +51 -31
- package/dist/chunk-Z5E72EIS.js.map +1 -0
- package/dist/content.js +1 -1
- package/dist/docs.json +25 -16
- package/dist/index.js +1 -1
- package/dist/search-index.json +1 -1
- package/dist/search.js +1 -1
- package/dist/search.js.map +1 -1
- package/dist/sections.json +25 -16
- package/package.json +1 -1
- package/dist/chunk-6TO62UOU.js +0 -1489
- package/dist/chunk-6TO62UOU.js.map +0 -1
- package/dist/chunk-EIUCL4CP.js.map +0 -1
- package/dist/chunk-H6M6M3MY.js.map +0 -1
- package/dist/chunk-NCTX3Y2J.js.map +0 -1
|
@@ -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.
|
|
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.12.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### Workers\n\nExecute worker agents for task-based processing:\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(agentId, {\n TOPIC: 'AI safety',\n});\n\n// Streaming: observe events in real-time\nfor await (const event of client.workers.execute(agentId, input)) {\n // Handle stream events\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
|
},
|
|
@@ -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.
|
|
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.12.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\u251C\u2500\u2500 prompts/ # Optional: Prompt templates\n\u2502 \u251C\u2500\u2500 system.md\n\u2502 \u2514\u2500\u2500 user-message.md\n\u2514\u2500\u2500 references/ # Optional: Reference documents\n \u2514\u2500\u2500 api-guidelines.md\n```\n\n### references/\n\nReference files are markdown documents with YAML frontmatter containing a `description`. The agent can fetch these on demand during execution. See [References](/docs/protocol/references) for details.\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
|
},
|
|
@@ -86,7 +86,7 @@ var docs_default = [
|
|
|
86
86
|
section: "client-sdk",
|
|
87
87
|
title: "Overview",
|
|
88
88
|
description: "Introduction to the Octavus Client SDKs for building chat interfaces.",
|
|
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.10.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.10.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.12.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.12.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",
|
|
90
90
|
excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
|
|
91
91
|
order: 1
|
|
92
92
|
},
|
|
@@ -513,7 +513,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
513
513
|
section: "client-sdk",
|
|
514
514
|
title: "File Uploads",
|
|
515
515
|
description: "Uploading images and files for vision models and document processing.",
|
|
516
|
-
content: "\n# File Uploads\n\nThe Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing.\n\n## Overview\n\nFile uploads follow a two-step flow:\n\n1. **Request upload URLs** from the platform via your backend\n2. **Upload files directly to S3** using presigned URLs\n3. **Send file references** with your message\n\nThis architecture keeps your API key secure on the server while enabling fast, direct uploads.\n\n## Setup\n\n### Backend: Upload URLs Endpoint\n\nCreate an endpoint that proxies upload URL requests to the Octavus platform:\n\n```typescript\n// app/api/upload-urls/route.ts (Next.js)\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { sessionId, files } = await request.json();\n\n // Get presigned URLs from Octavus\n const result = await octavus.files.getUploadUrls(sessionId, files);\n\n return NextResponse.json(result);\n}\n```\n\n### Client: Configure File Uploads\n\nPass `requestUploadUrls` to the chat hook:\n\n```tsx\nimport { useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\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 // Request upload URLs from your backend\n const requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const response = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return response.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n // ...\n}\n```\n\n## Uploading Files\n\n### Method 1: Upload Before Sending\n\nFor the best UX (showing upload progress), upload files first, then send:\n\n```tsx\nimport { useState, useRef } from 'react';\nimport type { FileReference } from '@octavus/react';\n\nfunction ChatInput({ sessionId }: { sessionId: string }) {\n const [pendingFiles, setPendingFiles] = useState<FileReference[]>([]);\n const [uploading, setUploading] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n async function handleFileSelect(event: React.ChangeEvent<HTMLInputElement>) {\n const files = event.target.files;\n if (!files?.length) return;\n\n setUploading(true);\n try {\n // Upload files with progress tracking\n const fileRefs = await uploadFiles(files, (fileIndex, progress) => {\n console.log(`File ${fileIndex}: ${progress}%`);\n });\n setPendingFiles((prev) => [...prev, ...fileRefs]);\n } finally {\n setUploading(false);\n }\n }\n\n async function handleSend(message: string) {\n await send(\n 'user-message',\n {\n USER_MESSAGE: message,\n FILES: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n {\n userMessage: {\n content: message,\n files: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n },\n );\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* File preview */}\n {pendingFiles.map((file) => (\n <img key={file.id} src={file.url} alt={file.filename} className=\"h-16\" />\n ))}\n\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*,.pdf\"\n multiple\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n\n <button onClick={() => fileInputRef.current?.click()} disabled={uploading}>\n {uploading ? 'Uploading...' : 'Attach'}\n </button>\n </div>\n );\n}\n```\n\n### Method 2: Upload on Send (Automatic)\n\nFor simpler implementations, pass `File` objects directly:\n\n```tsx\nasync function handleSend(message: string, files?: File[]) {\n await send(\n 'user-message',\n { USER_MESSAGE: message, FILES: files },\n { userMessage: { content: message, files } },\n );\n}\n```\n\nThe SDK automatically uploads the files before sending. Note: This doesn't provide upload progress.\n\n## FileReference Type\n\nFile references contain metadata and URLs:\n\n```typescript\ninterface FileReference {\n /** Unique file ID (platform-generated) */\n id: string;\n /** IANA media type (e.g., 'image/png', 'application/pdf') */\n mediaType: string;\n /** Presigned download URL (S3) */\n url: string;\n /** Original filename */\n filename?: string;\n /** File size in bytes */\n size?: number;\n}\n```\n\n## Protocol Integration\n\nTo accept files in your agent protocol, use the `file[]` type:\n\n```yaml\ntriggers:\n user-message:\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n FILES:\n type: file[]\n optional: true\n description: User-attached images for vision analysis\n\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input:\n - USER_MESSAGE\n files:\n - FILES # Attach files to the message\n display: hidden\n\n Respond to user:\n block: next-message\n```\n\nThe `file` type is a built-in type representing uploaded files. Use `file[]` for arrays of files.\n\n## Supported File Types\n\n| Type | 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## File 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\n## Rendering User Files\n\nUser-uploaded files appear as `UIFilePart` in user messages:\n\n```tsx\nfunction UserMessage({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'file') {\n if (part.mediaType.startsWith('image/')) {\n return (\n <img\n key={i}\n src={part.url}\n alt={part.filename || 'Uploaded image'}\n className=\"max-h-48 rounded-lg\"\n />\n );\n }\n return (\n <a key={i} href={part.url} className=\"text-blue-500\">\n \u{1F4C4} {part.filename}\n </a>\n );\n }\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## Server SDK: Files API\n\nThe Server SDK provides direct access to the Files API:\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// Get presigned upload URLs\nconst { files } = await client.files.getUploadUrls(sessionId, [\n { filename: 'photo.jpg', mediaType: 'image/jpeg', size: 102400 },\n { filename: 'doc.pdf', mediaType: 'application/pdf', size: 204800 },\n]);\n\n// files[0].id - Use in FileReference\n// files[0].uploadUrl - PUT to this URL to upload\n// files[0].downloadUrl - Use as FileReference.url\n```\n\n## Complete Example\n\nHere's a full chat input component with file upload:\n\n```tsx\n'use client';\n\nimport { useState, useRef, useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport, type FileReference } from '@octavus/react';\n\ninterface PendingFile {\n file: File;\n id: string;\n status: 'uploading' | 'done' | 'error';\n progress: number;\n fileRef?: FileReference;\n error?: string;\n}\n\nexport function Chat({ sessionId }: { sessionId: string }) {\n const [input, setInput] = useState('');\n const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const fileIdCounter = useRef(0);\n\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 requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const res = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return res.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n const isUploading = pendingFiles.some((f) => f.status === 'uploading');\n const hasErrors = pendingFiles.some((f) => f.status === 'error');\n const allReady = pendingFiles.every((f) => f.status === 'done');\n\n async function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const files = Array.from(e.target.files ?? []);\n if (!files.length) return;\n e.target.value = '';\n\n const newPending: PendingFile[] = files.map((file) => ({\n file,\n id: `pending-${++fileIdCounter.current}`,\n status: 'uploading',\n progress: 0,\n }));\n\n setPendingFiles((prev) => [...prev, ...newPending]);\n\n for (const pending of newPending) {\n try {\n const [fileRef] = await uploadFiles([pending.file], (_, progress) => {\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, progress } : f)),\n );\n });\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, status: 'done', fileRef } : f)),\n );\n } catch (err) {\n setPendingFiles((prev) =>\n prev.map((f) =>\n f.id === pending.id ? { ...f, status: 'error', error: String(err) } : f,\n ),\n );\n }\n }\n }\n\n async function handleSubmit() {\n if ((!input.trim() && !pendingFiles.length) || !allReady) return;\n\n const fileRefs = pendingFiles.filter((f) => f.fileRef).map((f) => f.fileRef!);\n\n await send(\n 'user-message',\n {\n USER_MESSAGE: input,\n FILES: fileRefs.length > 0 ? fileRefs : undefined,\n },\n {\n userMessage: {\n content: input,\n files: fileRefs.length > 0 ? fileRefs : undefined,\n },\n },\n );\n\n setInput('');\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* Messages */}\n {messages.map((msg) => (\n <div key={msg.id}>{/* ... render message */}</div>\n ))}\n\n {/* Pending files */}\n {pendingFiles.length > 0 && (\n <div className=\"flex gap-2\">\n {pendingFiles.map((f) => (\n <div key={f.id} className=\"relative\">\n <img\n src={URL.createObjectURL(f.file)}\n alt={f.file.name}\n className=\"h-16 w-16 object-cover rounded\"\n />\n {f.status === 'uploading' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/50\">\n <span className=\"text-white text-xs\">{f.progress}%</span>\n </div>\n )}\n <button\n onClick={() => setPendingFiles((prev) => prev.filter((p) => p.id !== f.id))}\n className=\"absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5\"\n >\n \xD7\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input */}\n <div className=\"flex gap-2\">\n <input type=\"file\" ref={fileInputRef} onChange={handleFileSelect} hidden />\n <button onClick={() => fileInputRef.current?.click()}>\u{1F4CE}</button>\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1\"\n />\n <button onClick={handleSubmit} disabled={isUploading || hasErrors}>\n {isUploading ? 'Uploading...' : 'Send'}\n </button>\n </div>\n </div>\n );\n}\n```\n",
|
|
516
|
+
content: "\n# File Uploads\n\nThe Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing.\n\n## Overview\n\nFile uploads follow a two-step flow:\n\n1. **Request upload URLs** from the platform via your backend\n2. **Upload files directly to S3** using presigned URLs\n3. **Send file references** with your message\n\nThis architecture keeps your API key secure on the server while enabling fast, direct uploads.\n\n## Setup\n\n### Backend: Upload URLs Endpoint\n\nCreate an endpoint that proxies upload URL requests to the Octavus platform:\n\n```typescript\n// app/api/upload-urls/route.ts (Next.js)\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { sessionId, files } = await request.json();\n\n // Get presigned URLs from Octavus\n const result = await octavus.files.getUploadUrls(sessionId, files);\n\n return NextResponse.json(result);\n}\n```\n\n### Client: Configure File Uploads\n\nPass `requestUploadUrls` to the chat hook:\n\n```tsx\nimport { useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\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 // Request upload URLs from your backend\n const requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const response = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return response.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n // Optional: configure upload timeout and retry behavior\n uploadOptions: {\n timeoutMs: 60_000, // Per-file timeout (default: 60s, set to 0 to disable)\n maxRetries: 2, // Retry attempts on transient failures (default: 2)\n retryDelayMs: 1_000, // Delay between retries (default: 1s)\n },\n });\n\n // ...\n}\n```\n\n## Uploading Files\n\n### Method 1: Upload Before Sending\n\nFor the best UX (showing upload progress), upload files first, then send:\n\n```tsx\nimport { useState, useRef } from 'react';\nimport type { FileReference } from '@octavus/react';\n\nfunction ChatInput({ sessionId }: { sessionId: string }) {\n const [pendingFiles, setPendingFiles] = useState<FileReference[]>([]);\n const [uploading, setUploading] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n async function handleFileSelect(event: React.ChangeEvent<HTMLInputElement>) {\n const files = event.target.files;\n if (!files?.length) return;\n\n setUploading(true);\n try {\n // Upload files with progress tracking\n const fileRefs = await uploadFiles(files, (fileIndex, progress) => {\n console.log(`File ${fileIndex}: ${progress}%`);\n });\n setPendingFiles((prev) => [...prev, ...fileRefs]);\n } finally {\n setUploading(false);\n }\n }\n\n async function handleSend(message: string) {\n await send(\n 'user-message',\n {\n USER_MESSAGE: message,\n FILES: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n {\n userMessage: {\n content: message,\n files: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n },\n );\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* File preview */}\n {pendingFiles.map((file) => (\n <img key={file.id} src={file.url} alt={file.filename} className=\"h-16\" />\n ))}\n\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*,.pdf\"\n multiple\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n\n <button onClick={() => fileInputRef.current?.click()} disabled={uploading}>\n {uploading ? 'Uploading...' : 'Attach'}\n </button>\n </div>\n );\n}\n```\n\n### Method 2: Upload on Send (Automatic)\n\nFor simpler implementations, pass `File` objects directly:\n\n```tsx\nasync function handleSend(message: string, files?: File[]) {\n await send(\n 'user-message',\n { USER_MESSAGE: message, FILES: files },\n { userMessage: { content: message, files } },\n );\n}\n```\n\nThe SDK automatically uploads the files before sending. Note: This doesn't provide upload progress.\n\n## Upload Reliability\n\nUploads include built-in timeout and retry logic for handling transient failures (network errors, server issues, mobile network switches).\n\n**Default behavior:**\n\n- **Timeout**: 60 seconds per file \u2014 prevents uploads from hanging on stalled connections\n- **Retries**: 2 automatic retries on transient failures (network errors, 5xx, 429)\n- **Retry delay**: 1 second between retries\n- **Non-retryable errors** (4xx like 403, 404) fail immediately without retrying\n\nOnly the S3 upload is retried \u2014 the presigned URL stays valid for 15 minutes. On retry, the progress callback resets to 0%.\n\nConfigure via `uploadOptions`:\n\n```typescript\nconst { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n uploadOptions: {\n timeoutMs: 120_000, // 2 minutes for large files\n maxRetries: 3,\n retryDelayMs: 2_000,\n },\n});\n```\n\nTo disable timeout or retries:\n\n```typescript\nuploadOptions: {\n timeoutMs: 0, // No timeout\n maxRetries: 0, // No retries\n}\n```\n\n### Using `OctavusChat` Directly\n\nWhen using the `OctavusChat` class directly (without the React hook), pass `uploadOptions` in the constructor:\n\n```typescript\nconst chat = new OctavusChat({\n transport,\n requestUploadUrls,\n uploadOptions: { timeoutMs: 120_000, maxRetries: 3 },\n});\n```\n\n## FileReference Type\n\nFile references contain metadata and URLs:\n\n```typescript\ninterface FileReference {\n /** Unique file ID (platform-generated) */\n id: string;\n /** IANA media type (e.g., 'image/png', 'application/pdf') */\n mediaType: string;\n /** Presigned download URL (S3) */\n url: string;\n /** Original filename */\n filename?: string;\n /** File size in bytes */\n size?: number;\n}\n```\n\n## Protocol Integration\n\nTo accept files in your agent protocol, use the `file[]` type:\n\n```yaml\ntriggers:\n user-message:\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n FILES:\n type: file[]\n optional: true\n description: User-attached images for vision analysis\n\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input:\n - USER_MESSAGE\n files:\n - FILES # Attach files to the message\n display: hidden\n\n Respond to user:\n block: next-message\n```\n\nThe `file` type is a built-in type representing uploaded files. Use `file[]` for arrays of files.\n\n## Supported File Types\n\n| Type | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Video | `video/mp4`, `video/webm`, `video/quicktime`, `video/mpeg` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n## File Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 100 MB |\n| Max total per request | 200 MB |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n\n## Rendering User Files\n\nUser-uploaded files appear as `UIFilePart` in user messages:\n\n```tsx\nfunction UserMessage({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'file') {\n if (part.mediaType.startsWith('image/')) {\n return (\n <img\n key={i}\n src={part.url}\n alt={part.filename || 'Uploaded image'}\n className=\"max-h-48 rounded-lg\"\n />\n );\n }\n return (\n <a key={i} href={part.url} className=\"text-blue-500\">\n \u{1F4C4} {part.filename}\n </a>\n );\n }\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## Server SDK: Files API\n\nThe Server SDK provides direct access to the Files API:\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// Get presigned upload URLs\nconst { files } = await client.files.getUploadUrls(sessionId, [\n { filename: 'photo.jpg', mediaType: 'image/jpeg', size: 102400 },\n { filename: 'doc.pdf', mediaType: 'application/pdf', size: 204800 },\n]);\n\n// files[0].id - Use in FileReference\n// files[0].uploadUrl - PUT to this URL to upload\n// files[0].downloadUrl - Use as FileReference.url\n```\n\n## Complete Example\n\nHere's a full chat input component with file upload:\n\n```tsx\n'use client';\n\nimport { useState, useRef, useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport, type FileReference } from '@octavus/react';\n\ninterface PendingFile {\n file: File;\n id: string;\n status: 'uploading' | 'done' | 'error';\n progress: number;\n fileRef?: FileReference;\n error?: string;\n}\n\nexport function Chat({ sessionId }: { sessionId: string }) {\n const [input, setInput] = useState('');\n const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const fileIdCounter = useRef(0);\n\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 requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const res = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return res.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n const isUploading = pendingFiles.some((f) => f.status === 'uploading');\n const hasErrors = pendingFiles.some((f) => f.status === 'error');\n const allReady = pendingFiles.every((f) => f.status === 'done');\n\n async function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const files = Array.from(e.target.files ?? []);\n if (!files.length) return;\n e.target.value = '';\n\n const newPending: PendingFile[] = files.map((file) => ({\n file,\n id: `pending-${++fileIdCounter.current}`,\n status: 'uploading',\n progress: 0,\n }));\n\n setPendingFiles((prev) => [...prev, ...newPending]);\n\n for (const pending of newPending) {\n try {\n const [fileRef] = await uploadFiles([pending.file], (_, progress) => {\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, progress } : f)),\n );\n });\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, status: 'done', fileRef } : f)),\n );\n } catch (err) {\n setPendingFiles((prev) =>\n prev.map((f) =>\n f.id === pending.id ? { ...f, status: 'error', error: String(err) } : f,\n ),\n );\n }\n }\n }\n\n async function handleSubmit() {\n if ((!input.trim() && !pendingFiles.length) || !allReady) return;\n\n const fileRefs = pendingFiles.filter((f) => f.fileRef).map((f) => f.fileRef!);\n\n await send(\n 'user-message',\n {\n USER_MESSAGE: input,\n FILES: fileRefs.length > 0 ? fileRefs : undefined,\n },\n {\n userMessage: {\n content: input,\n files: fileRefs.length > 0 ? fileRefs : undefined,\n },\n },\n );\n\n setInput('');\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* Messages */}\n {messages.map((msg) => (\n <div key={msg.id}>{/* ... render message */}</div>\n ))}\n\n {/* Pending files */}\n {pendingFiles.length > 0 && (\n <div className=\"flex gap-2\">\n {pendingFiles.map((f) => (\n <div key={f.id} className=\"relative\">\n <img\n src={URL.createObjectURL(f.file)}\n alt={f.file.name}\n className=\"h-16 w-16 object-cover rounded\"\n />\n {f.status === 'uploading' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/50\">\n <span className=\"text-white text-xs\">{f.progress}%</span>\n </div>\n )}\n <button\n onClick={() => setPendingFiles((prev) => prev.filter((p) => p.id !== f.id))}\n className=\"absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5\"\n >\n \xD7\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input */}\n <div className=\"flex gap-2\">\n <input type=\"file\" ref={fileInputRef} onChange={handleFileSelect} hidden />\n <button onClick={() => fileInputRef.current?.click()}>\u{1F4CE}</button>\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1\"\n />\n <button onClick={handleSubmit} disabled={isUploading || hasErrors}>\n {isUploading ? 'Uploading...' : 'Send'}\n </button>\n </div>\n </div>\n );\n}\n```\n",
|
|
517
517
|
excerpt: "File Uploads The Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing. Overview File...",
|
|
518
518
|
order: 8
|
|
519
519
|
},
|
|
@@ -540,7 +540,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
540
540
|
section: "protocol",
|
|
541
541
|
title: "Overview",
|
|
542
542
|
description: "Introduction to Octavus agent protocols.",
|
|
543
|
-
content: '\n# Protocol Overview\n\nAgent protocols define how an AI agent behaves. They\'re written in YAML and specify inputs, triggers, tools, and execution handlers.\n\n## Why Protocols?\n\nProtocols provide:\n\n- **Declarative definition** \u2014 Define behavior, not implementation\n- **Portable agents** \u2014 Move agents between projects\n- **Versioning** \u2014 Track changes with git\n- **Validation** \u2014 Catch errors before runtime\n- **Visualization** \u2014 Debug execution flows\n\n## Agent Formats\n\nOctavus supports two agent formats:\n\n| Format | Use Case | Structure |\n| ------------- | ------------------------------ | --------------------------------- |\n| `interactive` | Chat and multi-turn dialogue | `triggers` + `handlers` + `agent` |\n| `worker` | Background tasks and pipelines | `steps` + `output` |\n\n**Interactive agents** handle conversations \u2014 they respond to triggers (like user messages) and maintain session state across interactions.\n\n**Worker agents** execute tasks \u2014 they run steps sequentially and return an output value. Workers can be called independently or composed into interactive agents.\n\nSee [Workers](/docs/protocol/workers) for the worker protocol reference.\n\n## Interactive Protocol Structure\n\n```yaml\n# Agent inputs (provided when creating a session)\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\n# Persistent resources the agent can read/write\nresources:\n CONVERSATION_SUMMARY:\n description: Summary for handoff\n default: \'\'\n\n# How the agent can be invoked\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n request-human:\n description: User clicks "Talk to Human"\n\n# Temporary variables for execution (with types)\nvariables:\n SUMMARY:\n type: string\n TICKET:\n type: unknown\n\n# Tools the agent can use\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\n# Octavus skills (provider-agnostic code execution)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\n# Agent configuration (model, tools, etc.)\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account]\n skills: [qr-code] # Enable skills\n imageModel: google/gemini-2.5-flash-image # Enable image generation\n agentic: true # Allow multiple tool calls\n thinking: medium # Extended reasoning\n\n# What happens when triggers fire\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond to user:\n block: next-message\n```\n\n## File Structure\n\nEach agent is a folder with:\n\n```\nmy-agent/\n\u251C\u2500\u2500 protocol.yaml # Main logic (required)\n\u251C\u2500\u2500 settings.json # Agent metadata (required)\n\
|
|
543
|
+
content: '\n# Protocol Overview\n\nAgent protocols define how an AI agent behaves. They\'re written in YAML and specify inputs, triggers, tools, and execution handlers.\n\n## Why Protocols?\n\nProtocols provide:\n\n- **Declarative definition** \u2014 Define behavior, not implementation\n- **Portable agents** \u2014 Move agents between projects\n- **Versioning** \u2014 Track changes with git\n- **Validation** \u2014 Catch errors before runtime\n- **Visualization** \u2014 Debug execution flows\n\n## Agent Formats\n\nOctavus supports two agent formats:\n\n| Format | Use Case | Structure |\n| ------------- | ------------------------------ | --------------------------------- |\n| `interactive` | Chat and multi-turn dialogue | `triggers` + `handlers` + `agent` |\n| `worker` | Background tasks and pipelines | `steps` + `output` |\n\n**Interactive agents** handle conversations \u2014 they respond to triggers (like user messages) and maintain session state across interactions.\n\n**Worker agents** execute tasks \u2014 they run steps sequentially and return an output value. Workers can be called independently or composed into interactive agents.\n\nSee [Workers](/docs/protocol/workers) for the worker protocol reference.\n\n## Interactive Protocol Structure\n\n```yaml\n# Agent inputs (provided when creating a session)\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\n# Persistent resources the agent can read/write\nresources:\n CONVERSATION_SUMMARY:\n description: Summary for handoff\n default: \'\'\n\n# How the agent can be invoked\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n request-human:\n description: User clicks "Talk to Human"\n\n# Temporary variables for execution (with types)\nvariables:\n SUMMARY:\n type: string\n TICKET:\n type: unknown\n\n# Tools the agent can use\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\n# Octavus skills (provider-agnostic code execution)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\n# Agent configuration (model, tools, etc.)\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account]\n skills: [qr-code] # Enable skills\n imageModel: google/gemini-2.5-flash-image # Enable image generation\n webSearch: true # Enable web search\n agentic: true # Allow multiple tool calls\n thinking: medium # Extended reasoning\n\n# What happens when triggers fire\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond to user:\n block: next-message\n```\n\n## File Structure\n\nEach agent is a folder with:\n\n```\nmy-agent/\n\u251C\u2500\u2500 protocol.yaml # Main logic (required)\n\u251C\u2500\u2500 settings.json # Agent metadata (required)\n\u251C\u2500\u2500 prompts/ # Prompt templates (supports subdirectories)\n\u2502 \u251C\u2500\u2500 system.md\n\u2502 \u251C\u2500\u2500 user-message.md\n\u2502 \u2514\u2500\u2500 shared/\n\u2502 \u251C\u2500\u2500 company-info.md\n\u2502 \u2514\u2500\u2500 formatting-rules.md\n\u2514\u2500\u2500 references/ # On-demand context documents (optional)\n \u2514\u2500\u2500 api-guidelines.md\n```\n\nPrompts can be organized in subdirectories. In the protocol, reference nested prompts by their path relative to `prompts/` (without `.md`): `shared/company-info`.\n\nReferences are markdown files with YAML frontmatter that the agent can fetch on demand during execution. See [References](/docs/protocol/references).\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "What this agent does",\n "format": "interactive"\n}\n```\n\n| Field | Required | Description |\n| ------------- | -------- | ----------------------------------------------- |\n| `slug` | Yes | URL-safe identifier (lowercase, digits, dashes) |\n| `name` | Yes | Human-readable name |\n| `description` | No | Brief description |\n| `format` | Yes | `interactive` (chat) or `worker` (background) |\n\n## Naming Conventions\n\n- **Slugs**: `lowercase-with-dashes`\n- **Variables**: `UPPERCASE_SNAKE_CASE`\n- **Prompts**: `lowercase-with-dashes.md` (paths use `/` for subdirectories)\n- **Tools**: `lowercase-with-dashes`\n- **Triggers**: `lowercase-with-dashes`\n\n## Variables in Prompts\n\nReference variables with `{{VARIABLE_NAME}}`:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a support agent for {{COMPANY_NAME}}.\n\nHelp users with their {{PRODUCT_NAME}} questions.\n\n## Support Policies\n\n{{SUPPORT_POLICIES}}\n```\n\nVariables are replaced with their values at runtime. If a variable is not provided, the placeholder is kept as-is.\n\n## Prompt Interpolation\n\nInclude other prompts inside a prompt with `{{@path.md}}`:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a customer support agent.\n\n{{@shared/company-info.md}}\n\n{{@shared/formatting-rules.md}}\n\nHelp users with their questions.\n```\n\nThe referenced prompt content is inserted before variable interpolation, so variables in included prompts work the same way. Circular references are not allowed and will be caught during validation.\n\n## Next Steps\n\n- [Input & Resources](/docs/protocol/input-resources) \u2014 Defining agent inputs\n- [Triggers](/docs/protocol/triggers) \u2014 How agents are invoked\n- [Tools](/docs/protocol/tools) \u2014 External capabilities\n- [Skills](/docs/protocol/skills) \u2014 Code execution and knowledge packages\n- [References](/docs/protocol/references) \u2014 On-demand context documents\n- [Handlers](/docs/protocol/handlers) \u2014 Execution blocks\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n- [Workers](/docs/protocol/workers) \u2014 Worker agent format\n- [Provider Options](/docs/protocol/provider-options) \u2014 Provider-specific features\n- [Types](/docs/protocol/types) \u2014 Custom type definitions\n',
|
|
544
544
|
excerpt: "Protocol Overview Agent protocols define how an AI agent behaves. They're written in YAML and specify inputs, triggers, tools, and execution handlers. Why Protocols? Protocols provide: - Declarative...",
|
|
545
545
|
order: 1
|
|
546
546
|
},
|
|
@@ -567,8 +567,8 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
567
567
|
section: "protocol",
|
|
568
568
|
title: "Tools",
|
|
569
569
|
description: "Defining external tools implemented in your backend.",
|
|
570
|
-
content: '\n# Tools\n\nTools extend what agents can do. Octavus supports multiple types:\n\n1. **External Tools** \u2014 Defined in the protocol, implemented in your backend (this page)\n2. **Provider Tools** \u2014
|
|
571
|
-
excerpt: "Tools Tools extend what agents can do. Octavus supports multiple types: 1. External Tools \u2014 Defined in the protocol, implemented in your backend (this page) 2.
|
|
570
|
+
content: '\n# Tools\n\nTools extend what agents can do. Octavus supports multiple types:\n\n1. **External Tools** \u2014 Defined in the protocol, implemented in your backend (this page)\n2. **Built-in Tools** \u2014 Provider-agnostic tools managed by Octavus (web search, image generation)\n3. **Provider Tools** \u2014 Provider-specific tools executed by the provider (e.g., Anthropic\'s code execution)\n4. **Skills** \u2014 Code execution and knowledge packages (see [Skills](/docs/protocol/skills))\n\nThis page covers external tools. Built-in tools are enabled via agent config \u2014 see [Web Search](/docs/protocol/agent-config#web-search) and [Image Generation](/docs/protocol/agent-config#image-generation). For provider-specific tools, see [Provider Options](/docs/protocol/provider-options). For code execution, see [Skills](/docs/protocol/skills).\n\n## External Tools\n\nExternal tools are defined in the `tools:` section and implemented in your backend.\n\n## Defining Tools\n\n```yaml\ntools:\n get-user-account:\n description: Looking up your account information\n display: description\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\n### Tool Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------ |\n| `description` | Yes | What the tool does (shown to LLM and optionally user) |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` |\n| `parameters` | No | Input parameters the tool accepts |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Tool runs silently, user doesn\'t see it |\n| `name` | Shows tool name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams tool progress if available |\n\n## Parameters\n\nTool calls are always objects where each parameter name maps to a value. The LLM generates: `{ param1: value1, param2: value2, ... }`\n\n### Parameter Fields\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a custom type |\n| `description` | No | Describes what this parameter is for |\n| `optional` | No | If true, parameter is not required (default: false) |\n\n> **Tip**: You can use [custom types](/docs/protocol/types) for complex parameters like `type: ProductFilter` or `type: SearchOptions`.\n\n### Array Parameters\n\nFor array parameters, define a [top-level array type](/docs/protocol/types#top-level-array-types) and use it:\n\n```yaml\ntypes:\n CartItem:\n productId:\n type: string\n quantity:\n type: integer\n\n CartItemList:\n type: array\n items:\n type: CartItem\n\ntools:\n add-to-cart:\n description: Add items to cart\n parameters:\n items:\n type: CartItemList\n description: Items to add\n```\n\nThe tool receives: `{ items: [{ productId: "...", quantity: 1 }, ...] }`\n\n### Optional Parameters\n\nParameters are **required by default**. Use `optional: true` to make a parameter optional:\n\n```yaml\ntools:\n search-products:\n description: Search the product catalog\n parameters:\n query:\n type: string\n description: Search query\n\n category:\n type: string\n description: Filter by category\n optional: true\n\n maxPrice:\n type: number\n description: Maximum price filter\n optional: true\n\n inStock:\n type: boolean\n description: Only show in-stock items\n optional: true\n```\n\n## Making Tools Available\n\nTools defined in `tools:` are available. To make them usable by the LLM, add them to `agent.tools`:\n\n```yaml\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { 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, urgent\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools:\n - get-user-account\n - create-support-ticket # LLM can decide when to call these\n agentic: true\n```\n\n## Tool Invocation Modes\n\n### LLM-Decided (Agentic)\n\nThe LLM decides when to call tools based on the conversation:\n\n```yaml\nagent:\n tools: [get-user-account, create-support-ticket]\n agentic: true # Allow multiple tool calls\n maxSteps: 10 # Max tool call cycles\n```\n\n### Deterministic (Block-Based)\n\nForce tool calls at specific points in the handler:\n\n```yaml\nhandlers:\n request-human:\n # Always create a ticket when escalating\n Create support ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # From variable\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n## Tool Results\n\n### In Prompts\n\nTool results are stored in variables. Reference the variable in prompts:\n\n```markdown\n<!-- prompts/ticket-directive.md -->\n\nA support ticket has been created:\n{{TICKET}}\n\nLet the user know their ticket has been created.\n```\n\nWhen the `TICKET` variable contains an object, it\'s automatically serialized as JSON in the prompt:\n\n```\nA support ticket has been created:\n{\n "ticketId": "TKT-123ABC",\n "estimatedResponse": "24 hours"\n}\n\nLet the user know their ticket has been created.\n```\n\n> **Note**: Variables use `{{VARIABLE_NAME}}` syntax with `UPPERCASE_SNAKE_CASE`. Dot notation (like `{{TICKET.ticketId}}`) is not supported. Objects are automatically JSON-serialized.\n\n### In Variables\n\nStore tool results for later use:\n\n```yaml\nhandlers:\n request-human:\n Get account:\n block: tool-call\n tool: get-user-account\n input:\n userId: USER_ID\n output: ACCOUNT # Result stored here\n\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\n## Implementing Tools\n\nTools are implemented in your backend:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n \'get-user-account\': async (args) => {\n const userId = args.userId as string;\n const user = await db.users.findById(userId);\n\n return {\n name: user.name,\n email: user.email,\n plan: user.subscription.plan,\n createdAt: user.createdAt.toISOString(),\n };\n },\n\n \'create-support-ticket\': async (args) => {\n const ticket = await ticketService.create({\n summary: args.summary as string,\n priority: args.priority as string,\n });\n\n return {\n ticketId: ticket.id,\n estimatedResponse: getEstimatedTime(args.priority),\n };\n },\n },\n});\n```\n\n## Tool Best Practices\n\n### 1. Clear Descriptions\n\n```yaml\ntools:\n # Good - clear and specific\n get-user-account:\n description: >\n Retrieves the user\'s account information including name, email,\n subscription plan, and account creation date. Use this when the\n user asks about their account or you need to verify their identity.\n\n # Avoid - vague\n get-data:\n description: Gets some data\n```\n\n### 2. Document Constrained Values\n\n```yaml\ntools:\n create-support-ticket:\n parameters:\n priority:\n type: string\n description: Ticket priority level (low, medium, high, urgent)\n```\n',
|
|
571
|
+
excerpt: "Tools Tools extend what agents can do. Octavus supports multiple types: 1. External Tools \u2014 Defined in the protocol, implemented in your backend (this page) 2. Built-in Tools \u2014 Provider-agnostic...",
|
|
572
572
|
order: 4
|
|
573
573
|
},
|
|
574
574
|
{
|
|
@@ -594,7 +594,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
594
594
|
section: "protocol",
|
|
595
595
|
title: "Agent Config",
|
|
596
596
|
description: "Configuring the agent model and behavior.",
|
|
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| `sandboxTimeout` | No | Skill sandbox timeout in ms (default: 5 min, max: 1 hour) |\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 skills: [data-analysis] # Thread-specific skills\n imageModel: google/gemini-2.5-flash-image # Thread-specific image model\n```\n\nEach thread can have its own skills and image model. Skills referenced here must be defined in the protocol's `skills:` section. Workers use this same pattern since they don't have a global `agent:` section.\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 references: [api-guidelines] # On-demand context documents\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| `references` | No | List of references the LLM can fetch on demand |\n| `sandboxTimeout` | No | Skill sandbox timeout in ms (default: 5 min, max: 1 hour) |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `webSearch` | No | Enable built-in web search tool (provider-agnostic) |\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## References\n\nEnable on-demand context loading via reference documents:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines, error-codes]\n agentic: true\n```\n\nReferences are markdown files stored in the agent's `references/` directory. When enabled, the LLM can list available references and read their content using `octavus_reference_list` and `octavus_reference_read` tools.\n\nSee [References](/docs/protocol/references) 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## Web Search\n\nEnable the LLM to search the web for current information:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n webSearch: true\n agentic: true\n```\n\nWhen `webSearch` is enabled, the `octavus_web_search` tool becomes available. The LLM can decide when to search the web based on the conversation. Search results include source URLs that are emitted as citations in the UI.\n\nThis is a **provider-agnostic** built-in tool \u2014 it works with any LLM provider (Anthropic, Google, OpenAI, etc.). For Anthropic's own web search implementation, see [Provider Options](/docs/protocol/provider-options).\n\nUse cases:\n\n- Current events and real-time data\n- Fact verification and documentation lookups\n- Any information that may have changed since the model's training\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 skills: [data-analysis] # Thread-specific skills\n references: [escalation-policy] # Thread-specific references\n imageModel: google/gemini-2.5-flash-image # Thread-specific image model\n webSearch: true # Thread-specific web search\n```\n\nEach thread can have its own skills, references, image model, and web search setting. Skills must be defined in the protocol's `skills:` section. References must exist in the agent's `references/` directory. Workers use this same pattern since they don't have a global `agent:` section.\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 references: [support-policies] # On-demand context\n webSearch: true # Built-in web search\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",
|
|
598
598
|
excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
|
|
599
599
|
order: 7
|
|
600
600
|
},
|
|
@@ -603,7 +603,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
603
603
|
section: "protocol",
|
|
604
604
|
title: "Provider Options",
|
|
605
605
|
description: "Configuring provider-specific tools and features.",
|
|
606
|
-
content: "\n# Provider Options\n\nProvider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure.\n\n> **Note**: For provider-agnostic code execution, use [Octavus Skills](/docs/protocol/skills) instead. Octavus Skills work with any LLM provider and run in isolated sandbox environments.\n\n## Anthropic Options\n\nConfigure Anthropic-specific features when using `anthropic/*` models:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n```\n\n> **Note**: Provider options are validated against the model provider. Using `anthropic:` options with non-Anthropic models will result in a validation error.\n\n## Provider Tools\n\nProvider tools are executed server-side by the provider (Anthropic). Unlike external tools that you implement, provider tools are built-in capabilities.\n\n### Available Tools\n\n| Tool | Description |\n| ---------------- | -------------------------------------------- |\n| `web-search` | Search the web for current information |\n| `code-execution` | Execute Python/Bash in a sandboxed container |\n\n### Tool Configuration\n\n```yaml\nanthropic:\n tools:\n web-search:\n display: description # How to show in UI\n description: Searching... # Custom display text\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users during execution |\n\n### Web Search\n\nAllows the agent to search the web
|
|
606
|
+
content: "\n# Provider Options\n\nProvider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure.\n\n> **Note**: For provider-agnostic code execution, use [Octavus Skills](/docs/protocol/skills) instead. Octavus Skills work with any LLM provider and run in isolated sandbox environments.\n\n## Anthropic Options\n\nConfigure Anthropic-specific features when using `anthropic/*` models:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n```\n\n> **Note**: Provider options are validated against the model provider. Using `anthropic:` options with non-Anthropic models will result in a validation error.\n\n## Provider Tools\n\nProvider tools are executed server-side by the provider (Anthropic). Unlike external tools that you implement, provider tools are built-in capabilities.\n\n### Available Tools\n\n| Tool | Description |\n| ---------------- | -------------------------------------------- |\n| `web-search` | Search the web for current information |\n| `code-execution` | Execute Python/Bash in a sandboxed container |\n\n### Tool Configuration\n\n```yaml\nanthropic:\n tools:\n web-search:\n display: description # How to show in UI\n description: Searching... # Custom display text\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users during execution |\n\n### Web Search\n\nAllows the agent to search the web using Anthropic's built-in web search:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Looking up current information\n```\n\n> **Tip**: Octavus also provides a **provider-agnostic** web search via `webSearch: true` in the agent config. This works with any LLM provider and is the recommended approach for multi-provider agents. See [Web Search](/docs/protocol/agent-config#web-search) for details.\n\n### Code Execution\n\nEnables Python and Bash execution in a sandboxed container:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n code-execution:\n display: description\n description: Running analysis\n```\n\nUse cases:\n\n- Data analysis and calculations\n- File processing\n- Chart generation\n- Script execution\n\n> **Note**: Code execution is automatically enabled when skills are configured (skills require the container environment).\n\n## Skills\n\n> **Important**: This section covers **Anthropic's built-in skills** (provider-specific). For provider-agnostic skills that work with any LLM, see [Octavus Skills](/docs/protocol/skills).\n\nAnthropic skills are knowledge packages that give the agent specialized capabilities. They're loaded into Anthropic's code execution container at `/skills/{skill-id}/` and only work with Anthropic models.\n\n### Skill Configuration\n\n```yaml\nanthropic:\n skills:\n pdf:\n type: anthropic # 'anthropic' or 'custom'\n version: latest # Optional version\n display: description\n description: Processing PDF\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `type` | Yes | `anthropic` (built-in) or `custom` (uploaded) |\n| `version` | No | Skill version (default: `latest`) |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users |\n\n### Built-in Skills\n\nAnthropic provides several built-in skills:\n\n| Skill ID | Purpose |\n| -------- | ----------------------------------------------- |\n| `pdf` | PDF manipulation, text extraction, form filling |\n| `xlsx` | Excel spreadsheet operations and analysis |\n| `docx` | Word document creation and editing |\n| `pptx` | PowerPoint presentation creation |\n\n### Using Skills\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n skills:\n pdf:\n type: anthropic\n description: Processing your PDF\n xlsx:\n type: anthropic\n description: Analyzing spreadsheet\n```\n\nWhen skills are configured:\n\n1. Code execution is automatically enabled\n2. Skill files are loaded into the container\n3. The agent can read skill instructions and execute scripts\n\n### Custom Skills\n\nYou can create and upload custom skills to Anthropic:\n\n```yaml\nanthropic:\n skills:\n custom-analysis:\n type: custom\n version: latest\n description: Running custom analysis\n```\n\nCustom skills follow the [Agent Skills standard](https://agentskills.io) and contain:\n\n- `SKILL.md` with instructions and metadata\n- Optional `scripts/`, `references/`, and `assets/` directories\n\n### Octavus Skills vs Anthropic Skills\n\n| Feature | Anthropic Skills | Octavus Skills |\n| ----------------- | ------------------------ | ----------------------------- |\n| **Provider** | Anthropic only | Any (agnostic) |\n| **Execution** | Anthropic's container | Isolated sandbox |\n| **Configuration** | `agent.anthropic.skills` | `agent.skills` |\n| **Definition** | `anthropic:` section | `skills:` section |\n| **Use Case** | Claude-specific features | Cross-provider code execution |\n\nFor provider-agnostic code execution, use Octavus Skills defined in the protocol's `skills:` section and enabled via `agent.skills`. See [Skills](/docs/protocol/skills) for details.\n\n## Display Modes\n\nBoth tools and skills support display modes:\n\n| Mode | Behavior |\n| ------------- | ------------------------------- |\n| `hidden` | Not shown to users |\n| `name` | Shows the tool/skill name |\n| `description` | Shows the description (default) |\n| `stream` | Streams progress if available |\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input: [COMPANY_NAME, USER_ID]\n tools: [get-user-account] # External tools\n agentic: true\n thinking: medium\n\n # Anthropic-specific options\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n xlsx:\n type: anthropic\n display: description\n description: Analyzing spreadsheet\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\n## Validation\n\nThe protocol validator enforces:\n\n1. **Model match**: Provider options must match the model provider\n - `anthropic:` options require `anthropic/*` model\n - Using mismatched options results in a validation error\n\n2. **Valid tool types**: Only recognized tools are accepted\n - `web-search` and `code-execution` for Anthropic\n\n3. **Valid skill types**: Only `anthropic` or `custom` are accepted\n\n### Error Example\n\n```yaml\n# This will fail validation\nagent:\n model: openai/gpt-4o # OpenAI model\n anthropic: # Anthropic options - mismatch!\n tools:\n web-search: {}\n```\n\nError: `\"anthropic\" options require an anthropic model. Current model provider: \"openai\"`\n",
|
|
607
607
|
excerpt: "Provider Options Provider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure. > Note: For...",
|
|
608
608
|
order: 8
|
|
609
609
|
},
|
|
@@ -630,10 +630,19 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
630
630
|
section: "protocol",
|
|
631
631
|
title: "Workers",
|
|
632
632
|
description: "Defining worker agents for background and task-based execution.",
|
|
633
|
-
content: '\n# Workers\n\nWorkers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value.\n\n## When to Use Workers\n\nWorkers are ideal for:\n\n- **Background processing** \u2014 Long-running tasks that don\'t need conversation\n- **Composable tasks** \u2014 Reusable units of work called by other agents\n- **Pipelines** \u2014 Multi-step processing with structured output\n- **Parallel execution** \u2014 Tasks that can run independently\n\nUse interactive agents instead when:\n\n- **Conversation is needed** \u2014 Multi-turn dialogue with users\n- **Persistence matters** \u2014 State should survive across interactions\n- **Session context** \u2014 User context needs to persist\n\n## Worker vs Interactive\n\n| Aspect | Interactive | Worker |\n| ---------- | ---------------------------------- | ----------------------------- |\n| Structure | `triggers` + `handlers` + `agent` | `steps` + `output` |\n| LLM Config | Global `agent:` section | Per-thread via `start-thread` |\n| Invocation | Fire a named trigger | Direct execution with input |\n| Session | Persists across triggers (24h TTL) | Single execution |\n| Result | Streaming chat | Streaming + output value |\n\n## Protocol Structure\n\nWorkers use a simpler protocol structure than interactive agents:\n\n```yaml\n# Input schema - provided when worker is executed\ninput:\n TOPIC:\n type: string\n description: Topic to research\n DEPTH:\n type: string\n optional: true\n default: medium\n\n# Variables for intermediate results\nvariables:\n RESEARCH_DATA:\n type: string\n ANALYSIS:\n type: string\n description: Final analysis result\n\n# Tools available to the worker\ntools:\n web-search:\n description: Search the web\n parameters:\n query: { type: string }\n\n# Sequential execution steps\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC, DEPTH]\n tools: [web-search]\n maxSteps: 5\n\n Add research request:\n block: add-message\n thread: research\n role: user\n prompt: research-prompt\n input: [TOPIC, DEPTH]\n\n Generate research:\n block: next-message\n thread: research\n output: RESEARCH_DATA\n\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: analysis-system\n\n Add analysis request:\n block: add-message\n thread: analysis\n role: user\n prompt: analysis-prompt\n input: [RESEARCH_DATA]\n\n Generate analysis:\n block: next-message\n thread: analysis\n output: ANALYSIS\n\n# Output variable - the worker\'s return value\noutput: ANALYSIS\n```\n\n## settings.json\n\nWorkers are identified by the `format` field:\n\n```json\n{\n "slug": "research-assistant",\n "name": "Research Assistant",\n "description": "Researches topics and returns structured analysis",\n "format": "worker"\n}\n```\n\n## Key Differences\n\n### No Global Agent Config\n\nInteractive agents have a global `agent:` section that configures a main thread. Workers don\'t have this \u2014 every thread must be explicitly created via `start-thread`:\n\n```yaml\n# Interactive agent: Global config\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [tool-a, tool-b]\n\n# Worker: Each thread configured independently\nsteps:\n Start thread A:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n tools: [tool-a]\n\n Start thread B:\n block: start-thread\n thread: analysis\n model: openai/gpt-4o\n tools: [tool-b]\n```\n\nThis gives workers flexibility to use different models, tools, skills, and settings at different stages.\n\n### Steps Instead of Handlers\n\nWorkers use `steps:` instead of `handlers:`. Steps execute sequentially, like handler blocks:\n\n```yaml\n# Interactive: Handlers respond to triggers\nhandlers:\n user-message:\n Add message:\n block: add-message\n # ...\n\n# Worker: Steps execute in sequence\nsteps:\n Add message:\n block: add-message\n # ...\n```\n\n### Output Value\n\nWorkers can return an output value to the caller:\n\n```yaml\nvariables:\n RESULT:\n type: string\n\nsteps:\n # ... steps that populate RESULT ...\n\noutput: RESULT # Return this variable\'s value\n```\n\nThe `output` field references a variable declared in `variables:`. If omitted, the worker completes without returning a value.\n\n## Available Blocks\n\nWorkers support the same blocks as handlers:\n\n| Block | Purpose |\n| ------------------ | -------------------------------------------- |\n| `start-thread` | Create a named thread with LLM configuration |\n| `add-message` | Add a message to a thread |\n| `next-message` | Generate LLM response |\n| `tool-call` | Call a tool deterministically |\n| `set-resource` | Update a resource value |\n| `serialize-thread` | Convert thread to text |\n| `generate-image` | Generate an image from a prompt variable |\n\n### start-thread (Required for LLM)\n\nEvery thread must be initialized with `start-thread` before using `next-message`:\n\n```yaml\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC]\n tools: [web-search]\n thinking: medium\n maxSteps: 5\n```\n\nAll LLM configuration goes here:\n\n| Field | Description |\n| ------------- | ------------------------------------------------- |\n| `thread` | Thread name (defaults to block name) |\n| `model` | LLM model to use |\n| `system` | System prompt filename (required) |\n| `input` | Variables for system prompt |\n| `tools` | Tools available in this thread |\n| `skills` | Octavus skills available in this thread |\n| `imageModel` | Image generation model |\n| `thinking` | Extended reasoning level |\n| `temperature` | Model temperature |\n| `maxSteps` | Maximum tool call cycles (enables agentic if > 1) |\n\n## Simple Example\n\nA worker that generates a title from a summary:\n\n```yaml\n# Input\ninput:\n CONVERSATION_SUMMARY:\n type: string\n description: Summary to generate a title for\n\n# Variables\nvariables:\n TITLE:\n type: string\n description: The generated title\n\n# Steps\nsteps:\n Start title thread:\n block: start-thread\n thread: title-gen\n model: anthropic/claude-sonnet-4-5\n system: title-system\n\n Add title request:\n block: add-message\n thread: title-gen\n role: user\n prompt: title-request\n input: [CONVERSATION_SUMMARY]\n\n Generate title:\n block: next-message\n thread: title-gen\n output: TITLE\n display: stream\n\n# Output\noutput: TITLE\n```\n\n## Advanced Example\n\nA worker with multiple threads, tools, and agentic behavior:\n\n```yaml\ninput:\n USER_MESSAGE:\n type: string\n description: The user\'s message to respond to\n USER_ID:\n type: string\n description: User ID for account lookups\n optional: true\n\ntools:\n get-user-account:\n description: Looking up account information\n parameters:\n userId: { type: string }\n create-support-ticket:\n description: Creating a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string }\n\nvariables:\n ASSISTANT_RESPONSE:\n type: string\n CHAT_TRANSCRIPT:\n type: string\n CONVERSATION_SUMMARY:\n type: string\n\nsteps:\n # Thread 1: Chat with agentic tool calling\n Start chat thread:\n block: start-thread\n thread: chat\n model: anthropic/claude-sonnet-4-5\n system: chat-system\n input: [USER_ID]\n tools: [get-user-account, create-support-ticket]\n thinking: medium\n maxSteps: 5\n\n Add user message:\n block: add-message\n thread: chat\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Generate response:\n block: next-message\n thread: chat\n output: ASSISTANT_RESPONSE\n display: stream\n\n # Serialize for summary\n Save conversation:\n block: serialize-thread\n thread: chat\n output: CHAT_TRANSCRIPT\n\n # Thread 2: Summary generation\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n system: summary-system\n thinking: low\n\n Add summary request:\n block: add-message\n thread: summary\n role: user\n prompt: summary-request\n input: [CHAT_TRANSCRIPT]\n\n Generate summary:\n block: next-message\n thread: summary\n output: CONVERSATION_SUMMARY\n display: stream\n\noutput: CONVERSATION_SUMMARY\n```\n\n## Skills and Image Generation\n\nWorkers can use Octavus skills and image generation, configured per-thread via `start-thread`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generate QR codes\n\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n imageModel: google/gemini-2.5-flash-image\n maxSteps: 10\n```\n\nWorkers define their own skills independently -- they don\'t inherit skills from a parent interactive agent. Each thread gets its own sandbox scoped to only its listed skills.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Tool Handling\n\nWorkers support the same tool handling as interactive agents:\n\n- **Server tools** \u2014 Handled by tool handlers you provide\n- **Client tools** \u2014 Pause execution, return tool request to caller\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n\n// Streaming: observe events in real-time\nconst events = client.workers.execute(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n```\n\nSee [Server SDK Workers](/docs/server-sdk/workers) for tool handling details.\n\n## Stream Events\n\nWorkers emit the same events as interactive agents, plus worker-specific events:\n\n| Event | Description |\n| --------------- | ---------------------------------- |\n| `worker-start` | Worker execution begins |\n| `worker-result` | Worker completes (includes output) |\n\nAll standard events (text-delta, tool calls, etc.) are also emitted.\n\n## Calling Workers from Interactive Agents\n\nInteractive agents can call workers in two ways:\n\n1. **Deterministically** \u2014 Using the `run-worker` block\n2. **Agentically** \u2014 LLM calls worker as a tool\n\n### Worker Declaration\n\nFirst, declare workers in your interactive agent\'s protocol:\n\n```yaml\nworkers:\n generate-title:\n description: Generating conversation title\n display: description\n research-assistant:\n description: Researching topic\n display: stream\n tools:\n search: web-search # Map worker tool \u2192 parent tool\n```\n\n### run-worker Block\n\nCall a worker deterministically from a handler:\n\n```yaml\nhandlers:\n request-human:\n Generate title:\n block: run-worker\n worker: generate-title\n input:\n CONVERSATION_SUMMARY: SUMMARY\n output: CONVERSATION_TITLE\n```\n\n### LLM Tool Invocation\n\nMake workers available to the LLM:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n workers: [generate-title, research-assistant]\n agentic: true\n```\n\nThe LLM can then call workers as tools during conversation.\n\n### Display Modes\n\nControl how worker execution appears to users:\n\n| Mode | Behavior |\n| ------------- | --------------------------------- |\n| `hidden` | Worker runs silently |\n| `name` | Shows worker name |\n| `description` | Shows description text |\n| `stream` | Streams all worker events to user |\n\n### Tool Mapping\n\nMap parent tools to worker tools when the worker needs access to your tool handlers:\n\n```yaml\nworkers:\n research-assistant:\n description: Research topics\n tools:\n search: web-search # Worker\'s "search" \u2192 parent\'s "web-search"\n```\n\nWhen the worker calls its `search` tool, your `web-search` handler executes.\n\n## Next Steps\n\n- [Server SDK Workers](/docs/server-sdk/workers) \u2014 Executing workers from code\n- [Handlers](/docs/protocol/handlers) \u2014 Block reference for steps\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n',
|
|
633
|
+
content: '\n# Workers\n\nWorkers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value.\n\n## When to Use Workers\n\nWorkers are ideal for:\n\n- **Background processing** \u2014 Long-running tasks that don\'t need conversation\n- **Composable tasks** \u2014 Reusable units of work called by other agents\n- **Pipelines** \u2014 Multi-step processing with structured output\n- **Parallel execution** \u2014 Tasks that can run independently\n\nUse interactive agents instead when:\n\n- **Conversation is needed** \u2014 Multi-turn dialogue with users\n- **Persistence matters** \u2014 State should survive across interactions\n- **Session context** \u2014 User context needs to persist\n\n## Worker vs Interactive\n\n| Aspect | Interactive | Worker |\n| ---------- | ---------------------------------- | ----------------------------- |\n| Structure | `triggers` + `handlers` + `agent` | `steps` + `output` |\n| LLM Config | Global `agent:` section | Per-thread via `start-thread` |\n| Invocation | Fire a named trigger | Direct execution with input |\n| Session | Persists across triggers (24h TTL) | Single execution |\n| Result | Streaming chat | Streaming + output value |\n\n## Protocol Structure\n\nWorkers use a simpler protocol structure than interactive agents:\n\n```yaml\n# Input schema - provided when worker is executed\ninput:\n TOPIC:\n type: string\n description: Topic to research\n DEPTH:\n type: string\n optional: true\n default: medium\n\n# Variables for intermediate results\nvariables:\n RESEARCH_DATA:\n type: string\n ANALYSIS:\n type: string\n description: Final analysis result\n\n# Tools available to the worker\ntools:\n web-search:\n description: Search the web\n parameters:\n query: { type: string }\n\n# Sequential execution steps\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC, DEPTH]\n tools: [web-search]\n maxSteps: 5\n\n Add research request:\n block: add-message\n thread: research\n role: user\n prompt: research-prompt\n input: [TOPIC, DEPTH]\n\n Generate research:\n block: next-message\n thread: research\n output: RESEARCH_DATA\n\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: analysis-system\n\n Add analysis request:\n block: add-message\n thread: analysis\n role: user\n prompt: analysis-prompt\n input: [RESEARCH_DATA]\n\n Generate analysis:\n block: next-message\n thread: analysis\n output: ANALYSIS\n\n# Output variable - the worker\'s return value\noutput: ANALYSIS\n```\n\n## settings.json\n\nWorkers are identified by the `format` field:\n\n```json\n{\n "slug": "research-assistant",\n "name": "Research Assistant",\n "description": "Researches topics and returns structured analysis",\n "format": "worker"\n}\n```\n\n## Key Differences\n\n### No Global Agent Config\n\nInteractive agents have a global `agent:` section that configures a main thread. Workers don\'t have this \u2014 every thread must be explicitly created via `start-thread`:\n\n```yaml\n# Interactive agent: Global config\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [tool-a, tool-b]\n\n# Worker: Each thread configured independently\nsteps:\n Start thread A:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n tools: [tool-a]\n\n Start thread B:\n block: start-thread\n thread: analysis\n model: openai/gpt-4o\n tools: [tool-b]\n```\n\nThis gives workers flexibility to use different models, tools, skills, and settings at different stages.\n\n### Steps Instead of Handlers\n\nWorkers use `steps:` instead of `handlers:`. Steps execute sequentially, like handler blocks:\n\n```yaml\n# Interactive: Handlers respond to triggers\nhandlers:\n user-message:\n Add message:\n block: add-message\n # ...\n\n# Worker: Steps execute in sequence\nsteps:\n Add message:\n block: add-message\n # ...\n```\n\n### Output Value\n\nWorkers can return an output value to the caller:\n\n```yaml\nvariables:\n RESULT:\n type: string\n\nsteps:\n # ... steps that populate RESULT ...\n\noutput: RESULT # Return this variable\'s value\n```\n\nThe `output` field references a variable declared in `variables:`. If omitted, the worker completes without returning a value.\n\n## Available Blocks\n\nWorkers support the same blocks as handlers:\n\n| Block | Purpose |\n| ------------------ | -------------------------------------------- |\n| `start-thread` | Create a named thread with LLM configuration |\n| `add-message` | Add a message to a thread |\n| `next-message` | Generate LLM response |\n| `tool-call` | Call a tool deterministically |\n| `set-resource` | Update a resource value |\n| `serialize-thread` | Convert thread to text |\n| `generate-image` | Generate an image from a prompt variable |\n\n### start-thread (Required for LLM)\n\nEvery thread must be initialized with `start-thread` before using `next-message`:\n\n```yaml\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC]\n tools: [web-search]\n thinking: medium\n maxSteps: 5\n```\n\nAll LLM configuration goes here:\n\n| Field | Description |\n| ------------- | ------------------------------------------------- |\n| `thread` | Thread name (defaults to block name) |\n| `model` | LLM model to use |\n| `system` | System prompt filename (required) |\n| `input` | Variables for system prompt |\n| `tools` | Tools available in this thread |\n| `skills` | Octavus skills available in this thread |\n| `imageModel` | Image generation model |\n| `webSearch` | Enable built-in web search tool |\n| `thinking` | Extended reasoning level |\n| `temperature` | Model temperature |\n| `maxSteps` | Maximum tool call cycles (enables agentic if > 1) |\n\n## Simple Example\n\nA worker that generates a title from a summary:\n\n```yaml\n# Input\ninput:\n CONVERSATION_SUMMARY:\n type: string\n description: Summary to generate a title for\n\n# Variables\nvariables:\n TITLE:\n type: string\n description: The generated title\n\n# Steps\nsteps:\n Start title thread:\n block: start-thread\n thread: title-gen\n model: anthropic/claude-sonnet-4-5\n system: title-system\n\n Add title request:\n block: add-message\n thread: title-gen\n role: user\n prompt: title-request\n input: [CONVERSATION_SUMMARY]\n\n Generate title:\n block: next-message\n thread: title-gen\n output: TITLE\n display: stream\n\n# Output\noutput: TITLE\n```\n\n## Advanced Example\n\nA worker with multiple threads, tools, and agentic behavior:\n\n```yaml\ninput:\n USER_MESSAGE:\n type: string\n description: The user\'s message to respond to\n USER_ID:\n type: string\n description: User ID for account lookups\n optional: true\n\ntools:\n get-user-account:\n description: Looking up account information\n parameters:\n userId: { type: string }\n create-support-ticket:\n description: Creating a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string }\n\nvariables:\n ASSISTANT_RESPONSE:\n type: string\n CHAT_TRANSCRIPT:\n type: string\n CONVERSATION_SUMMARY:\n type: string\n\nsteps:\n # Thread 1: Chat with agentic tool calling\n Start chat thread:\n block: start-thread\n thread: chat\n model: anthropic/claude-sonnet-4-5\n system: chat-system\n input: [USER_ID]\n tools: [get-user-account, create-support-ticket]\n thinking: medium\n maxSteps: 5\n\n Add user message:\n block: add-message\n thread: chat\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Generate response:\n block: next-message\n thread: chat\n output: ASSISTANT_RESPONSE\n display: stream\n\n # Serialize for summary\n Save conversation:\n block: serialize-thread\n thread: chat\n output: CHAT_TRANSCRIPT\n\n # Thread 2: Summary generation\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n system: summary-system\n thinking: low\n\n Add summary request:\n block: add-message\n thread: summary\n role: user\n prompt: summary-request\n input: [CHAT_TRANSCRIPT]\n\n Generate summary:\n block: next-message\n thread: summary\n output: CONVERSATION_SUMMARY\n display: stream\n\noutput: CONVERSATION_SUMMARY\n```\n\n## Skills, Image Generation, and Web Search\n\nWorkers can use Octavus skills, image generation, and web search, configured per-thread via `start-thread`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generate QR codes\n\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n imageModel: google/gemini-2.5-flash-image\n webSearch: true\n maxSteps: 10\n```\n\nWorkers define their own skills independently -- they don\'t inherit skills from a parent interactive agent. Each thread gets its own sandbox scoped to only its listed skills.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Tool Handling\n\nWorkers support the same tool handling as interactive agents:\n\n- **Server tools** \u2014 Handled by tool handlers you provide\n- **Client tools** \u2014 Pause execution, return tool request to caller\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n\n// Streaming: observe events in real-time\nconst events = client.workers.execute(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n```\n\nSee [Server SDK Workers](/docs/server-sdk/workers) for tool handling details.\n\n## Stream Events\n\nWorkers emit the same events as interactive agents, plus worker-specific events:\n\n| Event | Description |\n| --------------- | ---------------------------------- |\n| `worker-start` | Worker execution begins |\n| `worker-result` | Worker completes (includes output) |\n\nAll standard events (text-delta, tool calls, etc.) are also emitted.\n\n## Calling Workers from Interactive Agents\n\nInteractive agents can call workers in two ways:\n\n1. **Deterministically** \u2014 Using the `run-worker` block\n2. **Agentically** \u2014 LLM calls worker as a tool\n\n### Worker Declaration\n\nFirst, declare workers in your interactive agent\'s protocol:\n\n```yaml\nworkers:\n generate-title:\n description: Generating conversation title\n display: description\n research-assistant:\n description: Researching topic\n display: stream\n tools:\n search: web-search # Map worker tool \u2192 parent tool\n```\n\n### run-worker Block\n\nCall a worker deterministically from a handler:\n\n```yaml\nhandlers:\n request-human:\n Generate title:\n block: run-worker\n worker: generate-title\n input:\n CONVERSATION_SUMMARY: SUMMARY\n output: CONVERSATION_TITLE\n```\n\n### LLM Tool Invocation\n\nMake workers available to the LLM:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n workers: [generate-title, research-assistant]\n agentic: true\n```\n\nThe LLM can then call workers as tools during conversation.\n\n### Display Modes\n\nControl how worker execution appears to users:\n\n| Mode | Behavior |\n| ------------- | --------------------------------- |\n| `hidden` | Worker runs silently |\n| `name` | Shows worker name |\n| `description` | Shows description text |\n| `stream` | Streams all worker events to user |\n\n### Tool Mapping\n\nMap parent tools to worker tools when the worker needs access to your tool handlers:\n\n```yaml\nworkers:\n research-assistant:\n description: Research topics\n tools:\n search: web-search # Worker\'s "search" \u2192 parent\'s "web-search"\n```\n\nWhen the worker calls its `search` tool, your `web-search` handler executes.\n\n## Next Steps\n\n- [Server SDK Workers](/docs/server-sdk/workers) \u2014 Executing workers from code\n- [Handlers](/docs/protocol/handlers) \u2014 Block reference for steps\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n',
|
|
634
634
|
excerpt: "Workers Workers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value. When to...",
|
|
635
635
|
order: 11
|
|
636
636
|
},
|
|
637
|
+
{
|
|
638
|
+
slug: "protocol/references",
|
|
639
|
+
section: "protocol",
|
|
640
|
+
title: "References",
|
|
641
|
+
description: "Using references for on-demand context loading in agents.",
|
|
642
|
+
content: "\n# References\n\nReferences are markdown documents that agents can fetch on demand. Instead of loading everything into the system prompt upfront, references let the agent decide what context it needs and load it when relevant.\n\n## Overview\n\nReferences are useful for:\n\n- **Large context** \u2014 Documents too long to include in every system prompt\n- **Selective loading** \u2014 Let the agent decide which context is relevant\n- **Shared knowledge** \u2014 Reusable documents across threads\n\n### How References Work\n\n1. **Definition**: Reference files live in the `references/` directory alongside your agent\n2. **Configuration**: List available references in `agent.references` or `start-thread.references`\n3. **Discovery**: The agent sees reference names and descriptions in its system prompt\n4. **Fetching**: The agent calls reference tools to read the full content when needed\n\n## Creating References\n\nEach reference is a markdown file with YAML frontmatter in the `references/` directory:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json\n\u251C\u2500\u2500 protocol.yaml\n\u251C\u2500\u2500 prompts/\n\u2502 \u2514\u2500\u2500 system.md\n\u2514\u2500\u2500 references/\n \u251C\u2500\u2500 api-guidelines.md\n \u2514\u2500\u2500 error-codes.md\n```\n\n### Reference Format\n\n```markdown\n---\ndescription: >\n API design guidelines including naming conventions,\n error handling patterns, and pagination standards.\n---\n\n# API Guidelines\n\n## Naming Conventions\n\nUse lowercase with dashes for URL paths...\n\n## Error Handling\n\nAll errors return a standard error envelope...\n```\n\nThe `description` field is required. It tells the agent what the reference contains so it can decide when to fetch it.\n\n### Naming Convention\n\nReference filenames use `lowercase-with-dashes`:\n\n- `api-guidelines.md`\n- `error-codes.md`\n- `coding-standards.md`\n\nThe filename (without `.md`) becomes the reference name used in the protocol.\n\n## Enabling References\n\nAfter creating reference files, specify which references are available in the protocol.\n\n### Interactive Agents\n\nList references in `agent.references`:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines, error-codes]\n agentic: true\n```\n\n### Workers and Named Threads\n\nList references per-thread in `start-thread.references`:\n\n```yaml\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines]\n maxSteps: 10\n```\n\nDifferent threads can have different references.\n\n## Reference Tools\n\nWhen references are enabled, the agent has access to two tools:\n\n| Tool | Purpose |\n| ------------------------ | ----------------------------------------------- |\n| `octavus_reference_list` | List all available references with descriptions |\n| `octavus_reference_read` | Read the full content of a specific reference |\n\nThe agent also sees reference names and descriptions in its system prompt, so it knows what's available without calling `octavus_reference_list`.\n\n## Example\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [review-pull-request]\n references: [coding-standards, api-guidelines]\n agentic: true\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond:\n block: next-message\n```\n\nWith `references/coding-standards.md`:\n\n```markdown\n---\ndescription: >\n Team coding standards including naming conventions,\n code organization, and review checklist.\n---\n\n# Coding Standards\n\n## Naming Conventions\n\n- Files: kebab-case\n- Variables: camelCase\n- Constants: UPPER_SNAKE_CASE\n ...\n```\n\nWhen a user asks the agent to review code, the agent will:\n\n1. See \"coding-standards\" and \"api-guidelines\" in its system prompt\n2. Decide which references are relevant to the review\n3. Call `octavus_reference_read` to load the relevant reference\n4. Use the loaded context to provide an informed review\n\n## Validation\n\nThe CLI and platform validate references during sync and deployment:\n\n- **Undefined references** \u2014 Referencing a name that doesn't have a matching file in `references/`\n- **Unused references** \u2014 A reference file exists but isn't listed in any `agent.references` or `start-thread.references`\n- **Invalid names** \u2014 Names that don't follow the `lowercase-with-dashes` convention\n- **Missing description** \u2014 Reference files without the required `description` in frontmatter\n\n## References vs Skills\n\n| Aspect | References | Skills |\n| ------------- | ----------------------------- | ------------------------------- |\n| **Purpose** | On-demand context documents | Code execution and file output |\n| **Content** | Markdown text | Documentation + scripts |\n| **Execution** | Synchronous text retrieval | Sandboxed code execution (E2B) |\n| **Scope** | Per-agent (stored with agent) | Per-organization (shared) |\n| **Tools** | List and read (2 tools) | Read, list, run, code (6 tools) |\n\nUse **references** when the agent needs access to text-based knowledge. Use **skills** when the agent needs to execute code or generate files.\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring references in agent settings\n- [Skills](/docs/protocol/skills) \u2014 Code execution and knowledge packages\n- [Workers](/docs/protocol/workers) \u2014 Using references in worker agents\n",
|
|
643
|
+
excerpt: "References References are markdown documents that agents can fetch on demand. Instead of loading everything into the system prompt upfront, references let the agent decide what context it needs and...",
|
|
644
|
+
order: 12
|
|
645
|
+
},
|
|
637
646
|
{
|
|
638
647
|
slug: "api-reference/overview",
|
|
639
648
|
section: "api-reference",
|
|
@@ -657,8 +666,8 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
657
666
|
section: "api-reference",
|
|
658
667
|
title: "Agents",
|
|
659
668
|
description: "Agent management API endpoints.",
|
|
660
|
-
content: '\n# Agents API\n\nManage agent definitions including protocols and
|
|
661
|
-
excerpt: "Agents API Manage agent definitions including protocols and
|
|
669
|
+
content: '\n# Agents API\n\nManage agent definitions including protocols, prompts, and references.\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 "references": [\n {\n "name": "api-guidelines",\n "description": "API design guidelines and conventions",\n "content": "# API Guidelines\\n\\nUse lowercase with dashes..."\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 "references": [\n {\n "name": "api-guidelines",\n "description": "API design guidelines and conventions",\n "content": "# API Guidelines\\n..."\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| `references` | array | No | Reference documents (name, description, content) |\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 "references": [\n {\n "name": "api-guidelines",\n "description": "Updated description",\n "content": "Updated content..."\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',
|
|
670
|
+
excerpt: "Agents API Manage agent definitions including protocols, prompts, and references. Permissions | Endpoint | Method | Permission Required | | ---------------------- | ------ |...",
|
|
662
671
|
order: 3
|
|
663
672
|
},
|
|
664
673
|
{
|
|
@@ -738,7 +747,7 @@ var sections_default = [
|
|
|
738
747
|
section: "server-sdk",
|
|
739
748
|
title: "Overview",
|
|
740
749
|
description: "Introduction to the Octavus Server SDK for backend integration.",
|
|
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.
|
|
750
|
+
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.12.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### Workers\n\nExecute worker agents for task-based processing:\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(agentId, {\n TOPIC: 'AI safety',\n});\n\n// Streaming: observe events in real-time\nfor await (const event of client.workers.execute(agentId, input)) {\n // Handle stream events\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",
|
|
742
751
|
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...",
|
|
743
752
|
order: 1
|
|
744
753
|
},
|
|
@@ -774,7 +783,7 @@ var sections_default = [
|
|
|
774
783
|
section: "server-sdk",
|
|
775
784
|
title: "CLI",
|
|
776
785
|
description: "Command-line interface for validating and syncing agent definitions.",
|
|
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.
|
|
786
|
+
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.12.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\u251C\u2500\u2500 prompts/ # Optional: Prompt templates\n\u2502 \u251C\u2500\u2500 system.md\n\u2502 \u2514\u2500\u2500 user-message.md\n\u2514\u2500\u2500 references/ # Optional: Reference documents\n \u2514\u2500\u2500 api-guidelines.md\n```\n\n### references/\n\nReference files are markdown documents with YAML frontmatter containing a `description`. The agent can fetch these on demand during execution. See [References](/docs/protocol/references) for details.\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',
|
|
778
787
|
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 ...",
|
|
779
788
|
order: 5
|
|
780
789
|
},
|
|
@@ -809,7 +818,7 @@ var sections_default = [
|
|
|
809
818
|
section: "client-sdk",
|
|
810
819
|
title: "Overview",
|
|
811
820
|
description: "Introduction to the Octavus Client SDKs for building chat interfaces.",
|
|
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.10.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.10.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",
|
|
821
|
+
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.12.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.12.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",
|
|
813
822
|
excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
|
|
814
823
|
order: 1
|
|
815
824
|
},
|
|
@@ -1236,7 +1245,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1236
1245
|
section: "client-sdk",
|
|
1237
1246
|
title: "File Uploads",
|
|
1238
1247
|
description: "Uploading images and files for vision models and document processing.",
|
|
1239
|
-
content: "\n# File Uploads\n\nThe Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing.\n\n## Overview\n\nFile uploads follow a two-step flow:\n\n1. **Request upload URLs** from the platform via your backend\n2. **Upload files directly to S3** using presigned URLs\n3. **Send file references** with your message\n\nThis architecture keeps your API key secure on the server while enabling fast, direct uploads.\n\n## Setup\n\n### Backend: Upload URLs Endpoint\n\nCreate an endpoint that proxies upload URL requests to the Octavus platform:\n\n```typescript\n// app/api/upload-urls/route.ts (Next.js)\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { sessionId, files } = await request.json();\n\n // Get presigned URLs from Octavus\n const result = await octavus.files.getUploadUrls(sessionId, files);\n\n return NextResponse.json(result);\n}\n```\n\n### Client: Configure File Uploads\n\nPass `requestUploadUrls` to the chat hook:\n\n```tsx\nimport { useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\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 // Request upload URLs from your backend\n const requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const response = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return response.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n // ...\n}\n```\n\n## Uploading Files\n\n### Method 1: Upload Before Sending\n\nFor the best UX (showing upload progress), upload files first, then send:\n\n```tsx\nimport { useState, useRef } from 'react';\nimport type { FileReference } from '@octavus/react';\n\nfunction ChatInput({ sessionId }: { sessionId: string }) {\n const [pendingFiles, setPendingFiles] = useState<FileReference[]>([]);\n const [uploading, setUploading] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n async function handleFileSelect(event: React.ChangeEvent<HTMLInputElement>) {\n const files = event.target.files;\n if (!files?.length) return;\n\n setUploading(true);\n try {\n // Upload files with progress tracking\n const fileRefs = await uploadFiles(files, (fileIndex, progress) => {\n console.log(`File ${fileIndex}: ${progress}%`);\n });\n setPendingFiles((prev) => [...prev, ...fileRefs]);\n } finally {\n setUploading(false);\n }\n }\n\n async function handleSend(message: string) {\n await send(\n 'user-message',\n {\n USER_MESSAGE: message,\n FILES: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n {\n userMessage: {\n content: message,\n files: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n },\n );\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* File preview */}\n {pendingFiles.map((file) => (\n <img key={file.id} src={file.url} alt={file.filename} className=\"h-16\" />\n ))}\n\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*,.pdf\"\n multiple\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n\n <button onClick={() => fileInputRef.current?.click()} disabled={uploading}>\n {uploading ? 'Uploading...' : 'Attach'}\n </button>\n </div>\n );\n}\n```\n\n### Method 2: Upload on Send (Automatic)\n\nFor simpler implementations, pass `File` objects directly:\n\n```tsx\nasync function handleSend(message: string, files?: File[]) {\n await send(\n 'user-message',\n { USER_MESSAGE: message, FILES: files },\n { userMessage: { content: message, files } },\n );\n}\n```\n\nThe SDK automatically uploads the files before sending. Note: This doesn't provide upload progress.\n\n## FileReference Type\n\nFile references contain metadata and URLs:\n\n```typescript\ninterface FileReference {\n /** Unique file ID (platform-generated) */\n id: string;\n /** IANA media type (e.g., 'image/png', 'application/pdf') */\n mediaType: string;\n /** Presigned download URL (S3) */\n url: string;\n /** Original filename */\n filename?: string;\n /** File size in bytes */\n size?: number;\n}\n```\n\n## Protocol Integration\n\nTo accept files in your agent protocol, use the `file[]` type:\n\n```yaml\ntriggers:\n user-message:\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n FILES:\n type: file[]\n optional: true\n description: User-attached images for vision analysis\n\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input:\n - USER_MESSAGE\n files:\n - FILES # Attach files to the message\n display: hidden\n\n Respond to user:\n block: next-message\n```\n\nThe `file` type is a built-in type representing uploaded files. Use `file[]` for arrays of files.\n\n## Supported File Types\n\n| Type | 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## File 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\n## Rendering User Files\n\nUser-uploaded files appear as `UIFilePart` in user messages:\n\n```tsx\nfunction UserMessage({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'file') {\n if (part.mediaType.startsWith('image/')) {\n return (\n <img\n key={i}\n src={part.url}\n alt={part.filename || 'Uploaded image'}\n className=\"max-h-48 rounded-lg\"\n />\n );\n }\n return (\n <a key={i} href={part.url} className=\"text-blue-500\">\n \u{1F4C4} {part.filename}\n </a>\n );\n }\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## Server SDK: Files API\n\nThe Server SDK provides direct access to the Files API:\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// Get presigned upload URLs\nconst { files } = await client.files.getUploadUrls(sessionId, [\n { filename: 'photo.jpg', mediaType: 'image/jpeg', size: 102400 },\n { filename: 'doc.pdf', mediaType: 'application/pdf', size: 204800 },\n]);\n\n// files[0].id - Use in FileReference\n// files[0].uploadUrl - PUT to this URL to upload\n// files[0].downloadUrl - Use as FileReference.url\n```\n\n## Complete Example\n\nHere's a full chat input component with file upload:\n\n```tsx\n'use client';\n\nimport { useState, useRef, useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport, type FileReference } from '@octavus/react';\n\ninterface PendingFile {\n file: File;\n id: string;\n status: 'uploading' | 'done' | 'error';\n progress: number;\n fileRef?: FileReference;\n error?: string;\n}\n\nexport function Chat({ sessionId }: { sessionId: string }) {\n const [input, setInput] = useState('');\n const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const fileIdCounter = useRef(0);\n\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 requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const res = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return res.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n const isUploading = pendingFiles.some((f) => f.status === 'uploading');\n const hasErrors = pendingFiles.some((f) => f.status === 'error');\n const allReady = pendingFiles.every((f) => f.status === 'done');\n\n async function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const files = Array.from(e.target.files ?? []);\n if (!files.length) return;\n e.target.value = '';\n\n const newPending: PendingFile[] = files.map((file) => ({\n file,\n id: `pending-${++fileIdCounter.current}`,\n status: 'uploading',\n progress: 0,\n }));\n\n setPendingFiles((prev) => [...prev, ...newPending]);\n\n for (const pending of newPending) {\n try {\n const [fileRef] = await uploadFiles([pending.file], (_, progress) => {\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, progress } : f)),\n );\n });\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, status: 'done', fileRef } : f)),\n );\n } catch (err) {\n setPendingFiles((prev) =>\n prev.map((f) =>\n f.id === pending.id ? { ...f, status: 'error', error: String(err) } : f,\n ),\n );\n }\n }\n }\n\n async function handleSubmit() {\n if ((!input.trim() && !pendingFiles.length) || !allReady) return;\n\n const fileRefs = pendingFiles.filter((f) => f.fileRef).map((f) => f.fileRef!);\n\n await send(\n 'user-message',\n {\n USER_MESSAGE: input,\n FILES: fileRefs.length > 0 ? fileRefs : undefined,\n },\n {\n userMessage: {\n content: input,\n files: fileRefs.length > 0 ? fileRefs : undefined,\n },\n },\n );\n\n setInput('');\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* Messages */}\n {messages.map((msg) => (\n <div key={msg.id}>{/* ... render message */}</div>\n ))}\n\n {/* Pending files */}\n {pendingFiles.length > 0 && (\n <div className=\"flex gap-2\">\n {pendingFiles.map((f) => (\n <div key={f.id} className=\"relative\">\n <img\n src={URL.createObjectURL(f.file)}\n alt={f.file.name}\n className=\"h-16 w-16 object-cover rounded\"\n />\n {f.status === 'uploading' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/50\">\n <span className=\"text-white text-xs\">{f.progress}%</span>\n </div>\n )}\n <button\n onClick={() => setPendingFiles((prev) => prev.filter((p) => p.id !== f.id))}\n className=\"absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5\"\n >\n \xD7\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input */}\n <div className=\"flex gap-2\">\n <input type=\"file\" ref={fileInputRef} onChange={handleFileSelect} hidden />\n <button onClick={() => fileInputRef.current?.click()}>\u{1F4CE}</button>\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1\"\n />\n <button onClick={handleSubmit} disabled={isUploading || hasErrors}>\n {isUploading ? 'Uploading...' : 'Send'}\n </button>\n </div>\n </div>\n );\n}\n```\n",
|
|
1248
|
+
content: "\n# File Uploads\n\nThe Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing.\n\n## Overview\n\nFile uploads follow a two-step flow:\n\n1. **Request upload URLs** from the platform via your backend\n2. **Upload files directly to S3** using presigned URLs\n3. **Send file references** with your message\n\nThis architecture keeps your API key secure on the server while enabling fast, direct uploads.\n\n## Setup\n\n### Backend: Upload URLs Endpoint\n\nCreate an endpoint that proxies upload URL requests to the Octavus platform:\n\n```typescript\n// app/api/upload-urls/route.ts (Next.js)\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { sessionId, files } = await request.json();\n\n // Get presigned URLs from Octavus\n const result = await octavus.files.getUploadUrls(sessionId, files);\n\n return NextResponse.json(result);\n}\n```\n\n### Client: Configure File Uploads\n\nPass `requestUploadUrls` to the chat hook:\n\n```tsx\nimport { useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\nfunction Chat({ sessionId }: { sessionId: string }) {\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 // Request upload URLs from your backend\n const requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const response = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return response.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n // Optional: configure upload timeout and retry behavior\n uploadOptions: {\n timeoutMs: 60_000, // Per-file timeout (default: 60s, set to 0 to disable)\n maxRetries: 2, // Retry attempts on transient failures (default: 2)\n retryDelayMs: 1_000, // Delay between retries (default: 1s)\n },\n });\n\n // ...\n}\n```\n\n## Uploading Files\n\n### Method 1: Upload Before Sending\n\nFor the best UX (showing upload progress), upload files first, then send:\n\n```tsx\nimport { useState, useRef } from 'react';\nimport type { FileReference } from '@octavus/react';\n\nfunction ChatInput({ sessionId }: { sessionId: string }) {\n const [pendingFiles, setPendingFiles] = useState<FileReference[]>([]);\n const [uploading, setUploading] = useState(false);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n async function handleFileSelect(event: React.ChangeEvent<HTMLInputElement>) {\n const files = event.target.files;\n if (!files?.length) return;\n\n setUploading(true);\n try {\n // Upload files with progress tracking\n const fileRefs = await uploadFiles(files, (fileIndex, progress) => {\n console.log(`File ${fileIndex}: ${progress}%`);\n });\n setPendingFiles((prev) => [...prev, ...fileRefs]);\n } finally {\n setUploading(false);\n }\n }\n\n async function handleSend(message: string) {\n await send(\n 'user-message',\n {\n USER_MESSAGE: message,\n FILES: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n {\n userMessage: {\n content: message,\n files: pendingFiles.length > 0 ? pendingFiles : undefined,\n },\n },\n );\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* File preview */}\n {pendingFiles.map((file) => (\n <img key={file.id} src={file.url} alt={file.filename} className=\"h-16\" />\n ))}\n\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*,.pdf\"\n multiple\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n\n <button onClick={() => fileInputRef.current?.click()} disabled={uploading}>\n {uploading ? 'Uploading...' : 'Attach'}\n </button>\n </div>\n );\n}\n```\n\n### Method 2: Upload on Send (Automatic)\n\nFor simpler implementations, pass `File` objects directly:\n\n```tsx\nasync function handleSend(message: string, files?: File[]) {\n await send(\n 'user-message',\n { USER_MESSAGE: message, FILES: files },\n { userMessage: { content: message, files } },\n );\n}\n```\n\nThe SDK automatically uploads the files before sending. Note: This doesn't provide upload progress.\n\n## Upload Reliability\n\nUploads include built-in timeout and retry logic for handling transient failures (network errors, server issues, mobile network switches).\n\n**Default behavior:**\n\n- **Timeout**: 60 seconds per file \u2014 prevents uploads from hanging on stalled connections\n- **Retries**: 2 automatic retries on transient failures (network errors, 5xx, 429)\n- **Retry delay**: 1 second between retries\n- **Non-retryable errors** (4xx like 403, 404) fail immediately without retrying\n\nOnly the S3 upload is retried \u2014 the presigned URL stays valid for 15 minutes. On retry, the progress callback resets to 0%.\n\nConfigure via `uploadOptions`:\n\n```typescript\nconst { send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n uploadOptions: {\n timeoutMs: 120_000, // 2 minutes for large files\n maxRetries: 3,\n retryDelayMs: 2_000,\n },\n});\n```\n\nTo disable timeout or retries:\n\n```typescript\nuploadOptions: {\n timeoutMs: 0, // No timeout\n maxRetries: 0, // No retries\n}\n```\n\n### Using `OctavusChat` Directly\n\nWhen using the `OctavusChat` class directly (without the React hook), pass `uploadOptions` in the constructor:\n\n```typescript\nconst chat = new OctavusChat({\n transport,\n requestUploadUrls,\n uploadOptions: { timeoutMs: 120_000, maxRetries: 3 },\n});\n```\n\n## FileReference Type\n\nFile references contain metadata and URLs:\n\n```typescript\ninterface FileReference {\n /** Unique file ID (platform-generated) */\n id: string;\n /** IANA media type (e.g., 'image/png', 'application/pdf') */\n mediaType: string;\n /** Presigned download URL (S3) */\n url: string;\n /** Original filename */\n filename?: string;\n /** File size in bytes */\n size?: number;\n}\n```\n\n## Protocol Integration\n\nTo accept files in your agent protocol, use the `file[]` type:\n\n```yaml\ntriggers:\n user-message:\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n FILES:\n type: file[]\n optional: true\n description: User-attached images for vision analysis\n\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input:\n - USER_MESSAGE\n files:\n - FILES # Attach files to the message\n display: hidden\n\n Respond to user:\n block: next-message\n```\n\nThe `file` type is a built-in type representing uploaded files. Use `file[]` for arrays of files.\n\n## Supported File Types\n\n| Type | Media Types |\n| --------- | -------------------------------------------------------------------- |\n| Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |\n| Video | `video/mp4`, `video/webm`, `video/quicktime`, `video/mpeg` |\n| Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |\n\n## File Limits\n\n| Limit | Value |\n| --------------------- | ---------- |\n| Max file size | 100 MB |\n| Max total per request | 200 MB |\n| Upload URL expiry | 15 minutes |\n| Download URL expiry | 24 hours |\n\n## Rendering User Files\n\nUser-uploaded files appear as `UIFilePart` in user messages:\n\n```tsx\nfunction UserMessage({ message }: { message: UIMessage }) {\n return (\n <div>\n {message.parts.map((part, i) => {\n if (part.type === 'file') {\n if (part.mediaType.startsWith('image/')) {\n return (\n <img\n key={i}\n src={part.url}\n alt={part.filename || 'Uploaded image'}\n className=\"max-h-48 rounded-lg\"\n />\n );\n }\n return (\n <a key={i} href={part.url} className=\"text-blue-500\">\n \u{1F4C4} {part.filename}\n </a>\n );\n }\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## Server SDK: Files API\n\nThe Server SDK provides direct access to the Files API:\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// Get presigned upload URLs\nconst { files } = await client.files.getUploadUrls(sessionId, [\n { filename: 'photo.jpg', mediaType: 'image/jpeg', size: 102400 },\n { filename: 'doc.pdf', mediaType: 'application/pdf', size: 204800 },\n]);\n\n// files[0].id - Use in FileReference\n// files[0].uploadUrl - PUT to this URL to upload\n// files[0].downloadUrl - Use as FileReference.url\n```\n\n## Complete Example\n\nHere's a full chat input component with file upload:\n\n```tsx\n'use client';\n\nimport { useState, useRef, useMemo, useCallback } from 'react';\nimport { useOctavusChat, createHttpTransport, type FileReference } from '@octavus/react';\n\ninterface PendingFile {\n file: File;\n id: string;\n status: 'uploading' | 'done' | 'error';\n progress: number;\n fileRef?: FileReference;\n error?: string;\n}\n\nexport function Chat({ sessionId }: { sessionId: string }) {\n const [input, setInput] = useState('');\n const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const fileIdCounter = useRef(0);\n\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 requestUploadUrls = useCallback(\n async (files: { filename: string; mediaType: string; size: number }[]) => {\n const res = await fetch('/api/upload-urls', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionId, files }),\n });\n return res.json();\n },\n [sessionId],\n );\n\n const { messages, status, send, uploadFiles } = useOctavusChat({\n transport,\n requestUploadUrls,\n });\n\n const isUploading = pendingFiles.some((f) => f.status === 'uploading');\n const hasErrors = pendingFiles.some((f) => f.status === 'error');\n const allReady = pendingFiles.every((f) => f.status === 'done');\n\n async function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const files = Array.from(e.target.files ?? []);\n if (!files.length) return;\n e.target.value = '';\n\n const newPending: PendingFile[] = files.map((file) => ({\n file,\n id: `pending-${++fileIdCounter.current}`,\n status: 'uploading',\n progress: 0,\n }));\n\n setPendingFiles((prev) => [...prev, ...newPending]);\n\n for (const pending of newPending) {\n try {\n const [fileRef] = await uploadFiles([pending.file], (_, progress) => {\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, progress } : f)),\n );\n });\n setPendingFiles((prev) =>\n prev.map((f) => (f.id === pending.id ? { ...f, status: 'done', fileRef } : f)),\n );\n } catch (err) {\n setPendingFiles((prev) =>\n prev.map((f) =>\n f.id === pending.id ? { ...f, status: 'error', error: String(err) } : f,\n ),\n );\n }\n }\n }\n\n async function handleSubmit() {\n if ((!input.trim() && !pendingFiles.length) || !allReady) return;\n\n const fileRefs = pendingFiles.filter((f) => f.fileRef).map((f) => f.fileRef!);\n\n await send(\n 'user-message',\n {\n USER_MESSAGE: input,\n FILES: fileRefs.length > 0 ? fileRefs : undefined,\n },\n {\n userMessage: {\n content: input,\n files: fileRefs.length > 0 ? fileRefs : undefined,\n },\n },\n );\n\n setInput('');\n setPendingFiles([]);\n }\n\n return (\n <div>\n {/* Messages */}\n {messages.map((msg) => (\n <div key={msg.id}>{/* ... render message */}</div>\n ))}\n\n {/* Pending files */}\n {pendingFiles.length > 0 && (\n <div className=\"flex gap-2\">\n {pendingFiles.map((f) => (\n <div key={f.id} className=\"relative\">\n <img\n src={URL.createObjectURL(f.file)}\n alt={f.file.name}\n className=\"h-16 w-16 object-cover rounded\"\n />\n {f.status === 'uploading' && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/50\">\n <span className=\"text-white text-xs\">{f.progress}%</span>\n </div>\n )}\n <button\n onClick={() => setPendingFiles((prev) => prev.filter((p) => p.id !== f.id))}\n className=\"absolute -top-2 -right-2 bg-red-500 text-white rounded-full w-5 h-5\"\n >\n \xD7\n </button>\n </div>\n ))}\n </div>\n )}\n\n {/* Input */}\n <div className=\"flex gap-2\">\n <input type=\"file\" ref={fileInputRef} onChange={handleFileSelect} hidden />\n <button onClick={() => fileInputRef.current?.click()}>\u{1F4CE}</button>\n <input\n value={input}\n onChange={(e) => setInput(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1\"\n />\n <button onClick={handleSubmit} disabled={isUploading || hasErrors}>\n {isUploading ? 'Uploading...' : 'Send'}\n </button>\n </div>\n </div>\n );\n}\n```\n",
|
|
1240
1249
|
excerpt: "File Uploads The Client SDK supports uploading images and documents that can be sent with messages. This enables vision model capabilities (analyzing images) and document processing. Overview File...",
|
|
1241
1250
|
order: 8
|
|
1242
1251
|
},
|
|
@@ -1271,7 +1280,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1271
1280
|
section: "protocol",
|
|
1272
1281
|
title: "Overview",
|
|
1273
1282
|
description: "Introduction to Octavus agent protocols.",
|
|
1274
|
-
content: '\n# Protocol Overview\n\nAgent protocols define how an AI agent behaves. They\'re written in YAML and specify inputs, triggers, tools, and execution handlers.\n\n## Why Protocols?\n\nProtocols provide:\n\n- **Declarative definition** \u2014 Define behavior, not implementation\n- **Portable agents** \u2014 Move agents between projects\n- **Versioning** \u2014 Track changes with git\n- **Validation** \u2014 Catch errors before runtime\n- **Visualization** \u2014 Debug execution flows\n\n## Agent Formats\n\nOctavus supports two agent formats:\n\n| Format | Use Case | Structure |\n| ------------- | ------------------------------ | --------------------------------- |\n| `interactive` | Chat and multi-turn dialogue | `triggers` + `handlers` + `agent` |\n| `worker` | Background tasks and pipelines | `steps` + `output` |\n\n**Interactive agents** handle conversations \u2014 they respond to triggers (like user messages) and maintain session state across interactions.\n\n**Worker agents** execute tasks \u2014 they run steps sequentially and return an output value. Workers can be called independently or composed into interactive agents.\n\nSee [Workers](/docs/protocol/workers) for the worker protocol reference.\n\n## Interactive Protocol Structure\n\n```yaml\n# Agent inputs (provided when creating a session)\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\n# Persistent resources the agent can read/write\nresources:\n CONVERSATION_SUMMARY:\n description: Summary for handoff\n default: \'\'\n\n# How the agent can be invoked\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n request-human:\n description: User clicks "Talk to Human"\n\n# Temporary variables for execution (with types)\nvariables:\n SUMMARY:\n type: string\n TICKET:\n type: unknown\n\n# Tools the agent can use\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\n# Octavus skills (provider-agnostic code execution)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\n# Agent configuration (model, tools, etc.)\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account]\n skills: [qr-code] # Enable skills\n imageModel: google/gemini-2.5-flash-image # Enable image generation\n agentic: true # Allow multiple tool calls\n thinking: medium # Extended reasoning\n\n# What happens when triggers fire\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond to user:\n block: next-message\n```\n\n## File Structure\n\nEach agent is a folder with:\n\n```\nmy-agent/\n\u251C\u2500\u2500 protocol.yaml # Main logic (required)\n\u251C\u2500\u2500 settings.json # Agent metadata (required)\n\
|
|
1283
|
+
content: '\n# Protocol Overview\n\nAgent protocols define how an AI agent behaves. They\'re written in YAML and specify inputs, triggers, tools, and execution handlers.\n\n## Why Protocols?\n\nProtocols provide:\n\n- **Declarative definition** \u2014 Define behavior, not implementation\n- **Portable agents** \u2014 Move agents between projects\n- **Versioning** \u2014 Track changes with git\n- **Validation** \u2014 Catch errors before runtime\n- **Visualization** \u2014 Debug execution flows\n\n## Agent Formats\n\nOctavus supports two agent formats:\n\n| Format | Use Case | Structure |\n| ------------- | ------------------------------ | --------------------------------- |\n| `interactive` | Chat and multi-turn dialogue | `triggers` + `handlers` + `agent` |\n| `worker` | Background tasks and pipelines | `steps` + `output` |\n\n**Interactive agents** handle conversations \u2014 they respond to triggers (like user messages) and maintain session state across interactions.\n\n**Worker agents** execute tasks \u2014 they run steps sequentially and return an output value. Workers can be called independently or composed into interactive agents.\n\nSee [Workers](/docs/protocol/workers) for the worker protocol reference.\n\n## Interactive Protocol Structure\n\n```yaml\n# Agent inputs (provided when creating a session)\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\n# Persistent resources the agent can read/write\nresources:\n CONVERSATION_SUMMARY:\n description: Summary for handoff\n default: \'\'\n\n# How the agent can be invoked\ntriggers:\n user-message:\n input:\n USER_MESSAGE: { type: string }\n request-human:\n description: User clicks "Talk to Human"\n\n# Temporary variables for execution (with types)\nvariables:\n SUMMARY:\n type: string\n TICKET:\n type: unknown\n\n# Tools the agent can use\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\n# Octavus skills (provider-agnostic code execution)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n\n# Agent configuration (model, tools, etc.)\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system # References prompts/system.md\n tools: [get-user-account]\n skills: [qr-code] # Enable skills\n imageModel: google/gemini-2.5-flash-image # Enable image generation\n webSearch: true # Enable web search\n agentic: true # Allow multiple tool calls\n thinking: medium # Extended reasoning\n\n# What happens when triggers fire\nhandlers:\n user-message:\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond to user:\n block: next-message\n```\n\n## File Structure\n\nEach agent is a folder with:\n\n```\nmy-agent/\n\u251C\u2500\u2500 protocol.yaml # Main logic (required)\n\u251C\u2500\u2500 settings.json # Agent metadata (required)\n\u251C\u2500\u2500 prompts/ # Prompt templates (supports subdirectories)\n\u2502 \u251C\u2500\u2500 system.md\n\u2502 \u251C\u2500\u2500 user-message.md\n\u2502 \u2514\u2500\u2500 shared/\n\u2502 \u251C\u2500\u2500 company-info.md\n\u2502 \u2514\u2500\u2500 formatting-rules.md\n\u2514\u2500\u2500 references/ # On-demand context documents (optional)\n \u2514\u2500\u2500 api-guidelines.md\n```\n\nPrompts can be organized in subdirectories. In the protocol, reference nested prompts by their path relative to `prompts/` (without `.md`): `shared/company-info`.\n\nReferences are markdown files with YAML frontmatter that the agent can fetch on demand during execution. See [References](/docs/protocol/references).\n\n### settings.json\n\n```json\n{\n "slug": "my-agent",\n "name": "My Agent",\n "description": "What this agent does",\n "format": "interactive"\n}\n```\n\n| Field | Required | Description |\n| ------------- | -------- | ----------------------------------------------- |\n| `slug` | Yes | URL-safe identifier (lowercase, digits, dashes) |\n| `name` | Yes | Human-readable name |\n| `description` | No | Brief description |\n| `format` | Yes | `interactive` (chat) or `worker` (background) |\n\n## Naming Conventions\n\n- **Slugs**: `lowercase-with-dashes`\n- **Variables**: `UPPERCASE_SNAKE_CASE`\n- **Prompts**: `lowercase-with-dashes.md` (paths use `/` for subdirectories)\n- **Tools**: `lowercase-with-dashes`\n- **Triggers**: `lowercase-with-dashes`\n\n## Variables in Prompts\n\nReference variables with `{{VARIABLE_NAME}}`:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a support agent for {{COMPANY_NAME}}.\n\nHelp users with their {{PRODUCT_NAME}} questions.\n\n## Support Policies\n\n{{SUPPORT_POLICIES}}\n```\n\nVariables are replaced with their values at runtime. If a variable is not provided, the placeholder is kept as-is.\n\n## Prompt Interpolation\n\nInclude other prompts inside a prompt with `{{@path.md}}`:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a customer support agent.\n\n{{@shared/company-info.md}}\n\n{{@shared/formatting-rules.md}}\n\nHelp users with their questions.\n```\n\nThe referenced prompt content is inserted before variable interpolation, so variables in included prompts work the same way. Circular references are not allowed and will be caught during validation.\n\n## Next Steps\n\n- [Input & Resources](/docs/protocol/input-resources) \u2014 Defining agent inputs\n- [Triggers](/docs/protocol/triggers) \u2014 How agents are invoked\n- [Tools](/docs/protocol/tools) \u2014 External capabilities\n- [Skills](/docs/protocol/skills) \u2014 Code execution and knowledge packages\n- [References](/docs/protocol/references) \u2014 On-demand context documents\n- [Handlers](/docs/protocol/handlers) \u2014 Execution blocks\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n- [Workers](/docs/protocol/workers) \u2014 Worker agent format\n- [Provider Options](/docs/protocol/provider-options) \u2014 Provider-specific features\n- [Types](/docs/protocol/types) \u2014 Custom type definitions\n',
|
|
1275
1284
|
excerpt: "Protocol Overview Agent protocols define how an AI agent behaves. They're written in YAML and specify inputs, triggers, tools, and execution handlers. Why Protocols? Protocols provide: - Declarative...",
|
|
1276
1285
|
order: 1
|
|
1277
1286
|
},
|
|
@@ -1298,8 +1307,8 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1298
1307
|
section: "protocol",
|
|
1299
1308
|
title: "Tools",
|
|
1300
1309
|
description: "Defining external tools implemented in your backend.",
|
|
1301
|
-
content: '\n# Tools\n\nTools extend what agents can do. Octavus supports multiple types:\n\n1. **External Tools** \u2014 Defined in the protocol, implemented in your backend (this page)\n2. **Provider Tools** \u2014
|
|
1302
|
-
excerpt: "Tools Tools extend what agents can do. Octavus supports multiple types: 1. External Tools \u2014 Defined in the protocol, implemented in your backend (this page) 2.
|
|
1310
|
+
content: '\n# Tools\n\nTools extend what agents can do. Octavus supports multiple types:\n\n1. **External Tools** \u2014 Defined in the protocol, implemented in your backend (this page)\n2. **Built-in Tools** \u2014 Provider-agnostic tools managed by Octavus (web search, image generation)\n3. **Provider Tools** \u2014 Provider-specific tools executed by the provider (e.g., Anthropic\'s code execution)\n4. **Skills** \u2014 Code execution and knowledge packages (see [Skills](/docs/protocol/skills))\n\nThis page covers external tools. Built-in tools are enabled via agent config \u2014 see [Web Search](/docs/protocol/agent-config#web-search) and [Image Generation](/docs/protocol/agent-config#image-generation). For provider-specific tools, see [Provider Options](/docs/protocol/provider-options). For code execution, see [Skills](/docs/protocol/skills).\n\n## External Tools\n\nExternal tools are defined in the `tools:` section and implemented in your backend.\n\n## Defining Tools\n\n```yaml\ntools:\n get-user-account:\n description: Looking up your account information\n display: description\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\n### Tool Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------ |\n| `description` | Yes | What the tool does (shown to LLM and optionally user) |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` |\n| `parameters` | No | Input parameters the tool accepts |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Tool runs silently, user doesn\'t see it |\n| `name` | Shows tool name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams tool progress if available |\n\n## Parameters\n\nTool calls are always objects where each parameter name maps to a value. The LLM generates: `{ param1: value1, param2: value2, ... }`\n\n### Parameter Fields\n\n| Field | Required | Description |\n| ------------- | -------- | -------------------------------------------------------------------------------- |\n| `type` | Yes | Data type: `string`, `number`, `integer`, `boolean`, `unknown`, or a custom type |\n| `description` | No | Describes what this parameter is for |\n| `optional` | No | If true, parameter is not required (default: false) |\n\n> **Tip**: You can use [custom types](/docs/protocol/types) for complex parameters like `type: ProductFilter` or `type: SearchOptions`.\n\n### Array Parameters\n\nFor array parameters, define a [top-level array type](/docs/protocol/types#top-level-array-types) and use it:\n\n```yaml\ntypes:\n CartItem:\n productId:\n type: string\n quantity:\n type: integer\n\n CartItemList:\n type: array\n items:\n type: CartItem\n\ntools:\n add-to-cart:\n description: Add items to cart\n parameters:\n items:\n type: CartItemList\n description: Items to add\n```\n\nThe tool receives: `{ items: [{ productId: "...", quantity: 1 }, ...] }`\n\n### Optional Parameters\n\nParameters are **required by default**. Use `optional: true` to make a parameter optional:\n\n```yaml\ntools:\n search-products:\n description: Search the product catalog\n parameters:\n query:\n type: string\n description: Search query\n\n category:\n type: string\n description: Filter by category\n optional: true\n\n maxPrice:\n type: number\n description: Maximum price filter\n optional: true\n\n inStock:\n type: boolean\n description: Only show in-stock items\n optional: true\n```\n\n## Making Tools Available\n\nTools defined in `tools:` are available. To make them usable by the LLM, add them to `agent.tools`:\n\n```yaml\ntools:\n get-user-account:\n description: Look up user account\n parameters:\n userId: { 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, urgent\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools:\n - get-user-account\n - create-support-ticket # LLM can decide when to call these\n agentic: true\n```\n\n## Tool Invocation Modes\n\n### LLM-Decided (Agentic)\n\nThe LLM decides when to call tools based on the conversation:\n\n```yaml\nagent:\n tools: [get-user-account, create-support-ticket]\n agentic: true # Allow multiple tool calls\n maxSteps: 10 # Max tool call cycles\n```\n\n### Deterministic (Block-Based)\n\nForce tool calls at specific points in the handler:\n\n```yaml\nhandlers:\n request-human:\n # Always create a ticket when escalating\n Create support ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # From variable\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n## Tool Results\n\n### In Prompts\n\nTool results are stored in variables. Reference the variable in prompts:\n\n```markdown\n<!-- prompts/ticket-directive.md -->\n\nA support ticket has been created:\n{{TICKET}}\n\nLet the user know their ticket has been created.\n```\n\nWhen the `TICKET` variable contains an object, it\'s automatically serialized as JSON in the prompt:\n\n```\nA support ticket has been created:\n{\n "ticketId": "TKT-123ABC",\n "estimatedResponse": "24 hours"\n}\n\nLet the user know their ticket has been created.\n```\n\n> **Note**: Variables use `{{VARIABLE_NAME}}` syntax with `UPPERCASE_SNAKE_CASE`. Dot notation (like `{{TICKET.ticketId}}`) is not supported. Objects are automatically JSON-serialized.\n\n### In Variables\n\nStore tool results for later use:\n\n```yaml\nhandlers:\n request-human:\n Get account:\n block: tool-call\n tool: get-user-account\n input:\n userId: USER_ID\n output: ACCOUNT # Result stored here\n\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\n## Implementing Tools\n\nTools are implemented in your backend:\n\n```typescript\nconst session = client.agentSessions.attach(sessionId, {\n tools: {\n \'get-user-account\': async (args) => {\n const userId = args.userId as string;\n const user = await db.users.findById(userId);\n\n return {\n name: user.name,\n email: user.email,\n plan: user.subscription.plan,\n createdAt: user.createdAt.toISOString(),\n };\n },\n\n \'create-support-ticket\': async (args) => {\n const ticket = await ticketService.create({\n summary: args.summary as string,\n priority: args.priority as string,\n });\n\n return {\n ticketId: ticket.id,\n estimatedResponse: getEstimatedTime(args.priority),\n };\n },\n },\n});\n```\n\n## Tool Best Practices\n\n### 1. Clear Descriptions\n\n```yaml\ntools:\n # Good - clear and specific\n get-user-account:\n description: >\n Retrieves the user\'s account information including name, email,\n subscription plan, and account creation date. Use this when the\n user asks about their account or you need to verify their identity.\n\n # Avoid - vague\n get-data:\n description: Gets some data\n```\n\n### 2. Document Constrained Values\n\n```yaml\ntools:\n create-support-ticket:\n parameters:\n priority:\n type: string\n description: Ticket priority level (low, medium, high, urgent)\n```\n',
|
|
1311
|
+
excerpt: "Tools Tools extend what agents can do. Octavus supports multiple types: 1. External Tools \u2014 Defined in the protocol, implemented in your backend (this page) 2. Built-in Tools \u2014 Provider-agnostic...",
|
|
1303
1312
|
order: 4
|
|
1304
1313
|
},
|
|
1305
1314
|
{
|
|
@@ -1325,7 +1334,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1325
1334
|
section: "protocol",
|
|
1326
1335
|
title: "Agent Config",
|
|
1327
1336
|
description: "Configuring the agent model and behavior.",
|
|
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| `sandboxTimeout` | No | Skill sandbox timeout in ms (default: 5 min, max: 1 hour) |\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 skills: [data-analysis] # Thread-specific skills\n imageModel: google/gemini-2.5-flash-image # Thread-specific image model\n```\n\nEach thread can have its own skills and image model. Skills referenced here must be defined in the protocol's `skills:` section. Workers use this same pattern since they don't have a global `agent:` section.\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",
|
|
1337
|
+
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 references: [api-guidelines] # On-demand context documents\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| `references` | No | List of references the LLM can fetch on demand |\n| `sandboxTimeout` | No | Skill sandbox timeout in ms (default: 5 min, max: 1 hour) |\n| `imageModel` | No | Image generation model (enables agentic image generation) |\n| `webSearch` | No | Enable built-in web search tool (provider-agnostic) |\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## References\n\nEnable on-demand context loading via reference documents:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines, error-codes]\n agentic: true\n```\n\nReferences are markdown files stored in the agent's `references/` directory. When enabled, the LLM can list available references and read their content using `octavus_reference_list` and `octavus_reference_read` tools.\n\nSee [References](/docs/protocol/references) 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## Web Search\n\nEnable the LLM to search the web for current information:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n webSearch: true\n agentic: true\n```\n\nWhen `webSearch` is enabled, the `octavus_web_search` tool becomes available. The LLM can decide when to search the web based on the conversation. Search results include source URLs that are emitted as citations in the UI.\n\nThis is a **provider-agnostic** built-in tool \u2014 it works with any LLM provider (Anthropic, Google, OpenAI, etc.). For Anthropic's own web search implementation, see [Provider Options](/docs/protocol/provider-options).\n\nUse cases:\n\n- Current events and real-time data\n- Fact verification and documentation lookups\n- Any information that may have changed since the model's training\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 skills: [data-analysis] # Thread-specific skills\n references: [escalation-policy] # Thread-specific references\n imageModel: google/gemini-2.5-flash-image # Thread-specific image model\n webSearch: true # Thread-specific web search\n```\n\nEach thread can have its own skills, references, image model, and web search setting. Skills must be defined in the protocol's `skills:` section. References must exist in the agent's `references/` directory. Workers use this same pattern since they don't have a global `agent:` section.\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 references: [support-policies] # On-demand context\n webSearch: true # Built-in web search\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",
|
|
1329
1338
|
excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
|
|
1330
1339
|
order: 7
|
|
1331
1340
|
},
|
|
@@ -1334,7 +1343,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1334
1343
|
section: "protocol",
|
|
1335
1344
|
title: "Provider Options",
|
|
1336
1345
|
description: "Configuring provider-specific tools and features.",
|
|
1337
|
-
content: "\n# Provider Options\n\nProvider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure.\n\n> **Note**: For provider-agnostic code execution, use [Octavus Skills](/docs/protocol/skills) instead. Octavus Skills work with any LLM provider and run in isolated sandbox environments.\n\n## Anthropic Options\n\nConfigure Anthropic-specific features when using `anthropic/*` models:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n```\n\n> **Note**: Provider options are validated against the model provider. Using `anthropic:` options with non-Anthropic models will result in a validation error.\n\n## Provider Tools\n\nProvider tools are executed server-side by the provider (Anthropic). Unlike external tools that you implement, provider tools are built-in capabilities.\n\n### Available Tools\n\n| Tool | Description |\n| ---------------- | -------------------------------------------- |\n| `web-search` | Search the web for current information |\n| `code-execution` | Execute Python/Bash in a sandboxed container |\n\n### Tool Configuration\n\n```yaml\nanthropic:\n tools:\n web-search:\n display: description # How to show in UI\n description: Searching... # Custom display text\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users during execution |\n\n### Web Search\n\nAllows the agent to search the web
|
|
1346
|
+
content: "\n# Provider Options\n\nProvider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure.\n\n> **Note**: For provider-agnostic code execution, use [Octavus Skills](/docs/protocol/skills) instead. Octavus Skills work with any LLM provider and run in isolated sandbox environments.\n\n## Anthropic Options\n\nConfigure Anthropic-specific features when using `anthropic/*` models:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n```\n\n> **Note**: Provider options are validated against the model provider. Using `anthropic:` options with non-Anthropic models will result in a validation error.\n\n## Provider Tools\n\nProvider tools are executed server-side by the provider (Anthropic). Unlike external tools that you implement, provider tools are built-in capabilities.\n\n### Available Tools\n\n| Tool | Description |\n| ---------------- | -------------------------------------------- |\n| `web-search` | Search the web for current information |\n| `code-execution` | Execute Python/Bash in a sandboxed container |\n\n### Tool Configuration\n\n```yaml\nanthropic:\n tools:\n web-search:\n display: description # How to show in UI\n description: Searching... # Custom display text\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users during execution |\n\n### Web Search\n\nAllows the agent to search the web using Anthropic's built-in web search:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n web-search:\n display: description\n description: Looking up current information\n```\n\n> **Tip**: Octavus also provides a **provider-agnostic** web search via `webSearch: true` in the agent config. This works with any LLM provider and is the recommended approach for multi-provider agents. See [Web Search](/docs/protocol/agent-config#web-search) for details.\n\n### Code Execution\n\nEnables Python and Bash execution in a sandboxed container:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n anthropic:\n tools:\n code-execution:\n display: description\n description: Running analysis\n```\n\nUse cases:\n\n- Data analysis and calculations\n- File processing\n- Chart generation\n- Script execution\n\n> **Note**: Code execution is automatically enabled when skills are configured (skills require the container environment).\n\n## Skills\n\n> **Important**: This section covers **Anthropic's built-in skills** (provider-specific). For provider-agnostic skills that work with any LLM, see [Octavus Skills](/docs/protocol/skills).\n\nAnthropic skills are knowledge packages that give the agent specialized capabilities. They're loaded into Anthropic's code execution container at `/skills/{skill-id}/` and only work with Anthropic models.\n\n### Skill Configuration\n\n```yaml\nanthropic:\n skills:\n pdf:\n type: anthropic # 'anthropic' or 'custom'\n version: latest # Optional version\n display: description\n description: Processing PDF\n```\n\n| Field | Required | Description |\n| ------------- | -------- | --------------------------------------------------------------------- |\n| `type` | Yes | `anthropic` (built-in) or `custom` (uploaded) |\n| `version` | No | Skill version (default: `latest`) |\n| `display` | No | `hidden`, `name`, `description`, or `stream` (default: `description`) |\n| `description` | No | Custom text shown to users |\n\n### Built-in Skills\n\nAnthropic provides several built-in skills:\n\n| Skill ID | Purpose |\n| -------- | ----------------------------------------------- |\n| `pdf` | PDF manipulation, text extraction, form filling |\n| `xlsx` | Excel spreadsheet operations and analysis |\n| `docx` | Word document creation and editing |\n| `pptx` | PowerPoint presentation creation |\n\n### Using Skills\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n anthropic:\n skills:\n pdf:\n type: anthropic\n description: Processing your PDF\n xlsx:\n type: anthropic\n description: Analyzing spreadsheet\n```\n\nWhen skills are configured:\n\n1. Code execution is automatically enabled\n2. Skill files are loaded into the container\n3. The agent can read skill instructions and execute scripts\n\n### Custom Skills\n\nYou can create and upload custom skills to Anthropic:\n\n```yaml\nanthropic:\n skills:\n custom-analysis:\n type: custom\n version: latest\n description: Running custom analysis\n```\n\nCustom skills follow the [Agent Skills standard](https://agentskills.io) and contain:\n\n- `SKILL.md` with instructions and metadata\n- Optional `scripts/`, `references/`, and `assets/` directories\n\n### Octavus Skills vs Anthropic Skills\n\n| Feature | Anthropic Skills | Octavus Skills |\n| ----------------- | ------------------------ | ----------------------------- |\n| **Provider** | Anthropic only | Any (agnostic) |\n| **Execution** | Anthropic's container | Isolated sandbox |\n| **Configuration** | `agent.anthropic.skills` | `agent.skills` |\n| **Definition** | `anthropic:` section | `skills:` section |\n| **Use Case** | Claude-specific features | Cross-provider code execution |\n\nFor provider-agnostic code execution, use Octavus Skills defined in the protocol's `skills:` section and enabled via `agent.skills`. See [Skills](/docs/protocol/skills) for details.\n\n## Display Modes\n\nBoth tools and skills support display modes:\n\n| Mode | Behavior |\n| ------------- | ------------------------------- |\n| `hidden` | Not shown to users |\n| `name` | Shows the tool/skill name |\n| `description` | Shows the description (default) |\n| `stream` | Streams progress if available |\n\n## Full Example\n\n```yaml\ninput:\n COMPANY_NAME: { type: string }\n USER_ID: { type: string, optional: true }\n\ntools:\n get-user-account:\n description: Looking up your account\n parameters:\n userId: { type: string }\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n input: [COMPANY_NAME, USER_ID]\n tools: [get-user-account] # External tools\n agentic: true\n thinking: medium\n\n # Anthropic-specific options\n anthropic:\n # Provider tools (server-side)\n tools:\n web-search:\n display: description\n description: Searching the web\n code-execution:\n display: description\n description: Running code\n\n # Skills (knowledge packages)\n skills:\n pdf:\n type: anthropic\n display: description\n description: Processing PDF document\n xlsx:\n type: anthropic\n display: description\n description: Analyzing spreadsheet\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\n## Validation\n\nThe protocol validator enforces:\n\n1. **Model match**: Provider options must match the model provider\n - `anthropic:` options require `anthropic/*` model\n - Using mismatched options results in a validation error\n\n2. **Valid tool types**: Only recognized tools are accepted\n - `web-search` and `code-execution` for Anthropic\n\n3. **Valid skill types**: Only `anthropic` or `custom` are accepted\n\n### Error Example\n\n```yaml\n# This will fail validation\nagent:\n model: openai/gpt-4o # OpenAI model\n anthropic: # Anthropic options - mismatch!\n tools:\n web-search: {}\n```\n\nError: `\"anthropic\" options require an anthropic model. Current model provider: \"openai\"`\n",
|
|
1338
1347
|
excerpt: "Provider Options Provider options let you enable provider-specific features like Anthropic's built-in tools and skills. These features run server-side on the provider's infrastructure. > Note: For...",
|
|
1339
1348
|
order: 8
|
|
1340
1349
|
},
|
|
@@ -1361,9 +1370,18 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1361
1370
|
section: "protocol",
|
|
1362
1371
|
title: "Workers",
|
|
1363
1372
|
description: "Defining worker agents for background and task-based execution.",
|
|
1364
|
-
content: '\n# Workers\n\nWorkers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value.\n\n## When to Use Workers\n\nWorkers are ideal for:\n\n- **Background processing** \u2014 Long-running tasks that don\'t need conversation\n- **Composable tasks** \u2014 Reusable units of work called by other agents\n- **Pipelines** \u2014 Multi-step processing with structured output\n- **Parallel execution** \u2014 Tasks that can run independently\n\nUse interactive agents instead when:\n\n- **Conversation is needed** \u2014 Multi-turn dialogue with users\n- **Persistence matters** \u2014 State should survive across interactions\n- **Session context** \u2014 User context needs to persist\n\n## Worker vs Interactive\n\n| Aspect | Interactive | Worker |\n| ---------- | ---------------------------------- | ----------------------------- |\n| Structure | `triggers` + `handlers` + `agent` | `steps` + `output` |\n| LLM Config | Global `agent:` section | Per-thread via `start-thread` |\n| Invocation | Fire a named trigger | Direct execution with input |\n| Session | Persists across triggers (24h TTL) | Single execution |\n| Result | Streaming chat | Streaming + output value |\n\n## Protocol Structure\n\nWorkers use a simpler protocol structure than interactive agents:\n\n```yaml\n# Input schema - provided when worker is executed\ninput:\n TOPIC:\n type: string\n description: Topic to research\n DEPTH:\n type: string\n optional: true\n default: medium\n\n# Variables for intermediate results\nvariables:\n RESEARCH_DATA:\n type: string\n ANALYSIS:\n type: string\n description: Final analysis result\n\n# Tools available to the worker\ntools:\n web-search:\n description: Search the web\n parameters:\n query: { type: string }\n\n# Sequential execution steps\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC, DEPTH]\n tools: [web-search]\n maxSteps: 5\n\n Add research request:\n block: add-message\n thread: research\n role: user\n prompt: research-prompt\n input: [TOPIC, DEPTH]\n\n Generate research:\n block: next-message\n thread: research\n output: RESEARCH_DATA\n\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: analysis-system\n\n Add analysis request:\n block: add-message\n thread: analysis\n role: user\n prompt: analysis-prompt\n input: [RESEARCH_DATA]\n\n Generate analysis:\n block: next-message\n thread: analysis\n output: ANALYSIS\n\n# Output variable - the worker\'s return value\noutput: ANALYSIS\n```\n\n## settings.json\n\nWorkers are identified by the `format` field:\n\n```json\n{\n "slug": "research-assistant",\n "name": "Research Assistant",\n "description": "Researches topics and returns structured analysis",\n "format": "worker"\n}\n```\n\n## Key Differences\n\n### No Global Agent Config\n\nInteractive agents have a global `agent:` section that configures a main thread. Workers don\'t have this \u2014 every thread must be explicitly created via `start-thread`:\n\n```yaml\n# Interactive agent: Global config\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [tool-a, tool-b]\n\n# Worker: Each thread configured independently\nsteps:\n Start thread A:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n tools: [tool-a]\n\n Start thread B:\n block: start-thread\n thread: analysis\n model: openai/gpt-4o\n tools: [tool-b]\n```\n\nThis gives workers flexibility to use different models, tools, skills, and settings at different stages.\n\n### Steps Instead of Handlers\n\nWorkers use `steps:` instead of `handlers:`. Steps execute sequentially, like handler blocks:\n\n```yaml\n# Interactive: Handlers respond to triggers\nhandlers:\n user-message:\n Add message:\n block: add-message\n # ...\n\n# Worker: Steps execute in sequence\nsteps:\n Add message:\n block: add-message\n # ...\n```\n\n### Output Value\n\nWorkers can return an output value to the caller:\n\n```yaml\nvariables:\n RESULT:\n type: string\n\nsteps:\n # ... steps that populate RESULT ...\n\noutput: RESULT # Return this variable\'s value\n```\n\nThe `output` field references a variable declared in `variables:`. If omitted, the worker completes without returning a value.\n\n## Available Blocks\n\nWorkers support the same blocks as handlers:\n\n| Block | Purpose |\n| ------------------ | -------------------------------------------- |\n| `start-thread` | Create a named thread with LLM configuration |\n| `add-message` | Add a message to a thread |\n| `next-message` | Generate LLM response |\n| `tool-call` | Call a tool deterministically |\n| `set-resource` | Update a resource value |\n| `serialize-thread` | Convert thread to text |\n| `generate-image` | Generate an image from a prompt variable |\n\n### start-thread (Required for LLM)\n\nEvery thread must be initialized with `start-thread` before using `next-message`:\n\n```yaml\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC]\n tools: [web-search]\n thinking: medium\n maxSteps: 5\n```\n\nAll LLM configuration goes here:\n\n| Field | Description |\n| ------------- | ------------------------------------------------- |\n| `thread` | Thread name (defaults to block name) |\n| `model` | LLM model to use |\n| `system` | System prompt filename (required) |\n| `input` | Variables for system prompt |\n| `tools` | Tools available in this thread |\n| `skills` | Octavus skills available in this thread |\n| `imageModel` | Image generation model |\n| `thinking` | Extended reasoning level |\n| `temperature` | Model temperature |\n| `maxSteps` | Maximum tool call cycles (enables agentic if > 1) |\n\n## Simple Example\n\nA worker that generates a title from a summary:\n\n```yaml\n# Input\ninput:\n CONVERSATION_SUMMARY:\n type: string\n description: Summary to generate a title for\n\n# Variables\nvariables:\n TITLE:\n type: string\n description: The generated title\n\n# Steps\nsteps:\n Start title thread:\n block: start-thread\n thread: title-gen\n model: anthropic/claude-sonnet-4-5\n system: title-system\n\n Add title request:\n block: add-message\n thread: title-gen\n role: user\n prompt: title-request\n input: [CONVERSATION_SUMMARY]\n\n Generate title:\n block: next-message\n thread: title-gen\n output: TITLE\n display: stream\n\n# Output\noutput: TITLE\n```\n\n## Advanced Example\n\nA worker with multiple threads, tools, and agentic behavior:\n\n```yaml\ninput:\n USER_MESSAGE:\n type: string\n description: The user\'s message to respond to\n USER_ID:\n type: string\n description: User ID for account lookups\n optional: true\n\ntools:\n get-user-account:\n description: Looking up account information\n parameters:\n userId: { type: string }\n create-support-ticket:\n description: Creating a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string }\n\nvariables:\n ASSISTANT_RESPONSE:\n type: string\n CHAT_TRANSCRIPT:\n type: string\n CONVERSATION_SUMMARY:\n type: string\n\nsteps:\n # Thread 1: Chat with agentic tool calling\n Start chat thread:\n block: start-thread\n thread: chat\n model: anthropic/claude-sonnet-4-5\n system: chat-system\n input: [USER_ID]\n tools: [get-user-account, create-support-ticket]\n thinking: medium\n maxSteps: 5\n\n Add user message:\n block: add-message\n thread: chat\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Generate response:\n block: next-message\n thread: chat\n output: ASSISTANT_RESPONSE\n display: stream\n\n # Serialize for summary\n Save conversation:\n block: serialize-thread\n thread: chat\n output: CHAT_TRANSCRIPT\n\n # Thread 2: Summary generation\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n system: summary-system\n thinking: low\n\n Add summary request:\n block: add-message\n thread: summary\n role: user\n prompt: summary-request\n input: [CHAT_TRANSCRIPT]\n\n Generate summary:\n block: next-message\n thread: summary\n output: CONVERSATION_SUMMARY\n display: stream\n\noutput: CONVERSATION_SUMMARY\n```\n\n## Skills and Image Generation\n\nWorkers can use Octavus skills and image generation, configured per-thread via `start-thread`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generate QR codes\n\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n imageModel: google/gemini-2.5-flash-image\n maxSteps: 10\n```\n\nWorkers define their own skills independently -- they don\'t inherit skills from a parent interactive agent. Each thread gets its own sandbox scoped to only its listed skills.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Tool Handling\n\nWorkers support the same tool handling as interactive agents:\n\n- **Server tools** \u2014 Handled by tool handlers you provide\n- **Client tools** \u2014 Pause execution, return tool request to caller\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n\n// Streaming: observe events in real-time\nconst events = client.workers.execute(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n```\n\nSee [Server SDK Workers](/docs/server-sdk/workers) for tool handling details.\n\n## Stream Events\n\nWorkers emit the same events as interactive agents, plus worker-specific events:\n\n| Event | Description |\n| --------------- | ---------------------------------- |\n| `worker-start` | Worker execution begins |\n| `worker-result` | Worker completes (includes output) |\n\nAll standard events (text-delta, tool calls, etc.) are also emitted.\n\n## Calling Workers from Interactive Agents\n\nInteractive agents can call workers in two ways:\n\n1. **Deterministically** \u2014 Using the `run-worker` block\n2. **Agentically** \u2014 LLM calls worker as a tool\n\n### Worker Declaration\n\nFirst, declare workers in your interactive agent\'s protocol:\n\n```yaml\nworkers:\n generate-title:\n description: Generating conversation title\n display: description\n research-assistant:\n description: Researching topic\n display: stream\n tools:\n search: web-search # Map worker tool \u2192 parent tool\n```\n\n### run-worker Block\n\nCall a worker deterministically from a handler:\n\n```yaml\nhandlers:\n request-human:\n Generate title:\n block: run-worker\n worker: generate-title\n input:\n CONVERSATION_SUMMARY: SUMMARY\n output: CONVERSATION_TITLE\n```\n\n### LLM Tool Invocation\n\nMake workers available to the LLM:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n workers: [generate-title, research-assistant]\n agentic: true\n```\n\nThe LLM can then call workers as tools during conversation.\n\n### Display Modes\n\nControl how worker execution appears to users:\n\n| Mode | Behavior |\n| ------------- | --------------------------------- |\n| `hidden` | Worker runs silently |\n| `name` | Shows worker name |\n| `description` | Shows description text |\n| `stream` | Streams all worker events to user |\n\n### Tool Mapping\n\nMap parent tools to worker tools when the worker needs access to your tool handlers:\n\n```yaml\nworkers:\n research-assistant:\n description: Research topics\n tools:\n search: web-search # Worker\'s "search" \u2192 parent\'s "web-search"\n```\n\nWhen the worker calls its `search` tool, your `web-search` handler executes.\n\n## Next Steps\n\n- [Server SDK Workers](/docs/server-sdk/workers) \u2014 Executing workers from code\n- [Handlers](/docs/protocol/handlers) \u2014 Block reference for steps\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n',
|
|
1373
|
+
content: '\n# Workers\n\nWorkers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value.\n\n## When to Use Workers\n\nWorkers are ideal for:\n\n- **Background processing** \u2014 Long-running tasks that don\'t need conversation\n- **Composable tasks** \u2014 Reusable units of work called by other agents\n- **Pipelines** \u2014 Multi-step processing with structured output\n- **Parallel execution** \u2014 Tasks that can run independently\n\nUse interactive agents instead when:\n\n- **Conversation is needed** \u2014 Multi-turn dialogue with users\n- **Persistence matters** \u2014 State should survive across interactions\n- **Session context** \u2014 User context needs to persist\n\n## Worker vs Interactive\n\n| Aspect | Interactive | Worker |\n| ---------- | ---------------------------------- | ----------------------------- |\n| Structure | `triggers` + `handlers` + `agent` | `steps` + `output` |\n| LLM Config | Global `agent:` section | Per-thread via `start-thread` |\n| Invocation | Fire a named trigger | Direct execution with input |\n| Session | Persists across triggers (24h TTL) | Single execution |\n| Result | Streaming chat | Streaming + output value |\n\n## Protocol Structure\n\nWorkers use a simpler protocol structure than interactive agents:\n\n```yaml\n# Input schema - provided when worker is executed\ninput:\n TOPIC:\n type: string\n description: Topic to research\n DEPTH:\n type: string\n optional: true\n default: medium\n\n# Variables for intermediate results\nvariables:\n RESEARCH_DATA:\n type: string\n ANALYSIS:\n type: string\n description: Final analysis result\n\n# Tools available to the worker\ntools:\n web-search:\n description: Search the web\n parameters:\n query: { type: string }\n\n# Sequential execution steps\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC, DEPTH]\n tools: [web-search]\n maxSteps: 5\n\n Add research request:\n block: add-message\n thread: research\n role: user\n prompt: research-prompt\n input: [TOPIC, DEPTH]\n\n Generate research:\n block: next-message\n thread: research\n output: RESEARCH_DATA\n\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: analysis-system\n\n Add analysis request:\n block: add-message\n thread: analysis\n role: user\n prompt: analysis-prompt\n input: [RESEARCH_DATA]\n\n Generate analysis:\n block: next-message\n thread: analysis\n output: ANALYSIS\n\n# Output variable - the worker\'s return value\noutput: ANALYSIS\n```\n\n## settings.json\n\nWorkers are identified by the `format` field:\n\n```json\n{\n "slug": "research-assistant",\n "name": "Research Assistant",\n "description": "Researches topics and returns structured analysis",\n "format": "worker"\n}\n```\n\n## Key Differences\n\n### No Global Agent Config\n\nInteractive agents have a global `agent:` section that configures a main thread. Workers don\'t have this \u2014 every thread must be explicitly created via `start-thread`:\n\n```yaml\n# Interactive agent: Global config\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [tool-a, tool-b]\n\n# Worker: Each thread configured independently\nsteps:\n Start thread A:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n tools: [tool-a]\n\n Start thread B:\n block: start-thread\n thread: analysis\n model: openai/gpt-4o\n tools: [tool-b]\n```\n\nThis gives workers flexibility to use different models, tools, skills, and settings at different stages.\n\n### Steps Instead of Handlers\n\nWorkers use `steps:` instead of `handlers:`. Steps execute sequentially, like handler blocks:\n\n```yaml\n# Interactive: Handlers respond to triggers\nhandlers:\n user-message:\n Add message:\n block: add-message\n # ...\n\n# Worker: Steps execute in sequence\nsteps:\n Add message:\n block: add-message\n # ...\n```\n\n### Output Value\n\nWorkers can return an output value to the caller:\n\n```yaml\nvariables:\n RESULT:\n type: string\n\nsteps:\n # ... steps that populate RESULT ...\n\noutput: RESULT # Return this variable\'s value\n```\n\nThe `output` field references a variable declared in `variables:`. If omitted, the worker completes without returning a value.\n\n## Available Blocks\n\nWorkers support the same blocks as handlers:\n\n| Block | Purpose |\n| ------------------ | -------------------------------------------- |\n| `start-thread` | Create a named thread with LLM configuration |\n| `add-message` | Add a message to a thread |\n| `next-message` | Generate LLM response |\n| `tool-call` | Call a tool deterministically |\n| `set-resource` | Update a resource value |\n| `serialize-thread` | Convert thread to text |\n| `generate-image` | Generate an image from a prompt variable |\n\n### start-thread (Required for LLM)\n\nEvery thread must be initialized with `start-thread` before using `next-message`:\n\n```yaml\nsteps:\n Start research:\n block: start-thread\n thread: research\n model: anthropic/claude-sonnet-4-5\n system: research-system\n input: [TOPIC]\n tools: [web-search]\n thinking: medium\n maxSteps: 5\n```\n\nAll LLM configuration goes here:\n\n| Field | Description |\n| ------------- | ------------------------------------------------- |\n| `thread` | Thread name (defaults to block name) |\n| `model` | LLM model to use |\n| `system` | System prompt filename (required) |\n| `input` | Variables for system prompt |\n| `tools` | Tools available in this thread |\n| `skills` | Octavus skills available in this thread |\n| `imageModel` | Image generation model |\n| `webSearch` | Enable built-in web search tool |\n| `thinking` | Extended reasoning level |\n| `temperature` | Model temperature |\n| `maxSteps` | Maximum tool call cycles (enables agentic if > 1) |\n\n## Simple Example\n\nA worker that generates a title from a summary:\n\n```yaml\n# Input\ninput:\n CONVERSATION_SUMMARY:\n type: string\n description: Summary to generate a title for\n\n# Variables\nvariables:\n TITLE:\n type: string\n description: The generated title\n\n# Steps\nsteps:\n Start title thread:\n block: start-thread\n thread: title-gen\n model: anthropic/claude-sonnet-4-5\n system: title-system\n\n Add title request:\n block: add-message\n thread: title-gen\n role: user\n prompt: title-request\n input: [CONVERSATION_SUMMARY]\n\n Generate title:\n block: next-message\n thread: title-gen\n output: TITLE\n display: stream\n\n# Output\noutput: TITLE\n```\n\n## Advanced Example\n\nA worker with multiple threads, tools, and agentic behavior:\n\n```yaml\ninput:\n USER_MESSAGE:\n type: string\n description: The user\'s message to respond to\n USER_ID:\n type: string\n description: User ID for account lookups\n optional: true\n\ntools:\n get-user-account:\n description: Looking up account information\n parameters:\n userId: { type: string }\n create-support-ticket:\n description: Creating a support ticket\n parameters:\n summary: { type: string }\n priority: { type: string }\n\nvariables:\n ASSISTANT_RESPONSE:\n type: string\n CHAT_TRANSCRIPT:\n type: string\n CONVERSATION_SUMMARY:\n type: string\n\nsteps:\n # Thread 1: Chat with agentic tool calling\n Start chat thread:\n block: start-thread\n thread: chat\n model: anthropic/claude-sonnet-4-5\n system: chat-system\n input: [USER_ID]\n tools: [get-user-account, create-support-ticket]\n thinking: medium\n maxSteps: 5\n\n Add user message:\n block: add-message\n thread: chat\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Generate response:\n block: next-message\n thread: chat\n output: ASSISTANT_RESPONSE\n display: stream\n\n # Serialize for summary\n Save conversation:\n block: serialize-thread\n thread: chat\n output: CHAT_TRANSCRIPT\n\n # Thread 2: Summary generation\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n system: summary-system\n thinking: low\n\n Add summary request:\n block: add-message\n thread: summary\n role: user\n prompt: summary-request\n input: [CHAT_TRANSCRIPT]\n\n Generate summary:\n block: next-message\n thread: summary\n output: CONVERSATION_SUMMARY\n display: stream\n\noutput: CONVERSATION_SUMMARY\n```\n\n## Skills, Image Generation, and Web Search\n\nWorkers can use Octavus skills, image generation, and web search, configured per-thread via `start-thread`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generate QR codes\n\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n imageModel: google/gemini-2.5-flash-image\n webSearch: true\n maxSteps: 10\n```\n\nWorkers define their own skills independently -- they don\'t inherit skills from a parent interactive agent. Each thread gets its own sandbox scoped to only its listed skills.\n\nSee [Skills](/docs/protocol/skills) for full documentation.\n\n## Tool Handling\n\nWorkers support the same tool handling as interactive agents:\n\n- **Server tools** \u2014 Handled by tool handlers you provide\n- **Client tools** \u2014 Pause execution, return tool request to caller\n\n```typescript\n// Non-streaming: get the output directly\nconst { output } = await client.workers.generate(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n\n// Streaming: observe events in real-time\nconst events = client.workers.execute(\n agentId,\n { TOPIC: \'AI safety\' },\n {\n tools: {\n \'web-search\': async (args) => await searchWeb(args.query),\n },\n },\n);\n```\n\nSee [Server SDK Workers](/docs/server-sdk/workers) for tool handling details.\n\n## Stream Events\n\nWorkers emit the same events as interactive agents, plus worker-specific events:\n\n| Event | Description |\n| --------------- | ---------------------------------- |\n| `worker-start` | Worker execution begins |\n| `worker-result` | Worker completes (includes output) |\n\nAll standard events (text-delta, tool calls, etc.) are also emitted.\n\n## Calling Workers from Interactive Agents\n\nInteractive agents can call workers in two ways:\n\n1. **Deterministically** \u2014 Using the `run-worker` block\n2. **Agentically** \u2014 LLM calls worker as a tool\n\n### Worker Declaration\n\nFirst, declare workers in your interactive agent\'s protocol:\n\n```yaml\nworkers:\n generate-title:\n description: Generating conversation title\n display: description\n research-assistant:\n description: Researching topic\n display: stream\n tools:\n search: web-search # Map worker tool \u2192 parent tool\n```\n\n### run-worker Block\n\nCall a worker deterministically from a handler:\n\n```yaml\nhandlers:\n request-human:\n Generate title:\n block: run-worker\n worker: generate-title\n input:\n CONVERSATION_SUMMARY: SUMMARY\n output: CONVERSATION_TITLE\n```\n\n### LLM Tool Invocation\n\nMake workers available to the LLM:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n workers: [generate-title, research-assistant]\n agentic: true\n```\n\nThe LLM can then call workers as tools during conversation.\n\n### Display Modes\n\nControl how worker execution appears to users:\n\n| Mode | Behavior |\n| ------------- | --------------------------------- |\n| `hidden` | Worker runs silently |\n| `name` | Shows worker name |\n| `description` | Shows description text |\n| `stream` | Streams all worker events to user |\n\n### Tool Mapping\n\nMap parent tools to worker tools when the worker needs access to your tool handlers:\n\n```yaml\nworkers:\n research-assistant:\n description: Research topics\n tools:\n search: web-search # Worker\'s "search" \u2192 parent\'s "web-search"\n```\n\nWhen the worker calls its `search` tool, your `web-search` handler executes.\n\n## Next Steps\n\n- [Server SDK Workers](/docs/server-sdk/workers) \u2014 Executing workers from code\n- [Handlers](/docs/protocol/handlers) \u2014 Block reference for steps\n- [Agent Config](/docs/protocol/agent-config) \u2014 Model and settings\n',
|
|
1365
1374
|
excerpt: "Workers Workers are agents designed for task-based execution. Unlike interactive agents that handle multi-turn conversations, workers execute a sequence of steps and return an output value. When to...",
|
|
1366
1375
|
order: 11
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
slug: "protocol/references",
|
|
1379
|
+
section: "protocol",
|
|
1380
|
+
title: "References",
|
|
1381
|
+
description: "Using references for on-demand context loading in agents.",
|
|
1382
|
+
content: "\n# References\n\nReferences are markdown documents that agents can fetch on demand. Instead of loading everything into the system prompt upfront, references let the agent decide what context it needs and load it when relevant.\n\n## Overview\n\nReferences are useful for:\n\n- **Large context** \u2014 Documents too long to include in every system prompt\n- **Selective loading** \u2014 Let the agent decide which context is relevant\n- **Shared knowledge** \u2014 Reusable documents across threads\n\n### How References Work\n\n1. **Definition**: Reference files live in the `references/` directory alongside your agent\n2. **Configuration**: List available references in `agent.references` or `start-thread.references`\n3. **Discovery**: The agent sees reference names and descriptions in its system prompt\n4. **Fetching**: The agent calls reference tools to read the full content when needed\n\n## Creating References\n\nEach reference is a markdown file with YAML frontmatter in the `references/` directory:\n\n```\nmy-agent/\n\u251C\u2500\u2500 settings.json\n\u251C\u2500\u2500 protocol.yaml\n\u251C\u2500\u2500 prompts/\n\u2502 \u2514\u2500\u2500 system.md\n\u2514\u2500\u2500 references/\n \u251C\u2500\u2500 api-guidelines.md\n \u2514\u2500\u2500 error-codes.md\n```\n\n### Reference Format\n\n```markdown\n---\ndescription: >\n API design guidelines including naming conventions,\n error handling patterns, and pagination standards.\n---\n\n# API Guidelines\n\n## Naming Conventions\n\nUse lowercase with dashes for URL paths...\n\n## Error Handling\n\nAll errors return a standard error envelope...\n```\n\nThe `description` field is required. It tells the agent what the reference contains so it can decide when to fetch it.\n\n### Naming Convention\n\nReference filenames use `lowercase-with-dashes`:\n\n- `api-guidelines.md`\n- `error-codes.md`\n- `coding-standards.md`\n\nThe filename (without `.md`) becomes the reference name used in the protocol.\n\n## Enabling References\n\nAfter creating reference files, specify which references are available in the protocol.\n\n### Interactive Agents\n\nList references in `agent.references`:\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines, error-codes]\n agentic: true\n```\n\n### Workers and Named Threads\n\nList references per-thread in `start-thread.references`:\n\n```yaml\nsteps:\n Start thread:\n block: start-thread\n thread: worker\n model: anthropic/claude-sonnet-4-5\n system: system\n references: [api-guidelines]\n maxSteps: 10\n```\n\nDifferent threads can have different references.\n\n## Reference Tools\n\nWhen references are enabled, the agent has access to two tools:\n\n| Tool | Purpose |\n| ------------------------ | ----------------------------------------------- |\n| `octavus_reference_list` | List all available references with descriptions |\n| `octavus_reference_read` | Read the full content of a specific reference |\n\nThe agent also sees reference names and descriptions in its system prompt, so it knows what's available without calling `octavus_reference_list`.\n\n## Example\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n tools: [review-pull-request]\n references: [coding-standards, api-guidelines]\n agentic: true\n\nhandlers:\n user-message:\n Add message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n\n Respond:\n block: next-message\n```\n\nWith `references/coding-standards.md`:\n\n```markdown\n---\ndescription: >\n Team coding standards including naming conventions,\n code organization, and review checklist.\n---\n\n# Coding Standards\n\n## Naming Conventions\n\n- Files: kebab-case\n- Variables: camelCase\n- Constants: UPPER_SNAKE_CASE\n ...\n```\n\nWhen a user asks the agent to review code, the agent will:\n\n1. See \"coding-standards\" and \"api-guidelines\" in its system prompt\n2. Decide which references are relevant to the review\n3. Call `octavus_reference_read` to load the relevant reference\n4. Use the loaded context to provide an informed review\n\n## Validation\n\nThe CLI and platform validate references during sync and deployment:\n\n- **Undefined references** \u2014 Referencing a name that doesn't have a matching file in `references/`\n- **Unused references** \u2014 A reference file exists but isn't listed in any `agent.references` or `start-thread.references`\n- **Invalid names** \u2014 Names that don't follow the `lowercase-with-dashes` convention\n- **Missing description** \u2014 Reference files without the required `description` in frontmatter\n\n## References vs Skills\n\n| Aspect | References | Skills |\n| ------------- | ----------------------------- | ------------------------------- |\n| **Purpose** | On-demand context documents | Code execution and file output |\n| **Content** | Markdown text | Documentation + scripts |\n| **Execution** | Synchronous text retrieval | Sandboxed code execution (E2B) |\n| **Scope** | Per-agent (stored with agent) | Per-organization (shared) |\n| **Tools** | List and read (2 tools) | Read, list, run, code (6 tools) |\n\nUse **references** when the agent needs access to text-based knowledge. Use **skills** when the agent needs to execute code or generate files.\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring references in agent settings\n- [Skills](/docs/protocol/skills) \u2014 Code execution and knowledge packages\n- [Workers](/docs/protocol/workers) \u2014 Using references in worker agents\n",
|
|
1383
|
+
excerpt: "References References are markdown documents that agents can fetch on demand. Instead of loading everything into the system prompt upfront, references let the agent decide what context it needs and...",
|
|
1384
|
+
order: 12
|
|
1367
1385
|
}
|
|
1368
1386
|
]
|
|
1369
1387
|
},
|
|
@@ -1396,8 +1414,8 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1396
1414
|
section: "api-reference",
|
|
1397
1415
|
title: "Agents",
|
|
1398
1416
|
description: "Agent management API endpoints.",
|
|
1399
|
-
content: '\n# Agents API\n\nManage agent definitions including protocols and
|
|
1400
|
-
excerpt: "Agents API Manage agent definitions including protocols and
|
|
1417
|
+
content: '\n# Agents API\n\nManage agent definitions including protocols, prompts, and references.\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 "references": [\n {\n "name": "api-guidelines",\n "description": "API design guidelines and conventions",\n "content": "# API Guidelines\\n\\nUse lowercase with dashes..."\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 "references": [\n {\n "name": "api-guidelines",\n "description": "API design guidelines and conventions",\n "content": "# API Guidelines\\n..."\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| `references` | array | No | Reference documents (name, description, content) |\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 "references": [\n {\n "name": "api-guidelines",\n "description": "Updated description",\n "content": "Updated content..."\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',
|
|
1418
|
+
excerpt: "Agents API Manage agent definitions including protocols, prompts, and references. Permissions | Endpoint | Method | Permission Required | | ---------------------- | ------ |...",
|
|
1401
1419
|
order: 3
|
|
1402
1420
|
}
|
|
1403
1421
|
]
|
|
@@ -1486,4 +1504,4 @@ export {
|
|
|
1486
1504
|
getDocSlugs,
|
|
1487
1505
|
getSectionBySlug
|
|
1488
1506
|
};
|
|
1489
|
-
//# sourceMappingURL=chunk-
|
|
1507
|
+
//# sourceMappingURL=chunk-SNBEHHFU.js.map
|