@octavus/docs 2.10.0 → 2.12.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.
@@ -96,6 +96,22 @@ return new Response(toSSEStream(events), {
96
96
  });
97
97
  ```
98
98
 
99
+ ### Workers
100
+
101
+ Execute worker agents for task-based processing:
102
+
103
+ ```typescript
104
+ // Non-streaming: get the output directly
105
+ const { output } = await client.workers.generate(agentId, {
106
+ TOPIC: 'AI safety',
107
+ });
108
+
109
+ // Streaming: observe events in real-time
110
+ for await (const event of client.workers.execute(agentId, input)) {
111
+ // Handle stream events
112
+ }
113
+ ```
114
+
99
115
  ## API Reference
100
116
 
101
117
  ### OctavusClient
@@ -169,11 +169,17 @@ The CLI expects agent definitions in a specific directory structure:
169
169
  my-agent/
170
170
  ├── settings.json # Required: Agent metadata
171
171
  ├── protocol.yaml # Required: Agent protocol
172
- └── prompts/ # Optional: Prompt templates
173
- ├── system.md
174
- └── user-message.md
172
+ ├── prompts/ # Optional: Prompt templates
173
+ ├── system.md
174
+ └── user-message.md
175
+ └── references/ # Optional: Reference documents
176
+ └── api-guidelines.md
175
177
  ```
176
178
 
179
+ ### references/
180
+
181
+ Reference 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.
182
+
177
183
  ### settings.json
178
184
 
179
185
  ```json
@@ -17,59 +17,56 @@ const client = new OctavusClient({
17
17
  apiKey: 'your-api-key',
18
18
  });
19
19
 
20
- // Execute a worker
21
- const events = client.workers.execute(agentId, {
20
+ const { output, sessionId } = await client.workers.generate(agentId, {
22
21
  TOPIC: 'AI safety',
23
22
  DEPTH: 'detailed',
24
23
  });
25
24
 
26
- // Process events
27
- for await (const event of events) {
28
- if (event.type === 'worker-start') {
29
- console.log(`Worker ${event.workerSlug} started`);
30
- }
31
- if (event.type === 'text-delta') {
32
- process.stdout.write(event.delta);
33
- }
34
- if (event.type === 'worker-result') {
35
- console.log('Output:', event.output);
36
- }
37
- }
25
+ console.log('Result:', output);
26
+ console.log(`Debug: ${client.baseUrl}/sessions/${sessionId}`);
38
27
  ```
39
28
 
40
29
  ## WorkersApi Reference
41
30
 
42
- ### execute()
31
+ ### generate()
43
32
 
44
- Execute a worker and stream the response.
33
+ Execute a worker and return the output directly.
45
34
 
46
35
  ```typescript
47
- async *execute(
36
+ async generate(
48
37
  agentId: string,
49
38
  input: Record<string, unknown>,
50
39
  options?: WorkerExecuteOptions
51
- ): AsyncGenerator<StreamEvent>
40
+ ): Promise<WorkerGenerateResult>
52
41
  ```
53
42
 
54
- **Parameters:**
55
-
56
- | Parameter | Type | Description |
57
- | --------- | ------------------------- | --------------------------- |
58
- | `agentId` | `string` | The worker agent ID |
59
- | `input` | `Record<string, unknown>` | Input values for the worker |
60
- | `options` | `WorkerExecuteOptions` | Optional configuration |
43
+ Runs the worker to completion and returns the output value. This is the simplest way to execute a worker.
61
44
 
62
- **Options:**
45
+ **Returns:**
63
46
 
64
47
  ```typescript
65
- interface WorkerExecuteOptions {
66
- /** Tool handlers for server-side tool execution */
67
- tools?: ToolHandlers;
68
- /** Abort signal to cancel the execution */
69
- signal?: AbortSignal;
48
+ interface WorkerGenerateResult {
49
+ /** The worker's output value */
50
+ output: unknown;
51
+ /** Session ID for debugging (usable for session URLs) */
52
+ sessionId: string;
70
53
  }
71
54
  ```
72
55
 
