@octavus/docs 2.15.0 → 2.17.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/01-getting-started/02-quickstart.md +1 -0
- package/content/02-server-sdk/01-overview.md +26 -0
- package/content/02-server-sdk/02-sessions.md +11 -0
- package/content/02-server-sdk/03-tools.md +4 -1
- package/content/02-server-sdk/04-streaming.md +9 -0
- package/content/02-server-sdk/08-computer.md +400 -0
- package/content/03-client-sdk/06-http-transport.md +2 -0
- package/content/04-protocol/01-overview.md +9 -0
- package/content/04-protocol/04-tools.md +5 -4
- package/content/04-protocol/05-skills.md +88 -8
- package/content/04-protocol/06-handlers.md +3 -1
- package/content/04-protocol/07-agent-config.md +65 -17
- package/content/04-protocol/09-skills-advanced.md +89 -8
- package/content/04-protocol/13-mcp-servers.md +289 -0
- package/content/06-examples/02-nextjs-chat.md +1 -0
- package/dist/chunk-4PNP4HF5.js +1549 -0
- package/dist/chunk-4PNP4HF5.js.map +1 -0
- package/dist/{chunk-2UFDUNPK.js → chunk-54ND2CTI.js} +25 -21
- package/dist/chunk-54ND2CTI.js.map +1 -0
- package/dist/chunk-B4A36GEV.js +1549 -0
- package/dist/chunk-B4A36GEV.js.map +1 -0
- package/dist/chunk-CFDET7QG.js +1549 -0
- package/dist/chunk-CFDET7QG.js.map +1 -0
- package/dist/chunk-DKVYIFV7.js +1549 -0
- package/dist/chunk-DKVYIFV7.js.map +1 -0
- package/dist/{chunk-JEOGYIRI.js → chunk-UZWGRPRR.js} +61 -21
- package/dist/chunk-UZWGRPRR.js.map +1 -0
- package/dist/content.js +1 -1
- package/dist/docs.json +34 -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 +34 -16
- package/package.json +1 -1
- package/dist/chunk-2UFDUNPK.js.map +0 -1
- package/dist/chunk-JEOGYIRI.js.map +0 -1
|
@@ -14,7 +14,7 @@ var docs_default = [
|
|
|
14
14
|
section: "getting-started",
|
|
15
15
|
title: "Quick Start",
|
|
16
16
|
description: "Get your first Octavus agent running in minutes.",
|
|
17
|
-
content: "\n# Quick Start\n\nThis guide will walk you through integrating Octavus into your application in under 10 minutes.\n\n## Prerequisites\n\n- Node.js 18+\n- An Octavus account with API key\n- A Next.js application (or any Node.js backend)\n\n## Test Your Agent First\n\nBefore integrating with SDKs, use **Agent Preview** to test your agent directly in the platform:\n\n1. Open your agent in the platform at `octavus.ai/platform/agents/[agentId]`\n2. Click the **Preview** tab\n3. Configure session inputs and tool mock responses\n4. Start a conversation to test agent behavior\n\nAgent Preview supports all trigger types, file attachments, tool mocking, and real-time streaming. This is the fastest way to iterate on your agent logic before writing any integration code.\n\n## Installation\n\nInstall the Octavus SDKs in your project:\n\n```bash\n# Server SDK for backend\nnpm install @octavus/server-sdk\n\n# React bindings for frontend\nnpm install @octavus/react\n```\n\n## Backend Setup\n\n### 1. Initialize the Client\n\nCreate an Octavus client instance in your backend:\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n### 2. Create a Session Endpoint\n\nCreate an API endpoint that creates sessions and returns the session ID:\n\n```typescript\n// app/api/chat/create/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\n// Agent ID - get from platform or CLI (see below)\nconst SUPPORT_AGENT_ID = process.env.OCTAVUS_SUPPORT_AGENT_ID!;\n\nexport async function POST(request: Request) {\n const { input } = await request.json();\n\n // Create a new session using the agent ID\n const sessionId = await octavus.agentSessions.create(SUPPORT_AGENT_ID, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n### Getting Your Agent ID\n\nThere are two ways to create and manage agents:\n\n**Option 1: Platform UI (Recommended for getting started)**\n\n1. Go to [octavus.ai](https://octavus.ai) and create an agent in the web editor\n2. Copy the agent ID from the URL (e.g., `octavus.ai/platform/agents/clxyz123abc456`)\n3. Add it to your `.env.local`: `OCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456`\n\n**Option 2: Local Development with CLI**\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\nnpm install --save-dev @octavus/cli\noctavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n```\n\nThe CLI approach is better for teams and CI/CD pipelines where you want agent definitions in your repository.\n\n### 3. Create a Trigger Endpoint\n\nCreate an endpoint that handles triggers and streams responses:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Define tool handlers that run on your server\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n return {\n name: 'Demo User',\n email: 'demo@example.com',\n plan: 'pro',\n };\n },\n 'create-support-ticket': async (args) => {\n // Create ticket in your system\n return {\n ticketId: 'TICKET-123',\n estimatedResponse: '24 hours',\n };\n },\n },\n });\n\n // Execute the request and convert to SSE stream\n const events = session.execute(payload, { signal: request.signal });\n\n // Return as streaming response\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n```\n\n## Frontend Setup\n\n### 1. Create a Chat Component\n\nUse the `useOctavusChat` hook with the HTTP transport:\n\n```tsx\n// components/chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create a stable transport instance\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, error, send } = useOctavusChat({ transport });\n\n const isStreaming = status === 'streaming';\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || isStreaming) return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Add user message and trigger in one call\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={isStreaming}\n />\n <button\n type=\"submit\"\n disabled={isStreaming}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50\"\n >\n Send\n </button>\n </div>\n </form>\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n const isUser = message.role === 'user';\n\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>\n <div\n className={`p-3 rounded-lg max-w-md ${isUser ? 'bg-blue-500 text-white' : 'bg-gray-100'}`}\n >\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\n {/* Streaming indicator */}\n {message.status === 'streaming' && (\n <span className=\"inline-block w-2 h-4 bg-gray-400 animate-pulse ml-1\" />\n )}\n </div>\n </div>\n );\n}\n```\n\n### 2. Create Session and Render Chat\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/chat';\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n async function createSession() {\n const response = await fetch('/api/chat/create', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n input: {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n },\n }),\n });\n const { sessionId } = await response.json();\n setSessionId(sessionId);\n }\n\n createSession();\n }, []);\n\n if (!sessionId) {\n return <div>Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Environment Variables\n\nAdd these to your `.env.local`:\n\n```bash\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key-here\n```\n\n## What's Next?\n\nNow that you have a basic integration working:\n\n- [Learn about the protocol](/docs/protocol/overview) to define custom agent behavior\n- [Explore the Server SDK](/docs/server-sdk/overview) for advanced backend features\n- [Build rich UIs](/docs/client-sdk/overview) with the Client SDK\n- [Handle tools on the client](/docs/client-sdk/client-tools) for interactive UIs and browser APIs\n",
|
|
17
|
+
content: "\n# Quick Start\n\nThis guide will walk you through integrating Octavus into your application in under 10 minutes.\n\n## Prerequisites\n\n- Node.js 18+\n- An Octavus account with API key\n- A Next.js application (or any Node.js backend)\n\n## Test Your Agent First\n\nBefore integrating with SDKs, use **Agent Preview** to test your agent directly in the platform:\n\n1. Open your agent in the platform at `octavus.ai/platform/agents/[agentId]`\n2. Click the **Preview** tab\n3. Configure session inputs and tool mock responses\n4. Start a conversation to test agent behavior\n\nAgent Preview supports all trigger types, file attachments, tool mocking, and real-time streaming. This is the fastest way to iterate on your agent logic before writing any integration code.\n\n## Installation\n\nInstall the Octavus SDKs in your project:\n\n```bash\n# Server SDK for backend\nnpm install @octavus/server-sdk\n\n# React bindings for frontend\nnpm install @octavus/react\n```\n\n## Backend Setup\n\n### 1. Initialize the Client\n\nCreate an Octavus client instance in your backend:\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n### 2. Create a Session Endpoint\n\nCreate an API endpoint that creates sessions and returns the session ID:\n\n```typescript\n// app/api/chat/create/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\n// Agent ID - get from platform or CLI (see below)\nconst SUPPORT_AGENT_ID = process.env.OCTAVUS_SUPPORT_AGENT_ID!;\n\nexport async function POST(request: Request) {\n const { input } = await request.json();\n\n // Create a new session using the agent ID\n const sessionId = await octavus.agentSessions.create(SUPPORT_AGENT_ID, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n### Getting Your Agent ID\n\nThere are two ways to create and manage agents:\n\n**Option 1: Platform UI (Recommended for getting started)**\n\n1. Go to [octavus.ai](https://octavus.ai) and create an agent in the web editor\n2. Copy the agent ID from the URL (e.g., `octavus.ai/platform/agents/clxyz123abc456`)\n3. Add it to your `.env.local`: `OCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456`\n\n**Option 2: Local Development with CLI**\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\nnpm install --save-dev @octavus/cli\noctavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n```\n\nThe CLI approach is better for teams and CI/CD pipelines where you want agent definitions in your repository.\n\n### 3. Create a Trigger Endpoint\n\nCreate an endpoint that handles triggers and streams responses:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Define tool handlers that run on your server\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n return {\n name: 'Demo User',\n email: 'demo@example.com',\n plan: 'pro',\n };\n },\n 'create-support-ticket': async (args) => {\n // Create ticket in your system\n return {\n ticketId: 'TICKET-123',\n estimatedResponse: '24 hours',\n };\n },\n },\n });\n\n // Execute the request and convert to SSE stream\n const events = session.execute(payload, { signal: request.signal });\n\n // Return as streaming response\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n}\n```\n\n## Frontend Setup\n\n### 1. Create a Chat Component\n\nUse the `useOctavusChat` hook with the HTTP transport:\n\n```tsx\n// components/chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create a stable transport instance\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, error, send } = useOctavusChat({ transport });\n\n const isStreaming = status === 'streaming';\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || isStreaming) return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Add user message and trigger in one call\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={isStreaming}\n />\n <button\n type=\"submit\"\n disabled={isStreaming}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50\"\n >\n Send\n </button>\n </div>\n </form>\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n const isUser = message.role === 'user';\n\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>\n <div\n className={`p-3 rounded-lg max-w-md ${isUser ? 'bg-blue-500 text-white' : 'bg-gray-100'}`}\n >\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\n {/* Streaming indicator */}\n {message.status === 'streaming' && (\n <span className=\"inline-block w-2 h-4 bg-gray-400 animate-pulse ml-1\" />\n )}\n </div>\n </div>\n );\n}\n```\n\n### 2. Create Session and Render Chat\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/chat';\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n async function createSession() {\n const response = await fetch('/api/chat/create', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n input: {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n },\n }),\n });\n const { sessionId } = await response.json();\n setSessionId(sessionId);\n }\n\n createSession();\n }, []);\n\n if (!sessionId) {\n return <div>Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Environment Variables\n\nAdd these to your `.env.local`:\n\n```bash\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key-here\n```\n\n## What's Next?\n\nNow that you have a basic integration working:\n\n- [Learn about the protocol](/docs/protocol/overview) to define custom agent behavior\n- [Explore the Server SDK](/docs/server-sdk/overview) for advanced backend features\n- [Build rich UIs](/docs/client-sdk/overview) with the Client SDK\n- [Handle tools on the client](/docs/client-sdk/client-tools) for interactive UIs and browser APIs\n",
|
|
18
18
|
excerpt: "Quick Start This guide will walk you through integrating Octavus into your application in under 10 minutes. Prerequisites - Node.js 18+ - An Octavus account with API key - A Next.js application (or...",
|
|
19
19
|
order: 2
|
|
20
20
|
},
|
|
@@ -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.16.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
|
},
|
|
@@ -50,7 +50,7 @@ var docs_default = [
|
|
|
50
50
|
section: "server-sdk",
|
|
51
51
|
title: "Streaming",
|
|
52
52
|
description: "Understanding stream events from the Server SDK.",
|
|
53
|
-
content: "\n# Streaming\n\nAll Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates.\n\n## Stream Response\n\nWhen you execute a request, you get an async generator of parsed events:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of StreamEvent\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// For HTTP endpoints, convert to SSE stream\nreturn new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n});\n\n// For sockets, iterate events directly\nfor await (const event of events) {\n conn.write(JSON.stringify(event));\n}\n```\n\n## Event Types\n\nThe stream emits various event types for lifecycle, text, reasoning, and tool interactions.\n\n### Lifecycle Events\n\n```typescript\n// Stream started\n{ type: 'start', messageId: '...', executionId: '...' }\n\n// Stream completed\n{ type: 'finish', finishReason: 'stop' }\n\n// Possible finish reasons:\n// - 'stop': Normal completion\n// - 'tool-calls': Waiting for server tool execution (handled by SDK internally)\n// - 'client-tool-calls': Waiting for client tool execution\n// - 'length': Max tokens reached\n// - 'content-filter': Content filtered\n// - 'error': Error occurred\n// - 'other': Other reason\n\n// Error event (see Error Handling docs for full structure)\n{ type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }\n```\n\n### Block Events\n\nTrack execution progress:\n\n```typescript\n// Block started\n{ type: 'block-start', blockId: '...', blockName: 'Respond to user', blockType: 'next-message', display: 'stream', thread: 'main' }\n\n// Block completed\n{ type: 'block-end', blockId: '...', summary: 'Generated response' }\n```\n\n### Text Events\n\nStreaming text content:\n\n```typescript\n// Text generation started\n{ type: 'text-start', id: '...' }\n\n// Incremental text (most common event)\n{ type: 'text-delta', id: '...', delta: 'Hello' }\n{ type: 'text-delta', id: '...', delta: '!' }\n{ type: 'text-delta', id: '...', delta: ' How' }\n{ type: 'text-delta', id: '...', delta: ' can' }\n{ type: 'text-delta', id: '...', delta: ' I' }\n{ type: 'text-delta', id: '...', delta: ' help?' }\n\n// Text generation ended\n{ type: 'text-end', id: '...' }\n```\n\n### Reasoning Events\n\nExtended reasoning (for supported models like Claude):\n\n```typescript\n// Reasoning started\n{ type: 'reasoning-start', id: '...' }\n\n// Reasoning content\n{ type: 'reasoning-delta', id: '...', delta: 'Let me analyze this request...' }\n\n// Reasoning ended\n{ type: 'reasoning-end', id: '...' }\n```\n\n### Tool Events\n\nTool call lifecycle:\n\n```typescript\n// Tool input started\n{ type: 'tool-input-start', toolCallId: '...', toolName: 'get-user-account', title: 'Looking up account' }\n\n// Tool input/arguments streaming\n{ type: 'tool-input-delta', toolCallId: '...', inputTextDelta: '{\"userId\":\"user-123\"}' }\n\n// Tool input streaming ended\n{ type: 'tool-input-end', toolCallId: '...' }\n\n// Tool input is complete and available\n{ type: 'tool-input-available', toolCallId: '...', toolName: 'get-user-account', input: { userId: 'user-123' } }\n\n// Tool output available (success)\n{ type: 'tool-output-available', toolCallId: '...', output: { name: 'Demo User', email: '...' } }\n\n// Tool output error (failure)\n{ type: 'tool-output-error', toolCallId: '...', error: 'User not found' }\n```\n\n### Resource Events\n\nResource updates:\n\n```typescript\n{ type: 'resource-update', name: 'CONVERSATION_SUMMARY', value: 'User asked about...' }\n```\n\n## Display Modes\n\nEach block/tool specifies how it should appear to users:\n\n| Mode | Description |\n| ------------- | ----------------------------------- |\n| `hidden` | Not shown to user (background work) |\n| `name` | Shows block/tool name |\n| `description` | Shows description text |\n| `stream` | Streams content to chat |\n\n**Note**: Hidden events are filtered before reaching the client SDK. Your frontend only sees user-facing events.\n\n## Stream Event Type\n\n```typescript\ntype StreamEvent =\n // Lifecycle\n | StartEvent\n | FinishEvent\n | ErrorEvent\n // Text\n | TextStartEvent\n | TextDeltaEvent\n | TextEndEvent\n // Reasoning\n | ReasoningStartEvent\n | ReasoningDeltaEvent\n | ReasoningEndEvent\n // Tool Input/Output\n | ToolInputStartEvent\n | ToolInputDeltaEvent\n | ToolInputEndEvent\n | ToolInputAvailableEvent\n | ToolOutputAvailableEvent\n | ToolOutputErrorEvent\n // Octavus-Specific\n | BlockStartEvent\n | BlockEndEvent\n | ResourceUpdateEvent\n | ToolRequestEvent\n | ClientToolRequestEvent;\n```\n\n### Client Tool Request\n\nWhen a tool has no server handler registered, the SDK emits a `client-tool-request` event:\n\n```typescript\n{\n type: 'client-tool-request',\n executionId: 'exec_abc123', // Use this to continue execution\n toolCalls: [ // Tools for client to handle\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n args: {}\n }\n ],\n serverToolResults: [ // Results from server tools in same batch\n {\n toolCallId: 'call_def',\n toolName: 'get-user-account',\n result: { name: 'Demo User' }\n }\n ]\n}\n```\n\nAfter the client handles the tools, send a `continue` request with all results:\n\n```typescript\nsession.execute({\n type: 'continue',\n executionId: 'exec_abc123',\n toolResults: [\n ...serverToolResults, // Include server results from the event\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n result: { lat: 40.7128, lng: -74.006 },\n },\n ],\n});\n```\n\nSee [Client Tools](/docs/client-sdk/client-tools) for full client-side implementation.\n\n## Error Events\n\nErrors are emitted as structured events with type classification:\n\n```typescript\n{\n type: 'error',\n errorType: 'rate_limit_error', // Error classification\n message: 'Rate limit exceeded', // Human-readable message\n source: 'provider', // 'platform' | 'provider' | 'tool'\n retryable: true, // Whether retry is possible\n retryAfter: 60, // Seconds to wait (rate limits)\n code: 'ANTHROPIC_429', // Machine-readable code\n provider: { // Provider details (when applicable)\n name: 'anthropic',\n statusCode: 429,\n requestId: 'req_...'\n }\n}\n```\n\n### Error Types\n\n| Type | Description |\n| ---------------------- | --------------------- |\n| `rate_limit_error` | Too many requests |\n| `authentication_error` | Invalid API key |\n| `provider_error` | LLM provider issue |\n| `provider_overloaded` | Provider at capacity |\n| `tool_error` | Tool execution failed |\n| `internal_error` | Platform error |\n\n### Tool Errors\n\nTool errors are captured per-tool and don't stop the stream:\n\n```typescript\n{ type: 'tool-output-error', toolCallId: '...', error: 'Handler threw exception' }\n```\n\nThe stream always ends with either `finish` or `error`.\n\nFor client-side error handling patterns, see [Error Handling](/docs/client-sdk/error-handling).\n",
|
|
53
|
+
content: "\n# Streaming\n\nAll Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates.\n\n## Stream Response\n\nWhen you execute a request, you get an async generator of parsed events:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of StreamEvent\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// For HTTP endpoints, convert to SSE stream\nreturn new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n});\n\n// For sockets, iterate events directly\nfor await (const event of events) {\n conn.write(JSON.stringify(event));\n}\n```\n\nThe `X-Accel-Buffering: no` header disables proxy buffering on Nginx-based infrastructure (including Vercel), ensuring SSE events are forwarded immediately instead of being batched.\n\n### Heartbeat\n\n`toSSEStream` automatically sends SSE comment lines (`: heartbeat`) every 15 seconds during idle periods. This prevents proxies and load balancers from closing the connection due to inactivity \u2014 particularly important during multi-step executions where the stream may be silent while waiting for tool processing or LLM responses.\n\nHeartbeat comments are ignored by all SSE parsers per the spec. No client-side handling is needed.\n\n## Event Types\n\nThe stream emits various event types for lifecycle, text, reasoning, and tool interactions.\n\n### Lifecycle Events\n\n```typescript\n// Stream started\n{ type: 'start', messageId: '...', executionId: '...' }\n\n// Stream completed\n{ type: 'finish', finishReason: 'stop' }\n\n// Possible finish reasons:\n// - 'stop': Normal completion\n// - 'tool-calls': Waiting for server tool execution (handled by SDK internally)\n// - 'client-tool-calls': Waiting for client tool execution\n// - 'length': Max tokens reached\n// - 'content-filter': Content filtered\n// - 'error': Error occurred\n// - 'other': Other reason\n\n// Error event (see Error Handling docs for full structure)\n{ type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }\n```\n\n### Block Events\n\nTrack execution progress:\n\n```typescript\n// Block started\n{ type: 'block-start', blockId: '...', blockName: 'Respond to user', blockType: 'next-message', display: 'stream', thread: 'main' }\n\n// Block completed\n{ type: 'block-end', blockId: '...', summary: 'Generated response' }\n```\n\n### Text Events\n\nStreaming text content:\n\n```typescript\n// Text generation started\n{ type: 'text-start', id: '...' }\n\n// Incremental text (most common event)\n{ type: 'text-delta', id: '...', delta: 'Hello' }\n{ type: 'text-delta', id: '...', delta: '!' }\n{ type: 'text-delta', id: '...', delta: ' How' }\n{ type: 'text-delta', id: '...', delta: ' can' }\n{ type: 'text-delta', id: '...', delta: ' I' }\n{ type: 'text-delta', id: '...', delta: ' help?' }\n\n// Text generation ended\n{ type: 'text-end', id: '...' }\n```\n\n### Reasoning Events\n\nExtended reasoning (for supported models like Claude):\n\n```typescript\n// Reasoning started\n{ type: 'reasoning-start', id: '...' }\n\n// Reasoning content\n{ type: 'reasoning-delta', id: '...', delta: 'Let me analyze this request...' }\n\n// Reasoning ended\n{ type: 'reasoning-end', id: '...' }\n```\n\n### Tool Events\n\nTool call lifecycle:\n\n```typescript\n// Tool input started\n{ type: 'tool-input-start', toolCallId: '...', toolName: 'get-user-account', title: 'Looking up account' }\n\n// Tool input/arguments streaming\n{ type: 'tool-input-delta', toolCallId: '...', inputTextDelta: '{\"userId\":\"user-123\"}' }\n\n// Tool input streaming ended\n{ type: 'tool-input-end', toolCallId: '...' }\n\n// Tool input is complete and available\n{ type: 'tool-input-available', toolCallId: '...', toolName: 'get-user-account', input: { userId: 'user-123' } }\n\n// Tool output available (success)\n{ type: 'tool-output-available', toolCallId: '...', output: { name: 'Demo User', email: '...' } }\n\n// Tool output error (failure)\n{ type: 'tool-output-error', toolCallId: '...', error: 'User not found' }\n```\n\n### Resource Events\n\nResource updates:\n\n```typescript\n{ type: 'resource-update', name: 'CONVERSATION_SUMMARY', value: 'User asked about...' }\n```\n\n## Display Modes\n\nEach block/tool specifies how it should appear to users:\n\n| Mode | Description |\n| ------------- | ----------------------------------- |\n| `hidden` | Not shown to user (background work) |\n| `name` | Shows block/tool name |\n| `description` | Shows description text |\n| `stream` | Streams content to chat |\n\n**Note**: Hidden events are filtered before reaching the client SDK. Your frontend only sees user-facing events.\n\n## Stream Event Type\n\n```typescript\ntype StreamEvent =\n // Lifecycle\n | StartEvent\n | FinishEvent\n | ErrorEvent\n // Text\n | TextStartEvent\n | TextDeltaEvent\n | TextEndEvent\n // Reasoning\n | ReasoningStartEvent\n | ReasoningDeltaEvent\n | ReasoningEndEvent\n // Tool Input/Output\n | ToolInputStartEvent\n | ToolInputDeltaEvent\n | ToolInputEndEvent\n | ToolInputAvailableEvent\n | ToolOutputAvailableEvent\n | ToolOutputErrorEvent\n // Octavus-Specific\n | BlockStartEvent\n | BlockEndEvent\n | ResourceUpdateEvent\n | ToolRequestEvent\n | ClientToolRequestEvent;\n```\n\n### Client Tool Request\n\nWhen a tool has no server handler registered, the SDK emits a `client-tool-request` event:\n\n```typescript\n{\n type: 'client-tool-request',\n executionId: 'exec_abc123', // Use this to continue execution\n toolCalls: [ // Tools for client to handle\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n args: {}\n }\n ],\n serverToolResults: [ // Results from server tools in same batch\n {\n toolCallId: 'call_def',\n toolName: 'get-user-account',\n result: { name: 'Demo User' }\n }\n ]\n}\n```\n\nAfter the client handles the tools, send a `continue` request with all results:\n\n```typescript\nsession.execute({\n type: 'continue',\n executionId: 'exec_abc123',\n toolResults: [\n ...serverToolResults, // Include server results from the event\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n result: { lat: 40.7128, lng: -74.006 },\n },\n ],\n});\n```\n\nSee [Client Tools](/docs/client-sdk/client-tools) for full client-side implementation.\n\n## Error Events\n\nErrors are emitted as structured events with type classification:\n\n```typescript\n{\n type: 'error',\n errorType: 'rate_limit_error', // Error classification\n message: 'Rate limit exceeded', // Human-readable message\n source: 'provider', // 'platform' | 'provider' | 'tool'\n retryable: true, // Whether retry is possible\n retryAfter: 60, // Seconds to wait (rate limits)\n code: 'ANTHROPIC_429', // Machine-readable code\n provider: { // Provider details (when applicable)\n name: 'anthropic',\n statusCode: 429,\n requestId: 'req_...'\n }\n}\n```\n\n### Error Types\n\n| Type | Description |\n| ---------------------- | --------------------- |\n| `rate_limit_error` | Too many requests |\n| `authentication_error` | Invalid API key |\n| `provider_error` | LLM provider issue |\n| `provider_overloaded` | Provider at capacity |\n| `tool_error` | Tool execution failed |\n| `internal_error` | Platform error |\n\n### Tool Errors\n\nTool errors are captured per-tool and don't stop the stream:\n\n```typescript\n{ type: 'tool-output-error', toolCallId: '...', error: 'Handler threw exception' }\n```\n\nThe stream always ends with either `finish` or `error`.\n\nFor client-side error handling patterns, see [Error Handling](/docs/client-sdk/error-handling).\n",
|
|
54
54
|
excerpt: "Streaming All Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates. Stream Response When you execute a request, you get an async...",
|
|
55
55
|
order: 4
|
|
56
56
|
},
|
|
@@ -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.16.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
|
},
|
|
@@ -81,12 +81,21 @@ var docs_default = [
|
|
|
81
81
|
excerpt: "Debugging Model Request Tracing Model request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014...",
|
|
82
82
|
order: 7
|
|
83
83
|
},
|
|
84
|
+
{
|
|
85
|
+
slug: "server-sdk/computer",
|
|
86
|
+
section: "server-sdk",
|
|
87
|
+
title: "Computer",
|
|
88
|
+
description: "Adding browser, filesystem, and shell capabilities to agents with @octavus/computer.",
|
|
89
|
+
content: "\n# Computer\n\nThe `@octavus/computer` package gives agents access to a physical or virtual machine's browser, filesystem, and shell. It connects to [MCP](https://modelcontextprotocol.io) servers, discovers their tools, and provides them to the server-sdk.\n\n**Current version:** `2.16.0`\n\n## Installation\n\n```bash\nnpm install @octavus/computer\n```\n\n## Quick Start\n\n```typescript\nimport { Computer } from '@octavus/computer';\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n '--browser-url=http://127.0.0.1:9222',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n ['/path/to/workspace'],\n ),\n shell: Computer.shell({ cwd: '/path/to/workspace', mode: 'unrestricted' }),\n },\n});\n\nawait computer.start();\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n\nconst session = client.agentSessions.attach(sessionId, {\n tools: { 'set-chat-title': titleHandler },\n computer,\n});\n```\n\nThe `computer` is passed to `attach()` \u2014 the server-sdk handles the rest. Tool schemas are sent to the platform, and tool calls flow back through the existing execution loop.\n\n## How It Works\n\n1. You configure MCP servers with namespaces (e.g., `browser`, `filesystem`, `shell`)\n2. `computer.start()` connects to all servers in parallel and discovers their tools\n3. Each tool is namespaced with `__` (e.g., `browser__navigate_page`, `filesystem__read_file`)\n4. The server-sdk sends tool schemas to the platform and handles tool call execution\n\nThe agent's protocol must declare matching `mcpServers` with `source: device` \u2014 see [MCP Servers](/docs/protocol/mcp-servers).\n\n## Entry Types\n\nThe `Computer` class supports three types of MCP entries:\n\n### Stdio (MCP Subprocess)\n\nSpawns an MCP server as a child process, communicating via stdin/stdout:\n\n```typescript\nComputer.stdio(command: string, args?: string[], options?: {\n env?: Record<string, string>;\n cwd?: string;\n})\n```\n\nUse this for local MCP servers installed as npm packages or standalone executables:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n '--browser-url=http://127.0.0.1:9222',\n '--no-usage-statistics',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n ['/Users/me/projects/my-app'],\n ),\n },\n});\n```\n\n### HTTP (Remote MCP Endpoint)\n\nConnects to an MCP server over Streamable HTTP:\n\n```typescript\nComputer.http(url: string, options?: {\n headers?: Record<string, string>;\n})\n```\n\nUse this for MCP servers running as HTTP services:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n docs: Computer.http('http://localhost:3001/mcp', {\n headers: { 'Authorization': 'Bearer token' },\n }),\n },\n});\n```\n\n### Shell (Built-in)\n\nProvides shell command execution without spawning an MCP subprocess:\n\n```typescript\nComputer.shell(options: {\n cwd?: string;\n mode: ShellMode;\n timeout?: number; // Default: 300,000ms (5 minutes)\n})\n```\n\nThis exposes a `run_command` tool (namespaced as `shell__run_command` when the key is `shell`). Commands execute in a login shell with the user's full environment.\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n shell: Computer.shell({\n cwd: '/Users/me/projects/my-app',\n mode: 'unrestricted',\n timeout: 300_000,\n }),\n },\n});\n```\n\n#### Shell Safety Modes\n\n| Mode | Description |\n| ------------------------------------------ | -------------------------------------------- |\n| `'unrestricted'` | All commands allowed (for dedicated machines) |\n| `{ allowedPatterns, blockedPatterns }` | Pattern-based command filtering |\n\nPattern-based filtering:\n\n```typescript\nComputer.shell({\n cwd: workspaceDir,\n mode: {\n blockedPatterns: [/rm\\s+-rf/, /sudo/],\n allowedPatterns: [/^git\\s/, /^npm\\s/, /^ls\\s/],\n },\n})\n```\n\nWhen `allowedPatterns` is set, only matching commands are permitted. When `blockedPatterns` is set, matching commands are rejected. Blocked patterns are checked first.\n\n## Lifecycle\n\n### Starting\n\n`computer.start()` connects to all configured MCP servers in parallel. If some servers fail to connect, the computer still starts with the remaining servers \u2014 only if *all* connections fail does it throw an error.\n\n```typescript\nconst { errors } = await computer.start();\n\nif (errors.length > 0) {\n console.warn('Some MCP servers failed to connect:', errors);\n}\n```\n\n### Stopping\n\n`computer.stop()` closes all MCP connections and kills managed processes:\n\n```typescript\nawait computer.stop();\n```\n\nAlways call `stop()` when the session ends to clean up MCP subprocesses. For managed processes (like Chrome), pass them in the config for automatic cleanup.\n\n## Chrome Launch Helper\n\nFor desktop applications that need to control a browser, `Computer.launchChrome()` launches Chrome with remote debugging enabled:\n\n```typescript\nconst browser = await Computer.launchChrome({\n profileDir: '/Users/me/.my-app/chrome-profiles/agent-1',\n debuggingPort: 9222, // Optional, auto-allocated if omitted\n flags: ['--window-size=1280,800'],\n});\n\nconsole.log(`Chrome running on port ${browser.port}, PID ${browser.pid}`);\n```\n\nPass the browser to `managedProcesses` for automatic cleanup when the computer stops:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n `--browser-url=http://127.0.0.1:${browser.port}`,\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n [workspaceDir],\n ),\n shell: Computer.shell({ cwd: workspaceDir, mode: 'unrestricted' }),\n },\n managedProcesses: [{ process: browser.process }],\n});\n```\n\n### ChromeLaunchOptions\n\n| Field | Required | Description |\n| ---------------- | -------- | ---------------------------------------------------- |\n| `profileDir` | Yes | Directory for Chrome's user data (profile isolation) |\n| `debuggingPort` | No | Port for remote debugging (auto-allocated if omitted) |\n| `flags` | No | Additional Chrome launch flags |\n\n## ToolProvider Interface\n\n`Computer` implements the `ToolProvider` interface from `@octavus/core`:\n\n```typescript\ninterface ToolProvider {\n toolHandlers(): Record<string, ToolHandler>;\n toolSchemas(): ToolSchema[];\n}\n```\n\nThe server-sdk accepts any `ToolProvider` on the `computer` option \u2014 you can implement your own if `@octavus/computer` doesn't fit your use case:\n\n```typescript\nconst customProvider: ToolProvider = {\n toolHandlers() {\n return {\n 'custom__my_tool': async (args) => {\n return { result: 'done' };\n },\n };\n },\n toolSchemas() {\n return [\n {\n name: 'custom__my_tool',\n description: 'A custom tool',\n inputSchema: {\n type: 'object',\n properties: {\n input: { type: 'string', description: 'Tool input' },\n },\n required: ['input'],\n },\n },\n ];\n },\n};\n\nconst session = client.agentSessions.attach(sessionId, {\n tools: { 'set-chat-title': titleHandler },\n computer: customProvider,\n});\n```\n\n## Complete Example\n\nA desktop application with browser, filesystem, and shell capabilities:\n\n```typescript\nimport { Computer } from '@octavus/computer';\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst WORKSPACE_DIR = '/Users/me/projects/my-app';\nconst PROFILE_DIR = '/Users/me/.my-app/chrome-profiles/agent';\n\nasync function startSession(sessionId: string) {\n // 1. Launch Chrome with remote debugging\n const browser = await Computer.launchChrome({\n profileDir: PROFILE_DIR,\n });\n\n // 2. Create computer with all capabilities\n const computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n `--browser-url=http://127.0.0.1:${browser.port}`,\n '--no-usage-statistics',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n [WORKSPACE_DIR],\n ),\n shell: Computer.shell({\n cwd: WORKSPACE_DIR,\n mode: 'unrestricted',\n }),\n },\n managedProcesses: [{ process: browser.process }],\n });\n\n // 3. Connect to all MCP servers\n const { errors } = await computer.start();\n if (errors.length > 0) {\n console.warn('Failed to connect:', errors);\n }\n\n // 4. Attach to session with computer\n const client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n });\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n 'set-chat-title': async (args) => {\n console.log('Chat title:', args.title);\n return { success: true };\n },\n },\n computer,\n });\n\n // 5. Execute and stream\n const events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Navigate to github.com and take a screenshot' },\n });\n\n for await (const event of events) {\n // Handle stream events\n }\n\n // 6. Clean up\n await computer.stop();\n}\n```\n\n## API Reference\n\n### Computer\n\n```typescript\nclass Computer implements ToolProvider {\n constructor(config: ComputerConfig);\n\n // Static factories for MCP entries\n static stdio(command: string, args?: string[], options?: {\n env?: Record<string, string>;\n cwd?: string;\n }): StdioConfig;\n\n static http(url: string, options?: {\n headers?: Record<string, string>;\n }): HttpConfig;\n\n static shell(options: {\n cwd?: string;\n mode: ShellMode;\n timeout?: number;\n }): ShellConfig;\n\n // Chrome launch helper\n static launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance>;\n\n // Lifecycle\n start(): Promise<{ errors: string[] }>;\n stop(): Promise<void>;\n\n // ToolProvider implementation\n toolHandlers(): Record<string, ToolHandler>;\n toolSchemas(): ToolSchema[];\n}\n```\n\n### ComputerConfig\n\n```typescript\ninterface ComputerConfig {\n mcpServers: Record<string, McpEntry>;\n managedProcesses?: { process: ChildProcess }[];\n}\n\ntype McpEntry = StdioConfig | HttpConfig | ShellConfig;\ntype ShellMode = 'unrestricted' | {\n allowedPatterns?: RegExp[];\n blockedPatterns?: RegExp[];\n};\n```\n\n### ChromeInstance\n\n```typescript\ninterface ChromeInstance {\n port: number;\n process: ChildProcess;\n pid: number;\n}\n```\n",
|
|
90
|
+
excerpt: "Computer The package gives agents access to a physical or virtual machine's browser, filesystem, and shell. It connects to MCP servers, discovers their tools, and provides them to the server-sdk....",
|
|
91
|
+
order: 8
|
|
92
|
+
},
|
|
84
93
|
{
|
|
85
94
|
slug: "client-sdk/overview",
|
|
86
95
|
section: "client-sdk",
|
|
87
96
|
title: "Overview",
|
|
88
97
|
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.14.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.14.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### Retry Last Trigger\n\nRe-execute the last trigger from the same starting point. Messages are rolled back to the state before the trigger, the user message is re-added (if any), and the agent re-executes. Already-uploaded files are reused without re-uploading.\n\n```tsx\nconst { retry, canRetry } = useOctavusChat({ transport });\n\n// Retry after an error, cancellation, or unsatisfactory result\nif (canRetry) {\n await retry();\n}\n```\n\n`canRetry` is `true` when a trigger has been sent and the chat is not currently streaming or awaiting input.\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 retry: () => Promise<void>; // Retry last trigger from same starting point\n canRetry: boolean; // Whether retry() can be called\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",
|
|
98
|
+
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.16.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.16.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### Retry Last Trigger\n\nRe-execute the last trigger from the same starting point. Messages are rolled back to the state before the trigger, the user message is re-added (if any), and the agent re-executes. Already-uploaded files are reused without re-uploading.\n\n```tsx\nconst { retry, canRetry } = useOctavusChat({ transport });\n\n// Retry after an error, cancellation, or unsatisfactory result\nif (canRetry) {\n await retry();\n}\n```\n\n`canRetry` is `true` when a trigger has been sent and the chat is not currently streaming or awaiting input.\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 retry: () => Promise<void>; // Retry last trigger from same starting point\n canRetry: boolean; // Whether retry() can be called\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
99
|
excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
|
|
91
100
|
order: 1
|
|
92
101
|
},
|
|
@@ -209,6 +218,7 @@ export async function POST(request: Request) {
|
|
|
209
218
|
'Content-Type': 'text/event-stream',
|
|
210
219
|
'Cache-Control': 'no-cache',
|
|
211
220
|
Connection: 'keep-alive',
|
|
221
|
+
'X-Accel-Buffering': 'no',
|
|
212
222
|
},
|
|
213
223
|
});
|
|
214
224
|
}
|
|
@@ -363,6 +373,7 @@ app.post('/api/trigger', async (req, res) => {
|
|
|
363
373
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
364
374
|
res.setHeader('Cache-Control', 'no-cache');
|
|
365
375
|
res.setHeader('Connection', 'keep-alive');
|
|
376
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
366
377
|
|
|
367
378
|
// Pipe the stream to the response
|
|
368
379
|
const reader = stream.getReader();
|
|
@@ -577,7 +588,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
577
588
|
section: "protocol",
|
|
578
589
|
title: "Skills",
|
|
579
590
|
description: "Using Octavus skills for code execution and specialized capabilities.",
|
|
580
|
-
content: "\n# Skills\n\nSkills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are self-contained packages with documentation and scripts that run in secure sandboxes.\n\n## Overview\n\nOctavus Skills provide **provider-agnostic** code execution. They work with any LLM provider (Anthropic, OpenAI, Google) by using explicit tool calls and system prompt injection.\n\n### How Skills Work\n\n1. **Skill Definition**: Skills are defined in the protocol's `skills:` section\n2. **Skill Resolution**: Skills are resolved from available sources (see below)\n3. **Sandbox Execution**: When a skill is used, code runs in an isolated sandbox environment\n4. **File Generation**: Files saved to `/output/` are automatically captured and made available for download\n\n### Skill Sources\n\nSkills come from two sources, visible in the Skills tab of your organization:\n\n| Source | Badge in UI | Visibility | Example |\n| ----------- | ----------- | ------------------------------ | ------------------ |\n| **Octavus** | `Octavus` | Available to all organizations | `qr-code` |\n| **Custom** | None | Private to your organization | `my-company-skill` |\n\nWhen you reference a skill in your protocol, Octavus resolves it from your available skills. If you create a custom skill with the same name as an Octavus skill, your custom skill takes precedence.\n\n## Defining Skills\n\nDefine skills in the protocol's `skills:` section:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n```\n\n### Skill Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------------------------------- |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` (default: `description`) |\n| `description` | No | Custom description shown to users (overrides skill's built-in description) |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Skill usage not shown to users |\n| `name` | Shows skill name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams progress if available |\n\n## Enabling Skills\n\nAfter defining skills in the `skills:` section, specify which skills are available. Skills work in both interactive agents and workers.\n\n### Interactive Agents\n\nReference skills in `agent.skills`:\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 tools: [get-user-account]\n skills: [qr-code]\n agentic: true\n```\n\n### Workers and Named Threads\n\nReference skills per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating 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 maxSteps: 10\n```\n\nThis also works for named threads in interactive agents, allowing different threads to have different skills.\n\n## Skill Tools\n\nWhen skills are enabled, the LLM has access to these tools:\n\n| Tool | Purpose |\n| -------------------- | --------------------------------------- |\n| `octavus_skill_read` | Read skill documentation (SKILL.md) |\n| `octavus_skill_list` | List available scripts in a skill |\n| `octavus_skill_run` | Execute a pre-built script from a skill |\n| `octavus_code_run` | Execute arbitrary Python/Bash code |\n| `octavus_file_write` | Create files in the sandbox |\n| `octavus_file_read` | Read files from the sandbox |\n\nThe LLM learns about available skills through system prompt injection and can use these tools to interact with skills.\n\n## Example: QR Code 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]\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\nWhen a user asks \"Create a QR code for octavus.ai\", the LLM will:\n\n1. Recognize the task matches the `qr-code` skill\n2. Call `octavus_skill_read` to learn how to use the skill\n3. Execute code (via `octavus_code_run` or `octavus_skill_run`) to generate the QR code\n4. Save the image to `/output/` in the sandbox\n5. The file is automatically captured and made available for download\n\n## File Output\n\nFiles saved to `/output/` in the sandbox are automatically:\n\n1. **Captured** after code execution\n2. **Uploaded** to S3 storage\n3. **Made available** via presigned URLs\n4. **Included** in the message as file parts\n\nFiles persist across page refreshes and are stored in the session's message history.\n\n## Skill Format\n\nSkills follow the [Agent Skills](https://agentskills.io) open standard:\n\n- `SKILL.md` - Required skill documentation with YAML frontmatter\n- `scripts/` - Optional executable code (Python/Bash)\n- `references/` - Optional documentation loaded as needed\n- `assets/` - Optional files used in outputs (templates, images)\n\n### SKILL.md Format\n\n````yaml\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\nversion: 1.0.0\nlicense: MIT\nauthor: Octavus Team\n---\n\n# QR Code Generator\n\n## Overview\n\nThis skill creates QR codes from text data using Python...\n\n## Quick Start\n\nGenerate a QR code with Python:\n\n```python\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n# ... code to generate QR code ...\n````\n\n## Scripts Reference\n\n### scripts/generate.py\n\nMain script for generating QR codes...\n\n````\n\n## Best Practices\n\n### 1. Clear Descriptions\n\nProvide clear, purpose-driven descriptions:\n\n```yaml\nskills:\n # Good - clear purpose\n qr-code:\n description: Generating QR codes for URLs, contact info, or any text data\n\n # Avoid - vague\n utility:\n description: Does stuff\n````\n\n### 2. When to Use Skills vs Tools\n\n| Use Skills When | Use Tools When |\n| ------------------------ | ---------------------------- |\n| Code execution needed | Simple API calls |\n| File generation | Database queries |\n| Complex calculations | External service integration |\n| Data processing | Authentication required |\n| Provider-agnostic needed | Backend-specific logic |\n\n### 3. Skill Selection\n\nDefine all skills available to this agent in the `skills:` section. Then specify which skills are available for the chat thread in `agent.skills`:\n\n```yaml\n# All skills available to this agent (defined once at protocol level)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n pdf-processor:\n display: description\n description: Processing PDFs\n\n# Skills available for this chat thread\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis] # Skills available for this thread\n```\n\n### 4. Display Modes\n\nChoose appropriate display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n## Comparison: Skills vs Tools vs Provider Options\n\n| Feature | Octavus Skills | External Tools | Provider Tools/Skills |\n| ------------------ | ----------------- | ------------------- | --------------------- |\n| **Execution** | Isolated sandbox | Your backend | Provider servers |\n| **Provider** | Any (agnostic) | N/A | Provider-specific |\n| **Code Execution** | Yes | No | Yes (provider tools) |\n| **File Output** | Yes | No | Yes (provider skills) |\n| **Implementation** | Skill packages | Your code | Built-in |\n| **Cost** | Sandbox + LLM API | Your infrastructure | Included in API |\n\n## Uploading Custom Skills\n\nYou can upload custom skills to your organization:\n\n1. Create a skill following the [Agent Skills](https://agentskills.io) format\n2. Package it as a `.skill` bundle (ZIP file)\n3. Upload via the platform UI\n4. Reference by slug in your protocol\n\n```yaml\nskills:\n custom-analysis:\n display: description\n description: Custom analysis tool\n\nagent:\n skills: [custom-analysis]\n```\n\n## Sandbox Timeout\n\nThe default sandbox timeout is 5 minutes. You can configure a custom timeout using `sandboxTimeout` in the agent config or on individual `start-thread` blocks:\n\n```yaml\n# Agent-level timeout (applies to main thread)\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes (in milliseconds)\n```\n\n```yaml\n# Thread-level timeout (overrides agent-level for this thread)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour\n```\n\nThread-level `sandboxTimeout` takes priority over agent-level. Maximum: 1 hour (3,600,000 ms).\n\n## Security\n\nSkills run in isolated sandbox environments:\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring skills in agent settings\n- [Provider Options](/docs/protocol/provider-options) \u2014 Anthropic's built-in skills\n- [Skills Advanced Guide](/docs/protocol/skills-advanced) \u2014 Best practices and advanced patterns\n",
|
|
591
|
+
content: "\n# Skills\n\nSkills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are self-contained packages with documentation and scripts that run in secure sandboxes.\n\n## Overview\n\nOctavus Skills provide **provider-agnostic** code execution. They work with any LLM provider (Anthropic, OpenAI, Google) by using explicit tool calls and system prompt injection.\n\n### How Skills Work\n\n1. **Skill Definition**: Skills are defined in the protocol's `skills:` section\n2. **Skill Resolution**: Skills are resolved from available sources (see below)\n3. **Sandbox Execution**: When a skill is used, code runs in an isolated sandbox environment\n4. **File Generation**: Files saved to `/output/` are automatically captured and made available for download\n\n### Skill Sources\n\nSkills come from two sources, visible in the Skills tab of your organization:\n\n| Source | Badge in UI | Visibility | Example |\n| ----------- | ----------- | ------------------------------ | ------------------ |\n| **Octavus** | `Octavus` | Available to all organizations | `qr-code` |\n| **Custom** | None | Private to your organization | `my-company-skill` |\n\nWhen you reference a skill in your protocol, Octavus resolves it from your available skills. If you create a custom skill with the same name as an Octavus skill, your custom skill takes precedence.\n\n## Defining Skills\n\nDefine skills in the protocol's `skills:` section:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n```\n\n### Skill Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------------------------------- |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` (default: `description`) |\n| `description` | No | Custom description shown to users (overrides skill's built-in description) |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Skill usage not shown to users |\n| `name` | Shows skill name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams progress if available |\n\n## Enabling Skills\n\nAfter defining skills in the `skills:` section, specify which skills are available. Skills work in both interactive agents and workers.\n\n### Interactive Agents\n\nReference skills in `agent.skills`:\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 tools: [get-user-account]\n skills: [qr-code]\n agentic: true\n```\n\n### Workers and Named Threads\n\nReference skills per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating 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 maxSteps: 10\n```\n\nThis also works for named threads in interactive agents, allowing different threads to have different skills.\n\n## Skill Tools\n\nWhen skills are enabled, the LLM has access to these tools:\n\n| Tool | Purpose | Availability |\n| -------------------- | --------------------------------------- | -------------------- |\n| `octavus_skill_read` | Read skill documentation (SKILL.md) | All skills |\n| `octavus_skill_list` | List available scripts in a skill | All skills |\n| `octavus_skill_run` | Execute a pre-built script from a skill | All skills |\n| `octavus_code_run` | Execute arbitrary Python/Bash code | Standard skills only |\n| `octavus_file_write` | Create files in the sandbox | Standard skills only |\n| `octavus_file_read` | Read files from the sandbox | Standard skills only |\n\nThe LLM learns about available skills through system prompt injection and can use these tools to interact with skills.\n\nSkills that have [secrets](#skill-secrets) configured run in **secure mode**, where only `octavus_skill_read`, `octavus_skill_list`, and `octavus_skill_run` are available. See [Skill Secrets](#skill-secrets) below.\n\n## Example: QR Code 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]\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\nWhen a user asks \"Create a QR code for octavus.ai\", the LLM will:\n\n1. Recognize the task matches the `qr-code` skill\n2. Call `octavus_skill_read` to learn how to use the skill\n3. Execute code (via `octavus_code_run` or `octavus_skill_run`) to generate the QR code\n4. Save the image to `/output/` in the sandbox\n5. The file is automatically captured and made available for download\n\n## File Output\n\nFiles saved to `/output/` in the sandbox are automatically:\n\n1. **Captured** after code execution\n2. **Uploaded** to S3 storage\n3. **Made available** via presigned URLs\n4. **Included** in the message as file parts\n\nFiles persist across page refreshes and are stored in the session's message history.\n\n## Skill Format\n\nSkills follow the [Agent Skills](https://agentskills.io) open standard:\n\n- `SKILL.md` - Required skill documentation with YAML frontmatter\n- `scripts/` - Optional executable code (Python/Bash)\n- `references/` - Optional documentation loaded as needed\n- `assets/` - Optional files used in outputs (templates, images)\n\n### SKILL.md Format\n\n````yaml\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\nversion: 1.0.0\nlicense: MIT\nauthor: Octavus Team\n---\n\n# QR Code Generator\n\n## Overview\n\nThis skill creates QR codes from text data using Python...\n\n## Quick Start\n\nGenerate a QR code with Python:\n\n```python\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n# ... code to generate QR code ...\n````\n\n## Scripts Reference\n\n### scripts/generate.py\n\nMain script for generating QR codes...\n\n````\n\n### Frontmatter Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------ |\n| `name` | Yes | Skill slug (lowercase, hyphens) |\n| `description` | Yes | What the skill does (shown to the LLM) |\n| `version` | No | Semantic version string |\n| `license` | No | License identifier |\n| `author` | No | Skill author |\n| `secrets` | No | Array of secret declarations (enables secure mode) |\n\n## Best Practices\n\n### 1. Clear Descriptions\n\nProvide clear, purpose-driven descriptions:\n\n```yaml\nskills:\n # Good - clear purpose\n qr-code:\n description: Generating QR codes for URLs, contact info, or any text data\n\n # Avoid - vague\n utility:\n description: Does stuff\n````\n\n### 2. When to Use Skills vs Tools\n\n| Use Skills When | Use Tools When |\n| ------------------------ | ---------------------------- |\n| Code execution needed | Simple API calls |\n| File generation | Database queries |\n| Complex calculations | External service integration |\n| Data processing | Authentication required |\n| Provider-agnostic needed | Backend-specific logic |\n\n### 3. Skill Selection\n\nDefine all skills available to this agent in the `skills:` section. Then specify which skills are available for the chat thread in `agent.skills`:\n\n```yaml\n# All skills available to this agent (defined once at protocol level)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n pdf-processor:\n display: description\n description: Processing PDFs\n\n# Skills available for this chat thread\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis] # Skills available for this thread\n```\n\n### 4. Display Modes\n\nChoose appropriate display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n## Comparison: Skills vs Tools vs Provider Options\n\n| Feature | Octavus Skills | External Tools | Provider Tools/Skills |\n| ------------------ | ----------------- | ------------------- | --------------------- |\n| **Execution** | Isolated sandbox | Your backend | Provider servers |\n| **Provider** | Any (agnostic) | N/A | Provider-specific |\n| **Code Execution** | Yes | No | Yes (provider tools) |\n| **File Output** | Yes | No | Yes (provider skills) |\n| **Implementation** | Skill packages | Your code | Built-in |\n| **Cost** | Sandbox + LLM API | Your infrastructure | Included in API |\n\n## Uploading Custom Skills\n\nYou can upload custom skills to your organization:\n\n1. Create a skill following the [Agent Skills](https://agentskills.io) format\n2. Package it as a `.skill` bundle (ZIP file)\n3. Upload via the platform UI\n4. Reference by slug in your protocol\n\n```yaml\nskills:\n custom-analysis:\n display: description\n description: Custom analysis tool\n\nagent:\n skills: [custom-analysis]\n```\n\n## Sandbox Timeout\n\nThe default sandbox timeout is 5 minutes. You can configure a custom timeout using `sandboxTimeout` in the agent config or on individual `start-thread` blocks:\n\n```yaml\n# Agent-level timeout (applies to main thread)\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes (in milliseconds)\n```\n\n```yaml\n# Thread-level timeout (overrides agent-level for this thread)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour\n```\n\nThread-level `sandboxTimeout` takes priority over agent-level. Maximum: 1 hour (3,600,000 ms).\n\n## Skill Secrets\n\nSkills can declare secrets they need to function. When an organization configures those secrets, the skill runs in **secure mode** with additional isolation.\n\n### Declaring Secrets\n\nAdd a `secrets` array to your SKILL.md frontmatter:\n\n```yaml\n---\nname: github\ndescription: >\n Run GitHub CLI (gh) commands to manage repos, issues, PRs, and more.\nsecrets:\n - name: GITHUB_TOKEN\n description: GitHub personal access token with repo access\n required: true\n - name: GITHUB_ORG\n description: Default GitHub organization\n required: false\n---\n```\n\nEach secret declaration has:\n\n| Field | Required | Description |\n| ------------- | -------- | ----------------------------------------------------------- |\n| `name` | Yes | Environment variable name (uppercase, e.g., `GITHUB_TOKEN`) |\n| `description` | No | Explains what this secret is for (shown in the UI) |\n| `required` | No | Whether the secret is required (defaults to `true`) |\n\nSecret names must match the pattern `^[A-Z_][A-Z0-9_]*$` (uppercase letters, digits, and underscores).\n\n### Configuring Secrets\n\nOrganization admins configure secret values through the skill editor in the platform UI. Each organization maintains its own independent set of secrets for each skill.\n\nSecrets are encrypted at rest and only decrypted at execution time.\n\n### Secure Mode\n\nWhen a skill has secrets configured for the organization, it automatically runs in **secure mode**:\n\n- The skill gets its own **isolated sandbox** (separate from other skills)\n- Secrets are injected as **environment variables** available to all scripts\n- Only `octavus_skill_read`, `octavus_skill_list`, and `octavus_skill_run` are available \u2014 `octavus_code_run`, `octavus_file_write`, and `octavus_file_read` are blocked\n- Scripts receive input as **JSON via stdin** (using the `input` parameter on `octavus_skill_run`) instead of CLI args\n- All output (stdout/stderr) is **automatically redacted** for secret values before being returned to the LLM\n\n### Writing Scripts for Secure Skills\n\nScripts in secure skills read input from stdin as JSON and access secrets from environment variables:\n\n```python\nimport json\nimport os\nimport sys\n\ninput_data = json.load(sys.stdin)\ntoken = os.environ.get('GITHUB_TOKEN')\n\n# Use the token and input_data to perform the task\n```\n\nFor standard skills (without secrets), scripts receive input as CLI arguments. For secure skills, always use stdin JSON.\n\n## Security\n\nSkills run in isolated sandbox environments:\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n- **Secret redaction** \u2014 output from secure skills is automatically scanned for secret values\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring skills in agent settings\n- [Provider Options](/docs/protocol/provider-options) \u2014 Anthropic's built-in skills\n- [Skills Advanced Guide](/docs/protocol/skills-advanced) \u2014 Best practices and advanced patterns\n",
|
|
581
592
|
excerpt: "Skills Skills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are...",
|
|
582
593
|
order: 5
|
|
583
594
|
},
|
|
@@ -586,7 +597,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
586
597
|
section: "protocol",
|
|
587
598
|
title: "Handlers",
|
|
588
599
|
description: "Defining execution handlers with blocks.",
|
|
589
|
-
content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n skills: [qr-code] # Octavus skills for this thread\n sandboxTimeout: 600000 # Skill sandbox timeout (default: 5 min, max: 1 hour)\n imageModel: google/gemini-2.5-flash-image # Image generation model\n```\n\nThe `model` field can also reference a variable for dynamic model selection
|
|
600
|
+
content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n backupModel: openai/gpt-4o # Failover on provider errors\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n skills: [qr-code] # Octavus skills for this thread\n sandboxTimeout: 600000 # Skill sandbox timeout (default: 5 min, max: 1 hour)\n imageModel: google/gemini-2.5-flash-image # Image generation model\n```\n\nThe `model` field can also reference a variable for dynamic model selection. The `backupModel` field follows the same format and supports variable references.\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nThe `input` field on blocks controls which variables are passed to the prompt. Only variables listed in `input` are available for interpolation.\n\nVariables can come from `protocol.input`, `protocol.resources`, `protocol.variables`, `trigger.input`, or outputs from prior blocks.\n\n```yaml\n# Array format (same name)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Array format (rename)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # Prompt sees CONVERSATION, value comes from CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n\n# Object format (rename)\ninput:\n CONVERSATION: CONVERSATION_TEXT\n TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
|
|
590
601
|
excerpt: "Handlers Handlers define what happens when a trigger fires. They contain execution blocks that run in sequence. Handler Structure Each block has a human-readable name (shown in debug UI) and a ...",
|
|
591
602
|
order: 6
|
|
592
603
|
},
|
|
@@ -595,7 +606,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
595
606
|
section: "protocol",
|
|
596
607
|
title: "Agent Config",
|
|
597
608
|
description: "Configuring the agent model and behavior.",
|
|
598
|
-
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",
|
|
609
|
+
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| `backupModel` | No | Backup model for automatic failover on provider errors |\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## Backup Model\n\nConfigure a fallback model that activates automatically when the primary model encounters a transient provider error (rate limits, outages, timeouts):\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n backupModel: openai/gpt-4o\n system: system\n```\n\nWhen a provider error occurs, the system retries once with the backup model. If the backup also fails, the original error is returned.\n\n**Key behaviors:**\n\n- Only transient provider errors trigger fallback \u2014 authentication and validation errors are not retried\n- Provider-specific options (like `anthropic:`) are only forwarded to the backup model if it uses the same provider\n- For streaming responses, fallback only occurs if no content has been sent to the client yet\n\nLike `model`, `backupModel` supports variable references:\n\n```yaml\ninput:\n BACKUP_MODEL:\n type: string\n description: Fallback model for provider errors\n\nagent:\n model: anthropic/claude-sonnet-4-5\n backupModel: BACKUP_MODEL\n system: system\n```\n\n> **Tip**: Use a different provider for your backup model (e.g., primary on Anthropic, backup on OpenAI) to maximize resilience against single-provider outages.\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 backupModel: openai/gpt-4o # Failover 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 model, backup model, 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 backupModel: openai/gpt-4o\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",
|
|
599
610
|
excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
|
|
600
611
|
order: 7
|
|
601
612
|
},
|
|
@@ -613,7 +624,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
613
624
|
section: "protocol",
|
|
614
625
|
title: "Skills Advanced Guide",
|
|
615
626
|
description: "Best practices and advanced patterns for using Octavus skills.",
|
|
616
|
-
content: "\n# Skills Advanced Guide\n\nThis guide covers advanced patterns and best practices for using Octavus skills in your agents.\n\n## When to Use Skills\n\nSkills are ideal for:\n\n- **Code execution** - Running Python/Bash scripts\n- **File generation** - Creating images, PDFs, reports\n- **Data processing** - Analyzing, transforming, or visualizing data\n- **Provider-agnostic needs** - Features that should work with any LLM\n\nUse external tools instead when:\n\n- **Simple API calls** - Database queries, external services\n- **Authentication required** - Accessing user-specific resources\n- **Backend integration** - Tight coupling with your infrastructure\n\n## Skill Selection Strategy\n\n### Defining Available Skills\n\nDefine all skills in the `skills:` section, then reference which skills are available where they're used:\n\n**Interactive agents** \u2014 reference in `agent.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n pdf-processor:\n display: description\n description: Processing PDFs\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\n**Workers and named threads** \u2014 reference per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n\nsteps:\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis]\n maxSteps: 10\n```\n\n### Match Skills to Use Cases\n\nDifferent threads can have different skills. Define all skills at the protocol level, then scope them to each thread:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n visualization:\n display: description\n description: Creating charts and visualizations\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\nFor a data analysis thread, you would specify `[data-analysis, visualization]` in `agent.skills` or in a `start-thread` block's `skills` field.\n\n## Display Mode Strategy\n\nChoose display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n### Guidelines\n\n- **`hidden`**: Background work that doesn't need user awareness\n- **`description`**: User-facing operations (default)\n- **`name`**: Quick operations where name is sufficient\n- **`stream`**: Long-running operations where progress matters\n\n## System Prompt Integration\n\nSkills are automatically injected into the system prompt. The LLM learns:\n\n1. **Available skills** - List of enabled skills with descriptions\n2. **How to use skills** - Instructions for using skill tools\n3. **Tool reference** - Available skill tools (`octavus_skill_read`, `octavus_code_run`, etc.)\n\nYou don't need to manually document skills in your system prompt. However, you can guide the LLM:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a helpful assistant that can generate QR codes.\n\n## When to Generate QR Codes\n\nGenerate QR codes when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Share WiFi credentials\n- Create scannable data\n\nUse the qr-code skill for all QR code generation tasks.\n```\n\n## Error Handling\n\nSkills handle errors gracefully:\n\n```yaml\n# Skill execution errors are returned to the LLM\n# The LLM can retry or explain the error to the user\n```\n\nCommon error scenarios:\n\n1. **Invalid skill slug** - Skill not found in organization\n2. **Code execution errors** - Syntax errors, runtime exceptions\n3. **Missing dependencies** - Required packages not installed\n4. **File I/O errors** - Permission issues, invalid paths\n\nThe LLM receives error messages and can:\n\n- Retry with corrected code\n- Explain errors to users\n- Suggest alternatives\n\n## File Output Patterns\n\n### Single File Output\n\n```python\n# Save single file to /output/\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\nqr = qrcode.QRCode()\nqr.add_data('https://example.com')\nimg = qr.make_image()\nimg.save(f'{output_dir}/qrcode.png')\n```\n\n### Multiple Files\n\n```python\n# Save multiple files\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Generate multiple outputs\nfor i in range(3):\n filename = f'{output_dir}/output_{i}.png'\n # ... generate file ...\n```\n\n### Structured Output\n\n```python\n# Save structured data + files\nimport json\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Save metadata\nmetadata = {\n 'files': ['chart.png', 'data.csv'],\n 'summary': 'Analysis complete'\n}\nwith open(f'{output_dir}/metadata.json', 'w') as f:\n json.dump(metadata, f)\n\n# Save actual files\n# ... generate chart.png and data.csv ...\n```\n\n## Performance Considerations\n\n### Lazy Initialization\n\nSandboxes are created only when a skill tool is first called:\n\n```yaml\nagent:\n skills: [qr-code] # Sandbox created on first skill tool call\n```\n\nThis means:\n\n- No cost if skills aren't used\n- Fast startup (no sandbox creation delay)\n- Each `next-message` execution gets its own sandbox with only the skills it needs\n\n### Timeout Limits\n\nSandboxes default to a 5-minute timeout. Configure `sandboxTimeout` on the agent config or per thread:\n\n```yaml\n# Agent-level\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes\n```\n\n```yaml\n# Thread-level (overrides agent-level)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour for long-running analysis\n```\n\nThread-level `sandboxTimeout` takes priority. Maximum: 1 hour (3,600,000 ms).\n\n### Sandbox Lifecycle\n\nEach `next-message` execution gets its own sandbox:\n\n- **Scoped** - Only contains the skills available to that thread\n- **Isolated** - Interactive agents and workers don't share sandboxes\n- **Resilient** - If a sandbox expires, it's transparently recreated\n- **Cleaned up** - Sandbox destroyed when the LLM call completes\n\n## Combining Skills with Tools\n\nSkills and tools can work together:\n\n```yaml\ntools:\n get-user-data:\n description: Fetch user data from database\n parameters:\n userId: { type: string }\n\nskills:\n data-analysis:\n display: description\n description: Analyzing data\n\nagent:\n tools: [get-user-data]\n skills: [data-analysis]\n agentic: true\n\nhandlers:\n analyze-user:\n Get user data:\n block: tool-call\n tool: get-user-data\n input:\n userId: USER_ID\n output: USER_DATA\n\n Analyze:\n block: next-message\n # LLM can use data-analysis skill with USER_DATA\n```\n\nPattern:\n\n1. Fetch data via tool (from your backend)\n2. LLM uses skill to analyze/process the data\n3. Generate outputs (files, reports)\n\n## Skill Development Tips\n\n### Writing SKILL.md\n\nFocus on **when** and **how** to use the skill:\n\n```markdown\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\n---\n\n# QR Code Generator\n\n## When to Use\n\nUse this skill when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Create scannable data\n\n## Quick Start\n\n[Clear examples of how to use the skill]\n```\n\n### Script Organization\n\nOrganize scripts logically:\n\n```\nskill-name/\n\u251C\u2500\u2500 SKILL.md\n\u2514\u2500\u2500 scripts/\n \u251C\u2500\u2500 generate.py # Main script\n \u251C\u2500\u2500 utils.py # Helper functions\n \u2514\u2500\u2500 requirements.txt # Dependencies\n```\n\n### Error Messages\n\nProvide helpful error messages:\n\n```python\ntry:\n # ... code ...\nexcept ValueError as e:\n print(f\"Error: Invalid input - {e}\")\n sys.exit(1)\n```\n\nThe LLM sees these errors and can retry or explain to users.\n\n## Security Considerations\n\n### Sandbox Isolation\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n### Input Validation\n\nSkills should validate inputs:\n\n```python\nimport sys\n\nif not data:\n print(\"Error: Data is required\")\n sys.exit(1)\n\nif len(data) > 1000:\n print(\"Error: Data too long (max 1000 characters)\")\n sys.exit(1)\n```\n\n### Resource Limits\n\nBe aware of:\n\n- **File size limits** - Large files may fail to upload\n- **Execution time** - Sandbox timeout (5-minute default, 1-hour maximum)\n- **Memory limits** - Sandbox environment constraints\n\n## Debugging Skills\n\n### Check Skill Documentation\n\nThe LLM can read skill docs:\n\n```python\n# LLM calls octavus_skill_read to see skill instructions\n```\n\n### Test Locally\n\nTest skills before uploading:\n\n```bash\n# Test skill locally\npython scripts/generate.py --data \"test\"\n```\n\n### Monitor Execution\n\nCheck execution logs in the platform debug view:\n\n- Tool calls and arguments\n- Code execution results\n- File outputs\n- Error messages\n\n## Common Patterns\n\n### Pattern 1: Generate and Return\n\n```yaml\n# User asks for QR code\n# LLM generates QR code\n# File automatically available for download\n```\n\n### Pattern 2: Analyze and Report\n\n```yaml\n# User provides data\n# LLM analyzes with skill\n# Generates report file\n# Returns summary + file link\n```\n\n### Pattern 3: Transform and Save\n\n```yaml\n# User uploads file (via tool)\n# LLM processes with skill\n# Generates transformed file\n# Returns new file link\n```\n\n## Best Practices Summary\n\n1. **Enable only needed skills** - Don't overwhelm the LLM\n2. **Choose appropriate display modes** - Match user experience needs\n3. **Write clear skill descriptions** - Help LLM understand when to use\n4. **Handle errors gracefully** - Provide helpful error messages\n5. **Test skills locally** - Verify before uploading\n6. **Monitor execution** - Check logs for issues\n7. **Combine with tools** - Use tools for data, skills for processing\n8. **Consider performance** - Be aware of timeouts and limits\n\n## Next Steps\n\n- [Skills](/docs/protocol/skills) - Basic skills documentation\n- [Agent Config](/docs/protocol/agent-config) - Configuring skills\n- [Tools](/docs/protocol/tools) - External tools integration\n",
|
|
627
|
+
content: "\n# Skills Advanced Guide\n\nThis guide covers advanced patterns and best practices for using Octavus skills in your agents.\n\n## When to Use Skills\n\nSkills are ideal for:\n\n- **Code execution** - Running Python/Bash scripts\n- **File generation** - Creating images, PDFs, reports\n- **Data processing** - Analyzing, transforming, or visualizing data\n- **Provider-agnostic needs** - Features that should work with any LLM\n\nUse external tools instead when:\n\n- **Simple API calls** - Database queries, external services\n- **Authentication required** - Accessing user-specific resources\n- **Backend integration** - Tight coupling with your infrastructure\n\n## Skill Selection Strategy\n\n### Defining Available Skills\n\nDefine all skills in the `skills:` section, then reference which skills are available where they're used:\n\n**Interactive agents** \u2014 reference in `agent.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n pdf-processor:\n display: description\n description: Processing PDFs\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\n**Workers and named threads** \u2014 reference per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n\nsteps:\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis]\n maxSteps: 10\n```\n\n### Match Skills to Use Cases\n\nDifferent threads can have different skills. Define all skills at the protocol level, then scope them to each thread:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n visualization:\n display: description\n description: Creating charts and visualizations\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\nFor a data analysis thread, you would specify `[data-analysis, visualization]` in `agent.skills` or in a `start-thread` block's `skills` field.\n\n## Display Mode Strategy\n\nChoose display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n### Guidelines\n\n- **`hidden`**: Background work that doesn't need user awareness\n- **`description`**: User-facing operations (default)\n- **`name`**: Quick operations where name is sufficient\n- **`stream`**: Long-running operations where progress matters\n\n## System Prompt Integration\n\nSkills are automatically injected into the system prompt. The LLM learns:\n\n1. **Available skills** - List of enabled skills with descriptions\n2. **How to use skills** - Instructions for using skill tools\n3. **Tool reference** - Available skill tools (`octavus_skill_read`, `octavus_code_run`, etc.)\n\nYou don't need to manually document skills in your system prompt. However, you can guide the LLM:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a helpful assistant that can generate QR codes.\n\n## When to Generate QR Codes\n\nGenerate QR codes when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Share WiFi credentials\n- Create scannable data\n\nUse the qr-code skill for all QR code generation tasks.\n```\n\n## Error Handling\n\nSkills handle errors gracefully:\n\n```yaml\n# Skill execution errors are returned to the LLM\n# The LLM can retry or explain the error to the user\n```\n\nCommon error scenarios:\n\n1. **Invalid skill slug** - Skill not found in organization\n2. **Code execution errors** - Syntax errors, runtime exceptions\n3. **Missing dependencies** - Required packages not installed\n4. **File I/O errors** - Permission issues, invalid paths\n\nThe LLM receives error messages and can:\n\n- Retry with corrected code\n- Explain errors to users\n- Suggest alternatives\n\n## File Output Patterns\n\n### Single File Output\n\n```python\n# Save single file to /output/\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\nqr = qrcode.QRCode()\nqr.add_data('https://example.com')\nimg = qr.make_image()\nimg.save(f'{output_dir}/qrcode.png')\n```\n\n### Multiple Files\n\n```python\n# Save multiple files\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Generate multiple outputs\nfor i in range(3):\n filename = f'{output_dir}/output_{i}.png'\n # ... generate file ...\n```\n\n### Structured Output\n\n```python\n# Save structured data + files\nimport json\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Save metadata\nmetadata = {\n 'files': ['chart.png', 'data.csv'],\n 'summary': 'Analysis complete'\n}\nwith open(f'{output_dir}/metadata.json', 'w') as f:\n json.dump(metadata, f)\n\n# Save actual files\n# ... generate chart.png and data.csv ...\n```\n\n## Performance Considerations\n\n### Lazy Initialization\n\nSandboxes are created only when a skill tool is first called:\n\n```yaml\nagent:\n skills: [qr-code] # Sandbox created on first skill tool call\n```\n\nThis means:\n\n- No cost if skills aren't used\n- Fast startup (no sandbox creation delay)\n- Each `next-message` execution gets its own sandbox with only the skills it needs\n\n### Timeout Limits\n\nSandboxes default to a 5-minute timeout. Configure `sandboxTimeout` on the agent config or per thread:\n\n```yaml\n# Agent-level\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes\n```\n\n```yaml\n# Thread-level (overrides agent-level)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour for long-running analysis\n```\n\nThread-level `sandboxTimeout` takes priority. Maximum: 1 hour (3,600,000 ms).\n\n### Sandbox Lifecycle\n\nEach `next-message` execution gets its own sandbox:\n\n- **Scoped** - Only contains the skills available to that thread\n- **Isolated** - Interactive agents and workers don't share sandboxes\n- **Resilient** - If a sandbox expires, it's transparently recreated\n- **Cleaned up** - Sandbox destroyed when the LLM call completes\n\n## Combining Skills with Tools\n\nSkills and tools can work together:\n\n```yaml\ntools:\n get-user-data:\n description: Fetch user data from database\n parameters:\n userId: { type: string }\n\nskills:\n data-analysis:\n display: description\n description: Analyzing data\n\nagent:\n tools: [get-user-data]\n skills: [data-analysis]\n agentic: true\n\nhandlers:\n analyze-user:\n Get user data:\n block: tool-call\n tool: get-user-data\n input:\n userId: USER_ID\n output: USER_DATA\n\n Analyze:\n block: next-message\n # LLM can use data-analysis skill with USER_DATA\n```\n\nPattern:\n\n1. Fetch data via tool (from your backend)\n2. LLM uses skill to analyze/process the data\n3. Generate outputs (files, reports)\n\n## Secure Skills\n\nWhen a skill declares secrets and an organization configures them, the skill runs in secure mode with its own isolated sandbox.\n\n### Standard vs Secure Skills\n\n| Aspect | Standard Skills | Secure Skills |\n| ------------------- | --------------------------------- | --------------------------------------------------- |\n| **Sandbox** | Shared with other standard skills | Isolated (one per skill) |\n| **Available tools** | All 6 skill tools | `skill_read`, `skill_list`, `skill_run` only |\n| **Script input** | CLI arguments via `args` | JSON via stdin (use `input` parameter) |\n| **Environment** | No secrets | Secrets as env vars |\n| **Output** | Raw stdout/stderr | Redacted (secret values replaced with `[REDACTED]`) |\n\n### Writing Scripts for Secure Skills\n\nSecure skill scripts receive structured input via stdin (JSON) and access secrets from environment variables:\n\n```python\n#!/usr/bin/env python3\nimport json\nimport os\nimport sys\nimport subprocess\n\ninput_data = json.load(sys.stdin)\ntoken = os.environ[\"GITHUB_TOKEN\"]\n\nrepo = input_data.get(\"repo\", \"\")\nresult = subprocess.run(\n [\"gh\", \"repo\", \"view\", repo, \"--json\", \"name,description\"],\n capture_output=True, text=True,\n env={**os.environ, \"GH_TOKEN\": token}\n)\n\nprint(result.stdout)\n```\n\nKey patterns:\n\n- **Read stdin**: `json.load(sys.stdin)` to get the `input` object from the `octavus_skill_run` call\n- **Access secrets**: `os.environ[\"SECRET_NAME\"]` \u2014 secrets are injected as env vars\n- **Print output**: Write results to stdout \u2014 the LLM sees the (redacted) stdout\n- **Error handling**: Write errors to stderr and exit with non-zero code\n\n### Declaring Secrets in SKILL.md\n\n```yaml\n---\nname: github\ndescription: >\n Run GitHub CLI (gh) commands to manage repos, issues, PRs, and more.\nsecrets:\n - name: GITHUB_TOKEN\n description: GitHub personal access token with repo access\n required: true\n - name: GITHUB_ORG\n description: Default GitHub organization\n required: false\n---\n```\n\n### Testing Secure Skills Locally\n\nYou can test scripts locally by piping JSON to stdin:\n\n```bash\necho '{\"repo\": \"octavus-ai/agent-sdk\"}' | GITHUB_TOKEN=ghp_xxx python scripts/list-issues.py\n```\n\n## Skill Development Tips\n\n### Writing SKILL.md\n\nFocus on **when** and **how** to use the skill:\n\n```markdown\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\n---\n\n# QR Code Generator\n\n## When to Use\n\nUse this skill when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Create scannable data\n\n## Quick Start\n\n[Clear examples of how to use the skill]\n```\n\n### Script Organization\n\nOrganize scripts logically:\n\n```\nskill-name/\n\u251C\u2500\u2500 SKILL.md\n\u2514\u2500\u2500 scripts/\n \u251C\u2500\u2500 generate.py # Main script\n \u251C\u2500\u2500 utils.py # Helper functions\n \u2514\u2500\u2500 requirements.txt # Dependencies\n```\n\n### Error Messages\n\nProvide helpful error messages:\n\n```python\ntry:\n # ... code ...\nexcept ValueError as e:\n print(f\"Error: Invalid input - {e}\")\n sys.exit(1)\n```\n\nThe LLM sees these errors and can retry or explain to users.\n\n## Security Considerations\n\n### Sandbox Isolation\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n### Secret Protection\n\nFor skills with configured secrets:\n\n- **Isolated sandbox** \u2014 each secure skill gets its own sandbox, preventing cross-skill secret leakage\n- **No arbitrary code** \u2014 `octavus_code_run`, `octavus_file_write`, and `octavus_file_read` are blocked for secure skills, so only pre-built scripts can execute\n- **Output redaction** \u2014 all stdout and stderr are scanned for secret values before being returned to the LLM\n- **Encrypted at rest** \u2014 secrets are encrypted using AES-256-GCM and only decrypted at execution time\n\n### Input Validation\n\nSkills should validate inputs:\n\n```python\nimport sys\n\nif not data:\n print(\"Error: Data is required\")\n sys.exit(1)\n\nif len(data) > 1000:\n print(\"Error: Data too long (max 1000 characters)\")\n sys.exit(1)\n```\n\n### Resource Limits\n\nBe aware of:\n\n- **File size limits** - Large files may fail to upload\n- **Execution time** - Sandbox timeout (5-minute default, 1-hour maximum)\n- **Memory limits** - Sandbox environment constraints\n\n## Debugging Skills\n\n### Check Skill Documentation\n\nThe LLM can read skill docs:\n\n```python\n# LLM calls octavus_skill_read to see skill instructions\n```\n\n### Test Locally\n\nTest skills before uploading:\n\n```bash\n# Test skill locally\npython scripts/generate.py --data \"test\"\n```\n\n### Monitor Execution\n\nCheck execution logs in the platform debug view:\n\n- Tool calls and arguments\n- Code execution results\n- File outputs\n- Error messages\n\n## Common Patterns\n\n### Pattern 1: Generate and Return\n\n```yaml\n# User asks for QR code\n# LLM generates QR code\n# File automatically available for download\n```\n\n### Pattern 2: Analyze and Report\n\n```yaml\n# User provides data\n# LLM analyzes with skill\n# Generates report file\n# Returns summary + file link\n```\n\n### Pattern 3: Transform and Save\n\n```yaml\n# User uploads file (via tool)\n# LLM processes with skill\n# Generates transformed file\n# Returns new file link\n```\n\n## Best Practices Summary\n\n1. **Enable only needed skills** \u2014 Don't overwhelm the LLM\n2. **Choose appropriate display modes** \u2014 Match user experience needs\n3. **Write clear skill descriptions** \u2014 Help LLM understand when to use\n4. **Handle errors gracefully** \u2014 Provide helpful error messages\n5. **Test skills locally** \u2014 Verify before uploading\n6. **Monitor execution** \u2014 Check logs for issues\n7. **Combine with tools** \u2014 Use tools for data, skills for processing\n8. **Consider performance** \u2014 Be aware of timeouts and limits\n9. **Use secrets for credentials** \u2014 Declare secrets in frontmatter instead of hardcoding tokens\n10. **Design scripts for stdin input** \u2014 Secure skills receive JSON via stdin, so plan for both input methods if the skill might be used in either mode\n\n## Next Steps\n\n- [Skills](/docs/protocol/skills) - Basic skills documentation\n- [Agent Config](/docs/protocol/agent-config) - Configuring skills\n- [Tools](/docs/protocol/tools) - External tools integration\n",
|
|
617
628
|
excerpt: "Skills Advanced Guide This guide covers advanced patterns and best practices for using Octavus skills in your agents. When to Use Skills Skills are ideal for: - Code execution - Running Python/Bash...",
|
|
618
629
|
order: 9
|
|
619
630
|
},
|
|
@@ -644,6 +655,15 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
644
655
|
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...",
|
|
645
656
|
order: 12
|
|
646
657
|
},
|
|
658
|
+
{
|
|
659
|
+
slug: "protocol/mcp-servers",
|
|
660
|
+
section: "protocol",
|
|
661
|
+
title: "MCP Servers",
|
|
662
|
+
description: "Connecting agents to external tools via Model Context Protocol (MCP).",
|
|
663
|
+
content: "\n# MCP Servers\n\nMCP servers extend your agent with tools from external services. Define them in your protocol, and agents automatically discover and use their tools at runtime.\n\nThere are two types of MCP servers:\n\n| Source | Description | Example |\n| -------- | -------------------------------------------------------------------- | ------------------------------- |\n| `remote` | HTTP-based MCP servers, managed by the platform | Figma, Sentry, GitHub |\n| `device` | Local MCP servers running on the consumer's machine via server-sdk | Browser automation, filesystem |\n\n## Defining MCP Servers\n\nMCP servers are defined in the `mcpServers:` section. The key becomes the **namespace** for all tools from that server.\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n```\n\n### Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------ |\n| `description` | Yes | What the MCP server provides |\n| `source` | Yes | `remote` (platform-managed) or `device` (consumer-provided) |\n| `display` | No | How tool calls appear in UI: `hidden`, `name`, `description` |\n\n### Display Modes\n\nDisplay modes control visibility of all tool calls from the MCP server, using the same modes as [regular tools](/docs/protocol/tools#display-modes):\n\n| Mode | Behavior |\n| ------------- | -------------------------------------------- |\n| `hidden` | Tool calls run silently |\n| `name` | Shows tool name while executing |\n| `description` | Shows tool description while executing |\n\n## Making MCP Servers Available\n\nLike tools, MCP servers defined in `mcpServers:` must be referenced in `agent.mcpServers` to be available:\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n\n filesystem:\n description: Filesystem access for reading and writing files\n source: device\n display: hidden\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n mcpServers: [figma, sentry, browser, filesystem]\n tools: [set-chat-title]\n agentic: true\n maxSteps: 100\n```\n\n## Tool Namespacing\n\nAll MCP tools are automatically namespaced using `__` (double underscore) as a separator. The namespace comes from the `mcpServers` key.\n\nFor example, a server defined as `browser:` that exposes `navigate_page` and `click` produces:\n\n- `browser__navigate_page`\n- `browser__click`\n\nA server defined as `figma:` that exposes `get_design_context` produces:\n\n- `figma__get_design_context`\n\nThe namespace is stripped before calling the MCP server \u2014 the server receives the original tool name. This convention matches Anthropic's MCP integration in Claude Desktop and ensures tool names stay unique across servers.\n\n### What the LLM Sees\n\nWhen an agent has both regular tools and MCP servers configured, the LLM sees all tools combined:\n\n```\nProtocol tools:\n set-chat-title\n\nRemote MCP tools (auto-discovered):\n figma__get_design_context\n figma__get_screenshot\n sentry__get_issues\n sentry__get_issue_details\n\nDevice MCP tools (auto-discovered):\n browser__navigate_page\n browser__click\n browser__take_snapshot\n filesystem__read_file\n filesystem__write_file\n filesystem__list_directory\n```\n\nYou don't define individual MCP tool schemas in the protocol \u2014 they're auto-discovered from each MCP server at runtime.\n\n## Remote MCP Servers\n\nRemote MCP servers (`source: remote`) connect to HTTP-based MCP endpoints. The platform manages the connection, authentication, and tool discovery.\n\nConfiguration happens in the Octavus platform UI:\n\n1. Add an MCP server to your project (URL + authentication)\n2. The server's slug must match the namespace in your protocol\n3. The platform connects, discovers tools, and makes them available to the agent\n\n### Authentication\n\nRemote MCP servers support multiple authentication methods:\n\n| Auth Type | Description |\n| ----------- | ------------------------------------ |\n| MCP OAuth | Standard MCP OAuth flow |\n| API Key | Static API key sent as a header |\n| Bearer | Bearer token authentication |\n| None | No authentication required |\n\nAuthentication is configured per-project \u2014 different projects can connect to the same MCP server with different credentials.\n\n## Device MCP Servers\n\nDevice MCP servers (`source: device`) run on the consumer's machine. The consumer provides the MCP tools via the `@octavus/computer` package (or any `ToolProvider` implementation) through the server-sdk.\n\nWhen an agent has device MCP servers:\n\n1. The consumer creates a `Computer` with matching namespaces\n2. `@octavus/computer` discovers tools from each MCP server\n3. Tool schemas are sent to the platform via the server-sdk\n4. Tool calls flow back to the consumer for execution\n\nSee [`@octavus/computer`](/docs/server-sdk/computer) for the full integration guide.\n\n### Namespace Matching\n\nThe `mcpServers` keys in the protocol must match the keys in the consumer's `Computer` configuration:\n\n```yaml\n# protocol.yaml\nmcpServers:\n browser: # \u2190 must match\n source: device\n filesystem: # \u2190 must match\n source: device\n```\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', ['--browser-url=...']),\n filesystem: Computer.stdio('@modelcontextprotocol/server-filesystem', [dir]),\n },\n});\n```\n\nIf the consumer provides a namespace not declared in the protocol, the platform rejects it.\n\n## Thread-Level Scoping\n\nThreads can scope which MCP servers are available, the same way they scope [tools](/docs/protocol/handlers#start-thread):\n\n```yaml\nhandlers:\n user-message:\n Start research:\n block: start-thread\n thread: research\n mcpServers: [figma, browser]\n tools: [set-chat-title]\n system: research-prompt\n```\n\nThis thread can use Figma and browser tools, but not sentry or filesystem \u2014 even if those are available on the main agent.\n\n## Full Example\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n filesystem:\n description: Filesystem access for reading and writing files\n source: device\n display: hidden\n shell:\n description: Shell command execution\n source: device\n display: name\n\ntools:\n set-chat-title:\n description: Set the title of the current chat.\n parameters:\n title: { type: string, description: The title to set }\n\nagent:\n model: anthropic/claude-opus-4-6\n system: system\n mcpServers: [figma, sentry, browser, filesystem, shell]\n tools: [set-chat-title]\n thinking: medium\n maxSteps: 300\n agentic: true\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### Cloud-Only Agent\n\nAgents that only use remote MCP servers don't need `@octavus/computer`:\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n\ntools:\n submit-code:\n description: Submit code to the user.\n parameters:\n code: { type: string }\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n mcpServers: [figma, sentry]\n tools: [submit-code]\n agentic: true\n```\n",
|
|
664
|
+
excerpt: "MCP Servers MCP servers extend your agent with tools from external services. Define them in your protocol, and agents automatically discover and use their tools at runtime. There are two types of MCP...",
|
|
665
|
+
order: 13
|
|
666
|
+
},
|
|
647
667
|
{
|
|
648
668
|
slug: "api-reference/overview",
|
|
649
669
|
section: "api-reference",
|
|
@@ -685,7 +705,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
685
705
|
section: "examples",
|
|
686
706
|
title: "Next.js Chat",
|
|
687
707
|
description: "Building a chat interface with Next.js and HTTP transport.",
|
|
688
|
-
content: "\n# Next.js Chat Example\n\nThis example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications.\n\n## What You're Building\n\nA chat interface that:\n\n- Creates sessions server-side\n- Streams AI responses in real-time\n- Handles tool calls on your server\n- Shows typing status during streaming\n\n## Architecture\n\n```mermaid\nflowchart LR\n Browser[\"Browser<br/>(React)\"] -->|\"POST /api/chat\"| NextJS[\"Next.js API<br/>Routes\"]\n NextJS -->|\"SSE\"| Browser\n NextJS --> Platform[\"Octavus Platform\"]\n```\n\n## Prerequisites\n\n- Next.js 14+ with App Router\n- Octavus account with API key\n- An agent configured in Octavus\n\n## Step 1: Install Dependencies\n\n```bash\nnpm install @octavus/server-sdk @octavus/react\n```\n\n## Step 2: Configure Environment\n\n```bash\n# .env.local\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key\n```\n\n## Step 3: Create the Octavus Client\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n## Step 4: Create Upload URLs Endpoint (Optional)\n\nIf your agent supports file uploads (images, documents), create an endpoint to get presigned URLs:\n\n```typescript\n// app/api/upload-urls/route.ts\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## Step 5: Create Session Endpoint\n\nSessions hold conversation state. Create one when the user opens the chat:\n\n```typescript\n// app/api/sessions/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { agentId, input } = await request.json();\n\n // Create a new session with initial input variables\n const sessionId = await octavus.agentSessions.create(agentId, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n**Protocol Note:** The `input` object contains variables defined in your agent's protocol. For example, if your agent has `COMPANY_NAME` as an input variable:\n\n```typescript\nconst sessionId = await octavus.agentSessions.create(agentId, {\n COMPANY_NAME: 'Acme Corp',\n USER_ID: user.id,\n});\n```\n\n## Step 6: Create Trigger Endpoint\n\nTriggers execute agent actions. The `user-message` trigger is the most common:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to the session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Server-side tool handlers run on YOUR server, not Octavus\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n const user = await db.users.findUnique({ where: { id: userId } });\n return {\n name: user.name,\n email: user.email,\n plan: user.plan,\n };\n },\n\n 'create-support-ticket': async (args) => {\n const ticket = await db.tickets.create({\n data: {\n summary: args.summary as string,\n priority: args.priority as string,\n },\n });\n return {\n ticketId: ticket.id,\n estimatedResponse: '24 hours',\n };\n },\n\n // Tools without handlers here are forwarded to the client\n // See Client Tools docs for handling on frontend\n },\n });\n\n // execute() handles both triggers and client tool continuations\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n```\n\n**Protocol Note:** Tool names and arguments are defined in your agent's protocol YAML. The tool handlers here must match those definitions. Tools without server handlers are forwarded to the client.\n\n## Step 7: Build the Chat Component\n\n```tsx\n// components/Chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create transport - 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 handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || status === 'streaming') return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Send triggers the 'user-message' action\n // The third argument adds the user message to the UI\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-screen\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <div key={msg.id} className={msg.role === 'user' ? 'text-right' : 'text-left'}>\n <div\n className={`inline-block p-3 rounded-lg ${\n msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-100'\n }`}\n >\n {msg.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n if (part.type === 'tool-call') {\n return (\n <div key={i} className=\"text-sm opacity-70\">\n Using {part.toolName}...\n </div>\n );\n }\n return null;\n })}\n </div>\n </div>\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={status === 'streaming'}\n />\n <button\n type=\"submit\"\n disabled={status === 'streaming'}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg\"\n >\n {status === 'streaming' ? 'Sending...' : 'Send'}\n </button>\n </div>\n </form>\n </div>\n );\n}\n```\n\n## Step 8: Create the Page\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/Chat';\n\nconst AGENT_ID = 'your-agent-id'; // From Octavus dashboard\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n // Create session on mount\n fetch('/api/sessions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n agentId: AGENT_ID,\n input: {\n COMPANY_NAME: 'Acme Corp',\n },\n }),\n })\n .then((res) => res.json())\n .then((data) => setSessionId(data.sessionId));\n }, []);\n\n if (!sessionId) {\n return <div className=\"p-8\">Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Protocol Integration\n\nYour agent's protocol defines the triggers and tools. Here's how the code maps to protocol:\n\n### Triggers\n\n```yaml\n# In your agent's protocol.yaml\ntriggers:\n user-message:\n description: User sends a chat message\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n```\n\nThe `send()` call maps directly:\n\n```typescript\nawait send(\n 'user-message', // trigger name\n { USER_MESSAGE: message }, // trigger inputs\n { userMessage: { content: message } },\n);\n```\n\n### Tools\n\n```yaml\n# In your agent's protocol.yaml\ntools:\n get-user-account:\n description: Fetch user account details\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\nTool handlers receive the parameters as `args`:\n\n```typescript\n'get-user-account': async (args) => {\n const userId = args.userId as string;\n // ...\n}\n```\n\n## Next Steps\n\n- [Protocol Overview](/docs/protocol/overview) \u2014 Define agent behavior\n- [Messages](/docs/client-sdk/messages) \u2014 Rich message rendering\n- [Streaming](/docs/client-sdk/streaming) \u2014 Advanced streaming UI\n",
|
|
708
|
+
content: "\n# Next.js Chat Example\n\nThis example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications.\n\n## What You're Building\n\nA chat interface that:\n\n- Creates sessions server-side\n- Streams AI responses in real-time\n- Handles tool calls on your server\n- Shows typing status during streaming\n\n## Architecture\n\n```mermaid\nflowchart LR\n Browser[\"Browser<br/>(React)\"] -->|\"POST /api/chat\"| NextJS[\"Next.js API<br/>Routes\"]\n NextJS -->|\"SSE\"| Browser\n NextJS --> Platform[\"Octavus Platform\"]\n```\n\n## Prerequisites\n\n- Next.js 14+ with App Router\n- Octavus account with API key\n- An agent configured in Octavus\n\n## Step 1: Install Dependencies\n\n```bash\nnpm install @octavus/server-sdk @octavus/react\n```\n\n## Step 2: Configure Environment\n\n```bash\n# .env.local\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key\n```\n\n## Step 3: Create the Octavus Client\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n## Step 4: Create Upload URLs Endpoint (Optional)\n\nIf your agent supports file uploads (images, documents), create an endpoint to get presigned URLs:\n\n```typescript\n// app/api/upload-urls/route.ts\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## Step 5: Create Session Endpoint\n\nSessions hold conversation state. Create one when the user opens the chat:\n\n```typescript\n// app/api/sessions/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { agentId, input } = await request.json();\n\n // Create a new session with initial input variables\n const sessionId = await octavus.agentSessions.create(agentId, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n**Protocol Note:** The `input` object contains variables defined in your agent's protocol. For example, if your agent has `COMPANY_NAME` as an input variable:\n\n```typescript\nconst sessionId = await octavus.agentSessions.create(agentId, {\n COMPANY_NAME: 'Acme Corp',\n USER_ID: user.id,\n});\n```\n\n## Step 6: Create Trigger Endpoint\n\nTriggers execute agent actions. The `user-message` trigger is the most common:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to the session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Server-side tool handlers run on YOUR server, not Octavus\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n const user = await db.users.findUnique({ where: { id: userId } });\n return {\n name: user.name,\n email: user.email,\n plan: user.plan,\n };\n },\n\n 'create-support-ticket': async (args) => {\n const ticket = await db.tickets.create({\n data: {\n summary: args.summary as string,\n priority: args.priority as string,\n },\n });\n return {\n ticketId: ticket.id,\n estimatedResponse: '24 hours',\n };\n },\n\n // Tools without handlers here are forwarded to the client\n // See Client Tools docs for handling on frontend\n },\n });\n\n // execute() handles both triggers and client tool continuations\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n}\n```\n\n**Protocol Note:** Tool names and arguments are defined in your agent's protocol YAML. The tool handlers here must match those definitions. Tools without server handlers are forwarded to the client.\n\n## Step 7: Build the Chat Component\n\n```tsx\n// components/Chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create transport - 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 handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || status === 'streaming') return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Send triggers the 'user-message' action\n // The third argument adds the user message to the UI\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-screen\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <div key={msg.id} className={msg.role === 'user' ? 'text-right' : 'text-left'}>\n <div\n className={`inline-block p-3 rounded-lg ${\n msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-100'\n }`}\n >\n {msg.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n if (part.type === 'tool-call') {\n return (\n <div key={i} className=\"text-sm opacity-70\">\n Using {part.toolName}...\n </div>\n );\n }\n return null;\n })}\n </div>\n </div>\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={status === 'streaming'}\n />\n <button\n type=\"submit\"\n disabled={status === 'streaming'}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg\"\n >\n {status === 'streaming' ? 'Sending...' : 'Send'}\n </button>\n </div>\n </form>\n </div>\n );\n}\n```\n\n## Step 8: Create the Page\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/Chat';\n\nconst AGENT_ID = 'your-agent-id'; // From Octavus dashboard\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n // Create session on mount\n fetch('/api/sessions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n agentId: AGENT_ID,\n input: {\n COMPANY_NAME: 'Acme Corp',\n },\n }),\n })\n .then((res) => res.json())\n .then((data) => setSessionId(data.sessionId));\n }, []);\n\n if (!sessionId) {\n return <div className=\"p-8\">Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Protocol Integration\n\nYour agent's protocol defines the triggers and tools. Here's how the code maps to protocol:\n\n### Triggers\n\n```yaml\n# In your agent's protocol.yaml\ntriggers:\n user-message:\n description: User sends a chat message\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n```\n\nThe `send()` call maps directly:\n\n```typescript\nawait send(\n 'user-message', // trigger name\n { USER_MESSAGE: message }, // trigger inputs\n { userMessage: { content: message } },\n);\n```\n\n### Tools\n\n```yaml\n# In your agent's protocol.yaml\ntools:\n get-user-account:\n description: Fetch user account details\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\nTool handlers receive the parameters as `args`:\n\n```typescript\n'get-user-account': async (args) => {\n const userId = args.userId as string;\n // ...\n}\n```\n\n## Next Steps\n\n- [Protocol Overview](/docs/protocol/overview) \u2014 Define agent behavior\n- [Messages](/docs/client-sdk/messages) \u2014 Rich message rendering\n- [Streaming](/docs/client-sdk/streaming) \u2014 Advanced streaming UI\n",
|
|
689
709
|
excerpt: "Next.js Chat Example This example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications. What You're Building A...",
|
|
690
710
|
order: 2
|
|
691
711
|
},
|
|
@@ -731,7 +751,7 @@ var sections_default = [
|
|
|
731
751
|
section: "getting-started",
|
|
732
752
|
title: "Quick Start",
|
|
733
753
|
description: "Get your first Octavus agent running in minutes.",
|
|
734
|
-
content: "\n# Quick Start\n\nThis guide will walk you through integrating Octavus into your application in under 10 minutes.\n\n## Prerequisites\n\n- Node.js 18+\n- An Octavus account with API key\n- A Next.js application (or any Node.js backend)\n\n## Test Your Agent First\n\nBefore integrating with SDKs, use **Agent Preview** to test your agent directly in the platform:\n\n1. Open your agent in the platform at `octavus.ai/platform/agents/[agentId]`\n2. Click the **Preview** tab\n3. Configure session inputs and tool mock responses\n4. Start a conversation to test agent behavior\n\nAgent Preview supports all trigger types, file attachments, tool mocking, and real-time streaming. This is the fastest way to iterate on your agent logic before writing any integration code.\n\n## Installation\n\nInstall the Octavus SDKs in your project:\n\n```bash\n# Server SDK for backend\nnpm install @octavus/server-sdk\n\n# React bindings for frontend\nnpm install @octavus/react\n```\n\n## Backend Setup\n\n### 1. Initialize the Client\n\nCreate an Octavus client instance in your backend:\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n### 2. Create a Session Endpoint\n\nCreate an API endpoint that creates sessions and returns the session ID:\n\n```typescript\n// app/api/chat/create/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\n// Agent ID - get from platform or CLI (see below)\nconst SUPPORT_AGENT_ID = process.env.OCTAVUS_SUPPORT_AGENT_ID!;\n\nexport async function POST(request: Request) {\n const { input } = await request.json();\n\n // Create a new session using the agent ID\n const sessionId = await octavus.agentSessions.create(SUPPORT_AGENT_ID, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n### Getting Your Agent ID\n\nThere are two ways to create and manage agents:\n\n**Option 1: Platform UI (Recommended for getting started)**\n\n1. Go to [octavus.ai](https://octavus.ai) and create an agent in the web editor\n2. Copy the agent ID from the URL (e.g., `octavus.ai/platform/agents/clxyz123abc456`)\n3. Add it to your `.env.local`: `OCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456`\n\n**Option 2: Local Development with CLI**\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\nnpm install --save-dev @octavus/cli\noctavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n```\n\nThe CLI approach is better for teams and CI/CD pipelines where you want agent definitions in your repository.\n\n### 3. Create a Trigger Endpoint\n\nCreate an endpoint that handles triggers and streams responses:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Define tool handlers that run on your server\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n return {\n name: 'Demo User',\n email: 'demo@example.com',\n plan: 'pro',\n };\n },\n 'create-support-ticket': async (args) => {\n // Create ticket in your system\n return {\n ticketId: 'TICKET-123',\n estimatedResponse: '24 hours',\n };\n },\n },\n });\n\n // Execute the request and convert to SSE stream\n const events = session.execute(payload, { signal: request.signal });\n\n // Return as streaming response\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n```\n\n## Frontend Setup\n\n### 1. Create a Chat Component\n\nUse the `useOctavusChat` hook with the HTTP transport:\n\n```tsx\n// components/chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create a stable transport instance\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, error, send } = useOctavusChat({ transport });\n\n const isStreaming = status === 'streaming';\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || isStreaming) return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Add user message and trigger in one call\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={isStreaming}\n />\n <button\n type=\"submit\"\n disabled={isStreaming}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50\"\n >\n Send\n </button>\n </div>\n </form>\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n const isUser = message.role === 'user';\n\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>\n <div\n className={`p-3 rounded-lg max-w-md ${isUser ? 'bg-blue-500 text-white' : 'bg-gray-100'}`}\n >\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\n {/* Streaming indicator */}\n {message.status === 'streaming' && (\n <span className=\"inline-block w-2 h-4 bg-gray-400 animate-pulse ml-1\" />\n )}\n </div>\n </div>\n );\n}\n```\n\n### 2. Create Session and Render Chat\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/chat';\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n async function createSession() {\n const response = await fetch('/api/chat/create', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n input: {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n },\n }),\n });\n const { sessionId } = await response.json();\n setSessionId(sessionId);\n }\n\n createSession();\n }, []);\n\n if (!sessionId) {\n return <div>Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Environment Variables\n\nAdd these to your `.env.local`:\n\n```bash\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key-here\n```\n\n## What's Next?\n\nNow that you have a basic integration working:\n\n- [Learn about the protocol](/docs/protocol/overview) to define custom agent behavior\n- [Explore the Server SDK](/docs/server-sdk/overview) for advanced backend features\n- [Build rich UIs](/docs/client-sdk/overview) with the Client SDK\n- [Handle tools on the client](/docs/client-sdk/client-tools) for interactive UIs and browser APIs\n",
|
|
754
|
+
content: "\n# Quick Start\n\nThis guide will walk you through integrating Octavus into your application in under 10 minutes.\n\n## Prerequisites\n\n- Node.js 18+\n- An Octavus account with API key\n- A Next.js application (or any Node.js backend)\n\n## Test Your Agent First\n\nBefore integrating with SDKs, use **Agent Preview** to test your agent directly in the platform:\n\n1. Open your agent in the platform at `octavus.ai/platform/agents/[agentId]`\n2. Click the **Preview** tab\n3. Configure session inputs and tool mock responses\n4. Start a conversation to test agent behavior\n\nAgent Preview supports all trigger types, file attachments, tool mocking, and real-time streaming. This is the fastest way to iterate on your agent logic before writing any integration code.\n\n## Installation\n\nInstall the Octavus SDKs in your project:\n\n```bash\n# Server SDK for backend\nnpm install @octavus/server-sdk\n\n# React bindings for frontend\nnpm install @octavus/react\n```\n\n## Backend Setup\n\n### 1. Initialize the Client\n\nCreate an Octavus client instance in your backend:\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n### 2. Create a Session Endpoint\n\nCreate an API endpoint that creates sessions and returns the session ID:\n\n```typescript\n// app/api/chat/create/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\n// Agent ID - get from platform or CLI (see below)\nconst SUPPORT_AGENT_ID = process.env.OCTAVUS_SUPPORT_AGENT_ID!;\n\nexport async function POST(request: Request) {\n const { input } = await request.json();\n\n // Create a new session using the agent ID\n const sessionId = await octavus.agentSessions.create(SUPPORT_AGENT_ID, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n### Getting Your Agent ID\n\nThere are two ways to create and manage agents:\n\n**Option 1: Platform UI (Recommended for getting started)**\n\n1. Go to [octavus.ai](https://octavus.ai) and create an agent in the web editor\n2. Copy the agent ID from the URL (e.g., `octavus.ai/platform/agents/clxyz123abc456`)\n3. Add it to your `.env.local`: `OCTAVUS_SUPPORT_AGENT_ID=clxyz123abc456`\n\n**Option 2: Local Development with CLI**\n\nFor version-controlled agent definitions, use the [Octavus CLI](/docs/server-sdk/cli):\n\n```bash\nnpm install --save-dev @octavus/cli\noctavus sync ./agents/support-chat\n# Output: Agent ID: clxyz123abc456\n```\n\nThe CLI approach is better for teams and CI/CD pipelines where you want agent definitions in your repository.\n\n### 3. Create a Trigger Endpoint\n\nCreate an endpoint that handles triggers and streams responses:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Define tool handlers that run on your server\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n return {\n name: 'Demo User',\n email: 'demo@example.com',\n plan: 'pro',\n };\n },\n 'create-support-ticket': async (args) => {\n // Create ticket in your system\n return {\n ticketId: 'TICKET-123',\n estimatedResponse: '24 hours',\n };\n },\n },\n });\n\n // Execute the request and convert to SSE stream\n const events = session.execute(payload, { signal: request.signal });\n\n // Return as streaming response\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n}\n```\n\n## Frontend Setup\n\n### 1. Create a Chat Component\n\nUse the `useOctavusChat` hook with the HTTP transport:\n\n```tsx\n// components/chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport, type UIMessage } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create a stable transport instance\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, error, send } = useOctavusChat({ transport });\n\n const isStreaming = status === 'streaming';\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || isStreaming) return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Add user message and trigger in one call\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <MessageBubble key={msg.id} message={msg} />\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={isStreaming}\n />\n <button\n type=\"submit\"\n disabled={isStreaming}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg disabled:opacity-50\"\n >\n Send\n </button>\n </div>\n </form>\n </div>\n );\n}\n\nfunction MessageBubble({ message }: { message: UIMessage }) {\n const isUser = message.role === 'user';\n\n return (\n <div className={`flex ${isUser ? 'justify-end' : 'justify-start'}`}>\n <div\n className={`p-3 rounded-lg max-w-md ${isUser ? 'bg-blue-500 text-white' : 'bg-gray-100'}`}\n >\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\n {/* Streaming indicator */}\n {message.status === 'streaming' && (\n <span className=\"inline-block w-2 h-4 bg-gray-400 animate-pulse ml-1\" />\n )}\n </div>\n </div>\n );\n}\n```\n\n### 2. Create Session and Render Chat\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/chat';\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n async function createSession() {\n const response = await fetch('/api/chat/create', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n input: {\n COMPANY_NAME: 'Acme Corp',\n PRODUCT_NAME: 'Widget Pro',\n },\n }),\n });\n const { sessionId } = await response.json();\n setSessionId(sessionId);\n }\n\n createSession();\n }, []);\n\n if (!sessionId) {\n return <div>Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Environment Variables\n\nAdd these to your `.env.local`:\n\n```bash\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key-here\n```\n\n## What's Next?\n\nNow that you have a basic integration working:\n\n- [Learn about the protocol](/docs/protocol/overview) to define custom agent behavior\n- [Explore the Server SDK](/docs/server-sdk/overview) for advanced backend features\n- [Build rich UIs](/docs/client-sdk/overview) with the Client SDK\n- [Handle tools on the client](/docs/client-sdk/client-tools) for interactive UIs and browser APIs\n",
|
|
735
755
|
excerpt: "Quick Start This guide will walk you through integrating Octavus into your application in under 10 minutes. Prerequisites - Node.js 18+ - An Octavus account with API key - A Next.js application (or...",
|
|
736
756
|
order: 2
|
|
737
757
|
}
|
|
@@ -748,7 +768,7 @@ var sections_default = [
|
|
|
748
768
|
section: "server-sdk",
|
|
749
769
|
title: "Overview",
|
|
750
770
|
description: "Introduction to the Octavus Server SDK for backend integration.",
|
|
751
|
-
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.
|
|
771
|
+
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.16.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",
|
|
752
772
|
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...",
|
|
753
773
|
order: 1
|
|
754
774
|
},
|
|
@@ -775,7 +795,7 @@ var sections_default = [
|
|
|
775
795
|
section: "server-sdk",
|
|
776
796
|
title: "Streaming",
|
|
777
797
|
description: "Understanding stream events from the Server SDK.",
|
|
778
|
-
content: "\n# Streaming\n\nAll Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates.\n\n## Stream Response\n\nWhen you execute a request, you get an async generator of parsed events:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of StreamEvent\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// For HTTP endpoints, convert to SSE stream\nreturn new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n});\n\n// For sockets, iterate events directly\nfor await (const event of events) {\n conn.write(JSON.stringify(event));\n}\n```\n\n## Event Types\n\nThe stream emits various event types for lifecycle, text, reasoning, and tool interactions.\n\n### Lifecycle Events\n\n```typescript\n// Stream started\n{ type: 'start', messageId: '...', executionId: '...' }\n\n// Stream completed\n{ type: 'finish', finishReason: 'stop' }\n\n// Possible finish reasons:\n// - 'stop': Normal completion\n// - 'tool-calls': Waiting for server tool execution (handled by SDK internally)\n// - 'client-tool-calls': Waiting for client tool execution\n// - 'length': Max tokens reached\n// - 'content-filter': Content filtered\n// - 'error': Error occurred\n// - 'other': Other reason\n\n// Error event (see Error Handling docs for full structure)\n{ type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }\n```\n\n### Block Events\n\nTrack execution progress:\n\n```typescript\n// Block started\n{ type: 'block-start', blockId: '...', blockName: 'Respond to user', blockType: 'next-message', display: 'stream', thread: 'main' }\n\n// Block completed\n{ type: 'block-end', blockId: '...', summary: 'Generated response' }\n```\n\n### Text Events\n\nStreaming text content:\n\n```typescript\n// Text generation started\n{ type: 'text-start', id: '...' }\n\n// Incremental text (most common event)\n{ type: 'text-delta', id: '...', delta: 'Hello' }\n{ type: 'text-delta', id: '...', delta: '!' }\n{ type: 'text-delta', id: '...', delta: ' How' }\n{ type: 'text-delta', id: '...', delta: ' can' }\n{ type: 'text-delta', id: '...', delta: ' I' }\n{ type: 'text-delta', id: '...', delta: ' help?' }\n\n// Text generation ended\n{ type: 'text-end', id: '...' }\n```\n\n### Reasoning Events\n\nExtended reasoning (for supported models like Claude):\n\n```typescript\n// Reasoning started\n{ type: 'reasoning-start', id: '...' }\n\n// Reasoning content\n{ type: 'reasoning-delta', id: '...', delta: 'Let me analyze this request...' }\n\n// Reasoning ended\n{ type: 'reasoning-end', id: '...' }\n```\n\n### Tool Events\n\nTool call lifecycle:\n\n```typescript\n// Tool input started\n{ type: 'tool-input-start', toolCallId: '...', toolName: 'get-user-account', title: 'Looking up account' }\n\n// Tool input/arguments streaming\n{ type: 'tool-input-delta', toolCallId: '...', inputTextDelta: '{\"userId\":\"user-123\"}' }\n\n// Tool input streaming ended\n{ type: 'tool-input-end', toolCallId: '...' }\n\n// Tool input is complete and available\n{ type: 'tool-input-available', toolCallId: '...', toolName: 'get-user-account', input: { userId: 'user-123' } }\n\n// Tool output available (success)\n{ type: 'tool-output-available', toolCallId: '...', output: { name: 'Demo User', email: '...' } }\n\n// Tool output error (failure)\n{ type: 'tool-output-error', toolCallId: '...', error: 'User not found' }\n```\n\n### Resource Events\n\nResource updates:\n\n```typescript\n{ type: 'resource-update', name: 'CONVERSATION_SUMMARY', value: 'User asked about...' }\n```\n\n## Display Modes\n\nEach block/tool specifies how it should appear to users:\n\n| Mode | Description |\n| ------------- | ----------------------------------- |\n| `hidden` | Not shown to user (background work) |\n| `name` | Shows block/tool name |\n| `description` | Shows description text |\n| `stream` | Streams content to chat |\n\n**Note**: Hidden events are filtered before reaching the client SDK. Your frontend only sees user-facing events.\n\n## Stream Event Type\n\n```typescript\ntype StreamEvent =\n // Lifecycle\n | StartEvent\n | FinishEvent\n | ErrorEvent\n // Text\n | TextStartEvent\n | TextDeltaEvent\n | TextEndEvent\n // Reasoning\n | ReasoningStartEvent\n | ReasoningDeltaEvent\n | ReasoningEndEvent\n // Tool Input/Output\n | ToolInputStartEvent\n | ToolInputDeltaEvent\n | ToolInputEndEvent\n | ToolInputAvailableEvent\n | ToolOutputAvailableEvent\n | ToolOutputErrorEvent\n // Octavus-Specific\n | BlockStartEvent\n | BlockEndEvent\n | ResourceUpdateEvent\n | ToolRequestEvent\n | ClientToolRequestEvent;\n```\n\n### Client Tool Request\n\nWhen a tool has no server handler registered, the SDK emits a `client-tool-request` event:\n\n```typescript\n{\n type: 'client-tool-request',\n executionId: 'exec_abc123', // Use this to continue execution\n toolCalls: [ // Tools for client to handle\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n args: {}\n }\n ],\n serverToolResults: [ // Results from server tools in same batch\n {\n toolCallId: 'call_def',\n toolName: 'get-user-account',\n result: { name: 'Demo User' }\n }\n ]\n}\n```\n\nAfter the client handles the tools, send a `continue` request with all results:\n\n```typescript\nsession.execute({\n type: 'continue',\n executionId: 'exec_abc123',\n toolResults: [\n ...serverToolResults, // Include server results from the event\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n result: { lat: 40.7128, lng: -74.006 },\n },\n ],\n});\n```\n\nSee [Client Tools](/docs/client-sdk/client-tools) for full client-side implementation.\n\n## Error Events\n\nErrors are emitted as structured events with type classification:\n\n```typescript\n{\n type: 'error',\n errorType: 'rate_limit_error', // Error classification\n message: 'Rate limit exceeded', // Human-readable message\n source: 'provider', // 'platform' | 'provider' | 'tool'\n retryable: true, // Whether retry is possible\n retryAfter: 60, // Seconds to wait (rate limits)\n code: 'ANTHROPIC_429', // Machine-readable code\n provider: { // Provider details (when applicable)\n name: 'anthropic',\n statusCode: 429,\n requestId: 'req_...'\n }\n}\n```\n\n### Error Types\n\n| Type | Description |\n| ---------------------- | --------------------- |\n| `rate_limit_error` | Too many requests |\n| `authentication_error` | Invalid API key |\n| `provider_error` | LLM provider issue |\n| `provider_overloaded` | Provider at capacity |\n| `tool_error` | Tool execution failed |\n| `internal_error` | Platform error |\n\n### Tool Errors\n\nTool errors are captured per-tool and don't stop the stream:\n\n```typescript\n{ type: 'tool-output-error', toolCallId: '...', error: 'Handler threw exception' }\n```\n\nThe stream always ends with either `finish` or `error`.\n\nFor client-side error handling patterns, see [Error Handling](/docs/client-sdk/error-handling).\n",
|
|
798
|
+
content: "\n# Streaming\n\nAll Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates.\n\n## Stream Response\n\nWhen you execute a request, you get an async generator of parsed events:\n\n```typescript\nimport { toSSEStream } from '@octavus/server-sdk';\n\n// execute() returns an async generator of StreamEvent\nconst events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Hello!' },\n});\n\n// For HTTP endpoints, convert to SSE stream\nreturn new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n});\n\n// For sockets, iterate events directly\nfor await (const event of events) {\n conn.write(JSON.stringify(event));\n}\n```\n\nThe `X-Accel-Buffering: no` header disables proxy buffering on Nginx-based infrastructure (including Vercel), ensuring SSE events are forwarded immediately instead of being batched.\n\n### Heartbeat\n\n`toSSEStream` automatically sends SSE comment lines (`: heartbeat`) every 15 seconds during idle periods. This prevents proxies and load balancers from closing the connection due to inactivity \u2014 particularly important during multi-step executions where the stream may be silent while waiting for tool processing or LLM responses.\n\nHeartbeat comments are ignored by all SSE parsers per the spec. No client-side handling is needed.\n\n## Event Types\n\nThe stream emits various event types for lifecycle, text, reasoning, and tool interactions.\n\n### Lifecycle Events\n\n```typescript\n// Stream started\n{ type: 'start', messageId: '...', executionId: '...' }\n\n// Stream completed\n{ type: 'finish', finishReason: 'stop' }\n\n// Possible finish reasons:\n// - 'stop': Normal completion\n// - 'tool-calls': Waiting for server tool execution (handled by SDK internally)\n// - 'client-tool-calls': Waiting for client tool execution\n// - 'length': Max tokens reached\n// - 'content-filter': Content filtered\n// - 'error': Error occurred\n// - 'other': Other reason\n\n// Error event (see Error Handling docs for full structure)\n{ type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }\n```\n\n### Block Events\n\nTrack execution progress:\n\n```typescript\n// Block started\n{ type: 'block-start', blockId: '...', blockName: 'Respond to user', blockType: 'next-message', display: 'stream', thread: 'main' }\n\n// Block completed\n{ type: 'block-end', blockId: '...', summary: 'Generated response' }\n```\n\n### Text Events\n\nStreaming text content:\n\n```typescript\n// Text generation started\n{ type: 'text-start', id: '...' }\n\n// Incremental text (most common event)\n{ type: 'text-delta', id: '...', delta: 'Hello' }\n{ type: 'text-delta', id: '...', delta: '!' }\n{ type: 'text-delta', id: '...', delta: ' How' }\n{ type: 'text-delta', id: '...', delta: ' can' }\n{ type: 'text-delta', id: '...', delta: ' I' }\n{ type: 'text-delta', id: '...', delta: ' help?' }\n\n// Text generation ended\n{ type: 'text-end', id: '...' }\n```\n\n### Reasoning Events\n\nExtended reasoning (for supported models like Claude):\n\n```typescript\n// Reasoning started\n{ type: 'reasoning-start', id: '...' }\n\n// Reasoning content\n{ type: 'reasoning-delta', id: '...', delta: 'Let me analyze this request...' }\n\n// Reasoning ended\n{ type: 'reasoning-end', id: '...' }\n```\n\n### Tool Events\n\nTool call lifecycle:\n\n```typescript\n// Tool input started\n{ type: 'tool-input-start', toolCallId: '...', toolName: 'get-user-account', title: 'Looking up account' }\n\n// Tool input/arguments streaming\n{ type: 'tool-input-delta', toolCallId: '...', inputTextDelta: '{\"userId\":\"user-123\"}' }\n\n// Tool input streaming ended\n{ type: 'tool-input-end', toolCallId: '...' }\n\n// Tool input is complete and available\n{ type: 'tool-input-available', toolCallId: '...', toolName: 'get-user-account', input: { userId: 'user-123' } }\n\n// Tool output available (success)\n{ type: 'tool-output-available', toolCallId: '...', output: { name: 'Demo User', email: '...' } }\n\n// Tool output error (failure)\n{ type: 'tool-output-error', toolCallId: '...', error: 'User not found' }\n```\n\n### Resource Events\n\nResource updates:\n\n```typescript\n{ type: 'resource-update', name: 'CONVERSATION_SUMMARY', value: 'User asked about...' }\n```\n\n## Display Modes\n\nEach block/tool specifies how it should appear to users:\n\n| Mode | Description |\n| ------------- | ----------------------------------- |\n| `hidden` | Not shown to user (background work) |\n| `name` | Shows block/tool name |\n| `description` | Shows description text |\n| `stream` | Streams content to chat |\n\n**Note**: Hidden events are filtered before reaching the client SDK. Your frontend only sees user-facing events.\n\n## Stream Event Type\n\n```typescript\ntype StreamEvent =\n // Lifecycle\n | StartEvent\n | FinishEvent\n | ErrorEvent\n // Text\n | TextStartEvent\n | TextDeltaEvent\n | TextEndEvent\n // Reasoning\n | ReasoningStartEvent\n | ReasoningDeltaEvent\n | ReasoningEndEvent\n // Tool Input/Output\n | ToolInputStartEvent\n | ToolInputDeltaEvent\n | ToolInputEndEvent\n | ToolInputAvailableEvent\n | ToolOutputAvailableEvent\n | ToolOutputErrorEvent\n // Octavus-Specific\n | BlockStartEvent\n | BlockEndEvent\n | ResourceUpdateEvent\n | ToolRequestEvent\n | ClientToolRequestEvent;\n```\n\n### Client Tool Request\n\nWhen a tool has no server handler registered, the SDK emits a `client-tool-request` event:\n\n```typescript\n{\n type: 'client-tool-request',\n executionId: 'exec_abc123', // Use this to continue execution\n toolCalls: [ // Tools for client to handle\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n args: {}\n }\n ],\n serverToolResults: [ // Results from server tools in same batch\n {\n toolCallId: 'call_def',\n toolName: 'get-user-account',\n result: { name: 'Demo User' }\n }\n ]\n}\n```\n\nAfter the client handles the tools, send a `continue` request with all results:\n\n```typescript\nsession.execute({\n type: 'continue',\n executionId: 'exec_abc123',\n toolResults: [\n ...serverToolResults, // Include server results from the event\n {\n toolCallId: 'call_xyz',\n toolName: 'get-browser-location',\n result: { lat: 40.7128, lng: -74.006 },\n },\n ],\n});\n```\n\nSee [Client Tools](/docs/client-sdk/client-tools) for full client-side implementation.\n\n## Error Events\n\nErrors are emitted as structured events with type classification:\n\n```typescript\n{\n type: 'error',\n errorType: 'rate_limit_error', // Error classification\n message: 'Rate limit exceeded', // Human-readable message\n source: 'provider', // 'platform' | 'provider' | 'tool'\n retryable: true, // Whether retry is possible\n retryAfter: 60, // Seconds to wait (rate limits)\n code: 'ANTHROPIC_429', // Machine-readable code\n provider: { // Provider details (when applicable)\n name: 'anthropic',\n statusCode: 429,\n requestId: 'req_...'\n }\n}\n```\n\n### Error Types\n\n| Type | Description |\n| ---------------------- | --------------------- |\n| `rate_limit_error` | Too many requests |\n| `authentication_error` | Invalid API key |\n| `provider_error` | LLM provider issue |\n| `provider_overloaded` | Provider at capacity |\n| `tool_error` | Tool execution failed |\n| `internal_error` | Platform error |\n\n### Tool Errors\n\nTool errors are captured per-tool and don't stop the stream:\n\n```typescript\n{ type: 'tool-output-error', toolCallId: '...', error: 'Handler threw exception' }\n```\n\nThe stream always ends with either `finish` or `error`.\n\nFor client-side error handling patterns, see [Error Handling](/docs/client-sdk/error-handling).\n",
|
|
779
799
|
excerpt: "Streaming All Octavus responses stream in real-time using Server-Sent Events (SSE). This enables responsive UX with incremental updates. Stream Response When you execute a request, you get an async...",
|
|
780
800
|
order: 4
|
|
781
801
|
},
|
|
@@ -784,7 +804,7 @@ var sections_default = [
|
|
|
784
804
|
section: "server-sdk",
|
|
785
805
|
title: "CLI",
|
|
786
806
|
description: "Command-line interface for validating and syncing agent definitions.",
|
|
787
|
-
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.
|
|
807
|
+
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.16.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',
|
|
788
808
|
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 ...",
|
|
789
809
|
order: 5
|
|
790
810
|
},
|
|
@@ -805,6 +825,15 @@ var sections_default = [
|
|
|
805
825
|
content: "\n# Debugging\n\n## Model Request Tracing\n\nModel request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014 system prompts, messages, tool definitions, and provider options \u2014 making it easier to debug agent behavior.\n\n### Enabling Tracing\n\nEnable tracing by setting `traceModelRequests: true` in the client config:\n\n```typescript\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: true,\n});\n```\n\nWhen enabled, the SDK sends an `X-Octavus-Trace: true` header with every request. The platform captures the full model request payload before each provider call and stores it in the execution logs.\n\nYou can also drive this from an environment variable for per-environment control:\n\n```typescript\nconst client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n traceModelRequests: process.env.TRACE_MODEL_REQUESTS === 'true',\n});\n```\n\n### What Gets Captured\n\n**LLM requests** include:\n\n- Full system prompt\n- All messages in AI SDK format (post-conversion)\n- Tool names, descriptions, and JSON schemas\n- Provider-specific options (thinking budgets, etc.)\n- Temperature, max steps, and thinking configuration\n\n**Image generation requests** include:\n\n- Image generation prompt\n- Requested size\n- Whether reference images were provided\n\n### Where Traces Appear\n\nTraces appear as **Model Request** entries in the execution log timeline, alongside existing entries like triggers, tool calls, and responses. Each trace is linked to the block that made the model call.\n\nIn the Octavus dashboard:\n\n- **Session debug view** \u2014 Full execution log with expandable model request entries\n- **Agent preview** \u2014 Activity panel shows model requests in the execution steps\n\nEach entry shows the raw JSON payload with a copy button for easy inspection.\n\n### Storage\n\nTraces are stored in Redis alongside other execution log entries with a 24-hour TTL. They are not permanently stored. A typical LLM trace with 10 messages and 5 tools is 10\u201350KB. Image traces are smaller (just prompt and metadata).\n\n### Recommendations\n\n| Environment | Recommendation |\n| ----------- | ---------------------------------------------------------- |\n| Development | Enable \u2014 helps debug agent behavior during development |\n| Staging | Enable \u2014 useful for pre-production testing |\n| Production | Disable (default) \u2014 saves storage for high-volume sessions |\n\n### Preview Sessions\n\nModel request tracing is always enabled for preview sessions in the Octavus dashboard. No configuration needed \u2014 the platform automatically traces all model requests when using the agent preview.\n",
|
|
806
826
|
excerpt: "Debugging Model Request Tracing Model request tracing captures the full payload sent to model providers (LLM and image) during agent execution. This helps you understand exactly what was sent \u2014...",
|
|
807
827
|
order: 7
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
slug: "server-sdk/computer",
|
|
831
|
+
section: "server-sdk",
|
|
832
|
+
title: "Computer",
|
|
833
|
+
description: "Adding browser, filesystem, and shell capabilities to agents with @octavus/computer.",
|
|
834
|
+
content: "\n# Computer\n\nThe `@octavus/computer` package gives agents access to a physical or virtual machine's browser, filesystem, and shell. It connects to [MCP](https://modelcontextprotocol.io) servers, discovers their tools, and provides them to the server-sdk.\n\n**Current version:** `2.16.0`\n\n## Installation\n\n```bash\nnpm install @octavus/computer\n```\n\n## Quick Start\n\n```typescript\nimport { Computer } from '@octavus/computer';\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n '--browser-url=http://127.0.0.1:9222',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n ['/path/to/workspace'],\n ),\n shell: Computer.shell({ cwd: '/path/to/workspace', mode: 'unrestricted' }),\n },\n});\n\nawait computer.start();\n\nconst client = new OctavusClient({\n baseUrl: 'https://octavus.ai',\n apiKey: 'your-api-key',\n});\n\nconst session = client.agentSessions.attach(sessionId, {\n tools: { 'set-chat-title': titleHandler },\n computer,\n});\n```\n\nThe `computer` is passed to `attach()` \u2014 the server-sdk handles the rest. Tool schemas are sent to the platform, and tool calls flow back through the existing execution loop.\n\n## How It Works\n\n1. You configure MCP servers with namespaces (e.g., `browser`, `filesystem`, `shell`)\n2. `computer.start()` connects to all servers in parallel and discovers their tools\n3. Each tool is namespaced with `__` (e.g., `browser__navigate_page`, `filesystem__read_file`)\n4. The server-sdk sends tool schemas to the platform and handles tool call execution\n\nThe agent's protocol must declare matching `mcpServers` with `source: device` \u2014 see [MCP Servers](/docs/protocol/mcp-servers).\n\n## Entry Types\n\nThe `Computer` class supports three types of MCP entries:\n\n### Stdio (MCP Subprocess)\n\nSpawns an MCP server as a child process, communicating via stdin/stdout:\n\n```typescript\nComputer.stdio(command: string, args?: string[], options?: {\n env?: Record<string, string>;\n cwd?: string;\n})\n```\n\nUse this for local MCP servers installed as npm packages or standalone executables:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n '--browser-url=http://127.0.0.1:9222',\n '--no-usage-statistics',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n ['/Users/me/projects/my-app'],\n ),\n },\n});\n```\n\n### HTTP (Remote MCP Endpoint)\n\nConnects to an MCP server over Streamable HTTP:\n\n```typescript\nComputer.http(url: string, options?: {\n headers?: Record<string, string>;\n})\n```\n\nUse this for MCP servers running as HTTP services:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n docs: Computer.http('http://localhost:3001/mcp', {\n headers: { 'Authorization': 'Bearer token' },\n }),\n },\n});\n```\n\n### Shell (Built-in)\n\nProvides shell command execution without spawning an MCP subprocess:\n\n```typescript\nComputer.shell(options: {\n cwd?: string;\n mode: ShellMode;\n timeout?: number; // Default: 300,000ms (5 minutes)\n})\n```\n\nThis exposes a `run_command` tool (namespaced as `shell__run_command` when the key is `shell`). Commands execute in a login shell with the user's full environment.\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n shell: Computer.shell({\n cwd: '/Users/me/projects/my-app',\n mode: 'unrestricted',\n timeout: 300_000,\n }),\n },\n});\n```\n\n#### Shell Safety Modes\n\n| Mode | Description |\n| ------------------------------------------ | -------------------------------------------- |\n| `'unrestricted'` | All commands allowed (for dedicated machines) |\n| `{ allowedPatterns, blockedPatterns }` | Pattern-based command filtering |\n\nPattern-based filtering:\n\n```typescript\nComputer.shell({\n cwd: workspaceDir,\n mode: {\n blockedPatterns: [/rm\\s+-rf/, /sudo/],\n allowedPatterns: [/^git\\s/, /^npm\\s/, /^ls\\s/],\n },\n})\n```\n\nWhen `allowedPatterns` is set, only matching commands are permitted. When `blockedPatterns` is set, matching commands are rejected. Blocked patterns are checked first.\n\n## Lifecycle\n\n### Starting\n\n`computer.start()` connects to all configured MCP servers in parallel. If some servers fail to connect, the computer still starts with the remaining servers \u2014 only if *all* connections fail does it throw an error.\n\n```typescript\nconst { errors } = await computer.start();\n\nif (errors.length > 0) {\n console.warn('Some MCP servers failed to connect:', errors);\n}\n```\n\n### Stopping\n\n`computer.stop()` closes all MCP connections and kills managed processes:\n\n```typescript\nawait computer.stop();\n```\n\nAlways call `stop()` when the session ends to clean up MCP subprocesses. For managed processes (like Chrome), pass them in the config for automatic cleanup.\n\n## Chrome Launch Helper\n\nFor desktop applications that need to control a browser, `Computer.launchChrome()` launches Chrome with remote debugging enabled:\n\n```typescript\nconst browser = await Computer.launchChrome({\n profileDir: '/Users/me/.my-app/chrome-profiles/agent-1',\n debuggingPort: 9222, // Optional, auto-allocated if omitted\n flags: ['--window-size=1280,800'],\n});\n\nconsole.log(`Chrome running on port ${browser.port}, PID ${browser.pid}`);\n```\n\nPass the browser to `managedProcesses` for automatic cleanup when the computer stops:\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n `--browser-url=http://127.0.0.1:${browser.port}`,\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n [workspaceDir],\n ),\n shell: Computer.shell({ cwd: workspaceDir, mode: 'unrestricted' }),\n },\n managedProcesses: [{ process: browser.process }],\n});\n```\n\n### ChromeLaunchOptions\n\n| Field | Required | Description |\n| ---------------- | -------- | ---------------------------------------------------- |\n| `profileDir` | Yes | Directory for Chrome's user data (profile isolation) |\n| `debuggingPort` | No | Port for remote debugging (auto-allocated if omitted) |\n| `flags` | No | Additional Chrome launch flags |\n\n## ToolProvider Interface\n\n`Computer` implements the `ToolProvider` interface from `@octavus/core`:\n\n```typescript\ninterface ToolProvider {\n toolHandlers(): Record<string, ToolHandler>;\n toolSchemas(): ToolSchema[];\n}\n```\n\nThe server-sdk accepts any `ToolProvider` on the `computer` option \u2014 you can implement your own if `@octavus/computer` doesn't fit your use case:\n\n```typescript\nconst customProvider: ToolProvider = {\n toolHandlers() {\n return {\n 'custom__my_tool': async (args) => {\n return { result: 'done' };\n },\n };\n },\n toolSchemas() {\n return [\n {\n name: 'custom__my_tool',\n description: 'A custom tool',\n inputSchema: {\n type: 'object',\n properties: {\n input: { type: 'string', description: 'Tool input' },\n },\n required: ['input'],\n },\n },\n ];\n },\n};\n\nconst session = client.agentSessions.attach(sessionId, {\n tools: { 'set-chat-title': titleHandler },\n computer: customProvider,\n});\n```\n\n## Complete Example\n\nA desktop application with browser, filesystem, and shell capabilities:\n\n```typescript\nimport { Computer } from '@octavus/computer';\nimport { OctavusClient } from '@octavus/server-sdk';\n\nconst WORKSPACE_DIR = '/Users/me/projects/my-app';\nconst PROFILE_DIR = '/Users/me/.my-app/chrome-profiles/agent';\n\nasync function startSession(sessionId: string) {\n // 1. Launch Chrome with remote debugging\n const browser = await Computer.launchChrome({\n profileDir: PROFILE_DIR,\n });\n\n // 2. Create computer with all capabilities\n const computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', [\n `--browser-url=http://127.0.0.1:${browser.port}`,\n '--no-usage-statistics',\n ]),\n filesystem: Computer.stdio(\n '@modelcontextprotocol/server-filesystem',\n [WORKSPACE_DIR],\n ),\n shell: Computer.shell({\n cwd: WORKSPACE_DIR,\n mode: 'unrestricted',\n }),\n },\n managedProcesses: [{ process: browser.process }],\n });\n\n // 3. Connect to all MCP servers\n const { errors } = await computer.start();\n if (errors.length > 0) {\n console.warn('Failed to connect:', errors);\n }\n\n // 4. Attach to session with computer\n const client = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n });\n\n const session = client.agentSessions.attach(sessionId, {\n tools: {\n 'set-chat-title': async (args) => {\n console.log('Chat title:', args.title);\n return { success: true };\n },\n },\n computer,\n });\n\n // 5. Execute and stream\n const events = session.execute({\n type: 'trigger',\n triggerName: 'user-message',\n input: { USER_MESSAGE: 'Navigate to github.com and take a screenshot' },\n });\n\n for await (const event of events) {\n // Handle stream events\n }\n\n // 6. Clean up\n await computer.stop();\n}\n```\n\n## API Reference\n\n### Computer\n\n```typescript\nclass Computer implements ToolProvider {\n constructor(config: ComputerConfig);\n\n // Static factories for MCP entries\n static stdio(command: string, args?: string[], options?: {\n env?: Record<string, string>;\n cwd?: string;\n }): StdioConfig;\n\n static http(url: string, options?: {\n headers?: Record<string, string>;\n }): HttpConfig;\n\n static shell(options: {\n cwd?: string;\n mode: ShellMode;\n timeout?: number;\n }): ShellConfig;\n\n // Chrome launch helper\n static launchChrome(options: ChromeLaunchOptions): Promise<ChromeInstance>;\n\n // Lifecycle\n start(): Promise<{ errors: string[] }>;\n stop(): Promise<void>;\n\n // ToolProvider implementation\n toolHandlers(): Record<string, ToolHandler>;\n toolSchemas(): ToolSchema[];\n}\n```\n\n### ComputerConfig\n\n```typescript\ninterface ComputerConfig {\n mcpServers: Record<string, McpEntry>;\n managedProcesses?: { process: ChildProcess }[];\n}\n\ntype McpEntry = StdioConfig | HttpConfig | ShellConfig;\ntype ShellMode = 'unrestricted' | {\n allowedPatterns?: RegExp[];\n blockedPatterns?: RegExp[];\n};\n```\n\n### ChromeInstance\n\n```typescript\ninterface ChromeInstance {\n port: number;\n process: ChildProcess;\n pid: number;\n}\n```\n",
|
|
835
|
+
excerpt: "Computer The package gives agents access to a physical or virtual machine's browser, filesystem, and shell. It connects to MCP servers, discovers their tools, and provides them to the server-sdk....",
|
|
836
|
+
order: 8
|
|
808
837
|
}
|
|
809
838
|
]
|
|
810
839
|
},
|
|
@@ -819,7 +848,7 @@ var sections_default = [
|
|
|
819
848
|
section: "client-sdk",
|
|
820
849
|
title: "Overview",
|
|
821
850
|
description: "Introduction to the Octavus Client SDKs for building chat interfaces.",
|
|
822
|
-
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.14.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.14.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### Retry Last Trigger\n\nRe-execute the last trigger from the same starting point. Messages are rolled back to the state before the trigger, the user message is re-added (if any), and the agent re-executes. Already-uploaded files are reused without re-uploading.\n\n```tsx\nconst { retry, canRetry } = useOctavusChat({ transport });\n\n// Retry after an error, cancellation, or unsatisfactory result\nif (canRetry) {\n await retry();\n}\n```\n\n`canRetry` is `true` when a trigger has been sent and the chat is not currently streaming or awaiting input.\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 retry: () => Promise<void>; // Retry last trigger from same starting point\n canRetry: boolean; // Whether retry() can be called\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",
|
|
851
|
+
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.16.0`\n\n### Other Frameworks\n\n```bash\nnpm install @octavus/client-sdk\n```\n\n**Current version:** `2.16.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### Retry Last Trigger\n\nRe-execute the last trigger from the same starting point. Messages are rolled back to the state before the trigger, the user message is re-added (if any), and the agent re-executes. Already-uploaded files are reused without re-uploading.\n\n```tsx\nconst { retry, canRetry } = useOctavusChat({ transport });\n\n// Retry after an error, cancellation, or unsatisfactory result\nif (canRetry) {\n await retry();\n}\n```\n\n`canRetry` is `true` when a trigger has been sent and the chat is not currently streaming or awaiting input.\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 retry: () => Promise<void>; // Retry last trigger from same starting point\n canRetry: boolean; // Whether retry() can be called\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",
|
|
823
852
|
excerpt: "Client SDK Overview Octavus provides two packages for frontend integration: | Package | Purpose | Use When | |...",
|
|
824
853
|
order: 1
|
|
825
854
|
},
|
|
@@ -942,6 +971,7 @@ export async function POST(request: Request) {
|
|
|
942
971
|
'Content-Type': 'text/event-stream',
|
|
943
972
|
'Cache-Control': 'no-cache',
|
|
944
973
|
Connection: 'keep-alive',
|
|
974
|
+
'X-Accel-Buffering': 'no',
|
|
945
975
|
},
|
|
946
976
|
});
|
|
947
977
|
}
|
|
@@ -1096,6 +1126,7 @@ app.post('/api/trigger', async (req, res) => {
|
|
|
1096
1126
|
res.setHeader('Content-Type', 'text/event-stream');
|
|
1097
1127
|
res.setHeader('Cache-Control', 'no-cache');
|
|
1098
1128
|
res.setHeader('Connection', 'keep-alive');
|
|
1129
|
+
res.setHeader('X-Accel-Buffering', 'no');
|
|
1099
1130
|
|
|
1100
1131
|
// Pipe the stream to the response
|
|
1101
1132
|
const reader = stream.getReader();
|
|
@@ -1318,7 +1349,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1318
1349
|
section: "protocol",
|
|
1319
1350
|
title: "Skills",
|
|
1320
1351
|
description: "Using Octavus skills for code execution and specialized capabilities.",
|
|
1321
|
-
content: "\n# Skills\n\nSkills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are self-contained packages with documentation and scripts that run in secure sandboxes.\n\n## Overview\n\nOctavus Skills provide **provider-agnostic** code execution. They work with any LLM provider (Anthropic, OpenAI, Google) by using explicit tool calls and system prompt injection.\n\n### How Skills Work\n\n1. **Skill Definition**: Skills are defined in the protocol's `skills:` section\n2. **Skill Resolution**: Skills are resolved from available sources (see below)\n3. **Sandbox Execution**: When a skill is used, code runs in an isolated sandbox environment\n4. **File Generation**: Files saved to `/output/` are automatically captured and made available for download\n\n### Skill Sources\n\nSkills come from two sources, visible in the Skills tab of your organization:\n\n| Source | Badge in UI | Visibility | Example |\n| ----------- | ----------- | ------------------------------ | ------------------ |\n| **Octavus** | `Octavus` | Available to all organizations | `qr-code` |\n| **Custom** | None | Private to your organization | `my-company-skill` |\n\nWhen you reference a skill in your protocol, Octavus resolves it from your available skills. If you create a custom skill with the same name as an Octavus skill, your custom skill takes precedence.\n\n## Defining Skills\n\nDefine skills in the protocol's `skills:` section:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n```\n\n### Skill Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------------------------------- |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` (default: `description`) |\n| `description` | No | Custom description shown to users (overrides skill's built-in description) |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Skill usage not shown to users |\n| `name` | Shows skill name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams progress if available |\n\n## Enabling Skills\n\nAfter defining skills in the `skills:` section, specify which skills are available. Skills work in both interactive agents and workers.\n\n### Interactive Agents\n\nReference skills in `agent.skills`:\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 tools: [get-user-account]\n skills: [qr-code]\n agentic: true\n```\n\n### Workers and Named Threads\n\nReference skills per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating 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 maxSteps: 10\n```\n\nThis also works for named threads in interactive agents, allowing different threads to have different skills.\n\n## Skill Tools\n\nWhen skills are enabled, the LLM has access to these tools:\n\n| Tool | Purpose |\n| -------------------- | --------------------------------------- |\n| `octavus_skill_read` | Read skill documentation (SKILL.md) |\n| `octavus_skill_list` | List available scripts in a skill |\n| `octavus_skill_run` | Execute a pre-built script from a skill |\n| `octavus_code_run` | Execute arbitrary Python/Bash code |\n| `octavus_file_write` | Create files in the sandbox |\n| `octavus_file_read` | Read files from the sandbox |\n\nThe LLM learns about available skills through system prompt injection and can use these tools to interact with skills.\n\n## Example: QR Code 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]\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\nWhen a user asks \"Create a QR code for octavus.ai\", the LLM will:\n\n1. Recognize the task matches the `qr-code` skill\n2. Call `octavus_skill_read` to learn how to use the skill\n3. Execute code (via `octavus_code_run` or `octavus_skill_run`) to generate the QR code\n4. Save the image to `/output/` in the sandbox\n5. The file is automatically captured and made available for download\n\n## File Output\n\nFiles saved to `/output/` in the sandbox are automatically:\n\n1. **Captured** after code execution\n2. **Uploaded** to S3 storage\n3. **Made available** via presigned URLs\n4. **Included** in the message as file parts\n\nFiles persist across page refreshes and are stored in the session's message history.\n\n## Skill Format\n\nSkills follow the [Agent Skills](https://agentskills.io) open standard:\n\n- `SKILL.md` - Required skill documentation with YAML frontmatter\n- `scripts/` - Optional executable code (Python/Bash)\n- `references/` - Optional documentation loaded as needed\n- `assets/` - Optional files used in outputs (templates, images)\n\n### SKILL.md Format\n\n````yaml\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\nversion: 1.0.0\nlicense: MIT\nauthor: Octavus Team\n---\n\n# QR Code Generator\n\n## Overview\n\nThis skill creates QR codes from text data using Python...\n\n## Quick Start\n\nGenerate a QR code with Python:\n\n```python\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n# ... code to generate QR code ...\n````\n\n## Scripts Reference\n\n### scripts/generate.py\n\nMain script for generating QR codes...\n\n````\n\n## Best Practices\n\n### 1. Clear Descriptions\n\nProvide clear, purpose-driven descriptions:\n\n```yaml\nskills:\n # Good - clear purpose\n qr-code:\n description: Generating QR codes for URLs, contact info, or any text data\n\n # Avoid - vague\n utility:\n description: Does stuff\n````\n\n### 2. When to Use Skills vs Tools\n\n| Use Skills When | Use Tools When |\n| ------------------------ | ---------------------------- |\n| Code execution needed | Simple API calls |\n| File generation | Database queries |\n| Complex calculations | External service integration |\n| Data processing | Authentication required |\n| Provider-agnostic needed | Backend-specific logic |\n\n### 3. Skill Selection\n\nDefine all skills available to this agent in the `skills:` section. Then specify which skills are available for the chat thread in `agent.skills`:\n\n```yaml\n# All skills available to this agent (defined once at protocol level)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n pdf-processor:\n display: description\n description: Processing PDFs\n\n# Skills available for this chat thread\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis] # Skills available for this thread\n```\n\n### 4. Display Modes\n\nChoose appropriate display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n## Comparison: Skills vs Tools vs Provider Options\n\n| Feature | Octavus Skills | External Tools | Provider Tools/Skills |\n| ------------------ | ----------------- | ------------------- | --------------------- |\n| **Execution** | Isolated sandbox | Your backend | Provider servers |\n| **Provider** | Any (agnostic) | N/A | Provider-specific |\n| **Code Execution** | Yes | No | Yes (provider tools) |\n| **File Output** | Yes | No | Yes (provider skills) |\n| **Implementation** | Skill packages | Your code | Built-in |\n| **Cost** | Sandbox + LLM API | Your infrastructure | Included in API |\n\n## Uploading Custom Skills\n\nYou can upload custom skills to your organization:\n\n1. Create a skill following the [Agent Skills](https://agentskills.io) format\n2. Package it as a `.skill` bundle (ZIP file)\n3. Upload via the platform UI\n4. Reference by slug in your protocol\n\n```yaml\nskills:\n custom-analysis:\n display: description\n description: Custom analysis tool\n\nagent:\n skills: [custom-analysis]\n```\n\n## Sandbox Timeout\n\nThe default sandbox timeout is 5 minutes. You can configure a custom timeout using `sandboxTimeout` in the agent config or on individual `start-thread` blocks:\n\n```yaml\n# Agent-level timeout (applies to main thread)\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes (in milliseconds)\n```\n\n```yaml\n# Thread-level timeout (overrides agent-level for this thread)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour\n```\n\nThread-level `sandboxTimeout` takes priority over agent-level. Maximum: 1 hour (3,600,000 ms).\n\n## Security\n\nSkills run in isolated sandbox environments:\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring skills in agent settings\n- [Provider Options](/docs/protocol/provider-options) \u2014 Anthropic's built-in skills\n- [Skills Advanced Guide](/docs/protocol/skills-advanced) \u2014 Best practices and advanced patterns\n",
|
|
1352
|
+
content: "\n# Skills\n\nSkills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are self-contained packages with documentation and scripts that run in secure sandboxes.\n\n## Overview\n\nOctavus Skills provide **provider-agnostic** code execution. They work with any LLM provider (Anthropic, OpenAI, Google) by using explicit tool calls and system prompt injection.\n\n### How Skills Work\n\n1. **Skill Definition**: Skills are defined in the protocol's `skills:` section\n2. **Skill Resolution**: Skills are resolved from available sources (see below)\n3. **Sandbox Execution**: When a skill is used, code runs in an isolated sandbox environment\n4. **File Generation**: Files saved to `/output/` are automatically captured and made available for download\n\n### Skill Sources\n\nSkills come from two sources, visible in the Skills tab of your organization:\n\n| Source | Badge in UI | Visibility | Example |\n| ----------- | ----------- | ------------------------------ | ------------------ |\n| **Octavus** | `Octavus` | Available to all organizations | `qr-code` |\n| **Custom** | None | Private to your organization | `my-company-skill` |\n\nWhen you reference a skill in your protocol, Octavus resolves it from your available skills. If you create a custom skill with the same name as an Octavus skill, your custom skill takes precedence.\n\n## Defining Skills\n\nDefine skills in the protocol's `skills:` section:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n```\n\n### Skill Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------------------------------- |\n| `display` | No | How to show in UI: `hidden`, `name`, `description`, `stream` (default: `description`) |\n| `description` | No | Custom description shown to users (overrides skill's built-in description) |\n\n### Display Modes\n\n| Mode | Behavior |\n| ------------- | ------------------------------------------- |\n| `hidden` | Skill usage not shown to users |\n| `name` | Shows skill name while executing |\n| `description` | Shows description while executing (default) |\n| `stream` | Streams progress if available |\n\n## Enabling Skills\n\nAfter defining skills in the `skills:` section, specify which skills are available. Skills work in both interactive agents and workers.\n\n### Interactive Agents\n\nReference skills in `agent.skills`:\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 tools: [get-user-account]\n skills: [qr-code]\n agentic: true\n```\n\n### Workers and Named Threads\n\nReference skills per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating 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 maxSteps: 10\n```\n\nThis also works for named threads in interactive agents, allowing different threads to have different skills.\n\n## Skill Tools\n\nWhen skills are enabled, the LLM has access to these tools:\n\n| Tool | Purpose | Availability |\n| -------------------- | --------------------------------------- | -------------------- |\n| `octavus_skill_read` | Read skill documentation (SKILL.md) | All skills |\n| `octavus_skill_list` | List available scripts in a skill | All skills |\n| `octavus_skill_run` | Execute a pre-built script from a skill | All skills |\n| `octavus_code_run` | Execute arbitrary Python/Bash code | Standard skills only |\n| `octavus_file_write` | Create files in the sandbox | Standard skills only |\n| `octavus_file_read` | Read files from the sandbox | Standard skills only |\n\nThe LLM learns about available skills through system prompt injection and can use these tools to interact with skills.\n\nSkills that have [secrets](#skill-secrets) configured run in **secure mode**, where only `octavus_skill_read`, `octavus_skill_list`, and `octavus_skill_run` are available. See [Skill Secrets](#skill-secrets) below.\n\n## Example: QR Code 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]\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\nWhen a user asks \"Create a QR code for octavus.ai\", the LLM will:\n\n1. Recognize the task matches the `qr-code` skill\n2. Call `octavus_skill_read` to learn how to use the skill\n3. Execute code (via `octavus_code_run` or `octavus_skill_run`) to generate the QR code\n4. Save the image to `/output/` in the sandbox\n5. The file is automatically captured and made available for download\n\n## File Output\n\nFiles saved to `/output/` in the sandbox are automatically:\n\n1. **Captured** after code execution\n2. **Uploaded** to S3 storage\n3. **Made available** via presigned URLs\n4. **Included** in the message as file parts\n\nFiles persist across page refreshes and are stored in the session's message history.\n\n## Skill Format\n\nSkills follow the [Agent Skills](https://agentskills.io) open standard:\n\n- `SKILL.md` - Required skill documentation with YAML frontmatter\n- `scripts/` - Optional executable code (Python/Bash)\n- `references/` - Optional documentation loaded as needed\n- `assets/` - Optional files used in outputs (templates, images)\n\n### SKILL.md Format\n\n````yaml\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\nversion: 1.0.0\nlicense: MIT\nauthor: Octavus Team\n---\n\n# QR Code Generator\n\n## Overview\n\nThis skill creates QR codes from text data using Python...\n\n## Quick Start\n\nGenerate a QR code with Python:\n\n```python\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n# ... code to generate QR code ...\n````\n\n## Scripts Reference\n\n### scripts/generate.py\n\nMain script for generating QR codes...\n\n````\n\n### Frontmatter Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------ |\n| `name` | Yes | Skill slug (lowercase, hyphens) |\n| `description` | Yes | What the skill does (shown to the LLM) |\n| `version` | No | Semantic version string |\n| `license` | No | License identifier |\n| `author` | No | Skill author |\n| `secrets` | No | Array of secret declarations (enables secure mode) |\n\n## Best Practices\n\n### 1. Clear Descriptions\n\nProvide clear, purpose-driven descriptions:\n\n```yaml\nskills:\n # Good - clear purpose\n qr-code:\n description: Generating QR codes for URLs, contact info, or any text data\n\n # Avoid - vague\n utility:\n description: Does stuff\n````\n\n### 2. When to Use Skills vs Tools\n\n| Use Skills When | Use Tools When |\n| ------------------------ | ---------------------------- |\n| Code execution needed | Simple API calls |\n| File generation | Database queries |\n| Complex calculations | External service integration |\n| Data processing | Authentication required |\n| Provider-agnostic needed | Backend-specific logic |\n\n### 3. Skill Selection\n\nDefine all skills available to this agent in the `skills:` section. Then specify which skills are available for the chat thread in `agent.skills`:\n\n```yaml\n# All skills available to this agent (defined once at protocol level)\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n pdf-processor:\n display: description\n description: Processing PDFs\n\n# Skills available for this chat thread\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis] # Skills available for this thread\n```\n\n### 4. Display Modes\n\nChoose appropriate display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n## Comparison: Skills vs Tools vs Provider Options\n\n| Feature | Octavus Skills | External Tools | Provider Tools/Skills |\n| ------------------ | ----------------- | ------------------- | --------------------- |\n| **Execution** | Isolated sandbox | Your backend | Provider servers |\n| **Provider** | Any (agnostic) | N/A | Provider-specific |\n| **Code Execution** | Yes | No | Yes (provider tools) |\n| **File Output** | Yes | No | Yes (provider skills) |\n| **Implementation** | Skill packages | Your code | Built-in |\n| **Cost** | Sandbox + LLM API | Your infrastructure | Included in API |\n\n## Uploading Custom Skills\n\nYou can upload custom skills to your organization:\n\n1. Create a skill following the [Agent Skills](https://agentskills.io) format\n2. Package it as a `.skill` bundle (ZIP file)\n3. Upload via the platform UI\n4. Reference by slug in your protocol\n\n```yaml\nskills:\n custom-analysis:\n display: description\n description: Custom analysis tool\n\nagent:\n skills: [custom-analysis]\n```\n\n## Sandbox Timeout\n\nThe default sandbox timeout is 5 minutes. You can configure a custom timeout using `sandboxTimeout` in the agent config or on individual `start-thread` blocks:\n\n```yaml\n# Agent-level timeout (applies to main thread)\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes (in milliseconds)\n```\n\n```yaml\n# Thread-level timeout (overrides agent-level for this thread)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour\n```\n\nThread-level `sandboxTimeout` takes priority over agent-level. Maximum: 1 hour (3,600,000 ms).\n\n## Skill Secrets\n\nSkills can declare secrets they need to function. When an organization configures those secrets, the skill runs in **secure mode** with additional isolation.\n\n### Declaring Secrets\n\nAdd a `secrets` array to your SKILL.md frontmatter:\n\n```yaml\n---\nname: github\ndescription: >\n Run GitHub CLI (gh) commands to manage repos, issues, PRs, and more.\nsecrets:\n - name: GITHUB_TOKEN\n description: GitHub personal access token with repo access\n required: true\n - name: GITHUB_ORG\n description: Default GitHub organization\n required: false\n---\n```\n\nEach secret declaration has:\n\n| Field | Required | Description |\n| ------------- | -------- | ----------------------------------------------------------- |\n| `name` | Yes | Environment variable name (uppercase, e.g., `GITHUB_TOKEN`) |\n| `description` | No | Explains what this secret is for (shown in the UI) |\n| `required` | No | Whether the secret is required (defaults to `true`) |\n\nSecret names must match the pattern `^[A-Z_][A-Z0-9_]*$` (uppercase letters, digits, and underscores).\n\n### Configuring Secrets\n\nOrganization admins configure secret values through the skill editor in the platform UI. Each organization maintains its own independent set of secrets for each skill.\n\nSecrets are encrypted at rest and only decrypted at execution time.\n\n### Secure Mode\n\nWhen a skill has secrets configured for the organization, it automatically runs in **secure mode**:\n\n- The skill gets its own **isolated sandbox** (separate from other skills)\n- Secrets are injected as **environment variables** available to all scripts\n- Only `octavus_skill_read`, `octavus_skill_list`, and `octavus_skill_run` are available \u2014 `octavus_code_run`, `octavus_file_write`, and `octavus_file_read` are blocked\n- Scripts receive input as **JSON via stdin** (using the `input` parameter on `octavus_skill_run`) instead of CLI args\n- All output (stdout/stderr) is **automatically redacted** for secret values before being returned to the LLM\n\n### Writing Scripts for Secure Skills\n\nScripts in secure skills read input from stdin as JSON and access secrets from environment variables:\n\n```python\nimport json\nimport os\nimport sys\n\ninput_data = json.load(sys.stdin)\ntoken = os.environ.get('GITHUB_TOKEN')\n\n# Use the token and input_data to perform the task\n```\n\nFor standard skills (without secrets), scripts receive input as CLI arguments. For secure skills, always use stdin JSON.\n\n## Security\n\nSkills run in isolated sandbox environments:\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n- **Secret redaction** \u2014 output from secure skills is automatically scanned for secret values\n\n## Next Steps\n\n- [Agent Config](/docs/protocol/agent-config) \u2014 Configuring skills in agent settings\n- [Provider Options](/docs/protocol/provider-options) \u2014 Anthropic's built-in skills\n- [Skills Advanced Guide](/docs/protocol/skills-advanced) \u2014 Best practices and advanced patterns\n",
|
|
1322
1353
|
excerpt: "Skills Skills are knowledge packages that enable agents to execute code and generate files in isolated sandbox environments. Unlike external tools (which you implement in your backend), skills are...",
|
|
1323
1354
|
order: 5
|
|
1324
1355
|
},
|
|
@@ -1327,7 +1358,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1327
1358
|
section: "protocol",
|
|
1328
1359
|
title: "Handlers",
|
|
1329
1360
|
description: "Defining execution handlers with blocks.",
|
|
1330
|
-
content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n skills: [qr-code] # Octavus skills for this thread\n sandboxTimeout: 600000 # Skill sandbox timeout (default: 5 min, max: 1 hour)\n imageModel: google/gemini-2.5-flash-image # Image generation model\n```\n\nThe `model` field can also reference a variable for dynamic model selection
|
|
1361
|
+
content: "\n# Handlers\n\nHandlers define what happens when a trigger fires. They contain execution blocks that run in sequence.\n\n## Handler Structure\n\n```yaml\nhandlers:\n trigger-name:\n Block Name:\n block: block-kind\n # block-specific properties\n\n Another Block:\n block: another-kind\n # ...\n```\n\nEach block has a human-readable name (shown in debug UI) and a `block` field that determines its behavior.\n\n## Block Kinds\n\n### next-message\n\nGenerate a response from the LLM:\n\n```yaml\nhandlers:\n user-message:\n Respond to user:\n block: next-message\n # Uses main conversation thread by default\n # Display defaults to 'stream'\n```\n\nWith options:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary # Use named thread\n display: stream # Show streaming content\n independent: true # Don't add to main chat\n output: SUMMARY # Store output in variable\n description: Generating summary # Shown in UI\n```\n\nFor structured output (typed JSON response):\n\n```yaml\nRespond with suggestions:\n block: next-message\n responseType: ChatResponse # Type defined in types section\n output: RESPONSE # Stores the parsed object\n```\n\nWhen `responseType` is specified:\n\n- The LLM generates JSON matching the type schema\n- The `output` variable receives the parsed object (not plain text)\n- The client receives a `UIObjectPart` for custom rendering\n\nSee [Types](/docs/protocol/types#structured-output) for more details.\n\n### add-message\n\nAdd a message to the conversation:\n\n```yaml\nAdd user message:\n block: add-message\n role: user # user | assistant | system\n prompt: user-message # Reference to prompt file\n input: [USER_MESSAGE] # Variables to interpolate\n display: hidden # Don't show in UI\n```\n\nFor internal directives (LLM sees it, user doesn't):\n\n```yaml\nAdd internal directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS]\n visible: false # LLM sees this, user doesn't\n```\n\nFor structured user input (object shown in UI, prompt for LLM context):\n\n```yaml\nAdd user message:\n block: add-message\n role: user\n prompt: user-message # Rendered for LLM context (hidden from UI)\n input: [USER_INPUT]\n uiContent: USER_INPUT # Variable shown in UI (object \u2192 object part)\n display: hidden\n```\n\nWhen `uiContent` is set:\n\n- The variable value is shown in the UI (string \u2192 text part, object \u2192 object part)\n- The prompt text is hidden from the UI but kept for LLM context\n- Useful for rich UI interactions where the visual differs from the LLM context\n\n### tool-call\n\nCall a tool deterministically:\n\n```yaml\nCreate ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY # Variable reference\n priority: medium # Literal value\n output: TICKET # Store result\n```\n\n### set-resource\n\nUpdate a persistent resource:\n\n```yaml\nSave summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY # Variable to save\n display: name # Show block name\n```\n\n### start-thread\n\nCreate a named conversation thread:\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary # Thread name\n model: anthropic/claude-sonnet-4-5 # Optional: different model\n backupModel: openai/gpt-4o # Failover on provider errors\n thinking: low # Extended reasoning level\n maxSteps: 1 # Tool call limit\n system: escalation-summary # System prompt\n input: [COMPANY_NAME] # Variables for prompt\n skills: [qr-code] # Octavus skills for this thread\n sandboxTimeout: 600000 # Skill sandbox timeout (default: 5 min, max: 1 hour)\n imageModel: google/gemini-2.5-flash-image # Image generation model\n```\n\nThe `model` field can also reference a variable for dynamic model selection. The `backupModel` field follows the same format and supports variable references.\n\n```yaml\nStart summary thread:\n block: start-thread\n thread: summary\n model: SUMMARY_MODEL # Resolved from input variable\n system: escalation-summary\n```\n\n### serialize-thread\n\nConvert conversation to text:\n\n```yaml\nSerialize conversation:\n block: serialize-thread\n thread: main # Which thread (default: main)\n format: markdown # markdown | json\n output: CONVERSATION_TEXT # Variable to store result\n```\n\n### generate-image\n\nGenerate an image from a prompt variable:\n\n```yaml\nGenerate image:\n block: generate-image\n prompt: OPTIMIZED_PROMPT # Variable containing the prompt\n imageModel: google/gemini-2.5-flash-image # Required image model\n size: 1024x1024 # 1024x1024 | 1792x1024 | 1024x1792\n output: GENERATED_IMAGE # Store URL in variable\n description: Generating your image... # Shown in UI\n```\n\nEdit an existing image using reference images:\n\n```yaml\nEdit image:\n block: generate-image\n prompt: EDIT_INSTRUCTIONS # e.g., \"Remove the background\"\n referenceImages: [SOURCE_IMAGE_URL] # Variable(s) containing image URLs\n imageModel: google/gemini-2.5-flash-image\n output: EDITED_IMAGE\n description: Editing image...\n```\n\n| Field | Required | Description |\n| ----------------- | -------- | --------------------------------------------------------------- |\n| `prompt` | Yes | Variable name containing the image prompt or edit instructions |\n| `imageModel` | Yes | Image model identifier (e.g., `google/gemini-2.5-flash-image`) |\n| `size` | No | Image dimensions: `1024x1024`, `1792x1024`, or `1024x1792` |\n| `referenceImages` | No | Variable names containing image URLs for editing/transformation |\n| `output` | No | Variable name to store the generated image URL |\n| `thread` | No | Thread to associate the output file with |\n| `description` | No | Description shown in the UI during generation |\n\nThis block is for deterministic image generation pipelines where the prompt is constructed programmatically (e.g., via prompt engineering in a separate thread). When `referenceImages` are provided, the prompt describes how to modify those images.\n\nFor agentic image generation where the LLM decides when to generate, configure `imageModel` in the [agent config](/docs/protocol/agent-config#image-generation).\n\n## Display Modes\n\nEvery block has a `display` property:\n\n| Mode | Default For | Behavior |\n| ------------- | ------------------------- | ----------------- |\n| `hidden` | add-message | Not shown to user |\n| `name` | set-resource | Shows block name |\n| `description` | tool-call, generate-image | Shows description |\n| `stream` | next-message | Streams content |\n\n## Complete Example\n\n```yaml\nhandlers:\n user-message:\n # Add the user's message to conversation\n Add user message:\n block: add-message\n role: user\n prompt: user-message\n input: [USER_MESSAGE]\n display: hidden\n\n # Generate response (LLM may call tools)\n Respond to user:\n block: next-message\n # display: stream (default)\n\n request-human:\n # Step 1: Serialize conversation for summary\n Serialize conversation:\n block: serialize-thread\n format: markdown\n output: CONVERSATION_TEXT\n\n # Step 2: Create separate thread for summarization\n Start summary thread:\n block: start-thread\n thread: summary\n model: anthropic/claude-sonnet-4-5\n thinking: low\n system: escalation-summary\n input: [COMPANY_NAME]\n\n # Step 3: Add request to summary thread\n Add summarize request:\n block: add-message\n thread: summary\n role: user\n prompt: summarize-request\n input:\n - CONVERSATION: CONVERSATION_TEXT\n\n # Step 4: Generate summary\n Generate summary:\n block: next-message\n thread: summary\n display: stream\n description: Summarizing your conversation\n independent: true\n output: SUMMARY\n\n # Step 5: Save to resource\n Save summary:\n block: set-resource\n resource: CONVERSATION_SUMMARY\n value: SUMMARY\n\n # Step 6: Create support ticket\n Create ticket:\n block: tool-call\n tool: create-support-ticket\n input:\n summary: SUMMARY\n priority: medium\n output: TICKET\n\n # Step 7: Add directive for response\n Add directive:\n block: add-message\n role: user\n prompt: ticket-directive\n input: [TICKET_DETAILS: TICKET]\n visible: false\n\n # Step 8: Respond to user\n Respond:\n block: next-message\n```\n\n## Block Input Mapping\n\nThe `input` field on blocks controls which variables are passed to the prompt. Only variables listed in `input` are available for interpolation.\n\nVariables can come from `protocol.input`, `protocol.resources`, `protocol.variables`, `trigger.input`, or outputs from prior blocks.\n\n```yaml\n# Array format (same name)\ninput: [USER_MESSAGE, COMPANY_NAME]\n\n# Array format (rename)\ninput:\n - CONVERSATION: CONVERSATION_TEXT # Prompt sees CONVERSATION, value comes from CONVERSATION_TEXT\n - TICKET_DETAILS: TICKET\n\n# Object format (rename)\ninput:\n CONVERSATION: CONVERSATION_TEXT\n TICKET_DETAILS: TICKET\n```\n\n## Independent Blocks\n\nUse `independent: true` for content that shouldn't go to the main chat:\n\n```yaml\nGenerate summary:\n block: next-message\n thread: summary\n independent: true # Output stored in variable, not main chat\n output: SUMMARY\n```\n\nThis is useful for:\n\n- Background processing\n- Summarization in separate threads\n- Generating content for tools\n",
|
|
1331
1362
|
excerpt: "Handlers Handlers define what happens when a trigger fires. They contain execution blocks that run in sequence. Handler Structure Each block has a human-readable name (shown in debug UI) and a ...",
|
|
1332
1363
|
order: 6
|
|
1333
1364
|
},
|
|
@@ -1336,7 +1367,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1336
1367
|
section: "protocol",
|
|
1337
1368
|
title: "Agent Config",
|
|
1338
1369
|
description: "Configuring the agent model and behavior.",
|
|
1339
|
-
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",
|
|
1370
|
+
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| `backupModel` | No | Backup model for automatic failover on provider errors |\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## Backup Model\n\nConfigure a fallback model that activates automatically when the primary model encounters a transient provider error (rate limits, outages, timeouts):\n\n```yaml\nagent:\n model: anthropic/claude-sonnet-4-5\n backupModel: openai/gpt-4o\n system: system\n```\n\nWhen a provider error occurs, the system retries once with the backup model. If the backup also fails, the original error is returned.\n\n**Key behaviors:**\n\n- Only transient provider errors trigger fallback \u2014 authentication and validation errors are not retried\n- Provider-specific options (like `anthropic:`) are only forwarded to the backup model if it uses the same provider\n- For streaming responses, fallback only occurs if no content has been sent to the client yet\n\nLike `model`, `backupModel` supports variable references:\n\n```yaml\ninput:\n BACKUP_MODEL:\n type: string\n description: Fallback model for provider errors\n\nagent:\n model: anthropic/claude-sonnet-4-5\n backupModel: BACKUP_MODEL\n system: system\n```\n\n> **Tip**: Use a different provider for your backup model (e.g., primary on Anthropic, backup on OpenAI) to maximize resilience against single-provider outages.\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 backupModel: openai/gpt-4o # Failover 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 model, backup model, 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 backupModel: openai/gpt-4o\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",
|
|
1340
1371
|
excerpt: "Agent Config The section configures the LLM model, system prompt, tools, and behavior. Basic Configuration Configuration Options | Field | Required | Description ...",
|
|
1341
1372
|
order: 7
|
|
1342
1373
|
},
|
|
@@ -1354,7 +1385,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1354
1385
|
section: "protocol",
|
|
1355
1386
|
title: "Skills Advanced Guide",
|
|
1356
1387
|
description: "Best practices and advanced patterns for using Octavus skills.",
|
|
1357
|
-
content: "\n# Skills Advanced Guide\n\nThis guide covers advanced patterns and best practices for using Octavus skills in your agents.\n\n## When to Use Skills\n\nSkills are ideal for:\n\n- **Code execution** - Running Python/Bash scripts\n- **File generation** - Creating images, PDFs, reports\n- **Data processing** - Analyzing, transforming, or visualizing data\n- **Provider-agnostic needs** - Features that should work with any LLM\n\nUse external tools instead when:\n\n- **Simple API calls** - Database queries, external services\n- **Authentication required** - Accessing user-specific resources\n- **Backend integration** - Tight coupling with your infrastructure\n\n## Skill Selection Strategy\n\n### Defining Available Skills\n\nDefine all skills in the `skills:` section, then reference which skills are available where they're used:\n\n**Interactive agents** \u2014 reference in `agent.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n pdf-processor:\n display: description\n description: Processing PDFs\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\n**Workers and named threads** \u2014 reference per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n\nsteps:\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis]\n maxSteps: 10\n```\n\n### Match Skills to Use Cases\n\nDifferent threads can have different skills. Define all skills at the protocol level, then scope them to each thread:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n visualization:\n display: description\n description: Creating charts and visualizations\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\nFor a data analysis thread, you would specify `[data-analysis, visualization]` in `agent.skills` or in a `start-thread` block's `skills` field.\n\n## Display Mode Strategy\n\nChoose display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n### Guidelines\n\n- **`hidden`**: Background work that doesn't need user awareness\n- **`description`**: User-facing operations (default)\n- **`name`**: Quick operations where name is sufficient\n- **`stream`**: Long-running operations where progress matters\n\n## System Prompt Integration\n\nSkills are automatically injected into the system prompt. The LLM learns:\n\n1. **Available skills** - List of enabled skills with descriptions\n2. **How to use skills** - Instructions for using skill tools\n3. **Tool reference** - Available skill tools (`octavus_skill_read`, `octavus_code_run`, etc.)\n\nYou don't need to manually document skills in your system prompt. However, you can guide the LLM:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a helpful assistant that can generate QR codes.\n\n## When to Generate QR Codes\n\nGenerate QR codes when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Share WiFi credentials\n- Create scannable data\n\nUse the qr-code skill for all QR code generation tasks.\n```\n\n## Error Handling\n\nSkills handle errors gracefully:\n\n```yaml\n# Skill execution errors are returned to the LLM\n# The LLM can retry or explain the error to the user\n```\n\nCommon error scenarios:\n\n1. **Invalid skill slug** - Skill not found in organization\n2. **Code execution errors** - Syntax errors, runtime exceptions\n3. **Missing dependencies** - Required packages not installed\n4. **File I/O errors** - Permission issues, invalid paths\n\nThe LLM receives error messages and can:\n\n- Retry with corrected code\n- Explain errors to users\n- Suggest alternatives\n\n## File Output Patterns\n\n### Single File Output\n\n```python\n# Save single file to /output/\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\nqr = qrcode.QRCode()\nqr.add_data('https://example.com')\nimg = qr.make_image()\nimg.save(f'{output_dir}/qrcode.png')\n```\n\n### Multiple Files\n\n```python\n# Save multiple files\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Generate multiple outputs\nfor i in range(3):\n filename = f'{output_dir}/output_{i}.png'\n # ... generate file ...\n```\n\n### Structured Output\n\n```python\n# Save structured data + files\nimport json\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Save metadata\nmetadata = {\n 'files': ['chart.png', 'data.csv'],\n 'summary': 'Analysis complete'\n}\nwith open(f'{output_dir}/metadata.json', 'w') as f:\n json.dump(metadata, f)\n\n# Save actual files\n# ... generate chart.png and data.csv ...\n```\n\n## Performance Considerations\n\n### Lazy Initialization\n\nSandboxes are created only when a skill tool is first called:\n\n```yaml\nagent:\n skills: [qr-code] # Sandbox created on first skill tool call\n```\n\nThis means:\n\n- No cost if skills aren't used\n- Fast startup (no sandbox creation delay)\n- Each `next-message` execution gets its own sandbox with only the skills it needs\n\n### Timeout Limits\n\nSandboxes default to a 5-minute timeout. Configure `sandboxTimeout` on the agent config or per thread:\n\n```yaml\n# Agent-level\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes\n```\n\n```yaml\n# Thread-level (overrides agent-level)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour for long-running analysis\n```\n\nThread-level `sandboxTimeout` takes priority. Maximum: 1 hour (3,600,000 ms).\n\n### Sandbox Lifecycle\n\nEach `next-message` execution gets its own sandbox:\n\n- **Scoped** - Only contains the skills available to that thread\n- **Isolated** - Interactive agents and workers don't share sandboxes\n- **Resilient** - If a sandbox expires, it's transparently recreated\n- **Cleaned up** - Sandbox destroyed when the LLM call completes\n\n## Combining Skills with Tools\n\nSkills and tools can work together:\n\n```yaml\ntools:\n get-user-data:\n description: Fetch user data from database\n parameters:\n userId: { type: string }\n\nskills:\n data-analysis:\n display: description\n description: Analyzing data\n\nagent:\n tools: [get-user-data]\n skills: [data-analysis]\n agentic: true\n\nhandlers:\n analyze-user:\n Get user data:\n block: tool-call\n tool: get-user-data\n input:\n userId: USER_ID\n output: USER_DATA\n\n Analyze:\n block: next-message\n # LLM can use data-analysis skill with USER_DATA\n```\n\nPattern:\n\n1. Fetch data via tool (from your backend)\n2. LLM uses skill to analyze/process the data\n3. Generate outputs (files, reports)\n\n## Skill Development Tips\n\n### Writing SKILL.md\n\nFocus on **when** and **how** to use the skill:\n\n```markdown\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\n---\n\n# QR Code Generator\n\n## When to Use\n\nUse this skill when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Create scannable data\n\n## Quick Start\n\n[Clear examples of how to use the skill]\n```\n\n### Script Organization\n\nOrganize scripts logically:\n\n```\nskill-name/\n\u251C\u2500\u2500 SKILL.md\n\u2514\u2500\u2500 scripts/\n \u251C\u2500\u2500 generate.py # Main script\n \u251C\u2500\u2500 utils.py # Helper functions\n \u2514\u2500\u2500 requirements.txt # Dependencies\n```\n\n### Error Messages\n\nProvide helpful error messages:\n\n```python\ntry:\n # ... code ...\nexcept ValueError as e:\n print(f\"Error: Invalid input - {e}\")\n sys.exit(1)\n```\n\nThe LLM sees these errors and can retry or explain to users.\n\n## Security Considerations\n\n### Sandbox Isolation\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n### Input Validation\n\nSkills should validate inputs:\n\n```python\nimport sys\n\nif not data:\n print(\"Error: Data is required\")\n sys.exit(1)\n\nif len(data) > 1000:\n print(\"Error: Data too long (max 1000 characters)\")\n sys.exit(1)\n```\n\n### Resource Limits\n\nBe aware of:\n\n- **File size limits** - Large files may fail to upload\n- **Execution time** - Sandbox timeout (5-minute default, 1-hour maximum)\n- **Memory limits** - Sandbox environment constraints\n\n## Debugging Skills\n\n### Check Skill Documentation\n\nThe LLM can read skill docs:\n\n```python\n# LLM calls octavus_skill_read to see skill instructions\n```\n\n### Test Locally\n\nTest skills before uploading:\n\n```bash\n# Test skill locally\npython scripts/generate.py --data \"test\"\n```\n\n### Monitor Execution\n\nCheck execution logs in the platform debug view:\n\n- Tool calls and arguments\n- Code execution results\n- File outputs\n- Error messages\n\n## Common Patterns\n\n### Pattern 1: Generate and Return\n\n```yaml\n# User asks for QR code\n# LLM generates QR code\n# File automatically available for download\n```\n\n### Pattern 2: Analyze and Report\n\n```yaml\n# User provides data\n# LLM analyzes with skill\n# Generates report file\n# Returns summary + file link\n```\n\n### Pattern 3: Transform and Save\n\n```yaml\n# User uploads file (via tool)\n# LLM processes with skill\n# Generates transformed file\n# Returns new file link\n```\n\n## Best Practices Summary\n\n1. **Enable only needed skills** - Don't overwhelm the LLM\n2. **Choose appropriate display modes** - Match user experience needs\n3. **Write clear skill descriptions** - Help LLM understand when to use\n4. **Handle errors gracefully** - Provide helpful error messages\n5. **Test skills locally** - Verify before uploading\n6. **Monitor execution** - Check logs for issues\n7. **Combine with tools** - Use tools for data, skills for processing\n8. **Consider performance** - Be aware of timeouts and limits\n\n## Next Steps\n\n- [Skills](/docs/protocol/skills) - Basic skills documentation\n- [Agent Config](/docs/protocol/agent-config) - Configuring skills\n- [Tools](/docs/protocol/tools) - External tools integration\n",
|
|
1388
|
+
content: "\n# Skills Advanced Guide\n\nThis guide covers advanced patterns and best practices for using Octavus skills in your agents.\n\n## When to Use Skills\n\nSkills are ideal for:\n\n- **Code execution** - Running Python/Bash scripts\n- **File generation** - Creating images, PDFs, reports\n- **Data processing** - Analyzing, transforming, or visualizing data\n- **Provider-agnostic needs** - Features that should work with any LLM\n\nUse external tools instead when:\n\n- **Simple API calls** - Database queries, external services\n- **Authentication required** - Accessing user-specific resources\n- **Backend integration** - Tight coupling with your infrastructure\n\n## Skill Selection Strategy\n\n### Defining Available Skills\n\nDefine all skills in the `skills:` section, then reference which skills are available where they're used:\n\n**Interactive agents** \u2014 reference in `agent.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n pdf-processor:\n display: description\n description: Processing PDFs\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\n**Workers and named threads** \u2014 reference per-thread in `start-thread.skills`:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data\n\nsteps:\n Start analysis:\n block: start-thread\n thread: analysis\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code, data-analysis]\n maxSteps: 10\n```\n\n### Match Skills to Use Cases\n\nDifferent threads can have different skills. Define all skills at the protocol level, then scope them to each thread:\n\n```yaml\nskills:\n qr-code:\n display: description\n description: Generating QR codes\n data-analysis:\n display: description\n description: Analyzing data and generating reports\n visualization:\n display: description\n description: Creating charts and visualizations\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n skills: [qr-code]\n```\n\nFor a data analysis thread, you would specify `[data-analysis, visualization]` in `agent.skills` or in a `start-thread` block's `skills` field.\n\n## Display Mode Strategy\n\nChoose display modes based on user experience:\n\n```yaml\nskills:\n # Background processing - hide from user\n data-analysis:\n display: hidden\n\n # User-facing generation - show description\n qr-code:\n display: description\n\n # Interactive progress - stream updates\n report-generation:\n display: stream\n```\n\n### Guidelines\n\n- **`hidden`**: Background work that doesn't need user awareness\n- **`description`**: User-facing operations (default)\n- **`name`**: Quick operations where name is sufficient\n- **`stream`**: Long-running operations where progress matters\n\n## System Prompt Integration\n\nSkills are automatically injected into the system prompt. The LLM learns:\n\n1. **Available skills** - List of enabled skills with descriptions\n2. **How to use skills** - Instructions for using skill tools\n3. **Tool reference** - Available skill tools (`octavus_skill_read`, `octavus_code_run`, etc.)\n\nYou don't need to manually document skills in your system prompt. However, you can guide the LLM:\n\n```markdown\n<!-- prompts/system.md -->\n\nYou are a helpful assistant that can generate QR codes.\n\n## When to Generate QR Codes\n\nGenerate QR codes when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Share WiFi credentials\n- Create scannable data\n\nUse the qr-code skill for all QR code generation tasks.\n```\n\n## Error Handling\n\nSkills handle errors gracefully:\n\n```yaml\n# Skill execution errors are returned to the LLM\n# The LLM can retry or explain the error to the user\n```\n\nCommon error scenarios:\n\n1. **Invalid skill slug** - Skill not found in organization\n2. **Code execution errors** - Syntax errors, runtime exceptions\n3. **Missing dependencies** - Required packages not installed\n4. **File I/O errors** - Permission issues, invalid paths\n\nThe LLM receives error messages and can:\n\n- Retry with corrected code\n- Explain errors to users\n- Suggest alternatives\n\n## File Output Patterns\n\n### Single File Output\n\n```python\n# Save single file to /output/\nimport qrcode\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\nqr = qrcode.QRCode()\nqr.add_data('https://example.com')\nimg = qr.make_image()\nimg.save(f'{output_dir}/qrcode.png')\n```\n\n### Multiple Files\n\n```python\n# Save multiple files\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Generate multiple outputs\nfor i in range(3):\n filename = f'{output_dir}/output_{i}.png'\n # ... generate file ...\n```\n\n### Structured Output\n\n```python\n# Save structured data + files\nimport json\nimport os\n\noutput_dir = os.environ.get('OUTPUT_DIR', '/output')\n\n# Save metadata\nmetadata = {\n 'files': ['chart.png', 'data.csv'],\n 'summary': 'Analysis complete'\n}\nwith open(f'{output_dir}/metadata.json', 'w') as f:\n json.dump(metadata, f)\n\n# Save actual files\n# ... generate chart.png and data.csv ...\n```\n\n## Performance Considerations\n\n### Lazy Initialization\n\nSandboxes are created only when a skill tool is first called:\n\n```yaml\nagent:\n skills: [qr-code] # Sandbox created on first skill tool call\n```\n\nThis means:\n\n- No cost if skills aren't used\n- Fast startup (no sandbox creation delay)\n- Each `next-message` execution gets its own sandbox with only the skills it needs\n\n### Timeout Limits\n\nSandboxes default to a 5-minute timeout. Configure `sandboxTimeout` on the agent config or per thread:\n\n```yaml\n# Agent-level\nagent:\n model: anthropic/claude-sonnet-4-5\n skills: [data-analysis]\n sandboxTimeout: 1800000 # 30 minutes\n```\n\n```yaml\n# Thread-level (overrides agent-level)\nsteps:\n Start thread:\n block: start-thread\n thread: analysis\n skills: [data-analysis]\n sandboxTimeout: 3600000 # 1 hour for long-running analysis\n```\n\nThread-level `sandboxTimeout` takes priority. Maximum: 1 hour (3,600,000 ms).\n\n### Sandbox Lifecycle\n\nEach `next-message` execution gets its own sandbox:\n\n- **Scoped** - Only contains the skills available to that thread\n- **Isolated** - Interactive agents and workers don't share sandboxes\n- **Resilient** - If a sandbox expires, it's transparently recreated\n- **Cleaned up** - Sandbox destroyed when the LLM call completes\n\n## Combining Skills with Tools\n\nSkills and tools can work together:\n\n```yaml\ntools:\n get-user-data:\n description: Fetch user data from database\n parameters:\n userId: { type: string }\n\nskills:\n data-analysis:\n display: description\n description: Analyzing data\n\nagent:\n tools: [get-user-data]\n skills: [data-analysis]\n agentic: true\n\nhandlers:\n analyze-user:\n Get user data:\n block: tool-call\n tool: get-user-data\n input:\n userId: USER_ID\n output: USER_DATA\n\n Analyze:\n block: next-message\n # LLM can use data-analysis skill with USER_DATA\n```\n\nPattern:\n\n1. Fetch data via tool (from your backend)\n2. LLM uses skill to analyze/process the data\n3. Generate outputs (files, reports)\n\n## Secure Skills\n\nWhen a skill declares secrets and an organization configures them, the skill runs in secure mode with its own isolated sandbox.\n\n### Standard vs Secure Skills\n\n| Aspect | Standard Skills | Secure Skills |\n| ------------------- | --------------------------------- | --------------------------------------------------- |\n| **Sandbox** | Shared with other standard skills | Isolated (one per skill) |\n| **Available tools** | All 6 skill tools | `skill_read`, `skill_list`, `skill_run` only |\n| **Script input** | CLI arguments via `args` | JSON via stdin (use `input` parameter) |\n| **Environment** | No secrets | Secrets as env vars |\n| **Output** | Raw stdout/stderr | Redacted (secret values replaced with `[REDACTED]`) |\n\n### Writing Scripts for Secure Skills\n\nSecure skill scripts receive structured input via stdin (JSON) and access secrets from environment variables:\n\n```python\n#!/usr/bin/env python3\nimport json\nimport os\nimport sys\nimport subprocess\n\ninput_data = json.load(sys.stdin)\ntoken = os.environ[\"GITHUB_TOKEN\"]\n\nrepo = input_data.get(\"repo\", \"\")\nresult = subprocess.run(\n [\"gh\", \"repo\", \"view\", repo, \"--json\", \"name,description\"],\n capture_output=True, text=True,\n env={**os.environ, \"GH_TOKEN\": token}\n)\n\nprint(result.stdout)\n```\n\nKey patterns:\n\n- **Read stdin**: `json.load(sys.stdin)` to get the `input` object from the `octavus_skill_run` call\n- **Access secrets**: `os.environ[\"SECRET_NAME\"]` \u2014 secrets are injected as env vars\n- **Print output**: Write results to stdout \u2014 the LLM sees the (redacted) stdout\n- **Error handling**: Write errors to stderr and exit with non-zero code\n\n### Declaring Secrets in SKILL.md\n\n```yaml\n---\nname: github\ndescription: >\n Run GitHub CLI (gh) commands to manage repos, issues, PRs, and more.\nsecrets:\n - name: GITHUB_TOKEN\n description: GitHub personal access token with repo access\n required: true\n - name: GITHUB_ORG\n description: Default GitHub organization\n required: false\n---\n```\n\n### Testing Secure Skills Locally\n\nYou can test scripts locally by piping JSON to stdin:\n\n```bash\necho '{\"repo\": \"octavus-ai/agent-sdk\"}' | GITHUB_TOKEN=ghp_xxx python scripts/list-issues.py\n```\n\n## Skill Development Tips\n\n### Writing SKILL.md\n\nFocus on **when** and **how** to use the skill:\n\n```markdown\n---\nname: qr-code\ndescription: >\n Generate QR codes from text, URLs, or data. Use when the user needs to create\n a QR code for any purpose - sharing links, contact information, WiFi credentials,\n or any text data that should be scannable.\n---\n\n# QR Code Generator\n\n## When to Use\n\nUse this skill when users want to:\n\n- Share URLs easily\n- Provide contact information\n- Create scannable data\n\n## Quick Start\n\n[Clear examples of how to use the skill]\n```\n\n### Script Organization\n\nOrganize scripts logically:\n\n```\nskill-name/\n\u251C\u2500\u2500 SKILL.md\n\u2514\u2500\u2500 scripts/\n \u251C\u2500\u2500 generate.py # Main script\n \u251C\u2500\u2500 utils.py # Helper functions\n \u2514\u2500\u2500 requirements.txt # Dependencies\n```\n\n### Error Messages\n\nProvide helpful error messages:\n\n```python\ntry:\n # ... code ...\nexcept ValueError as e:\n print(f\"Error: Invalid input - {e}\")\n sys.exit(1)\n```\n\nThe LLM sees these errors and can retry or explain to users.\n\n## Security Considerations\n\n### Sandbox Isolation\n\n- **No network access** (unless explicitly configured)\n- **No persistent storage** (sandbox destroyed after each `next-message` execution)\n- **File output only** via `/output/` directory\n- **Time limits** enforced (5-minute default, configurable via `sandboxTimeout`)\n\n### Secret Protection\n\nFor skills with configured secrets:\n\n- **Isolated sandbox** \u2014 each secure skill gets its own sandbox, preventing cross-skill secret leakage\n- **No arbitrary code** \u2014 `octavus_code_run`, `octavus_file_write`, and `octavus_file_read` are blocked for secure skills, so only pre-built scripts can execute\n- **Output redaction** \u2014 all stdout and stderr are scanned for secret values before being returned to the LLM\n- **Encrypted at rest** \u2014 secrets are encrypted using AES-256-GCM and only decrypted at execution time\n\n### Input Validation\n\nSkills should validate inputs:\n\n```python\nimport sys\n\nif not data:\n print(\"Error: Data is required\")\n sys.exit(1)\n\nif len(data) > 1000:\n print(\"Error: Data too long (max 1000 characters)\")\n sys.exit(1)\n```\n\n### Resource Limits\n\nBe aware of:\n\n- **File size limits** - Large files may fail to upload\n- **Execution time** - Sandbox timeout (5-minute default, 1-hour maximum)\n- **Memory limits** - Sandbox environment constraints\n\n## Debugging Skills\n\n### Check Skill Documentation\n\nThe LLM can read skill docs:\n\n```python\n# LLM calls octavus_skill_read to see skill instructions\n```\n\n### Test Locally\n\nTest skills before uploading:\n\n```bash\n# Test skill locally\npython scripts/generate.py --data \"test\"\n```\n\n### Monitor Execution\n\nCheck execution logs in the platform debug view:\n\n- Tool calls and arguments\n- Code execution results\n- File outputs\n- Error messages\n\n## Common Patterns\n\n### Pattern 1: Generate and Return\n\n```yaml\n# User asks for QR code\n# LLM generates QR code\n# File automatically available for download\n```\n\n### Pattern 2: Analyze and Report\n\n```yaml\n# User provides data\n# LLM analyzes with skill\n# Generates report file\n# Returns summary + file link\n```\n\n### Pattern 3: Transform and Save\n\n```yaml\n# User uploads file (via tool)\n# LLM processes with skill\n# Generates transformed file\n# Returns new file link\n```\n\n## Best Practices Summary\n\n1. **Enable only needed skills** \u2014 Don't overwhelm the LLM\n2. **Choose appropriate display modes** \u2014 Match user experience needs\n3. **Write clear skill descriptions** \u2014 Help LLM understand when to use\n4. **Handle errors gracefully** \u2014 Provide helpful error messages\n5. **Test skills locally** \u2014 Verify before uploading\n6. **Monitor execution** \u2014 Check logs for issues\n7. **Combine with tools** \u2014 Use tools for data, skills for processing\n8. **Consider performance** \u2014 Be aware of timeouts and limits\n9. **Use secrets for credentials** \u2014 Declare secrets in frontmatter instead of hardcoding tokens\n10. **Design scripts for stdin input** \u2014 Secure skills receive JSON via stdin, so plan for both input methods if the skill might be used in either mode\n\n## Next Steps\n\n- [Skills](/docs/protocol/skills) - Basic skills documentation\n- [Agent Config](/docs/protocol/agent-config) - Configuring skills\n- [Tools](/docs/protocol/tools) - External tools integration\n",
|
|
1358
1389
|
excerpt: "Skills Advanced Guide This guide covers advanced patterns and best practices for using Octavus skills in your agents. When to Use Skills Skills are ideal for: - Code execution - Running Python/Bash...",
|
|
1359
1390
|
order: 9
|
|
1360
1391
|
},
|
|
@@ -1384,6 +1415,15 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1384
1415
|
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",
|
|
1385
1416
|
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...",
|
|
1386
1417
|
order: 12
|
|
1418
|
+
},
|
|
1419
|
+
{
|
|
1420
|
+
slug: "protocol/mcp-servers",
|
|
1421
|
+
section: "protocol",
|
|
1422
|
+
title: "MCP Servers",
|
|
1423
|
+
description: "Connecting agents to external tools via Model Context Protocol (MCP).",
|
|
1424
|
+
content: "\n# MCP Servers\n\nMCP servers extend your agent with tools from external services. Define them in your protocol, and agents automatically discover and use their tools at runtime.\n\nThere are two types of MCP servers:\n\n| Source | Description | Example |\n| -------- | -------------------------------------------------------------------- | ------------------------------- |\n| `remote` | HTTP-based MCP servers, managed by the platform | Figma, Sentry, GitHub |\n| `device` | Local MCP servers running on the consumer's machine via server-sdk | Browser automation, filesystem |\n\n## Defining MCP Servers\n\nMCP servers are defined in the `mcpServers:` section. The key becomes the **namespace** for all tools from that server.\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n```\n\n### Fields\n\n| Field | Required | Description |\n| ------------- | -------- | ------------------------------------------------------------ |\n| `description` | Yes | What the MCP server provides |\n| `source` | Yes | `remote` (platform-managed) or `device` (consumer-provided) |\n| `display` | No | How tool calls appear in UI: `hidden`, `name`, `description` |\n\n### Display Modes\n\nDisplay modes control visibility of all tool calls from the MCP server, using the same modes as [regular tools](/docs/protocol/tools#display-modes):\n\n| Mode | Behavior |\n| ------------- | -------------------------------------------- |\n| `hidden` | Tool calls run silently |\n| `name` | Shows tool name while executing |\n| `description` | Shows tool description while executing |\n\n## Making MCP Servers Available\n\nLike tools, MCP servers defined in `mcpServers:` must be referenced in `agent.mcpServers` to be available:\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n\n filesystem:\n description: Filesystem access for reading and writing files\n source: device\n display: hidden\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n mcpServers: [figma, sentry, browser, filesystem]\n tools: [set-chat-title]\n agentic: true\n maxSteps: 100\n```\n\n## Tool Namespacing\n\nAll MCP tools are automatically namespaced using `__` (double underscore) as a separator. The namespace comes from the `mcpServers` key.\n\nFor example, a server defined as `browser:` that exposes `navigate_page` and `click` produces:\n\n- `browser__navigate_page`\n- `browser__click`\n\nA server defined as `figma:` that exposes `get_design_context` produces:\n\n- `figma__get_design_context`\n\nThe namespace is stripped before calling the MCP server \u2014 the server receives the original tool name. This convention matches Anthropic's MCP integration in Claude Desktop and ensures tool names stay unique across servers.\n\n### What the LLM Sees\n\nWhen an agent has both regular tools and MCP servers configured, the LLM sees all tools combined:\n\n```\nProtocol tools:\n set-chat-title\n\nRemote MCP tools (auto-discovered):\n figma__get_design_context\n figma__get_screenshot\n sentry__get_issues\n sentry__get_issue_details\n\nDevice MCP tools (auto-discovered):\n browser__navigate_page\n browser__click\n browser__take_snapshot\n filesystem__read_file\n filesystem__write_file\n filesystem__list_directory\n```\n\nYou don't define individual MCP tool schemas in the protocol \u2014 they're auto-discovered from each MCP server at runtime.\n\n## Remote MCP Servers\n\nRemote MCP servers (`source: remote`) connect to HTTP-based MCP endpoints. The platform manages the connection, authentication, and tool discovery.\n\nConfiguration happens in the Octavus platform UI:\n\n1. Add an MCP server to your project (URL + authentication)\n2. The server's slug must match the namespace in your protocol\n3. The platform connects, discovers tools, and makes them available to the agent\n\n### Authentication\n\nRemote MCP servers support multiple authentication methods:\n\n| Auth Type | Description |\n| ----------- | ------------------------------------ |\n| MCP OAuth | Standard MCP OAuth flow |\n| API Key | Static API key sent as a header |\n| Bearer | Bearer token authentication |\n| None | No authentication required |\n\nAuthentication is configured per-project \u2014 different projects can connect to the same MCP server with different credentials.\n\n## Device MCP Servers\n\nDevice MCP servers (`source: device`) run on the consumer's machine. The consumer provides the MCP tools via the `@octavus/computer` package (or any `ToolProvider` implementation) through the server-sdk.\n\nWhen an agent has device MCP servers:\n\n1. The consumer creates a `Computer` with matching namespaces\n2. `@octavus/computer` discovers tools from each MCP server\n3. Tool schemas are sent to the platform via the server-sdk\n4. Tool calls flow back to the consumer for execution\n\nSee [`@octavus/computer`](/docs/server-sdk/computer) for the full integration guide.\n\n### Namespace Matching\n\nThe `mcpServers` keys in the protocol must match the keys in the consumer's `Computer` configuration:\n\n```yaml\n# protocol.yaml\nmcpServers:\n browser: # \u2190 must match\n source: device\n filesystem: # \u2190 must match\n source: device\n```\n\n```typescript\nconst computer = new Computer({\n mcpServers: {\n browser: Computer.stdio('chrome-devtools-mcp', ['--browser-url=...']),\n filesystem: Computer.stdio('@modelcontextprotocol/server-filesystem', [dir]),\n },\n});\n```\n\nIf the consumer provides a namespace not declared in the protocol, the platform rejects it.\n\n## Thread-Level Scoping\n\nThreads can scope which MCP servers are available, the same way they scope [tools](/docs/protocol/handlers#start-thread):\n\n```yaml\nhandlers:\n user-message:\n Start research:\n block: start-thread\n thread: research\n mcpServers: [figma, browser]\n tools: [set-chat-title]\n system: research-prompt\n```\n\nThis thread can use Figma and browser tools, but not sentry or filesystem \u2014 even if those are available on the main agent.\n\n## Full Example\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n browser:\n description: Chrome DevTools browser automation\n source: device\n display: name\n filesystem:\n description: Filesystem access for reading and writing files\n source: device\n display: hidden\n shell:\n description: Shell command execution\n source: device\n display: name\n\ntools:\n set-chat-title:\n description: Set the title of the current chat.\n parameters:\n title: { type: string, description: The title to set }\n\nagent:\n model: anthropic/claude-opus-4-6\n system: system\n mcpServers: [figma, sentry, browser, filesystem, shell]\n tools: [set-chat-title]\n thinking: medium\n maxSteps: 300\n agentic: true\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### Cloud-Only Agent\n\nAgents that only use remote MCP servers don't need `@octavus/computer`:\n\n```yaml\nmcpServers:\n figma:\n description: Figma design tool integration\n source: remote\n display: description\n sentry:\n description: Error tracking and debugging\n source: remote\n display: name\n\ntools:\n submit-code:\n description: Submit code to the user.\n parameters:\n code: { type: string }\n\nagent:\n model: anthropic/claude-sonnet-4-5\n system: system\n mcpServers: [figma, sentry]\n tools: [submit-code]\n agentic: true\n```\n",
|
|
1425
|
+
excerpt: "MCP Servers MCP servers extend your agent with tools from external services. Define them in your protocol, and agents automatically discover and use their tools at runtime. There are two types of MCP...",
|
|
1426
|
+
order: 13
|
|
1387
1427
|
}
|
|
1388
1428
|
]
|
|
1389
1429
|
},
|
|
@@ -1442,7 +1482,7 @@ See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list
|
|
|
1442
1482
|
section: "examples",
|
|
1443
1483
|
title: "Next.js Chat",
|
|
1444
1484
|
description: "Building a chat interface with Next.js and HTTP transport.",
|
|
1445
|
-
content: "\n# Next.js Chat Example\n\nThis example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications.\n\n## What You're Building\n\nA chat interface that:\n\n- Creates sessions server-side\n- Streams AI responses in real-time\n- Handles tool calls on your server\n- Shows typing status during streaming\n\n## Architecture\n\n```mermaid\nflowchart LR\n Browser[\"Browser<br/>(React)\"] -->|\"POST /api/chat\"| NextJS[\"Next.js API<br/>Routes\"]\n NextJS -->|\"SSE\"| Browser\n NextJS --> Platform[\"Octavus Platform\"]\n```\n\n## Prerequisites\n\n- Next.js 14+ with App Router\n- Octavus account with API key\n- An agent configured in Octavus\n\n## Step 1: Install Dependencies\n\n```bash\nnpm install @octavus/server-sdk @octavus/react\n```\n\n## Step 2: Configure Environment\n\n```bash\n# .env.local\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key\n```\n\n## Step 3: Create the Octavus Client\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n## Step 4: Create Upload URLs Endpoint (Optional)\n\nIf your agent supports file uploads (images, documents), create an endpoint to get presigned URLs:\n\n```typescript\n// app/api/upload-urls/route.ts\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## Step 5: Create Session Endpoint\n\nSessions hold conversation state. Create one when the user opens the chat:\n\n```typescript\n// app/api/sessions/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { agentId, input } = await request.json();\n\n // Create a new session with initial input variables\n const sessionId = await octavus.agentSessions.create(agentId, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n**Protocol Note:** The `input` object contains variables defined in your agent's protocol. For example, if your agent has `COMPANY_NAME` as an input variable:\n\n```typescript\nconst sessionId = await octavus.agentSessions.create(agentId, {\n COMPANY_NAME: 'Acme Corp',\n USER_ID: user.id,\n});\n```\n\n## Step 6: Create Trigger Endpoint\n\nTriggers execute agent actions. The `user-message` trigger is the most common:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to the session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Server-side tool handlers run on YOUR server, not Octavus\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n const user = await db.users.findUnique({ where: { id: userId } });\n return {\n name: user.name,\n email: user.email,\n plan: user.plan,\n };\n },\n\n 'create-support-ticket': async (args) => {\n const ticket = await db.tickets.create({\n data: {\n summary: args.summary as string,\n priority: args.priority as string,\n },\n });\n return {\n ticketId: ticket.id,\n estimatedResponse: '24 hours',\n };\n },\n\n // Tools without handlers here are forwarded to the client\n // See Client Tools docs for handling on frontend\n },\n });\n\n // execute() handles both triggers and client tool continuations\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n```\n\n**Protocol Note:** Tool names and arguments are defined in your agent's protocol YAML. The tool handlers here must match those definitions. Tools without server handlers are forwarded to the client.\n\n## Step 7: Build the Chat Component\n\n```tsx\n// components/Chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create transport - 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 handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || status === 'streaming') return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Send triggers the 'user-message' action\n // The third argument adds the user message to the UI\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-screen\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <div key={msg.id} className={msg.role === 'user' ? 'text-right' : 'text-left'}>\n <div\n className={`inline-block p-3 rounded-lg ${\n msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-100'\n }`}\n >\n {msg.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n if (part.type === 'tool-call') {\n return (\n <div key={i} className=\"text-sm opacity-70\">\n Using {part.toolName}...\n </div>\n );\n }\n return null;\n })}\n </div>\n </div>\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={status === 'streaming'}\n />\n <button\n type=\"submit\"\n disabled={status === 'streaming'}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg\"\n >\n {status === 'streaming' ? 'Sending...' : 'Send'}\n </button>\n </div>\n </form>\n </div>\n );\n}\n```\n\n## Step 8: Create the Page\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/Chat';\n\nconst AGENT_ID = 'your-agent-id'; // From Octavus dashboard\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n // Create session on mount\n fetch('/api/sessions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n agentId: AGENT_ID,\n input: {\n COMPANY_NAME: 'Acme Corp',\n },\n }),\n })\n .then((res) => res.json())\n .then((data) => setSessionId(data.sessionId));\n }, []);\n\n if (!sessionId) {\n return <div className=\"p-8\">Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Protocol Integration\n\nYour agent's protocol defines the triggers and tools. Here's how the code maps to protocol:\n\n### Triggers\n\n```yaml\n# In your agent's protocol.yaml\ntriggers:\n user-message:\n description: User sends a chat message\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n```\n\nThe `send()` call maps directly:\n\n```typescript\nawait send(\n 'user-message', // trigger name\n { USER_MESSAGE: message }, // trigger inputs\n { userMessage: { content: message } },\n);\n```\n\n### Tools\n\n```yaml\n# In your agent's protocol.yaml\ntools:\n get-user-account:\n description: Fetch user account details\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\nTool handlers receive the parameters as `args`:\n\n```typescript\n'get-user-account': async (args) => {\n const userId = args.userId as string;\n // ...\n}\n```\n\n## Next Steps\n\n- [Protocol Overview](/docs/protocol/overview) \u2014 Define agent behavior\n- [Messages](/docs/client-sdk/messages) \u2014 Rich message rendering\n- [Streaming](/docs/client-sdk/streaming) \u2014 Advanced streaming UI\n",
|
|
1485
|
+
content: "\n# Next.js Chat Example\n\nThis example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications.\n\n## What You're Building\n\nA chat interface that:\n\n- Creates sessions server-side\n- Streams AI responses in real-time\n- Handles tool calls on your server\n- Shows typing status during streaming\n\n## Architecture\n\n```mermaid\nflowchart LR\n Browser[\"Browser<br/>(React)\"] -->|\"POST /api/chat\"| NextJS[\"Next.js API<br/>Routes\"]\n NextJS -->|\"SSE\"| Browser\n NextJS --> Platform[\"Octavus Platform\"]\n```\n\n## Prerequisites\n\n- Next.js 14+ with App Router\n- Octavus account with API key\n- An agent configured in Octavus\n\n## Step 1: Install Dependencies\n\n```bash\nnpm install @octavus/server-sdk @octavus/react\n```\n\n## Step 2: Configure Environment\n\n```bash\n# .env.local\nOCTAVUS_API_URL=https://octavus.ai\nOCTAVUS_API_KEY=your-api-key\n```\n\n## Step 3: Create the Octavus Client\n\n```typescript\n// lib/octavus.ts\nimport { OctavusClient } from '@octavus/server-sdk';\n\nexport const octavus = new OctavusClient({\n baseUrl: process.env.OCTAVUS_API_URL!,\n apiKey: process.env.OCTAVUS_API_KEY!,\n});\n```\n\n## Step 4: Create Upload URLs Endpoint (Optional)\n\nIf your agent supports file uploads (images, documents), create an endpoint to get presigned URLs:\n\n```typescript\n// app/api/upload-urls/route.ts\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## Step 5: Create Session Endpoint\n\nSessions hold conversation state. Create one when the user opens the chat:\n\n```typescript\n// app/api/sessions/route.ts\nimport { NextResponse } from 'next/server';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const { agentId, input } = await request.json();\n\n // Create a new session with initial input variables\n const sessionId = await octavus.agentSessions.create(agentId, input);\n\n return NextResponse.json({ sessionId });\n}\n```\n\n**Protocol Note:** The `input` object contains variables defined in your agent's protocol. For example, if your agent has `COMPANY_NAME` as an input variable:\n\n```typescript\nconst sessionId = await octavus.agentSessions.create(agentId, {\n COMPANY_NAME: 'Acme Corp',\n USER_ID: user.id,\n});\n```\n\n## Step 6: Create Trigger Endpoint\n\nTriggers execute agent actions. The `user-message` trigger is the most common:\n\n```typescript\n// app/api/trigger/route.ts\nimport { toSSEStream } from '@octavus/server-sdk';\nimport { octavus } from '@/lib/octavus';\n\nexport async function POST(request: Request) {\n const body = await request.json();\n const { sessionId, ...payload } = body;\n\n // Attach to the session with tool handlers\n const session = octavus.agentSessions.attach(sessionId, {\n tools: {\n // Server-side tool handlers run on YOUR server, not Octavus\n 'get-user-account': async (args) => {\n const userId = args.userId as string;\n // Fetch from your database\n const user = await db.users.findUnique({ where: { id: userId } });\n return {\n name: user.name,\n email: user.email,\n plan: user.plan,\n };\n },\n\n 'create-support-ticket': async (args) => {\n const ticket = await db.tickets.create({\n data: {\n summary: args.summary as string,\n priority: args.priority as string,\n },\n });\n return {\n ticketId: ticket.id,\n estimatedResponse: '24 hours',\n };\n },\n\n // Tools without handlers here are forwarded to the client\n // See Client Tools docs for handling on frontend\n },\n });\n\n // execute() handles both triggers and client tool continuations\n const events = session.execute(payload, { signal: request.signal });\n\n return new Response(toSSEStream(events), {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n}\n```\n\n**Protocol Note:** Tool names and arguments are defined in your agent's protocol YAML. The tool handlers here must match those definitions. Tools without server handlers are forwarded to the client.\n\n## Step 7: Build the Chat Component\n\n```tsx\n// components/Chat.tsx\n'use client';\n\nimport { useState, useMemo } from 'react';\nimport { useOctavusChat, createHttpTransport } from '@octavus/react';\n\ninterface ChatProps {\n sessionId: string;\n}\n\nexport function Chat({ sessionId }: ChatProps) {\n const [inputValue, setInputValue] = useState('');\n\n // Create transport - 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 handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!inputValue.trim() || status === 'streaming') return;\n\n const message = inputValue.trim();\n setInputValue('');\n\n // Send triggers the 'user-message' action\n // The third argument adds the user message to the UI\n await send('user-message', { USER_MESSAGE: message }, { userMessage: { content: message } });\n };\n\n return (\n <div className=\"flex flex-col h-screen\">\n {/* Messages */}\n <div className=\"flex-1 overflow-y-auto p-4 space-y-4\">\n {messages.map((msg) => (\n <div key={msg.id} className={msg.role === 'user' ? 'text-right' : 'text-left'}>\n <div\n className={`inline-block p-3 rounded-lg ${\n msg.role === 'user' ? 'bg-blue-500 text-white' : 'bg-gray-100'\n }`}\n >\n {msg.parts.map((part, i) => {\n if (part.type === 'text') {\n return <p key={i}>{part.text}</p>;\n }\n if (part.type === 'tool-call') {\n return (\n <div key={i} className=\"text-sm opacity-70\">\n Using {part.toolName}...\n </div>\n );\n }\n return null;\n })}\n </div>\n </div>\n ))}\n </div>\n\n {/* Input */}\n <form onSubmit={handleSubmit} className=\"p-4 border-t\">\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n placeholder=\"Type a message...\"\n className=\"flex-1 px-4 py-2 border rounded-lg\"\n disabled={status === 'streaming'}\n />\n <button\n type=\"submit\"\n disabled={status === 'streaming'}\n className=\"px-4 py-2 bg-blue-500 text-white rounded-lg\"\n >\n {status === 'streaming' ? 'Sending...' : 'Send'}\n </button>\n </div>\n </form>\n </div>\n );\n}\n```\n\n## Step 8: Create the Page\n\n```tsx\n// app/chat/page.tsx\n'use client';\n\nimport { useEffect, useState } from 'react';\nimport { Chat } from '@/components/Chat';\n\nconst AGENT_ID = 'your-agent-id'; // From Octavus dashboard\n\nexport default function ChatPage() {\n const [sessionId, setSessionId] = useState<string | null>(null);\n\n useEffect(() => {\n // Create session on mount\n fetch('/api/sessions', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n agentId: AGENT_ID,\n input: {\n COMPANY_NAME: 'Acme Corp',\n },\n }),\n })\n .then((res) => res.json())\n .then((data) => setSessionId(data.sessionId));\n }, []);\n\n if (!sessionId) {\n return <div className=\"p-8\">Loading...</div>;\n }\n\n return <Chat sessionId={sessionId} />;\n}\n```\n\n## Protocol Integration\n\nYour agent's protocol defines the triggers and tools. Here's how the code maps to protocol:\n\n### Triggers\n\n```yaml\n# In your agent's protocol.yaml\ntriggers:\n user-message:\n description: User sends a chat message\n input:\n USER_MESSAGE:\n type: string\n description: The user's message\n```\n\nThe `send()` call maps directly:\n\n```typescript\nawait send(\n 'user-message', // trigger name\n { USER_MESSAGE: message }, // trigger inputs\n { userMessage: { content: message } },\n);\n```\n\n### Tools\n\n```yaml\n# In your agent's protocol.yaml\ntools:\n get-user-account:\n description: Fetch user account details\n parameters:\n userId:\n type: string\n description: The user ID to look up\n```\n\nTool handlers receive the parameters as `args`:\n\n```typescript\n'get-user-account': async (args) => {\n const userId = args.userId as string;\n // ...\n}\n```\n\n## Next Steps\n\n- [Protocol Overview](/docs/protocol/overview) \u2014 Define agent behavior\n- [Messages](/docs/client-sdk/messages) \u2014 Rich message rendering\n- [Streaming](/docs/client-sdk/streaming) \u2014 Advanced streaming UI\n",
|
|
1446
1486
|
excerpt: "Next.js Chat Example This example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications. What You're Building A...",
|
|
1447
1487
|
order: 2
|
|
1448
1488
|
},
|
|
@@ -1506,4 +1546,4 @@ export {
|
|
|
1506
1546
|
getDocSlugs,
|
|
1507
1547
|
getSectionBySlug
|
|
1508
1548
|
};
|
|
1509
|
-
//# sourceMappingURL=chunk-
|
|
1549
|
+
//# sourceMappingURL=chunk-UZWGRPRR.js.map
|