@octavus/docs 1.0.0 → 2.1.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.
Files changed (52) hide show
  1. package/content/01-getting-started/02-quickstart.md +8 -5
  2. package/content/02-server-sdk/01-overview.md +22 -6
  3. package/content/02-server-sdk/02-sessions.md +80 -10
  4. package/content/02-server-sdk/03-tools.md +39 -14
  5. package/content/02-server-sdk/04-streaming.md +55 -7
  6. package/content/02-server-sdk/05-cli.md +9 -9
  7. package/content/03-client-sdk/01-overview.md +22 -9
  8. package/content/03-client-sdk/02-messages.md +6 -4
  9. package/content/03-client-sdk/03-streaming.md +7 -3
  10. package/content/03-client-sdk/05-socket-transport.md +40 -44
  11. package/content/03-client-sdk/06-http-transport.md +81 -17
  12. package/content/03-client-sdk/07-structured-output.md +3 -2
  13. package/content/03-client-sdk/08-file-uploads.md +6 -4
  14. package/content/03-client-sdk/10-client-tools.md +557 -0
  15. package/content/04-protocol/02-input-resources.md +12 -0
  16. package/content/04-protocol/03-triggers.md +8 -5
  17. package/content/04-protocol/06-handlers.md +10 -0
  18. package/content/04-protocol/07-agent-config.md +34 -1
  19. package/content/05-api-reference/01-overview.md +18 -0
  20. package/content/05-api-reference/02-sessions.md +2 -0
  21. package/content/05-api-reference/03-agents.md +12 -0
  22. package/content/06-examples/02-nextjs-chat.md +12 -7
  23. package/content/06-examples/03-socket-chat.md +34 -40
  24. package/content/07-migration/01-v1-to-v2.md +366 -0
  25. package/content/07-migration/_meta.md +4 -0
  26. package/dist/chunk-3ER2T7S7.js +663 -0
  27. package/dist/chunk-3ER2T7S7.js.map +1 -0
  28. package/dist/chunk-GI574O6S.js +1435 -0
  29. package/dist/chunk-GI574O6S.js.map +1 -0
  30. package/dist/{chunk-WJ2W3DUC.js → chunk-HFF2TVGV.js} +13 -13
  31. package/dist/chunk-HFF2TVGV.js.map +1 -0
  32. package/dist/chunk-KUB6BGPR.js +1435 -0
  33. package/dist/chunk-KUB6BGPR.js.map +1 -0
  34. package/dist/chunk-S5JUVAKE.js +1409 -0
  35. package/dist/chunk-S5JUVAKE.js.map +1 -0
  36. package/dist/chunk-TMJG4CJH.js +1409 -0
  37. package/dist/chunk-TMJG4CJH.js.map +1 -0
  38. package/dist/chunk-WKCT4ABS.js +1435 -0
  39. package/dist/chunk-WKCT4ABS.js.map +1 -0
  40. package/dist/chunk-YJPO6KOJ.js +1435 -0
  41. package/dist/chunk-YJPO6KOJ.js.map +1 -0
  42. package/dist/chunk-ZSCRYD5P.js +1409 -0
  43. package/dist/chunk-ZSCRYD5P.js.map +1 -0
  44. package/dist/content.js +1 -1
  45. package/dist/docs.json +44 -26
  46. package/dist/index.js +1 -1
  47. package/dist/search-index.json +1 -1
  48. package/dist/search.js +1 -1
  49. package/dist/search.js.map +1 -1
  50. package/dist/sections.json +52 -26
  51. package/package.json +1 -1
  52. package/dist/chunk-WJ2W3DUC.js.map +0 -1
@@ -131,7 +131,7 @@ The server creates a session on first trigger message:
131
131
 
