@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.
- package/content/01-getting-started/02-quickstart.md +8 -5
- package/content/02-server-sdk/01-overview.md +22 -6
- package/content/02-server-sdk/02-sessions.md +51 -10
- package/content/02-server-sdk/03-tools.md +39 -14
- package/content/02-server-sdk/04-streaming.md +55 -7
- package/content/02-server-sdk/05-cli.md +9 -9
- package/content/03-client-sdk/01-overview.md +22 -9
- package/content/03-client-sdk/02-messages.md +6 -4
- package/content/03-client-sdk/03-streaming.md +7 -3
- package/content/03-client-sdk/05-socket-transport.md +31 -10
- package/content/03-client-sdk/06-http-transport.md +81 -17
- package/content/03-client-sdk/07-structured-output.md +3 -2
- package/content/03-client-sdk/08-file-uploads.md +6 -4
- package/content/03-client-sdk/10-client-tools.md +557 -0
- package/content/04-protocol/02-input-resources.md +12 -0
- package/content/04-protocol/03-triggers.md +8 -5
- package/content/04-protocol/06-handlers.md +10 -0
- package/content/04-protocol/07-agent-config.md +34 -1
- package/content/05-api-reference/01-overview.md +18 -0
- package/content/05-api-reference/02-sessions.md +2 -0
- package/content/05-api-reference/03-agents.md +12 -0
- package/content/06-examples/02-nextjs-chat.md +12 -7
- package/content/06-examples/03-socket-chat.md +27 -13
- package/content/07-migration/01-v1-to-v2.md +366 -0
- package/content/07-migration/_meta.md +4 -0
- package/dist/chunk-3ER2T7S7.js +663 -0
- package/dist/chunk-3ER2T7S7.js.map +1 -0
- package/dist/{chunk-WJ2W3DUC.js → chunk-HFF2TVGV.js} +13 -13
- package/dist/chunk-HFF2TVGV.js.map +1 -0
- package/dist/chunk-S5JUVAKE.js +1409 -0
- package/dist/chunk-S5JUVAKE.js.map +1 -0
- package/dist/chunk-TMJG4CJH.js +1409 -0
- package/dist/chunk-TMJG4CJH.js.map +1 -0
- package/dist/chunk-YJPO6KOJ.js +1435 -0
- package/dist/chunk-YJPO6KOJ.js.map +1 -0
- package/dist/chunk-ZSCRYD5P.js +1409 -0
- package/dist/chunk-ZSCRYD5P.js.map +1 -0
- package/dist/content.js +1 -1
- package/dist/docs.json +44 -26
- 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 +52 -26
- package/package.json +1 -1
- 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
|
|
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
|
-
//
|
|
135
|
-
const events = session.
|
|
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
|
-
|
|
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,
|
|
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
|
-
//
|
|
87
|
-
const events = session.
|
|
88
|
-
|
|
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
|
|
161
|
+
Handles request execution and streaming for a specific session.
|
|
160
162
|
|
|
161
163
|
```typescript
|
|
162
164
|
class AgentSession {
|
|
163
|
-
//
|
|
164
|
-
|
|
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
|
-
##
|
|
93
|
+
## Executing Requests
|
|
94
94
|
|
|
95
|
-
Once attached,
|
|
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
|
-
//
|
|
101
|
-
const events = session.
|
|
102
|
-
|
|
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
|
-
|
|
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.
|
|
147
|
-
Execute
|
|
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
|
|
8
|
+
Tools extend what agents can do. In Octavus, tools can execute either on your server or on the client side.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Server Tools vs Client Tools
|
|
11
11
|
|
|
12
|
-
|
|
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
|
|
163
|
+
participant Client as Client SDK
|
|
150
164
|
|
|
151
165
|
LLM->>Platform: 1. Decides to call tool
|
|
152
|
-
Platform-->>
|
|
153
|
-
Platform-->>
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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-->>
|
|
183
|
+
Platform-->>Client: text-delta events
|
|
163
184
|
|
|
164
|
-
Note over LLM,
|
|
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.
|
|
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
|
|
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
|
-
//
|
|
18
|
-
const events = session.
|
|
19
|
-
|
|
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
|
|
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
|
|
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_... #
|
|
36
|
+
OCTAVUS_CLI_API_KEY=oct_sk_... # "Agents" permission only
|
|
37
37
|
|
|
38
38
|
# Production .env
|
|
39
|
-
OCTAVUS_API_KEY=oct_sk_... #
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
175
|
-
const events = session.
|
|
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
|
-
|
|
285
|
-
|
|
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).
|