@octavus/docs 1.0.0 → 2.0.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 (46) 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 +51 -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 +31 -10
  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 +27 -13
  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-WJ2W3DUC.js → chunk-HFF2TVGV.js} +13 -13
  29. package/dist/chunk-HFF2TVGV.js.map +1 -0
  30. package/dist/chunk-S5JUVAKE.js +1409 -0
  31. package/dist/chunk-S5JUVAKE.js.map +1 -0
  32. package/dist/chunk-TMJG4CJH.js +1409 -0
  33. package/dist/chunk-TMJG4CJH.js.map +1 -0
  34. package/dist/chunk-YJPO6KOJ.js +1435 -0
  35. package/dist/chunk-YJPO6KOJ.js.map +1 -0
  36. package/dist/chunk-ZSCRYD5P.js +1409 -0
  37. package/dist/chunk-ZSCRYD5P.js.map +1 -0
  38. package/dist/content.js +1 -1
  39. package/dist/docs.json +44 -26
  40. package/dist/index.js +1 -1
  41. package/dist/search-index.json +1 -1
  42. package/dist/search.js +1 -1
  43. package/dist/search.js.map +1 -1
  44. package/dist/sections.json +52 -26
  45. package/package.json +1 -1
  46. package/dist/chunk-WJ2W3DUC.js.map +0 -1
@@ -106,7 +106,8 @@ import { toSSEStream } from '@octavus/server-sdk';
106
106
  import { octavus } from '@/lib/octavus';
107
107
 