132
132
  ```typescript
133
133
  import sockjs from 'sockjs';
134
- import { OctavusClient, type AgentSession } from '@octavus/server-sdk';
134
+ import { OctavusClient, type AgentSession, type SocketMessage } from '@octavus/server-sdk';
135
135
 
136
136
  const client = new OctavusClient({
137
137
  baseUrl: process.env.OCTAVUS_API_URL!,
@@ -141,7 +141,8 @@ const client = new OctavusClient({
141
141
  function createSocketHandler() {
142
142
  return (conn: sockjs.Connection) => {
143
143
  let session: AgentSession | null = null;
144
- let abortController: AbortController | null = null;
144
+
145
+ const send = (data: unknown) => conn.write(JSON.stringify(data));
145
146
 
146
147
  conn.on('data', (rawData: string) => {
147
148
  void handleMessage(rawData);
@@ -150,43 +151,30 @@ function createSocketHandler() {
150
151
  async function handleMessage(rawData: string) {
151
152
  const msg = JSON.parse(rawData);
152
153
 
153
- if (msg.type === 'stop') {
154
- abortController?.abort();
155
- return;
156
- }
157
-
158
- if (msg.type === 'trigger') {
154
+ if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {
159
155
  // Create session lazily on first trigger
160
- if (!session) {
156
+ if (!session && msg.type === 'trigger') {
161
157
  const sessionId = await client.agentSessions.create('your-agent-id', {
162
- // Initial input variables
163
158
  COMPANY_NAME: 'Acme Corp',
164
159
  });
165
160
  session = client.agentSessions.attach(sessionId, {
166
161
  tools: {
167
- // Your tool handlers
162
+ // Server-side tool handlers only
163
+ // Tools without handlers are forwarded to the client
168
164
  },
169
165
  });
170
166
  }
171
167
 
172
- abortController = new AbortController();
168
+ if (!session) return;
173
169
 
174
- // Iterate events directly no SSE parsing needed
175
- const events = session.trigger(msg.triggerName, msg.input, {
176
- signal: abortController.signal,
170
+ // handleSocketMessage manages abort controller internally
171
+ await session.handleSocketMessage(msg as SocketMessage, {
172
+ onEvent: send,
177
173
  });
178
-
179
- try {
180
- for await (const event of events) {
181
- conn.write(JSON.stringify(event));
182
- }
183
- } catch {
184
- // Handle errors
185
- }
186
174
  }
187
175
  }
188
176
 
189
- conn.on('close', () => abortController?.abort());
177
+ conn.on('close', () => {});
190
178
  };
191
179
  }
192
180
 
@@ -241,9 +229,12 @@ When `sessionId` changes, the hook automatically reinitializes with the new tran
241
229
  When using client-provided sessionId, the server must handle an `init` message:
242
230
 
243
231
  ```typescript