56
+ **Throws:** `WorkerError` if the worker fails or completes without producing output.
57
+
58
+ ### execute()
59
+
60
+ Execute a worker and stream the response. Use this when you need to observe intermediate events like text deltas, tool calls, or progress tracking.
61
+
62
+ ```typescript
63
+ async *execute(
64
+ agentId: string,
65
+ input: Record<string, unknown>,
66
+ options?: WorkerExecuteOptions
67
+ ): AsyncGenerator<StreamEvent>
68
+ ```
69
+
73
70
  ### continue()
74
71
 
75
72
  Continue execution after client-side tool handling.
@@ -85,19 +82,39 @@ async *continue(
85
82
 
86
83
  Use this when the worker has tools without server-side handlers. The execution pauses with a `client-tool-request` event, you execute the tools, then call `continue()` to resume.
87
84
 
85
+ ### Shared Options
86
+
87
+ All methods accept the same options:
88
+
89
+ ```typescript
90
+ interface WorkerExecuteOptions {
91
+ /** Tool handlers for server-side tool execution */
92
+ tools?: ToolHandlers;
93
+ /** Abort signal to cancel the execution */
94
+ signal?: AbortSignal;
95
+ }
96
+ ```
97
+
98
+ **Parameters:**
99
+
100
+ | Parameter | Type | Description |
101
+ | --------- | ------------------------- | --------------------------- |
102
+ | `agentId` | `string` | The worker agent ID |
103
+ | `input` | `Record<string, unknown>` | Input values for the worker |
104
+ | `options` | `WorkerExecuteOptions` | Optional configuration |
105
+
88
106
  ## Tool Handlers
89
107
 
90
108
  Provide tool handlers to execute tools server-side:
91
109
 
92
110
  ```typescript
93
- const events = client.workers.execute(
111
+ const { output } = await client.workers.generate(
94
112
  agentId,
95
113
  { TOPIC: 'AI safety' },
96
114
  {
97
115
  tools: {
98
116
  'web-search': async (args) => {
99
- const results = await searchWeb(args.query);
100
- return results;
117
+ return await searchWeb(args.query);
101
118
  },
102
119
  'get-user-data': async (args) => {
103
120
  return await db.users.findById(args.userId);
@@ -109,85 +126,141 @@ const events = client.workers.execute(
109
126
 
110
127
  Tools defined in the worker protocol but not provided as handlers become client tools — the execution pauses and emits a `client-tool-request` event.
111
128
 
112
- ## Stream Events
129
+ ## Error Handling
113
130
 
114
- Workers emit standard stream events plus worker-specific events.
131
+ ### WorkerError (generate)
115
132
 
116
- ### Worker Events
133
+ `generate()` throws a `WorkerError` on failure. The error includes an optional `sessionId` for constructing debug URLs:
117
134
 
118
135
  ```typescript
119
- // Worker started
120
- {
121
- type: 'worker-start',
122
- workerId: string, // Unique ID (also used as session ID for debug)
123
- workerSlug: string, // The worker's slug
124
- description?: string, // Display description for UI
136
+ import { OctavusClient, WorkerError } from '@octavus/server-sdk';
137
+
138
+ try {
139
+ const { output } = await client.workers.generate(agentId, input);
140
+ console.log('Result:', output);
141
+ } catch (error) {
142
+ if (error instanceof WorkerError) {
143
+ console.error('Worker failed:', error.message);
144
+ if (error.sessionId) {
145
+ console.error(`Debug: ${client.baseUrl}/sessions/${error.sessionId}`);
146
+ }
147
+ }
125
148
  }
149
+ ```
126
150
 
127
- // Worker completed
128
- {
129
- type: 'worker-result',
130
- workerId: string,
131
- output?: unknown, // The worker's output value
132
- error?: string, // Error message if worker failed
151
+ ### Stream Errors (execute)
152
+
153
+ When using `execute()`, errors appear as stream events:
154
+
155
+ ```typescript
156
+ for await (const event of client.workers.execute(agentId, input)) {
157
+ if (event.type === 'error') {
158
+ console.error(`Error: ${event.message}`);
159
+ console.error(`Type: ${event.errorType}`);
160
+ console.error(`Retryable: ${event.retryable}`);
161
+ }
162
+
163
+ if (event.type === 'worker-result' && event.error) {
164
+ console.error(`Worker failed: ${event.error}`);
165
+ }
133
166
  }
134
167
  ```
135
168
 
136
- ### Common Events
169
+ ### Error Types
137
170
 
138
- | Event | Description |
139
- | ----------------------- | --------------------------- |
140
- | `start` | Execution started |
141
- | `finish` | Execution completed |
142
- | `text-start` | Text generation started |
143
- | `text-delta` | Text chunk received |
144
- | `text-end` | Text generation ended |
145
- | `block-start` | Step started |
146
- | `block-end` | Step completed |
147
- | `tool-input-available` | Tool arguments ready |
148
- | `tool-output-available` | Tool result ready |
149
- | `client-tool-request` | Client tools need execution |
150
- | `error` | Error occurred |
171
+ | Type | Description |
172
+ | ------------------ | --------------------- |
173
+ | `validation_error` | Invalid input |
174
+ | `not_found_error` | Worker not found |
175
+ | `provider_error` | LLM provider error |
176
+ | `tool_error` | Tool execution failed |
177
+ | `execution_error` | Worker step failed |
151
178
 
152
- ## Extracting Output
179
+ ## Cancellation
153
180
 
154
- To get just the worker's output value:
181
+ Use an abort signal to cancel execution:
155
182
 
156
183
  ```typescript
157
- async function executeWorker(
158
- client: OctavusClient,
159
- agentId: string,
160
- input: Record<string, unknown>,
161
- ): Promise<unknown> {
162
- const events = client.workers.execute(agentId, input);
184
+ const { output } = await client.workers.generate(agentId, input, {
185
+ signal: AbortSignal.timeout(30_000),
186
+ });
187
+ ```
163
188
 
164
- for await (const event of events) {
165
- if (event.type === 'worker-result') {
166
- if (event.error) {
167
- throw new Error(event.error);
168
- }
169
- return event.output;
170
- }
189
+ With `execute()` and a manual controller:
190
+
191
+ ```typescript
192
+ const controller = new AbortController();
193
+ setTimeout(() => controller.abort(), 30000);
194
+
195
+ try {
196
+ for await (const event of client.workers.execute(agentId, input, {
197
+ signal: controller.signal,
198
+ })) {
199
+ // Process events
200
+ }
201
+ } catch (error) {
202
+ if (error.name === 'AbortError') {
203
+ console.log('Worker cancelled');
171
204
  }
205
+ }
206
+ ```
207
+
208
+ ## Streaming
172
209
 
173
- return undefined;
210
+ When you need real-time visibility into the worker's execution — text generation, tool calls, or progress — use `execute()` instead of `generate()`.
211
+
212
+ ### Basic Streaming
213
+
214
+ ```typescript
215
+ const events = client.workers.execute(agentId, {
216
+ TOPIC: 'AI safety',
217
+ DEPTH: 'detailed',
218
+ });
219
+
220
+ for await (const event of events) {
221
+ if (event.type === 'worker-start') {
222
+ console.log(`Worker ${event.workerSlug} started`);
223
+ }
224
+ if (event.type === 'text-delta') {
225
+ process.stdout.write(event.delta);
226
+ }
227
+ if (event.type === 'worker-result') {
228
+ console.log('Output:', event.output);
229
+ }
174
230
  }
231
+ ```
232
+
233
+ ### Streaming to HTTP Response
234
+
235
+ Convert worker events to an SSE stream:
236
+
237
+ ```typescript
238
+ import { toSSEStream } from '@octavus/server-sdk';
239
+
240
+ export async function POST(request: Request) {
241
+ const { agentId, input } = await request.json();
242
+
243
+ const events = client.workers.execute(agentId, input, {
244
+ tools: {
245
+ search: async (args) => await search(args.query),
246
+ },
247
+ });
175
248
 
176
- // Usage
177
- const analysis = await executeWorker(client, agentId, { TOPIC: 'AI' });
249
+ return new Response(toSSEStream(events), {
250
+ headers: { 'Content-Type': 'text/event-stream' },
251
+ });
252
+ }
178
253
  ```
179
254
 
180
- ## Client Tool Continuation
255
+ ### Client Tool Continuation
181
256
 
182
257
  When workers have tools without handlers, execution pauses:
183
258
 
184
259
  ```typescript
185
260
  for await (const event of client.workers.execute(agentId, input)) {
186
261
  if (event.type === 'client-tool-request') {
187
- // Execute tools client-side
188
262
  const results = await executeClientTools(event.toolCalls);
189
263
 
190
- // Continue execution
191
264
  for await (const ev of client.workers.continue(agentId, event.executionId, results)) {
192
265
  // Handle remaining events
193
266
  }
@@ -210,84 +283,87 @@ The `client-tool-request` event includes:
210
283
  }
211
284
  ```
212
285
 
213
- ## Streaming to HTTP Response
286
+ ### Stream Events
214
287
 
215
- Convert worker events to an SSE stream:
216
-
217
- ```typescript
218
- import { toSSEStream } from '@octavus/server-sdk';
288
+ Workers emit standard stream events plus worker-specific events.
219
289
 
220
- export async function POST(request: Request) {
221
- const { agentId, input } = await request.json();
290
+ #### Worker Events
222
291
 
223
- const events = client.workers.execute(agentId, input, {
224
- tools: {
225
- search: async (args) => await search(args.query),
226
- },
227
- });
292
+ ```typescript
293
+ // Worker started
294
+ {
295
+ type: 'worker-start',
296
+ workerId: string, // Unique ID (also used as session ID for debug)
297
+ workerSlug: string, // The worker's slug
298
+ description?: string, // Display description for UI
299
+ }
228
300
 
229
- return new Response(toSSEStream(events), {
230
- headers: { 'Content-Type': 'text/event-stream' },
231
- });
301
+ // Worker completed
302
+ {
303
+ type: 'worker-result',
304
+ workerId: string,
305
+ output?: unknown, // The worker's output value
306
+ error?: string, // Error message if worker failed
232
307
  }
233
308
  ```
234
309
 
235
- ## Cancellation
310
+ #### Common Events
236
311
 
237
- Use an abort signal to cancel execution:
312
+ | Event | Description |
313
+ | ----------------------- | --------------------------- |
314
+ | `start` | Execution started |
315
+ | `finish` | Execution completed |
316
+ | `text-start` | Text generation started |
317
+ | `text-delta` | Text chunk received |
318
+ | `text-end` | Text generation ended |
319
+ | `block-start` | Step started |
320
+ | `block-end` | Step completed |
321
+ | `tool-input-available` | Tool arguments ready |
322
+ | `tool-output-available` | Tool result ready |
323
+ | `client-tool-request` | Client tools need execution |
324
+ | `error` | Error occurred |
238
325
 
239
- ```typescript
240
- const controller = new AbortController();
326
+ ## Full Examples
241
327
 
242
- // Cancel after 30 seconds
243
- setTimeout(() => controller.abort(), 30000);
328
+ ### generate()
329
+
330
+ ```typescript
331
+ import { OctavusClient, WorkerError } from '@octavus/server-sdk';
244
332
 
245
- const events = client.workers.execute(agentId, input, {
246
- signal: controller.signal,
333
+ const client = new OctavusClient({
334
+ baseUrl: 'https://octavus.ai',
335
+ apiKey: process.env.OCTAVUS_API_KEY!,
247
336
  });
248
337
 
249
338
  try {
250
- for await (const event of events) {
251
- // Process events
252
- }
253
- } catch (error) {
254
- if (error.name === 'AbortError') {
255
- console.log('Worker cancelled');
256
- }
257
- }
258
- ```
259
-
260
- ## Error Handling
261
-
262
- Errors can occur at different levels:
263
-
264
- ```typescript
265
- for await (const event of client.workers.execute(agentId, input)) {
266
- // Stream-level error event
267
- if (event.type === 'error') {
268
- console.error(`Error: ${event.message}`);
269
- console.error(`Type: ${event.errorType}`);
270
- console.error(`Retryable: ${event.retryable}`);
271
- }
339
+ const { output, sessionId } = await client.workers.generate(
340
+ 'research-assistant-id',
341
+ {
342
+ TOPIC: 'AI safety best practices',
343
+ DEPTH: 'detailed',
344
+ },
345
+ {
346
+ tools: {
347
+ 'web-search': async ({ query }) => await performWebSearch(query),
348
+ },
349
+ signal: AbortSignal.timeout(120_000),
350
+ },
351
+ );
272
352
 
273
- // Worker-level error in result
274
- if (event.type === 'worker-result' && event.error) {
275
- console.error(`Worker failed: ${event.error}`);
353
+ console.log('Result:', output);
354
+ } catch (error) {
355
+ if (error instanceof WorkerError) {
356
+ console.error('Failed:', error.message);
357
+ if (error.sessionId) {
358
+ console.error(`Debug: ${client.baseUrl}/sessions/${error.sessionId}`);
359
+ }
276
360
  }
277
361
  }
278
362
  ```
279
363
 
280
- Error types include:
281
-
282
- | Type | Description |
283
- | ------------------ | --------------------- |
284
- | `validation_error` | Invalid input |
285
- | `not_found_error` | Worker not found |
286
- | `provider_error` | LLM provider error |
287
- | `tool_error` | Tool execution failed |
288
- | `execution_error` | Worker step failed |
364
+ ### execute()
289
365
 
290
- ## Full Example
366
+ For full control over streaming events and progress tracking:
291
367
 
292
368
  ```typescript
293
369
  import { OctavusClient, type StreamEvent } from '@octavus/server-sdk';
@@ -348,7 +424,6 @@ async function runResearchWorker(topic: string) {
348
424
  return output;
349
425
  }
350
426
 
351
- // Run the worker
352
427
  const result = await runResearchWorker('AI safety best practices');
353
428
  console.log('Result:', result);
354
429
  ```
@@ -77,6 +77,12 @@ function Chat({ sessionId }: { sessionId: string }) {
77
77
  const { messages, status, send, uploadFiles } = useOctavusChat({
78
78
  transport,
79
79
  requestUploadUrls,
80
+ // Optional: configure upload timeout and retry behavior
81
+ uploadOptions: {
82
+ timeoutMs: 60_000, // Per-file timeout (default: 60s, set to 0 to disable)
83
+ maxRetries: 2, // Retry attempts on transient failures (default: 2)
84
+ retryDelayMs: 1_000, // Delay between retries (default: 1s)
85
+ },
80
86
  });
81
87
 
82
88
  // ...
@@ -176,6 +182,54 @@ async function handleSend(message: string, files?: File[]) {
176
182
 
177
183
  The SDK automatically uploads the files before sending. Note: This doesn't provide upload progress.
178
184
 
185
+ ## Upload Reliability
186
+
187
+ Uploads include built-in timeout and retry logic for handling transient failures (network errors, server issues, mobile network switches).
188
+
189
+ **Default behavior:**
190
+
191
+ - **Timeout**: 60 seconds per file — prevents uploads from hanging on stalled connections
192
+ - **Retries**: 2 automatic retries on transient failures (network errors, 5xx, 429)
193
+ - **Retry delay**: 1 second between retries
194
+ - **Non-retryable errors** (4xx like 403, 404) fail immediately without retrying
195
+
196
+ Only the S3 upload is retried — the presigned URL stays valid for 15 minutes. On retry, the progress callback resets to 0%.
197
+
198
+ Configure via `uploadOptions`:
199
+
200
+ ```typescript
201
+ const { send, uploadFiles } = useOctavusChat({
202
+ transport,
203
+ requestUploadUrls,
204
+ uploadOptions: {
205
+ timeoutMs: 120_000, // 2 minutes for large files
206
+ maxRetries: 3,
207
+ retryDelayMs: 2_000,
208
+ },
209
+ });
210
+ ```
211
+
212
+ To disable timeout or retries:
213
+
214
+ ```typescript
215
+ uploadOptions: {
216
+ timeoutMs: 0, // No timeout
217
+ maxRetries: 0, // No retries
218
+ }
219
+ ```
220
+
221
+ ### Using `OctavusChat` Directly
222
+
223
+ When using the `OctavusChat` class directly (without the React hook), pass `uploadOptions` in the constructor:
224
+
225
+ ```typescript
226
+ const chat = new OctavusChat({
227
+ transport,
228
+ requestUploadUrls,
229
+ uploadOptions: { timeoutMs: 120_000, maxRetries: 3 },
230
+ });
231
+ ```
232
+
179
233
  ## FileReference Type
180
234
 
181
235
  File references contain metadata and URLs:
@@ -234,15 +288,15 @@ The `file` type is a built-in type representing uploaded files. Use `file[]` for
234
288
  | Type | Media Types |
235
289
  | --------- | -------------------------------------------------------------------- |
236
290
  | Images | `image/jpeg`, `image/png`, `image/gif`, `image/webp` |
291
+ | Video | `video/mp4`, `video/webm`, `video/quicktime`, `video/mpeg` |
237
292
  | Documents | `application/pdf`, `text/plain`, `text/markdown`, `application/json` |
238
293
 
239
294
  ## File Limits
240
295
 
241
296
  | Limit | Value |
242
297
  | --------------------- | ---------- |
243
- | Max file size | 10 MB |
244
- | Max total per request | 50 MB |
245
- | Max files per request | 20 |
298
+ | Max file size | 100 MB |
299
+ | Max total per request | 200 MB |
246
300
  | Upload URL expiry | 15 minutes |
247
301
  | Download URL expiry | 24 hours |
248
302
 
@@ -105,12 +105,20 @@ Each agent is a folder with:
105
105
  my-agent/
106
106
  ├── protocol.yaml # Main logic (required)
107
107
  ├── settings.json # Agent metadata (required)
108
- └── prompts/ # Prompt templates
109
- ├── system.md
110
- ├── user-message.md
111
- └── escalation-summary.md
108
+ ├── prompts/ # Prompt templates (supports subdirectories)
109
+ ├── system.md
110
+ ├── user-message.md
111
+ └── shared/
112
+ │ ├── company-info.md
113
+ │ └── formatting-rules.md
114
+ └── references/ # On-demand context documents (optional)
115
+ └── api-guidelines.md
112
116
  ```
113
117
 
118
+ Prompts can be organized in subdirectories. In the protocol, reference nested prompts by their path relative to `prompts/` (without `.md`): `shared/company-info`.
119
+
120
+ References are markdown files with YAML frontmatter that the agent can fetch on demand during execution. See [References](/docs/protocol/references).
121
+
114
122
  ### settings.json
115
123
 
116
124
  ```json
@@ -133,7 +141,7 @@ my-agent/
133
141
 
134
142
  - **Slugs**: `lowercase-with-dashes`
135
143
  - **Variables**: `UPPERCASE_SNAKE_CASE`
136
- - **Prompts**: `lowercase-with-dashes.md`
144
+ - **Prompts**: `lowercase-with-dashes.md` (paths use `/` for subdirectories)
137
145
  - **Tools**: `lowercase-with-dashes`
138
146
  - **Triggers**: `lowercase-with-dashes`
139
147
 
@@ -153,7 +161,25 @@ Help users with their {{PRODUCT_NAME}} questions.
153
161
  {{SUPPORT_POLICIES}}
154
162
  ```
155
163
 
156
- Variables are replaced with their values at runtime. If a variable is not provided, it's replaced with an empty string.
164
+ Variables are replaced with their values at runtime. If a variable is not provided, the placeholder is kept as-is.
165
+
166
+ ## Prompt Interpolation
167
+
168
+ Include other prompts inside a prompt with `{{@path.md}}`:
169
+
170
+ ```markdown
171
+ <!-- prompts/system.md -->
172
+
173
+ You are a customer support agent.
174
+
175
+ {{@shared/company-info.md}}
176
+
177
+ {{@shared/formatting-rules.md}}
178
+
179
+ Help users with their questions.
180
+ ```
181
+
182
+ The referenced prompt content is inserted before variable interpolation, so variables in included prompts work the same way. Circular references are not allowed and will be caught during validation.
157
183
 
158
184
  ## Next Steps
159
185
 
@@ -161,6 +187,7 @@ Variables are replaced with their values at runtime. If a variable is not provid
161
187
  - [Triggers](/docs/protocol/triggers) — How agents are invoked
162
188
  - [Tools](/docs/protocol/tools) — External capabilities
163
189
  - [Skills](/docs/protocol/skills) — Code execution and knowledge packages
190
+ - [References](/docs/protocol/references) — On-demand context documents
164
191
  - [Handlers](/docs/protocol/handlers) — Execution blocks
165
192
  - [Agent Config](/docs/protocol/agent-config) — Model and settings
166
193
  - [Workers](/docs/protocol/workers) — Worker agent format