108
108
  export async function POST(request: Request) {
109
- const { sessionId, triggerName, input } = await request.json();
109
+ const body = await request.json();
110
+ const { sessionId, ...payload } = body;
110
111
 
111
112
  // Attach to session with tool handlers
112
113
  const session = octavus.agentSessions.attach(sessionId, {
@@ -131,8 +132,8 @@ export async function POST(request: Request) {
131
132
  },
132
133
  });
133
134
 
134
- // Trigger the action and convert to SSE stream
135
- const events = session.trigger(triggerName, input);
135
+ // Execute the request and convert to SSE stream
136
+ const events = session.execute(payload, { signal: request.signal });
136
137
 
137
138
  // Return as streaming response
138
139
  return new Response(toSSEStream(events), {
@@ -169,11 +170,12 @@ export function Chat({ sessionId }: ChatProps) {
169
170
  const transport = useMemo(
170
171
  () =>
171
172
  createHttpTransport({
172
- triggerRequest: (triggerName, input) =>
173
+ request: (payload, options) =>
173
174
  fetch('/api/trigger', {
174
175
  method: 'POST',
175
176
  headers: { 'Content-Type': 'application/json' },
176
- body: JSON.stringify({ sessionId, triggerName, input }),
177
+ body: JSON.stringify({ sessionId, ...payload }),
178
+ signal: options?.signal,
177
179
  }),
178
180
  }),
179
181
  [sessionId],
@@ -307,3 +309,4 @@ Now that you have a basic integration working:
307
309
  - [Learn about the protocol](/docs/protocol/overview) to define custom agent behavior
308
310
  - [Explore the Server SDK](/docs/server-sdk/overview) for advanced backend features
309
311
  - [Build rich UIs](/docs/client-sdk/overview) with the Client SDK
312
+ - [Handle tools on the client](/docs/client-sdk/client-tools) for interactive UIs and browser APIs
@@ -83,9 +83,11 @@ All responses stream in real-time:
83
83
  ```typescript
84
84
  import { toSSEStream } from '@octavus/server-sdk';
85
85
 
86
- // trigger() returns an async generator of events
87
- const events = session.trigger('user-message', {
88
- USER_MESSAGE: 'Hello!',
86
+ // execute() returns an async generator of events
87
+ const events = session.execute({
88
+ type: 'trigger',
89
+ triggerName: 'user-message',
90
+ input: { USER_MESSAGE: 'Hello!' },
89
91
  });
90
92
 
91
93
  // Convert to SSE stream for HTTP responses
@@ -156,17 +158,31 @@ interface UISessionState {
156
158
 
157
159
  ### AgentSession
158
160
 
159
- Handles triggering and streaming for a specific session.
161
+ Handles request execution and streaming for a specific session.
160
162
 
161
163
  ```typescript
162
164
  class AgentSession {
163
- // Trigger an action and stream parsed events
164
- trigger(triggerName: string, input?: Record<string, unknown>): AsyncGenerator<StreamEvent>;
165
+ // Execute a request and stream parsed events
166
+ execute(request: SessionRequest, options?: TriggerOptions): AsyncGenerator<StreamEvent>;
165
167
 
166
168
  // Get the session ID
167
169
  getSessionId(): string;
168
170
  }
169
171
 
172
+ type SessionRequest = TriggerRequest | ContinueRequest;
173
+
174
+ interface TriggerRequest {
175
+ type: 'trigger';
176
+ triggerName: string;
177
+ input?: Record<string, unknown>;
178
+ }
179
+
180
+ interface ContinueRequest {
181
+ type: 'continue';
182
+ executionId: string;
183
+ toolResults: ToolResult[];
184
+ }
185
+
170
186
  // Helper to convert events to SSE stream
171
187
  function toSSEStream(events: AsyncIterable<StreamEvent>): ReadableStream<Uint8Array>;
172
188
  ```
@@ -90,17 +90,18 @@ const session = client.agentSessions.attach(sessionId, {
90
90
  });
91
91
  ```
92
92
 
93
- ## Triggering Actions
93
+ ## Executing Requests
94
94
 
95
- Once attached, trigger actions on the session:
95
+ Once attached, execute requests on the session using `execute()`:
96
96
 
97
97
  ```typescript
98
98
  import { toSSEStream } from '@octavus/server-sdk';
99
99
 
100
- // trigger() returns an async generator of events
101
- const events = session.trigger('user-message', {
102
- USER_MESSAGE: 'How do I reset my password?',
103
- });
100
+ // execute() handles both triggers and client tool continuations
101
+ const events = session.execute(
102
+ { type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello!' } },
103
+ { signal: request.signal },
104
+ );
104
105
 
105
106
  // Convert to SSE stream for HTTP responses
106
107
  return new Response(toSSEStream(events), {
@@ -108,13 +109,53 @@ return new Response(toSSEStream(events), {
108
109
  });
109
110
  ```
110
111
 
112
+ ### Request Types
113
+
114
+ The `execute()` method accepts a discriminated union:
115
+
116
+ ```typescript
117
+ type SessionRequest = TriggerRequest | ContinueRequest;
118
+
119
+ // Start a new conversation turn
120
+ interface TriggerRequest {
121
+ type: 'trigger';
122
+ triggerName: string;
123
+ input?: Record<string, unknown>;
124
+ }
125
+
126
+ // Continue after client-side tool handling
127
+ interface ContinueRequest {
128
+ type: 'continue';
129
+ executionId: string;
130
+ toolResults: ToolResult[];
131
+ }
132
+ ```
133
+
134
+ This makes it easy to pass requests through from the client:
135
+
136
+ ```typescript
137
+ // Simple passthrough from HTTP request body
138
+ export async function POST(request: Request) {
139
+ const body = await request.json();
140
+ const { sessionId, ...payload } = body;
141
+
142
+ const session = client.agentSessions.attach(sessionId, {
143
+ tools: {
144
+ /* ... */
145
+ },
146
+ });
147
+ const events = session.execute(payload, { signal: request.signal });
148
+
149
+ return new Response(toSSEStream(events));
150
+ }
151
+ ```
152
+
111
153
  ### Stop Support
112
154
 
113
155
  Pass an abort signal to allow clients to stop generation:
114
156
 
115
157
  ```typescript
116
- // In your API route handler
117
- const events = session.trigger(triggerName, input, {
158
+ const events = session.execute(request, {
118
159
  signal: request.signal, // Forward the client's abort signal
119
160
  });
120
161
  ```
@@ -143,8 +184,8 @@ flowchart TD
143
184
  Configure tool handlers
144
185
  Configure resource watchers`"]
145
186
 
146
- C -.- C1["`**session.trigger()**
147
- Execute handler
187
+ C -.- C1["`**session.execute()**
188
+ Execute request
148
189
  Stream events
149
190
  Update state`"]
150
191
 
@@ -5,11 +5,25 @@ description: Implementing tool handlers with the Server SDK.
5
5
 
6
6
  # Tools
7
7
 
8
- Tools extend what agents can do. In Octavus, tools execute on your server, giving you full control over data access and authentication.
8
+ Tools extend what agents can do. In Octavus, tools can execute either on your server or on the client side.
9
9
 
10
- ## Why Tools Run on Your Server
10
+ ## Server Tools vs Client Tools
11
11
 
12
- Unlike traditional AI platforms where tools run in a sandbox, Octavus tools execute in your backend:
12
+ | Location | Use Case | Registration |
13
+ | ---------- | ------------------------------------------------- | --------------------------------------- |
14
+ | **Server** | Database queries, API calls, sensitive operations | Register handler in `attach()` |
15
+ | **Client** | Browser APIs, interactive UIs, confirmations | No server handler (forwarded to client) |
16
+
17
+ When the Server SDK encounters a tool call:
18
+
19
+ 1. **Handler exists** → Execute on server, continue automatically
20
+ 2. **No handler** → Forward to client via `client-tool-request` event
21
+
22
+ For client-side tool handling, see [Client Tools](/docs/client-sdk/client-tools).
23
+
24
+ ## Why Server Tools
25
+
26
+ Server-side tools give you full control:
13
27
 
14
28
  - ✅ **Full data access** — Query your database directly
15
29
  - ✅ **Your authentication** — Use your existing auth context
@@ -146,22 +160,29 @@ sequenceDiagram
146
160
  participant LLM
147
161
  participant Platform as Octavus Platform
148
162
  participant SDK as Server SDK
149
- participant UI as Your Frontend
163
+ participant Client as Client SDK
150
164
 
151
165
  LLM->>Platform: 1. Decides to call tool
152
- Platform-->>UI: tool-input-start, tool-input-delta
153
- Platform-->>UI: tool-input-available
166
+ Platform-->>Client: tool-input-start, tool-input-delta
167
+ Platform-->>Client: tool-input-available
154
168
  Platform-->>SDK: 2. tool-request (stream pauses)
155
169
 
156
- Note over SDK: 3. Execute handler<br/>tools['get-user']()
157
-
158
- SDK-->>UI: 4. tool-output-available
159
- SDK->>Platform: 5. POST /trigger with results
160
- Platform->>LLM: 6. Continue with results
170
+ alt Has server handler
171
+ Note over SDK: 3a. Execute handler<br/>tools['get-user']()
172
+ SDK-->>Client: tool-output-available
173
+ SDK->>Platform: Continue with results
174
+ else No server handler
175
+ SDK-->>Client: 3b. client-tool-request
176
+ Note over Client: Execute client tool<br/>or show interactive UI
177
+ Client->>SDK: Tool results
178
+ SDK->>Platform: Continue with results
179
+ end
180
+
181
+ Platform->>LLM: 4. Process results
161
182
  LLM-->>Platform: Response
162
- Platform-->>UI: text-delta events
183
+ Platform-->>Client: text-delta events
163
184
 
164
- Note over LLM,UI: 7. Repeat if more tools needed
185
+ Note over LLM,Client: 5. Repeat if more tools needed
165
186
  ```
166
187
 
167
188
  ## Accessing Request Context
@@ -173,6 +194,9 @@ import { toSSEStream } from '@octavus/server-sdk';
173
194
 
174
195
  // In your API route
175
196
  export async function POST(request: Request) {
197
+ const body = await request.json();
198
+ const { sessionId, ...payload } = body;
199
+
176
200
  const authToken = request.headers.get('Authorization');
177
201
  const user = await validateToken(authToken);
178
202
 
@@ -190,10 +214,11 @@ export async function POST(request: Request) {
190
214
  createdBy: user.email,
191
215
  });
192
216
  },
217
+ // Tools without handlers here are forwarded to the client
193
218
  },
194
219
  });
195
220
 
196
- const events = session.trigger(triggerName, input);
221
+ const events = session.execute(payload, { signal: request.signal });
197
222
  return new Response(toSSEStream(events));
198
223
  }
199
224
  ```
@@ -9,14 +9,16 @@ All Octavus responses stream in real-time using Server-Sent Events (SSE). This e
9
9
 
10
10
  ## Stream Response
11
11
 
12
- When you trigger an action, you get an async generator of parsed events:
12
+ When you execute a request, you get an async generator of parsed events:
13
13
 
14
14
  ```typescript
15
15
  import { toSSEStream } from '@octavus/server-sdk';
16
16
 
17
- // trigger() returns an async generator of StreamEvent
18
- const events = session.trigger('user-message', {
19
- USER_MESSAGE: 'Hello!',
17
+ // execute() returns an async generator of StreamEvent
18
+ const events = session.execute({
19
+ type: 'trigger',
20
+ triggerName: 'user-message',
21
+ input: { USER_MESSAGE: 'Hello!' },
20
22
  });
21
23
 
22
24
  // For HTTP endpoints, convert to SSE stream
@@ -42,14 +44,15 @@ The stream emits various event types for lifecycle, text, reasoning, and tool in
42
44
 
43
45
  ```typescript
44
46
  // Stream started
45
- { type: 'start', messageId: '...' }
47
+ { type: 'start', messageId: '...', executionId: '...' }
46
48
 
47
49
  // Stream completed
48
50
  { type: 'finish', finishReason: 'stop' }
49
51
 
50
52
  // Possible finish reasons:
51
53
  // - 'stop': Normal completion
52
- // - 'tool-calls': Waiting for tool execution (handled by SDK)
54
+ // - 'tool-calls': Waiting for server tool execution (handled by SDK internally)
55
+ // - 'client-tool-calls': Waiting for client tool execution
53
56
  // - 'length': Max tokens reached
54
57
  // - 'content-filter': Content filtered
55
58
  // - 'error': Error occurred
@@ -178,9 +181,54 @@ type StreamEvent =
178
181
  | BlockStartEvent
179
182
  | BlockEndEvent
180
183
  | ResourceUpdateEvent
181
- | ToolRequestEvent;
184
+ | ToolRequestEvent
185
+ | ClientToolRequestEvent;
182
186
  ```
183
187
 
188
+ ### Client Tool Request
189
+
190
+ When a tool has no server handler registered, the SDK emits a `client-tool-request` event:
191
+
192
+ ```typescript
193
+ {
194
+ type: 'client-tool-request',
195
+ executionId: 'exec_abc123', // Use this to continue execution
196
+ toolCalls: [ // Tools for client to handle
197
+ {
198
+ toolCallId: 'call_xyz',
199
+ toolName: 'get-browser-location',
200
+ args: {}
201
+ }
202
+ ],
203
+ serverToolResults: [ // Results from server tools in same batch
204
+ {
205
+ toolCallId: 'call_def',
206
+ toolName: 'get-user-account',
207
+ result: { name: 'Demo User' }
208
+ }
209
+ ]
210
+ }
211
+ ```
212
+
213
+ After the client handles the tools, send a `continue` request with all results:
214
+
215
+ ```typescript
216
+ session.execute({
217
+ type: 'continue',
218
+ executionId: 'exec_abc123',
219
+ toolResults: [
220
+ ...serverToolResults, // Include server results from the event
221
+ {
222
+ toolCallId: 'call_xyz',
223
+ toolName: 'get-browser-location',
224
+ result: { lat: 40.7128, lng: -74.006 },
225
+ },
226
+ ],
227
+ });
228
+ ```
229
+
230
+ See [Client Tools](/docs/client-sdk/client-tools) for full client-side implementation.
231
+
184
232
  ## Error Events
185
233
 
186
234
  Errors are emitted as structured events with type classification:
@@ -17,26 +17,26 @@ npm install --save-dev @octavus/cli
17
17
 
18
18
  ## Configuration
19
19
 
20
- The CLI requires an API key with agent management permissions.
20
+ The CLI requires an API key with the **Agents** permission.
21
21
 
22
22
  ### Environment Variables
23
23
 
24
- | Variable | Description |
25
- | --------------------- | ------------------------------------------------------- |
26
- | `OCTAVUS_CLI_API_KEY` | API key with agent management permissions (recommended) |
27
- | `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |
28
- | `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |
24
+ | Variable | Description |
25
+ | --------------------- | ---------------------------------------------- |
26
+ | `OCTAVUS_CLI_API_KEY` | API key with "Agents" permission (recommended) |
27
+ | `OCTAVUS_API_KEY` | Fallback if `OCTAVUS_CLI_API_KEY` not set |
28
+ | `OCTAVUS_API_URL` | Optional, defaults to `https://octavus.ai` |
29
29
 
30
30
  ### Two-Key Strategy (Recommended)
31
31
 
32
- For production deployments, use separate API keys:
32
+ For production deployments, use separate API keys with minimal permissions:
33
33
 
34
34
  ```bash
35
35
  # CI/CD or .env.local (not committed)
36
- OCTAVUS_CLI_API_KEY=oct_sk_... # Agent management permissions
36
+ OCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only
37
37
 
38
38
  # Production .env
39
- OCTAVUS_API_KEY=oct_sk_... # Session-only permissions
39
+ OCTAVUS_API_KEY=oct_sk_... # "Sessions" permission only
40
40
  ```
41
41
 
42
42
  This ensures production servers only have session permissions (smaller blast radius if leaked), while agent management is restricted to development/CI environments.
@@ -58,11 +58,12 @@ function Chat({ sessionId }: { sessionId: string }) {
58
58
  const transport = useMemo(
59
59
  () =>
60
60
  createHttpTransport({
61
- triggerRequest: (triggerName, input) =>
61
+ request: (payload, options) =>
62
62
  fetch('/api/trigger', {
63
63
  method: 'POST',
64
64
  headers: { 'Content-Type': 'application/json' },
65
- body: JSON.stringify({ sessionId, triggerName, input }),
65
+ body: JSON.stringify({ sessionId, ...payload }),
66
+ signal: options?.signal,
66
67
  }),
67
68
  }),
68
69
  [sessionId],
@@ -105,11 +106,12 @@ The `OctavusChat` class can be used with any framework or vanilla JavaScript:
105
106
  import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';
106
107
 
107
108
  const transport = createHttpTransport({
108
- triggerRequest: (triggerName, input) =>
109
+ request: (payload, options) =>
109
110
  fetch('/api/trigger', {
110
111
  method: 'POST',
111
112
  headers: { 'Content-Type': 'application/json' },
112
- body: JSON.stringify({ sessionId, triggerName, input }),
113
+ body: JSON.stringify({ sessionId, ...payload }),
114
+ signal: options?.signal,
113
115
  }),
114
116
  });
115
117
 
@@ -168,7 +170,8 @@ message.parts.map((part) => {
168
170
  ```tsx
169
171
  const { status } = useOctavusChat({ transport });
170
172
 
171
- // status: 'idle' | 'streaming' | 'error'
173
+ // status: 'idle' | 'streaming' | 'error' | 'awaiting-input'
174
+ // 'awaiting-input' occurs when interactive client tools need user action
172
175
  ```
173
176
 
174
177
  ### Stop Streaming
@@ -196,6 +199,11 @@ interface OctavusChatOptions {
196
199
  files: { filename: string; mediaType: string; size: number }[],
197
200
  ) => Promise<UploadUrlsResponse>;
198
201
 
202
+ // Optional: Client-side tool handlers
203
+ // - Function: executes automatically and returns result
204
+ // - 'interactive': appears in pendingClientTools for user input
205
+ clientTools?: Record<string, ClientToolHandler>;
206
+
199
207
  // Optional: Pre-populate with existing messages (session restore)
200
208
  initialMessages?: UIMessage[];
201
209
 
@@ -209,13 +217,16 @@ interface OctavusChatOptions {
209
217
  interface UseOctavusChatReturn {
210
218
  // State
211
219
  messages: UIMessage[];
212
- status: ChatStatus; // 'idle' | 'streaming' | 'error'
220
+ status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'
213
221
  error: OctavusError | null; // Structured error with type, source, retryable
214
222
 
215
223
  // Connection (socket transport only - undefined for HTTP)
216
224
  connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'
217
225
  connectionError: Error | undefined;
218
226
 
227
+ // Client tools (interactive tools awaiting user input)
228
+ pendingClientTools: Record<string, InteractiveTool[]>; // Keyed by tool name
229
+
219
230
  // Actions
220
231
  send: (
221
232
  triggerName: string,
@@ -251,11 +262,11 @@ Creates an HTTP/SSE transport using native `fetch()`:
251
262
  import { createHttpTransport } from '@octavus/react';
252
263
 
253
264
  const transport = createHttpTransport({
254
- triggerRequest: (triggerName, input, options) =>
265
+ request: (payload, options) =>
255
266
  fetch('/api/trigger', {
256
267
  method: 'POST',
257
268
  headers: { 'Content-Type': 'application/json' },
258
- body: JSON.stringify({ sessionId, triggerName, input }),
269
+ body: JSON.stringify({ sessionId, ...payload }),
259
270
  signal: options?.signal,
260
271
  }),
261
272
  });
@@ -308,8 +319,9 @@ class OctavusChat {
308
319
 
309
320
  // State (read-only)
310
321
  readonly messages: UIMessage[];
311
- readonly status: ChatStatus;
322
+ readonly status: ChatStatus; // 'idle' | 'streaming' | 'error' | 'awaiting-input'
312
323
  readonly error: OctavusError | null; // Structured error
324
+ readonly pendingClientTools: Record<string, InteractiveTool[]>; // Interactive tools
313
325
 
314
326
  // Actions
315
327
  send(
@@ -330,6 +342,7 @@ class OctavusChat {
330
342
  - [Socket Transport](/docs/client-sdk/socket-transport) — WebSocket and SockJS integration
331
343
  - [Messages](/docs/client-sdk/messages) — Working with message state
332
344
  - [Streaming](/docs/client-sdk/streaming) — Building streaming UIs
345
+ - [Client Tools](/docs/client-sdk/client-tools) — Interactive browser-side tool handling
333
346
  - [Operations](/docs/client-sdk/execution-blocks) — Showing agent progress
334
347
  - [Error Handling](/docs/client-sdk/error-handling) — Handling errors with type guards
335
348
  - [File Uploads](/docs/client-sdk/file-uploads) — Uploading images and documents
@@ -119,11 +119,12 @@ function Chat({ sessionId }: { sessionId: string }) {
119
119
  const transport = useMemo(
120
120
  () =>
121
121
  createHttpTransport({
122
- triggerRequest: (triggerName, input) =>
122
+ request: (payload, options) =>
123
123
  fetch('/api/trigger', {
124
124
  method: 'POST',
125
125
  headers: { 'Content-Type': 'application/json' },
126
- body: JSON.stringify({ sessionId, triggerName, input }),
126
+ body: JSON.stringify({ sessionId, ...payload }),
127
+ signal: options?.signal,
127
128
  }),
128
129
  }),
129
130
  [sessionId],
@@ -346,11 +347,12 @@ function Chat({ sessionId, initialMessages }: ChatProps) {
346
347
  const transport = useMemo(
347
348
  () =>
348
349
  createHttpTransport({
349
- triggerRequest: (triggerName, input) =>
350
+ request: (payload, options) =>
350
351
  fetch('/api/trigger', {
351
352
  method: 'POST',
352
353
  headers: { 'Content-Type': 'application/json' },
353
- body: JSON.stringify({ sessionId, triggerName, input }),
354
+ body: JSON.stringify({ sessionId, ...payload }),
355
+ signal: options?.signal,
354
356
  }),
355
357
  }),
356
358
  [sessionId],
@@ -12,7 +12,8 @@ The Client SDK provides real-time access to streaming content through the messag
12
12
  ```tsx
13
13
  const { messages, status, error } = useOctavusChat({ transport });
14
14
 
15
- // status: 'idle' | 'streaming' | 'error'
15
+ // status: 'idle' | 'streaming' | 'error' | 'awaiting-input'
16
+ // 'awaiting-input' occurs when interactive client tools need user action
16
17
  // Each message has status: 'streaming' | 'done'
17
18
  // Each part has its own status too
18
19
  ```
@@ -27,11 +28,12 @@ function Chat({ sessionId }: { sessionId: string }) {
27
28
  const transport = useMemo(
28
29
  () =>
29
30
  createHttpTransport({
30
- triggerRequest: (triggerName, input) =>
31
+ request: (payload, options) =>
31
32
  fetch('/api/trigger', {
32
33
  method: 'POST',
33
34
  headers: { 'Content-Type': 'application/json' },
34
- body: JSON.stringify({ sessionId, triggerName, input }),
35
+ body: JSON.stringify({ sessionId, ...payload }),
36
+ signal: options?.signal,
35
37
  }),
36
38
  }),
37
39
  [sessionId],
@@ -148,6 +150,8 @@ function StatusIndicator({ status }: { status: ChatStatus }) {
148
150
  return null;
149
151
  case 'streaming':
150
152
  return <div>Agent is responding...</div>;
153
+ case 'awaiting-input':
154
+ return <div className="text-amber-500">Waiting for your input...</div>;
151
155
  case 'error':
152
156
  return <div className="text-red-500">Something went wrong</div>;
153
157
  }
@@ -155,24 +155,28 @@ function createSocketHandler() {
155
155
  return;
156
156
  }
157
157
 
158
- if (msg.type === 'trigger') {
158
+ // Handle both trigger and continue messages
159
+ if (msg.type === 'trigger' || msg.type === 'continue') {
159
160
  // Create session lazily on first trigger
160
- if (!session) {
161
+ if (!session && msg.type === 'trigger') {
161
162
  const sessionId = await client.agentSessions.create('your-agent-id', {
162
163
  // Initial input variables
163
164
  COMPANY_NAME: 'Acme Corp',
164
165
  });
165
166
  session = client.agentSessions.attach(sessionId, {
166
167
  tools: {
167
- // Your tool handlers
168
+ // Server-side tool handlers only
169
+ // Tools without handlers are forwarded to the client
168
170
  },
169
171
  });
170
172
  }
171
173
 
174
+ if (!session) return;
175
+
172
176
  abortController = new AbortController();
173
177
 
174
- // Iterate events directly no SSE parsing needed
175
- const events = session.trigger(msg.triggerName, msg.input, {
178
+ // execute() handles both triggers and continuations
179
+ const events = session.execute(msg, {
176
180
  signal: abortController.signal,
177
181
  });
178
182
 
@@ -256,7 +260,7 @@ sockServer.on('connection', (conn) => {
256
260
  if (msg.type === 'init') {
257
261
  session = client.agentSessions.attach(msg.sessionId, {
258
262
  tools: {
259
- /* ... */
263
+ // Server-side tool handlers
260
264
  },
261
265
  });
262
266
  return;
@@ -281,8 +285,16 @@ sockServer.on('connection', (conn) => {
281
285
  return;
282
286
  }
283
287
 
284
- if (msg.type === 'trigger') {
285
- // ... handle trigger (same as server-managed pattern)
288
+ // Handle both trigger and continue messages
289
+ if (msg.type === 'trigger' || msg.type === 'continue') {
290
+ abortController = new AbortController();
291
+
292
+ // execute() handles both triggers and continuations
293
+ const events = session.execute(msg, { signal: abortController.signal });
294
+
295
+ for await (const event of events) {
296
+ conn.write(JSON.stringify(event));
297
+ }
286
298
  }
287
299
  }
288
300
  });
@@ -505,9 +517,12 @@ const SockJS: typeof import('sockjs-client') = require('sockjs-client');
505
517
  // Initialize session (only for client-provided sessionId pattern)
506
518
  { type: 'init', sessionId: string }
507
519
 
508
- // Trigger an action
520
+ // Trigger an action (start a new conversation turn)
509
521
  { type: 'trigger', triggerName: string, input?: Record<string, unknown> }
510
522
 
523
+ // Continue execution (after client-side tool handling)
524
+ { type: 'continue', executionId: string, toolResults: ToolResult[] }
525
+
511
526
  // Stop current stream
512
527
  { type: 'stop' }
513
528
  ```
@@ -518,13 +533,19 @@ The server sends Octavus `StreamEvent` objects as JSON. See [Streaming Events](/
518
533
 
519
534
  ```typescript
520
535
  // Examples
521
- { type: 'start', messageId: '...' }
536
+ { type: 'start', messageId: '...', executionId: '...' }
522
537
  { type: 'text-delta', id: '...', delta: 'Hello' }
523
538
  { type: 'tool-input-start', toolCallId: '...', toolName: 'get-user' }
524
539
  { type: 'finish', finishReason: 'stop' }
525
540
  { type: 'error', errorType: 'internal_error', message: 'Something went wrong', source: 'platform', retryable: false }
541
+
542
+ // Client tool request (tools without server handlers)
543
+ { type: 'client-tool-request', executionId: '...', toolCalls: [...], serverToolResults: [...] }
544
+ { type: 'finish', finishReason: 'client-tool-calls', executionId: '...' }
526
545
  ```
527
546
 
547
+ When a `client-tool-request` event is received, the client handles the tools and sends a `continue` message to resume.
548
+
528
549
  ## Full Example
529
550
 
530
551
  For a complete walkthrough of building a chat interface with SockJS, see the [Socket Chat Example](/docs/examples/socket-chat).