232
+ import type { SocketMessage } from '@octavus/server-sdk';
233
+
244
234
  sockServer.on('connection', (conn) => {
245
235
  let session: AgentSession | null = null;
246
- let abortController: AbortController | null = null;
236
+
237
+ const send = (data: unknown) => conn.write(JSON.stringify(data));
247
238
 
248
239
  conn.on('data', (rawData: string) => {
249
240
  void handleMessage(rawData);
@@ -256,33 +247,29 @@ sockServer.on('connection', (conn) => {
256
247
  if (msg.type === 'init') {
257
248
  session = client.agentSessions.attach(msg.sessionId, {
258
249
  tools: {
259
- /* ... */
250
+ // Server-side tool handlers
260
251
  },
261
252
  });
262
253
  return;
263
254
  }
264
255
 
265
- if (msg.type === 'stop') {
266
- abortController?.abort();
267
- return;
268
- }
269
-
270
256
  // All other messages require initialized session
271
257
  if (!session) {
272
- conn.write(
273
- JSON.stringify({
274
- type: 'error',
275
- errorType: 'validation_error',
276
- message: 'Session not initialized. Send init message first.',
277
- source: 'platform',
278
- retryable: false,
279
- }),
280
- );
258
+ send({
259
+ type: 'error',
260
+ errorType: 'validation_error',
261
+ message: 'Session not initialized. Send init message first.',
262
+ source: 'platform',
263
+ retryable: false,
264
+ });
281
265
  return;
282
266
  }
283
267
 
284
- if (msg.type === 'trigger') {
285
- // ... handle trigger (same as server-managed pattern)
268
+ // handleSocketMessage handles trigger, continue, and stop
269
+ if (msg.type === 'trigger' || msg.type === 'continue' || msg.type === 'stop') {
270
+ await session.handleSocketMessage(msg as SocketMessage, {
271
+ onEvent: send,
272
+ });
286
273
  }
287
274
  }
288
275
  });
@@ -505,9 +492,12 @@ const SockJS: typeof import('sockjs-client') = require('sockjs-client');
505
492
  // Initialize session (only for client-provided sessionId pattern)
506
493
  { type: 'init', sessionId: string }
507
494
 
508
- // Trigger an action
495
+ // Trigger an action (start a new conversation turn)
509
496
  { type: 'trigger', triggerName: string, input?: Record<string, unknown> }
510
497
 
498
+ // Continue execution (after client-side tool handling)
499
+ { type: 'continue', executionId: string, toolResults: ToolResult[] }
500
+
511
501
  // Stop current stream
512
502
  { type: 'stop' }
513
503
  ```
@@ -518,13 +508,19 @@ The server sends Octavus `StreamEvent` objects as JSON. See [Streaming Events](/
518
508
 
519
509
  ```typescript
520
510
  // Examples
521
- { type: 'start', messageId: '...' }
511
+ { type: 'start', messageId: '...', executionId: '...' }
522
512
  { type: 'text-delta', id: '...', delta: 'Hello' }
523
513
  { type: 'tool-input-start', toolCallId: '...', toolName: 'get-user' }
524
514
  { type: 'finish', finishReason: 'stop' }
525
515
  { type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }
516
+
517
+ // Client tool request (tools without server handlers)
518
+ { type: 'client-tool-request', executionId: '...', toolCalls: [...], serverToolResults: [...] }
519
+ { type: 'finish', finishReason: 'client-tool-calls', executionId: '...' }
526
520
  ```
527
521
 
522
+ When a `client-tool-request` event is received, the client handles the tools and sends a `continue` message to resume.
523
+
528
524
  ## Full Example
529
525
 
530
526
  For a complete walkthrough of building a chat interface with SockJS, see the [Socket Chat Example](/docs/examples/socket-chat).
@@ -28,11 +28,11 @@ function Chat({ sessionId }: { sessionId: string }) {
28
28
  const transport = useMemo(
29
29
  () =>
30
30
  createHttpTransport({
31
- triggerRequest: (triggerName, input, options) =>
31
+ request: (payload, options) =>
32
32
  fetch('/api/trigger', {
33
33
  method: 'POST',
34
34
  headers: { 'Content-Type': 'application/json' },
35
- body: JSON.stringify({ sessionId, triggerName, input }),
35
+ body: JSON.stringify({ sessionId, ...payload }),
36
36
  signal: options?.signal,
37
37
  }),
38
38
  }),
@@ -61,7 +61,8 @@ const client = new OctavusClient({
61
61
  });
62
62
 
63
63
  export async function POST(request: Request) {
64
- const { sessionId, triggerName, input } = await request.json();
64
+ const body = await request.json();
65
+ const { sessionId, ...payload } = body;
65
66
 
66
67
  const session = client.agentSessions.attach(sessionId, {
67
68
  tools: {
@@ -71,8 +72,8 @@ export async function POST(request: Request) {
71
72
  },
72
73
  });
73
74
 
74
- // trigger() returns an async generator, toSSEStream() converts to SSE format
75
- const events = session.trigger(triggerName, input, { signal: request.signal });
75
+ // execute() handles both triggers and client tool continuations
76
+ const events = session.execute(payload, { signal: request.signal });
76
77
 
77
78
  return new Response(toSSEStream(events), {
78
79
  headers: {
@@ -197,7 +198,7 @@ return (
197
198
  );
198
199
  ```
199
200
 
200
- > **Important**: For stop to work end-to-end, pass the `options.signal` to your `fetch()` call and forward `request.signal` to `session.trigger()` on the server.
201
+ > **Important**: For stop to work end-to-end, pass the `options.signal` to your `fetch()` call and forward `request.signal` to `session.execute()` on the server.
201
202
 
202
203
  ## Express Server
203
204
 
@@ -208,21 +209,25 @@ import express from 'express';
208
209
  import { OctavusClient, toSSEStream } from '@octavus/server-sdk';
209
210
 
210
211
  const app = express();
212
+ app.use(express.json());
213
+
211
214
  const client = new OctavusClient({
212
215
  baseUrl: process.env.OCTAVUS_API_URL!,
213
216
  apiKey: process.env.OCTAVUS_API_KEY!,
214
217
  });
215
218
 
216
219
  app.post('/api/trigger', async (req, res) => {
217
- const { sessionId, triggerName, input } = req.body;
220
+ const { sessionId, ...payload } = req.body;
218
221
 
219
222
  const session = client.agentSessions.attach(sessionId, {
220
223
  tools: {
221
- // Your tool handlers
224
+ // Server-side tool handlers only
225
+ // Tools without handlers are forwarded to the client
222
226
  },
223
227
  });
224
228
 
225
- const events = session.trigger(triggerName, input);
229
+ // execute() handles both triggers and continuations
230
+ const events = session.execute(payload);
226
231
  const stream = toSSEStream(events);
227
232
 
228
233
  // Set SSE headers
@@ -250,27 +255,56 @@ app.post('/api/trigger', async (req, res) => {
250
255
 
251
256
  ```typescript
252
257
  interface HttpTransportOptions {
253
- triggerRequest: (
254
- triggerName: string,
255
- input?: Record<string, unknown>,
256
- options?: TriggerRequestOptions,
257
- ) => Promise<Response>;
258
+ // Single request handler for both triggers and continuations
259
+ request: (request: HttpRequest, options?: HttpRequestOptions) => Promise<Response>;
258
260
  }
259
261
 
260
- interface TriggerRequestOptions {
262
+ interface HttpRequestOptions {
261
263
  signal?: AbortSignal;
262
264
  }
265
+
266
+ // Discriminated union for request types
267
+ type HttpRequest = TriggerRequest | ContinueRequest;
268
+
269
+ // Start a new conversation turn
270
+ interface TriggerRequest {
271
+ type: 'trigger';
272
+ triggerName: string;
273
+ input?: Record<string, unknown>;
274
+ }
275
+
276
+ // Continue after client-side tool handling
277
+ interface ContinueRequest {
278
+ type: 'continue';
279
+ executionId: string;
280
+ toolResults: ToolResult[];
281
+ }
282
+ ```
283
+
284
+ The `request` function receives a discriminated union. Spread the request onto your payload:
285
+
286
+ ```typescript
287
+ request: (payload, options) =>
288
+ fetch('/api/trigger', {
289
+ method: 'POST',
290
+ headers: { 'Content-Type': 'application/json' },
291
+ body: JSON.stringify({ sessionId, ...payload }),
292
+ signal: options?.signal,
293
+ });
263
294
  ```
264
295
 
265
296
  ## Protocol
266
297
 
267
298
  ### Request Format
268
299
 
269
- The `triggerRequest` function should send a POST request with:
300
+ The `request` function receives a discriminated union with `type` to identify the request kind:
301
+
302
+ **Trigger Request** (start a new turn):
270
303
 
271
304
  ```json
272
305
  {
273
306
  "sessionId": "sess_abc123",
307
+ "type": "trigger",
274
308
  "triggerName": "user-message",
275
309
  "input": {
276
310
  "USER_MESSAGE": "Hello"
@@ -278,12 +312,29 @@ The `triggerRequest` function should send a POST request with:
278
312
  }
279
313
  ```
280
314
 
315
+ **Continue Request** (after client tool handling):
316
+
317
+ ```json
318
+ {
319
+ "sessionId": "sess_abc123",
320
+ "type": "continue",
321
+ "executionId": "exec_xyz789",
322
+ "toolResults": [
323
+ {
324
+ "toolCallId": "call_abc",
325
+ "toolName": "get-browser-location",
326
+ "result": { "lat": 40.7128, "lng": -74.006 }
327
+ }
328
+ ]
329
+ }
330
+ ```
331
+
281
332
  ### Response Format
282
333
 
283
334
  The server responds with an SSE stream:
284
335
 
285
336
  ```
286
- data: {"type":"start","messageId":"msg_xyz"}
337
+ data: {"type":"start","messageId":"msg_xyz","executionId":"exec_xyz789"}
287
338
 
288
339
  data: {"type":"text-delta","id":"msg_xyz","delta":"Hello"}
289
340
 
@@ -294,11 +345,24 @@ data: {"type":"finish","finishReason":"stop"}
294
345
  data: [DONE]
295
346
  ```
296
347
 
348
+ If client tools are needed, the stream pauses with a `client-tool-request` event:
349
+
350
+ ```
351
+ data: {"type":"client-tool-request","executionId":"exec_xyz789","toolCalls":[...]}
352
+
353
+ data: {"type":"finish","finishReason":"client-tool-calls","executionId":"exec_xyz789"}
354
+
355
+ data: [DONE]
356
+ ```
357
+
358
+ The client handles the tools and sends a `continue` request to resume.
359
+
297
360
  See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list of event types.
298
361
 
299
362
  ## Next Steps
300
363
 
301
364
  - [Quick Start](/docs/getting-started/quickstart) — Complete Next.js integration guide
365
+ - [Client Tools](/docs/client-sdk/client-tools) — Handling tools on the client side
302
366
  - [Messages](/docs/client-sdk/messages) — Working with message state
303
367
  - [Streaming](/docs/client-sdk/streaming) — Building streaming UIs
304
368
  - [Error Handling](/docs/client-sdk/error-handling) — Handling errors with type guards
@@ -288,11 +288,12 @@ function Chat({ sessionId }: { sessionId: string }) {
288
288
  const transport = useMemo(
289
289
  () =>
290
290
  createHttpTransport({
291
- triggerRequest: (triggerName, input) =>
291
+ request: (payload, options) =>
292
292
  fetch('/api/trigger', {
293
293
  method: 'POST',
294
294
  headers: { 'Content-Type': 'application/json' },
295
- body: JSON.stringify({ sessionId, triggerName, input }),
295
+ body: JSON.stringify({ sessionId, ...payload }),
296
+ signal: options?.signal,
296
297
  }),
297
298
  }),
298
299
  [sessionId],
@@ -50,11 +50,12 @@ function Chat({ sessionId }: { sessionId: string }) {
50
50
  const transport = useMemo(
51
51
  () =>
52
52
  createHttpTransport({
53
- triggerRequest: (triggerName, input) =>
53
+ request: (payload, options) =>
54
54
  fetch('/api/trigger', {
55
55
  method: 'POST',
56
56
  headers: { 'Content-Type': 'application/json' },
57
- body: JSON.stringify({ sessionId, triggerName, input }),
57
+ body: JSON.stringify({ sessionId, ...payload }),
58
+ signal: options?.signal,
58
59
  }),
59
60
  }),
60
61
  [sessionId],
@@ -332,11 +333,12 @@ export function Chat({ sessionId }: { sessionId: string }) {
332
333
  const transport = useMemo(
333
334
  () =>
334
335
  createHttpTransport({
335
- triggerRequest: (triggerName, input) =>
336
+ request: (payload, options) =>
336
337
  fetch('/api/trigger', {
337
338
  method: 'POST',
338
339
  headers: { 'Content-Type': 'application/json' },
339
- body: JSON.stringify({ sessionId, triggerName, input }),
340
+ body: JSON.stringify({ sessionId, ...payload }),
341
+ signal: options?.signal,
340
342
  }),
341
343
  }),
342
344
  [sessionId],