@octavus/client-sdk 0.0.6 → 0.0.7
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/dist/index.d.ts +187 -22
- package/dist/index.js +394 -296
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StreamEvent, UIMessage, UIMessagePart } from '@octavus/core';
|
|
2
2
|
export * from '@octavus/core';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Transport interface for delivering events from server to client.
|
|
6
|
+
*
|
|
7
|
+
* Abstracts the connection mechanism (HTTP/SSE or WebSocket) behind a unified
|
|
8
|
+
* async iterator interface. Use `createHttpTransport` or `createSocketTransport`
|
|
9
|
+
* to create an implementation.
|
|
10
|
+
*/
|
|
11
|
+
interface Transport {
|
|
12
|
+
/**
|
|
13
|
+
* Trigger the agent and stream events.
|
|
14
|
+
* @param triggerName - The trigger name defined in the agent's protocol
|
|
15
|
+
* @param input - Input parameters for variable substitution
|
|
16
|
+
*/
|
|
17
|
+
trigger(triggerName: string, input?: Record<string, unknown>): AsyncIterable<StreamEvent>;
|
|
18
|
+
/** Stop the current stream. Safe to call when no stream is active. */
|
|
19
|
+
stop(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
4
22
|
type ChatStatus = 'idle' | 'streaming' | 'error';
|
|
5
|
-
type TriggerFunction = (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;
|
|
6
23
|
/**
|
|
7
24
|
* Input for creating a user message.
|
|
8
25
|
* Currently supports text content. Will be extended to support attachments, images, etc.
|
|
@@ -12,8 +29,11 @@ interface UserMessageInput {
|
|
|
12
29
|
content: string;
|
|
13
30
|
}
|
|
14
31
|
interface OctavusChatOptions {
|
|
15
|
-
/**
|
|
16
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Transport for streaming events.
|
|
34
|
+
* Use `createHttpTransport` for HTTP/SSE or `createSocketTransport` for WebSocket/SockJS.
|
|
35
|
+
*/
|
|
36
|
+
transport: Transport;
|
|
17
37
|
/** Initial messages (for session refresh) */
|
|
18
38
|
initialMessages?: UIMessage[];
|
|
19
39
|
/** Callback when an error occurs */
|
|
@@ -28,27 +48,34 @@ type Listener = () => void;
|
|
|
28
48
|
* Framework-agnostic chat client for Octavus agents.
|
|
29
49
|
* Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.
|
|
30
50
|
*
|
|
31
|
-
* @example
|
|
51
|
+
* @example HTTP transport (Next.js, etc.)
|
|
32
52
|
* ```typescript
|
|
33
|
-
*
|
|
34
|
-
* onTrigger: (triggerName, input) =>
|
|
35
|
-
* fetch('/api/trigger', {
|
|
36
|
-
* method: 'POST',
|
|
37
|
-
* body: JSON.stringify({ sessionId, triggerName, input }),
|
|
38
|
-
* }),
|
|
39
|
-
* });
|
|
53
|
+
* import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';
|
|
40
54
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
55
|
+
* const chat = new OctavusChat({
|
|
56
|
+
* transport: createHttpTransport({
|
|
57
|
+
* triggerRequest: (triggerName, input) =>
|
|
58
|
+
* fetch('/api/trigger', {
|
|
59
|
+
* method: 'POST',
|
|
60
|
+
* body: JSON.stringify({ sessionId, triggerName, input }),
|
|
61
|
+
* }),
|
|
62
|
+
* }),
|
|
45
63
|
* });
|
|
64
|
+
* ```
|
|
46
65
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
66
|
+
* @example Socket transport (WebSocket, SockJS, Meteor)
|
|
67
|
+
* ```typescript
|
|
68
|
+
* import { OctavusChat, createSocketTransport } from '@octavus/client-sdk';
|
|
49
69
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
70
|
+
* const chat = new OctavusChat({
|
|
71
|
+
* transport: createSocketTransport({
|
|
72
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
73
|
+
* const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);
|
|
74
|
+
* ws.onopen = () => resolve(ws);
|
|
75
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
76
|
+
* }),
|
|
77
|
+
* }),
|
|
78
|
+
* });
|
|
52
79
|
* ```
|
|
53
80
|
*/
|
|
54
81
|
declare class OctavusChat {
|
|
@@ -56,7 +83,7 @@ declare class OctavusChat {
|
|
|
56
83
|
private _status;
|
|
57
84
|
private _error;
|
|
58
85
|
private options;
|
|
59
|
-
private
|
|
86
|
+
private transport;
|
|
60
87
|
private streamingState;
|
|
61
88
|
private listeners;
|
|
62
89
|
constructor(options: OctavusChatOptions);
|
|
@@ -84,6 +111,7 @@ declare class OctavusChat {
|
|
|
84
111
|
}): Promise<void>;
|
|
85
112
|
/** Stop the current streaming and finalize any partial message */
|
|
86
113
|
stop(): void;
|
|
114
|
+
private handleStreamEvent;
|
|
87
115
|
private updateStreamingMessage;
|
|
88
116
|
}
|
|
89
117
|
|
|
@@ -95,4 +123,141 @@ declare function parseSSEStream(response: Response): AsyncGenerator<StreamEvent,
|
|
|
95
123
|
/** Non-main thread content (e.g., "summary") is typically displayed differently. */
|
|
96
124
|
declare function isOtherThread(part: UIMessagePart): boolean;
|
|
97
125
|
|
|
98
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Options for creating an HTTP transport.
|
|
128
|
+
*/
|
|
129
|
+
interface HttpTransportOptions {
|
|
130
|
+
/**
|
|
131
|
+
* Function to make the trigger request.
|
|
132
|
+
* Called each time `send()` is invoked on the chat.
|
|
133
|
+
*
|
|
134
|
+
* @param triggerName - The trigger name (e.g., 'user-message')
|
|
135
|
+
* @param input - Input parameters for the trigger
|
|
136
|
+
* @returns Response with SSE stream body
|
|
137
|
+
*/
|
|
138
|
+
triggerRequest: (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Create an HTTP transport using native fetch() and SSE parsing.
|
|
142
|
+
* This is the default transport for Next.js and other HTTP-based applications.
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const transport = createHttpTransport({
|
|
147
|
+
* triggerRequest: (triggerName, input) =>
|
|
148
|
+
* fetch('/api/octavus', {
|
|
149
|
+
* method: 'POST',
|
|
150
|
+
* headers: { 'Content-Type': 'application/json' },
|
|
151
|
+
* body: JSON.stringify({ sessionId, triggerName, input }),
|
|
152
|
+
* }),
|
|
153
|
+
* });
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
declare function createHttpTransport(options: HttpTransportOptions): Transport;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Socket interface compatible with both WebSocket and SockJS.
|
|
160
|
+
*
|
|
161
|
+
* Uses MessageEvent for the message handler, which both WebSocket and SockJS support.
|
|
162
|
+
* The `| undefined` union accommodates SockJS's optional property typing.
|
|
163
|
+
*/
|
|
164
|
+
interface SocketLike {
|
|
165
|
+
send(data: string): void;
|
|
166
|
+
close(): void;
|
|
167
|
+
readyState: number;
|
|
168
|
+
onmessage: ((event: MessageEvent) => void) | null | undefined;
|
|
169
|
+
onclose: ((event: CloseEvent) => void) | null | undefined;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Options for creating a socket transport.
|
|
173
|
+
*/
|
|
174
|
+
interface SocketTransportOptions {
|
|
175
|
+
/**
|
|
176
|
+
* Function to create and connect the socket.
|
|
177
|
+
* Works directly with WebSocket and SockJS - no wrappers needed.
|
|
178
|
+
*
|
|
179
|
+
* @example Native WebSocket
|
|
180
|
+
* ```typescript
|
|
181
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
182
|
+
* const ws = new WebSocket('wss://api.example.com/stream?sessionId=xxx');
|
|
183
|
+
* ws.onopen = () => resolve(ws);
|
|
184
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
185
|
+
* })
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* @example SockJS
|
|
189
|
+
* ```typescript
|
|
190
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
191
|
+
* const sock = new SockJS('/chat-service');
|
|
192
|
+
* sock.onopen = () => resolve(sock);
|
|
193
|
+
* sock.onerror = () => reject(new Error('Connection failed'));
|
|
194
|
+
* })
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
connect: () => Promise<SocketLike>;
|
|
198
|
+
/**
|
|
199
|
+
* Called for every message received (parsed as JSON).
|
|
200
|
+
* Use this to handle custom (non-Octavus) events.
|
|
201
|
+
* Octavus StreamEvents are handled automatically by the transport.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```typescript
|
|
205
|
+
* onMessage: (data) => {
|
|
206
|
+
* const msg = data as { type: string };
|
|
207
|
+
* if (msg.type === 'typing-indicator') {
|
|
208
|
+
* setIsTyping(true);
|
|
209
|
+
* }
|
|
210
|
+
* if (msg.type === 'presence-update') {
|
|
211
|
+
* updatePresence(msg.users);
|
|
212
|
+
* }
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
onMessage?: (data: unknown) => void;
|
|
217
|
+
/**
|
|
218
|
+
* Called when the socket connection closes.
|
|
219
|
+
* Use this for cleanup or reconnection logic.
|
|
220
|
+
*/
|
|
221
|
+
onClose?: () => void;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Create a socket transport that works with any WebSocket-like connection.
|
|
225
|
+
* Supports native WebSocket, SockJS, or any compatible socket implementation.
|
|
226
|
+
*
|
|
227
|
+
* The server should send StreamEvent format (same as SSE) over the socket.
|
|
228
|
+
* Unknown events are safely ignored using Zod validation.
|
|
229
|
+
*
|
|
230
|
+
* @example Basic usage with WebSocket
|
|
231
|
+
* ```typescript
|
|
232
|
+
* const transport = createSocketTransport({
|
|
233
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
234
|
+
* const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);
|
|
235
|
+
* ws.onopen = () => resolve(ws);
|
|
236
|
+
* ws.onerror = () => reject(new Error('Connection failed'));
|
|
237
|
+
* }),
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @example With SockJS and custom events
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const transport = createSocketTransport({
|
|
244
|
+
* connect: () => new Promise((resolve, reject) => {
|
|
245
|
+
* const sock = new SockJS('/octavus-stream');
|
|
246
|
+
* sock.onopen = () => resolve(sock);
|
|
247
|
+
* sock.onerror = () => reject(new Error('Connection failed'));
|
|
248
|
+
* }),
|
|
249
|
+
* onMessage: (data) => {
|
|
250
|
+
* const msg = data as { type: string };
|
|
251
|
+
* if (msg.type === 'typing-indicator') {
|
|
252
|
+
* setIsTyping(msg.isTyping);
|
|
253
|
+
* }
|
|
254
|
+
* },
|
|
255
|
+
* onClose: () => {
|
|
256
|
+
* console.log('Socket closed, attempting reconnect...');
|
|
257
|
+
* },
|
|
258
|
+
* });
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
declare function createSocketTransport(options: SocketTransportOptions): Transport;
|
|
262
|
+
|
|
263
|
+
export { type ChatStatus, type HttpTransportOptions, OctavusChat, type OctavusChatOptions, type SocketLike, type SocketTransportOptions, type Transport, type UserMessageInput, createHttpTransport, createSocketTransport, isOtherThread, parseSSEStream };
|
package/dist/index.js
CHANGED
|
@@ -2,54 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
generateId
|
|
4
4
|
} from "@octavus/core";
|
|
5
|
-
|
|
6
|
-
// src/stream/reader.ts
|
|
7
|
-
import { safeParseStreamEvent } from "@octavus/core";
|
|
8
|
-
async function* parseSSEStream(response) {
|
|
9
|
-
const reader = response.body?.getReader();
|
|
10
|
-
if (!reader) {
|
|
11
|
-
throw new Error("Response body is not readable");
|
|
12
|
-
}
|
|
13
|
-
const decoder = new TextDecoder();
|
|
14
|
-
let buffer = "";
|
|
15
|
-
try {
|
|
16
|
-
let reading = true;
|
|
17
|
-
while (reading) {
|
|
18
|
-
const { done, value } = await reader.read();
|
|
19
|
-
if (done) {
|
|
20
|
-
reading = false;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
buffer += decoder.decode(value, { stream: true });
|
|
24
|
-
const lines = buffer.split("\n");
|
|
25
|
-
buffer = lines.pop() ?? "";
|
|
26
|
-
for (const line of lines) {
|
|
27
|
-
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
|
28
|
-
try {
|
|
29
|
-
const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));
|
|
30
|
-
if (parsed.success) {
|
|
31
|
-
yield parsed.data;
|
|
32
|
-
}
|
|
33
|
-
} catch {
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
} finally {
|
|
39
|
-
reader.releaseLock();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// src/chat.ts
|
|
44
5
|
var OPERATION_BLOCK_TYPES = /* @__PURE__ */ new Set(["set-resource", "serialize-thread"]);
|
|
45
|
-
async function parseErrorResponse(response) {
|
|
46
|
-
try {
|
|
47
|
-
const data = await response.json();
|
|
48
|
-
return data.error ?? data.message ?? `Request failed: ${response.status}`;
|
|
49
|
-
} catch {
|
|
50
|
-
return `Request failed: ${response.status}`;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
6
|
function createUserMessage(input) {
|
|
54
7
|
return {
|
|
55
8
|
id: generateId(),
|
|
@@ -84,13 +37,14 @@ var OctavusChat = class {
|
|
|
84
37
|
_status = "idle";
|
|
85
38
|
_error = null;
|
|
86
39
|
options;
|
|
87
|
-
|
|
40
|
+
transport;
|
|
88
41
|
streamingState = null;
|
|
89
42
|
// Listener sets for reactive frameworks
|
|
90
43
|
listeners = /* @__PURE__ */ new Set();
|
|
91
44
|
constructor(options) {
|
|
92
45
|
this.options = options;
|
|
93
46
|
this._messages = options.initialMessages ?? [];
|
|
47
|
+
this.transport = options.transport;
|
|
94
48
|
}
|
|
95
49
|
// =========================================================================
|
|
96
50
|
// Public Getters
|
|
@@ -144,9 +98,7 @@ var OctavusChat = class {
|
|
|
144
98
|
* @param options.userMessage - If provided, adds a user message to the chat before triggering
|
|
145
99
|
*/
|
|
146
100
|
async send(triggerName, input, sendOptions) {
|
|
147
|
-
this.
|
|
148
|
-
const abortController = new AbortController();
|
|
149
|
-
this.abortController = abortController;
|
|
101
|
+
this.transport.stop();
|
|
150
102
|
if (sendOptions?.userMessage !== void 0) {
|
|
151
103
|
const userMsg = createUserMessage(sendOptions.userMessage);
|
|
152
104
|
this.setMessages([...this._messages, userMsg]);
|
|
@@ -155,247 +107,9 @@ var OctavusChat = class {
|
|
|
155
107
|
this.setError(null);
|
|
156
108
|
this.streamingState = createEmptyStreamingState();
|
|
157
109
|
try {
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
throw new Error(errorMessage);
|
|
162
|
-
}
|
|
163
|
-
for await (const event of parseSSEStream(response)) {
|
|
164
|
-
if (abortController.signal.aborted) {
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
const state = this.streamingState;
|
|
168
|
-
if (!state) break;
|
|
169
|
-
switch (event.type) {
|
|
170
|
-
case "start":
|
|
171
|
-
break;
|
|
172
|
-
case "block-start": {
|
|
173
|
-
const block = {
|
|
174
|
-
blockId: event.blockId,
|
|
175
|
-
blockName: event.blockName,
|
|
176
|
-
blockType: event.blockType,
|
|
177
|
-
display: event.display,
|
|
178
|
-
description: event.description,
|
|
179
|
-
outputToChat: event.outputToChat ?? true,
|
|
180
|
-
thread: event.thread,
|
|
181
|
-
reasoning: "",
|
|
182
|
-
text: "",
|
|
183
|
-
toolCalls: /* @__PURE__ */ new Map()
|
|
184
|
-
};
|
|
185
|
-
state.blocks.set(event.blockId, block);
|
|
186
|
-
state.activeBlock = block;
|
|
187
|
-
const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);
|
|
188
|
-
const isHidden = event.display === "hidden";
|
|
189
|
-
if (isOperation && !isHidden) {
|
|
190
|
-
const thread = event.thread;
|
|
191
|
-
const operationPart = {
|
|
192
|
-
type: "operation",
|
|
193
|
-
operationId: event.blockId,
|
|
194
|
-
name: event.description || event.blockName,
|
|
195
|
-
operationType: event.blockType,
|
|
196
|
-
status: "running",
|
|
197
|
-
thread: thread && thread !== "main" ? thread : void 0
|
|
198
|
-
};
|
|
199
|
-
state.parts.push(operationPart);
|
|
200
|
-
}
|
|
201
|
-
state.currentTextPartIndex = null;
|
|
202
|
-
state.currentReasoningPartIndex = null;
|
|
203
|
-
this.updateStreamingMessage();
|
|
204
|
-
break;
|
|
205
|
-
}
|
|
206
|
-
case "block-end": {
|
|
207
|
-
const operationPartIndex = state.parts.findIndex(
|
|
208
|
-
(p) => p.type === "operation" && p.operationId === event.blockId
|
|
209
|
-
);
|
|
210
|
-
if (operationPartIndex >= 0) {
|
|
211
|
-
const part = state.parts[operationPartIndex];
|
|
212
|
-
state.parts[operationPartIndex] = { ...part, status: "done" };
|
|
213
|
-
}
|
|
214
|
-
if (state.activeBlock?.blockId === event.blockId) {
|
|
215
|
-
state.activeBlock = null;
|
|
216
|
-
}
|
|
217
|
-
this.updateStreamingMessage();
|
|
218
|
-
break;
|
|
219
|
-
}
|
|
220
|
-
case "reasoning-start": {
|
|
221
|
-
const thread = state.activeBlock?.thread;
|
|
222
|
-
const reasoningPart = {
|
|
223
|
-
type: "reasoning",
|
|
224
|
-
text: "",
|
|
225
|
-
status: "streaming",
|
|
226
|
-
thread: thread && thread !== "main" ? thread : void 0
|
|
227
|
-
};
|
|
228
|
-
state.parts.push(reasoningPart);
|
|
229
|
-
state.currentReasoningPartIndex = state.parts.length - 1;
|
|
230
|
-
this.updateStreamingMessage();
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case "reasoning-delta": {
|
|
234
|
-
if (state.currentReasoningPartIndex !== null) {
|
|
235
|
-
const part = state.parts[state.currentReasoningPartIndex];
|
|
236
|
-
part.text += event.delta;
|
|
237
|
-
state.parts[state.currentReasoningPartIndex] = { ...part };
|
|
238
|
-
}
|
|
239
|
-
if (state.activeBlock) {
|
|
240
|
-
state.activeBlock.reasoning += event.delta;
|
|
241
|
-
}
|
|
242
|
-
this.updateStreamingMessage();
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
245
|
-
case "reasoning-end": {
|
|
246
|
-
if (state.currentReasoningPartIndex !== null) {
|
|
247
|
-
const part = state.parts[state.currentReasoningPartIndex];
|
|
248
|
-
part.status = "done";
|
|
249
|
-
state.parts[state.currentReasoningPartIndex] = { ...part };
|
|
250
|
-
state.currentReasoningPartIndex = null;
|
|
251
|
-
}
|
|
252
|
-
this.updateStreamingMessage();
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
case "text-start": {
|
|
256
|
-
const thread = state.activeBlock?.thread;
|
|
257
|
-
const isOtherThread2 = Boolean(thread && thread !== "main");
|
|
258
|
-
const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread2;
|
|
259
|
-
if (shouldAddPart) {
|
|
260
|
-
const textPart = {
|
|
261
|
-
type: "text",
|
|
262
|
-
text: "",
|
|
263
|
-
status: "streaming",
|
|
264
|
-
thread: isOtherThread2 ? thread : void 0
|
|
265
|
-
};
|
|
266
|
-
state.parts.push(textPart);
|
|
267
|
-
state.currentTextPartIndex = state.parts.length - 1;
|
|
268
|
-
}
|
|
269
|
-
this.updateStreamingMessage();
|
|
270
|
-
break;
|
|
271
|
-
}
|
|
272
|
-
case "text-delta": {
|
|
273
|
-
if (state.currentTextPartIndex !== null) {
|
|
274
|
-
const part = state.parts[state.currentTextPartIndex];
|
|
275
|
-
part.text += event.delta;
|
|
276
|
-
state.parts[state.currentTextPartIndex] = { ...part };
|
|
277
|
-
}
|
|
278
|
-
if (state.activeBlock) {
|
|
279
|
-
state.activeBlock.text += event.delta;
|
|
280
|
-
}
|
|
281
|
-
this.updateStreamingMessage();
|
|
282
|
-
break;
|
|
283
|
-
}
|
|
284
|
-
case "text-end": {
|
|
285
|
-
if (state.currentTextPartIndex !== null) {
|
|
286
|
-
const part = state.parts[state.currentTextPartIndex];
|
|
287
|
-
part.status = "done";
|
|
288
|
-
state.parts[state.currentTextPartIndex] = { ...part };
|
|
289
|
-
state.currentTextPartIndex = null;
|
|
290
|
-
}
|
|
291
|
-
this.updateStreamingMessage();
|
|
292
|
-
break;
|
|
293
|
-
}
|
|
294
|
-
case "tool-input-start": {
|
|
295
|
-
const thread = state.activeBlock?.thread;
|
|
296
|
-
const toolPart = {
|
|
297
|
-
type: "tool-call",
|
|
298
|
-
toolCallId: event.toolCallId,
|
|
299
|
-
toolName: event.toolName,
|
|
300
|
-
displayName: event.title,
|
|
301
|
-
args: {},
|
|
302
|
-
status: "pending",
|
|
303
|
-
thread: thread && thread !== "main" ? thread : void 0
|
|
304
|
-
};
|
|
305
|
-
state.parts.push(toolPart);
|
|
306
|
-
if (state.activeBlock) {
|
|
307
|
-
state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
|
|
308
|
-
}
|
|
309
|
-
this.updateStreamingMessage();
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
case "tool-input-delta": {
|
|
313
|
-
const toolPartIndex = state.parts.findIndex(
|
|
314
|
-
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
315
|
-
);
|
|
316
|
-
if (toolPartIndex >= 0) {
|
|
317
|
-
try {
|
|
318
|
-
const part = state.parts[toolPartIndex];
|
|
319
|
-
part.args = JSON.parse(event.inputTextDelta);
|
|
320
|
-
state.parts[toolPartIndex] = { ...part };
|
|
321
|
-
this.updateStreamingMessage();
|
|
322
|
-
} catch {
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
case "tool-input-end":
|
|
328
|
-
break;
|
|
329
|
-
case "tool-input-available": {
|
|
330
|
-
const toolPartIndex = state.parts.findIndex(
|
|
331
|
-
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
332
|
-
);
|
|
333
|
-
if (toolPartIndex >= 0) {
|
|
334
|
-
const part = state.parts[toolPartIndex];
|
|
335
|
-
part.args = event.input;
|
|
336
|
-
part.status = "running";
|
|
337
|
-
state.parts[toolPartIndex] = { ...part };
|
|
338
|
-
this.updateStreamingMessage();
|
|
339
|
-
}
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
case "tool-output-available": {
|
|
343
|
-
const toolPartIndex = state.parts.findIndex(
|
|
344
|
-
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
345
|
-
);
|
|
346
|
-
if (toolPartIndex >= 0) {
|
|
347
|
-
const part = state.parts[toolPartIndex];
|
|
348
|
-
part.result = event.output;
|
|
349
|
-
part.status = "done";
|
|
350
|
-
state.parts[toolPartIndex] = { ...part };
|
|
351
|
-
this.updateStreamingMessage();
|
|
352
|
-
}
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
case "tool-output-error": {
|
|
356
|
-
const toolPartIndex = state.parts.findIndex(
|
|
357
|
-
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
358
|
-
);
|
|
359
|
-
if (toolPartIndex >= 0) {
|
|
360
|
-
const part = state.parts[toolPartIndex];
|
|
361
|
-
part.error = event.errorText;
|
|
362
|
-
part.status = "error";
|
|
363
|
-
state.parts[toolPartIndex] = { ...part };
|
|
364
|
-
this.updateStreamingMessage();
|
|
365
|
-
}
|
|
366
|
-
break;
|
|
367
|
-
}
|
|
368
|
-
case "resource-update":
|
|
369
|
-
this.options.onResourceUpdate?.(event.name, event.value);
|
|
370
|
-
break;
|
|
371
|
-
case "finish": {
|
|
372
|
-
const finalMessage = buildMessageFromState(state, "done");
|
|
373
|
-
finalMessage.parts = finalMessage.parts.map((part) => {
|
|
374
|
-
if (part.type === "text" || part.type === "reasoning") {
|
|
375
|
-
return { ...part, status: "done" };
|
|
376
|
-
}
|
|
377
|
-
return part;
|
|
378
|
-
});
|
|
379
|
-
if (finalMessage.parts.length > 0) {
|
|
380
|
-
const messages = [...this._messages];
|
|
381
|
-
const lastMsg = messages[messages.length - 1];
|
|
382
|
-
if (lastMsg && lastMsg.id === state.messageId) {
|
|
383
|
-
messages[messages.length - 1] = finalMessage;
|
|
384
|
-
} else {
|
|
385
|
-
messages.push(finalMessage);
|
|
386
|
-
}
|
|
387
|
-
this.setMessages(messages);
|
|
388
|
-
}
|
|
389
|
-
this.setStatus("idle");
|
|
390
|
-
this.streamingState = null;
|
|
391
|
-
this.options.onFinish?.();
|
|
392
|
-
break;
|
|
393
|
-
}
|
|
394
|
-
case "error":
|
|
395
|
-
throw new Error(event.errorText);
|
|
396
|
-
case "tool-request":
|
|
397
|
-
break;
|
|
398
|
-
}
|
|
110
|
+
for await (const event of this.transport.trigger(triggerName, input)) {
|
|
111
|
+
if (this.streamingState === null) break;
|
|
112
|
+
this.handleStreamEvent(event, this.streamingState);
|
|
399
113
|
}
|
|
400
114
|
} catch (err) {
|
|
401
115
|
const errorObj = err instanceof Error ? err : new Error("Unknown error");
|
|
@@ -403,14 +117,11 @@ var OctavusChat = class {
|
|
|
403
117
|
this.setStatus("error");
|
|
404
118
|
this.streamingState = null;
|
|
405
119
|
this.options.onError?.(errorObj);
|
|
406
|
-
} finally {
|
|
407
|
-
this.abortController = null;
|
|
408
120
|
}
|
|
409
121
|
}
|
|
410
122
|
/** Stop the current streaming and finalize any partial message */
|
|
411
123
|
stop() {
|
|
412
|
-
this.
|
|
413
|
-
this.abortController = null;
|
|
124
|
+
this.transport.stop();
|
|
414
125
|
const state = this.streamingState;
|
|
415
126
|
if (state && state.parts.length > 0) {
|
|
416
127
|
const finalMessage = buildMessageFromState(state, "done");
|
|
@@ -429,6 +140,238 @@ var OctavusChat = class {
|
|
|
429
140
|
// =========================================================================
|
|
430
141
|
// Private Helpers
|
|
431
142
|
// =========================================================================
|
|
143
|
+
handleStreamEvent(event, state) {
|
|
144
|
+
switch (event.type) {
|
|
145
|
+
case "start":
|
|
146
|
+
break;
|
|
147
|
+
case "block-start": {
|
|
148
|
+
const block = {
|
|
149
|
+
blockId: event.blockId,
|
|
150
|
+
blockName: event.blockName,
|
|
151
|
+
blockType: event.blockType,
|
|
152
|
+
display: event.display,
|
|
153
|
+
description: event.description,
|
|
154
|
+
outputToChat: event.outputToChat ?? true,
|
|
155
|
+
thread: event.thread,
|
|
156
|
+
reasoning: "",
|
|
157
|
+
text: "",
|
|
158
|
+
toolCalls: /* @__PURE__ */ new Map()
|
|
159
|
+
};
|
|
160
|
+
state.blocks.set(event.blockId, block);
|
|
161
|
+
state.activeBlock = block;
|
|
162
|
+
const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);
|
|
163
|
+
const isHidden = event.display === "hidden";
|
|
164
|
+
if (isOperation && !isHidden) {
|
|
165
|
+
const thread = event.thread;
|
|
166
|
+
const operationPart = {
|
|
167
|
+
type: "operation",
|
|
168
|
+
operationId: event.blockId,
|
|
169
|
+
name: event.description ?? event.blockName,
|
|
170
|
+
operationType: event.blockType,
|
|
171
|
+
status: "running",
|
|
172
|
+
thread: thread && thread !== "main" ? thread : void 0
|
|
173
|
+
};
|
|
174
|
+
state.parts.push(operationPart);
|
|
175
|
+
}
|
|
176
|
+
state.currentTextPartIndex = null;
|
|
177
|
+
state.currentReasoningPartIndex = null;
|
|
178
|
+
this.updateStreamingMessage();
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
case "block-end": {
|
|
182
|
+
const operationPartIndex = state.parts.findIndex(
|
|
183
|
+
(p) => p.type === "operation" && p.operationId === event.blockId
|
|
184
|
+
);
|
|
185
|
+
if (operationPartIndex >= 0) {
|
|
186
|
+
const part = state.parts[operationPartIndex];
|
|
187
|
+
state.parts[operationPartIndex] = { ...part, status: "done" };
|
|
188
|
+
}
|
|
189
|
+
if (state.activeBlock?.blockId === event.blockId) {
|
|
190
|
+
state.activeBlock = null;
|
|
191
|
+
}
|
|
192
|
+
this.updateStreamingMessage();
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case "reasoning-start": {
|
|
196
|
+
const thread = state.activeBlock?.thread;
|
|
197
|
+
const reasoningPart = {
|
|
198
|
+
type: "reasoning",
|
|
199
|
+
text: "",
|
|
200
|
+
status: "streaming",
|
|
201
|
+
thread: thread && thread !== "main" ? thread : void 0
|
|
202
|
+
};
|
|
203
|
+
state.parts.push(reasoningPart);
|
|
204
|
+
state.currentReasoningPartIndex = state.parts.length - 1;
|
|
205
|
+
this.updateStreamingMessage();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case "reasoning-delta": {
|
|
209
|
+
if (state.currentReasoningPartIndex !== null) {
|
|
210
|
+
const part = state.parts[state.currentReasoningPartIndex];
|
|
211
|
+
part.text += event.delta;
|
|
212
|
+
state.parts[state.currentReasoningPartIndex] = { ...part };
|
|
213
|
+
}
|
|
214
|
+
if (state.activeBlock) {
|
|
215
|
+
state.activeBlock.reasoning += event.delta;
|
|
216
|
+
}
|
|
217
|
+
this.updateStreamingMessage();
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case "reasoning-end": {
|
|
221
|
+
if (state.currentReasoningPartIndex !== null) {
|
|
222
|
+
const part = state.parts[state.currentReasoningPartIndex];
|
|
223
|
+
part.status = "done";
|
|
224
|
+
state.parts[state.currentReasoningPartIndex] = { ...part };
|
|
225
|
+
state.currentReasoningPartIndex = null;
|
|
226
|
+
}
|
|
227
|
+
this.updateStreamingMessage();
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "text-start": {
|
|
231
|
+
const thread = state.activeBlock?.thread;
|
|
232
|
+
const isOtherThread2 = Boolean(thread && thread !== "main");
|
|
233
|
+
const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread2;
|
|
234
|
+
if (shouldAddPart) {
|
|
235
|
+
const textPart = {
|
|
236
|
+
type: "text",
|
|
237
|
+
text: "",
|
|
238
|
+
status: "streaming",
|
|
239
|
+
thread: isOtherThread2 ? thread : void 0
|
|
240
|
+
};
|
|
241
|
+
state.parts.push(textPart);
|
|
242
|
+
state.currentTextPartIndex = state.parts.length - 1;
|
|
243
|
+
}
|
|
244
|
+
this.updateStreamingMessage();
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case "text-delta": {
|
|
248
|
+
if (state.currentTextPartIndex !== null) {
|
|
249
|
+
const part = state.parts[state.currentTextPartIndex];
|
|
250
|
+
part.text += event.delta;
|
|
251
|
+
state.parts[state.currentTextPartIndex] = { ...part };
|
|
252
|
+
}
|
|
253
|
+
if (state.activeBlock) {
|
|
254
|
+
state.activeBlock.text += event.delta;
|
|
255
|
+
}
|
|
256
|
+
this.updateStreamingMessage();
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
case "text-end": {
|
|
260
|
+
if (state.currentTextPartIndex !== null) {
|
|
261
|
+
const part = state.parts[state.currentTextPartIndex];
|
|
262
|
+
part.status = "done";
|
|
263
|
+
state.parts[state.currentTextPartIndex] = { ...part };
|
|
264
|
+
state.currentTextPartIndex = null;
|
|
265
|
+
}
|
|
266
|
+
this.updateStreamingMessage();
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
case "tool-input-start": {
|
|
270
|
+
const thread = state.activeBlock?.thread;
|
|
271
|
+
const toolPart = {
|
|
272
|
+
type: "tool-call",
|
|
273
|
+
toolCallId: event.toolCallId,
|
|
274
|
+
toolName: event.toolName,
|
|
275
|
+
displayName: event.title,
|
|
276
|
+
args: {},
|
|
277
|
+
status: "pending",
|
|
278
|
+
thread: thread && thread !== "main" ? thread : void 0
|
|
279
|
+
};
|
|
280
|
+
state.parts.push(toolPart);
|
|
281
|
+
if (state.activeBlock) {
|
|
282
|
+
state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
|
|
283
|
+
}
|
|
284
|
+
this.updateStreamingMessage();
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case "tool-input-delta": {
|
|
288
|
+
const toolPartIndex = state.parts.findIndex(
|
|
289
|
+
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
290
|
+
);
|
|
291
|
+
if (toolPartIndex >= 0) {
|
|
292
|
+
try {
|
|
293
|
+
const part = state.parts[toolPartIndex];
|
|
294
|
+
part.args = JSON.parse(event.inputTextDelta);
|
|
295
|
+
state.parts[toolPartIndex] = { ...part };
|
|
296
|
+
this.updateStreamingMessage();
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
case "tool-input-end":
|
|
303
|
+
break;
|
|
304
|
+
case "tool-input-available": {
|
|
305
|
+
const toolPartIndex = state.parts.findIndex(
|
|
306
|
+
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
307
|
+
);
|
|
308
|
+
if (toolPartIndex >= 0) {
|
|
309
|
+
const part = state.parts[toolPartIndex];
|
|
310
|
+
part.args = event.input;
|
|
311
|
+
part.status = "running";
|
|
312
|
+
state.parts[toolPartIndex] = { ...part };
|
|
313
|
+
this.updateStreamingMessage();
|
|
314
|
+
}
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case "tool-output-available": {
|
|
318
|
+
const toolPartIndex = state.parts.findIndex(
|
|
319
|
+
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
320
|
+
);
|
|
321
|
+
if (toolPartIndex >= 0) {
|
|
322
|
+
const part = state.parts[toolPartIndex];
|
|
323
|
+
part.result = event.output;
|
|
324
|
+
part.status = "done";
|
|
325
|
+
state.parts[toolPartIndex] = { ...part };
|
|
326
|
+
this.updateStreamingMessage();
|
|
327
|
+
}
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case "tool-output-error": {
|
|
331
|
+
const toolPartIndex = state.parts.findIndex(
|
|
332
|
+
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
333
|
+
);
|
|
334
|
+
if (toolPartIndex >= 0) {
|
|
335
|
+
const part = state.parts[toolPartIndex];
|
|
336
|
+
part.error = event.errorText;
|
|
337
|
+
part.status = "error";
|
|
338
|
+
state.parts[toolPartIndex] = { ...part };
|
|
339
|
+
this.updateStreamingMessage();
|
|
340
|
+
}
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
case "resource-update":
|
|
344
|
+
this.options.onResourceUpdate?.(event.name, event.value);
|
|
345
|
+
break;
|
|
346
|
+
case "finish": {
|
|
347
|
+
const finalMessage = buildMessageFromState(state, "done");
|
|
348
|
+
finalMessage.parts = finalMessage.parts.map((part) => {
|
|
349
|
+
if (part.type === "text" || part.type === "reasoning") {
|
|
350
|
+
return { ...part, status: "done" };
|
|
351
|
+
}
|
|
352
|
+
return part;
|
|
353
|
+
});
|
|
354
|
+
if (finalMessage.parts.length > 0) {
|
|
355
|
+
const messages = [...this._messages];
|
|
356
|
+
const lastMsg = messages[messages.length - 1];
|
|
357
|
+
if (lastMsg && lastMsg.id === state.messageId) {
|
|
358
|
+
messages[messages.length - 1] = finalMessage;
|
|
359
|
+
} else {
|
|
360
|
+
messages.push(finalMessage);
|
|
361
|
+
}
|
|
362
|
+
this.setMessages(messages);
|
|
363
|
+
}
|
|
364
|
+
this.setStatus("idle");
|
|
365
|
+
this.streamingState = null;
|
|
366
|
+
this.options.onFinish?.();
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "error":
|
|
370
|
+
throw new Error(event.errorText);
|
|
371
|
+
case "tool-request":
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
432
375
|
updateStreamingMessage() {
|
|
433
376
|
const state = this.streamingState;
|
|
434
377
|
if (!state) return;
|
|
@@ -444,12 +387,167 @@ var OctavusChat = class {
|
|
|
444
387
|
}
|
|
445
388
|
};
|
|
446
389
|
|
|
390
|
+
// src/stream/reader.ts
|
|
391
|
+
import { safeParseStreamEvent } from "@octavus/core";
|
|
392
|
+
async function* parseSSEStream(response) {
|
|
393
|
+
const reader = response.body?.getReader();
|
|
394
|
+
if (!reader) {
|
|
395
|
+
throw new Error("Response body is not readable");
|
|
396
|
+
}
|
|
397
|
+
const decoder = new TextDecoder();
|
|
398
|
+
let buffer = "";
|
|
399
|
+
try {
|
|
400
|
+
let reading = true;
|
|
401
|
+
while (reading) {
|
|
402
|
+
const { done, value } = await reader.read();
|
|
403
|
+
if (done) {
|
|
404
|
+
reading = false;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
buffer += decoder.decode(value, { stream: true });
|
|
408
|
+
const lines = buffer.split("\n");
|
|
409
|
+
buffer = lines.pop() ?? "";
|
|
410
|
+
for (const line of lines) {
|
|
411
|
+
if (line.startsWith("data: ") && line !== "data: [DONE]") {
|
|
412
|
+
try {
|
|
413
|
+
const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));
|
|
414
|
+
if (parsed.success) {
|
|
415
|
+
yield parsed.data;
|
|
416
|
+
}
|
|
417
|
+
} catch {
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
} finally {
|
|
423
|
+
reader.releaseLock();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
447
427
|
// src/utils/message-helpers.ts
|
|
448
428
|
function isOtherThread(part) {
|
|
449
429
|
return Boolean(part.thread && part.thread !== "main");
|
|
450
430
|
}
|
|
431
|
+
|
|
432
|
+
// src/transports/http.ts
|
|
433
|
+
function createHttpTransport(options) {
|
|
434
|
+
let abortController = null;
|
|
435
|
+
return {
|
|
436
|
+
async *trigger(triggerName, input) {
|
|
437
|
+
abortController = new AbortController();
|
|
438
|
+
const response = await options.triggerRequest(triggerName, input);
|
|
439
|
+
if (!response.ok) {
|
|
440
|
+
const errorText = await response.text().catch(() => `Request failed: ${response.status}`);
|
|
441
|
+
throw new Error(errorText);
|
|
442
|
+
}
|
|
443
|
+
if (!response.body) {
|
|
444
|
+
throw new Error("Response body is empty");
|
|
445
|
+
}
|
|
446
|
+
for await (const event of parseSSEStream(response)) {
|
|
447
|
+
if (abortController.signal.aborted) {
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
yield event;
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
stop() {
|
|
454
|
+
abortController?.abort();
|
|
455
|
+
abortController = null;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/transports/socket.ts
|
|
461
|
+
import { safeParseStreamEvent as safeParseStreamEvent2 } from "@octavus/core";
|
|
462
|
+
var SOCKET_OPEN = 1;
|
|
463
|
+
function createSocketTransport(options) {
|
|
464
|
+
let socket = null;
|
|
465
|
+
let eventQueue = [];
|
|
466
|
+
let eventResolver = null;
|
|
467
|
+
let isStreaming = false;
|
|
468
|
+
function enqueueEvent(event) {
|
|
469
|
+
if (eventResolver) {
|
|
470
|
+
eventResolver(event);
|
|
471
|
+
eventResolver = null;
|
|
472
|
+
} else {
|
|
473
|
+
eventQueue.push(event);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function nextEvent() {
|
|
477
|
+
if (eventQueue.length > 0) {
|
|
478
|
+
return Promise.resolve(eventQueue.shift());
|
|
479
|
+
}
|
|
480
|
+
if (!isStreaming) {
|
|
481
|
+
return Promise.resolve(null);
|
|
482
|
+
}
|
|
483
|
+
return new Promise((resolve) => {
|
|
484
|
+
eventResolver = resolve;
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
async function connect() {
|
|
488
|
+
const sock = await options.connect();
|
|
489
|
+
sock.onmessage = (e) => {
|
|
490
|
+
try {
|
|
491
|
+
const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
|
|
492
|
+
options.onMessage?.(data);
|
|
493
|
+
const result = safeParseStreamEvent2(data);
|
|
494
|
+
if (result.success) {
|
|
495
|
+
const event = result.data;
|
|
496
|
+
enqueueEvent(event);
|
|
497
|
+
if (event.type === "finish" || event.type === "error") {
|
|
498
|
+
isStreaming = false;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} catch {
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
sock.onclose = () => {
|
|
505
|
+
options.onClose?.();
|
|
506
|
+
isStreaming = false;
|
|
507
|
+
if (eventResolver) {
|
|
508
|
+
eventResolver(null);
|
|
509
|
+
eventResolver = null;
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
return sock;
|
|
513
|
+
}
|
|
514
|
+
return {
|
|
515
|
+
async *trigger(triggerName, input) {
|
|
516
|
+
if (socket?.readyState !== SOCKET_OPEN) {
|
|
517
|
+
socket = await connect();
|
|
518
|
+
}
|
|
519
|
+
eventQueue = [];
|
|
520
|
+
isStreaming = true;
|
|
521
|
+
socket.send(
|
|
522
|
+
JSON.stringify({
|
|
523
|
+
type: "trigger",
|
|
524
|
+
triggerName,
|
|
525
|
+
input
|
|
526
|
+
})
|
|
527
|
+
);
|
|
528
|
+
while (true) {
|
|
529
|
+
const event = await nextEvent();
|
|
530
|
+
if (event === null) break;
|
|
531
|
+
yield event;
|
|
532
|
+
if (event.type === "finish" || event.type === "error") break;
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
stop() {
|
|
536
|
+
if (socket?.readyState === SOCKET_OPEN) {
|
|
537
|
+
socket.send(JSON.stringify({ type: "stop" }));
|
|
538
|
+
}
|
|
539
|
+
isStreaming = false;
|
|
540
|
+
if (eventResolver) {
|
|
541
|
+
eventResolver(null);
|
|
542
|
+
eventResolver = null;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
}
|
|
451
547
|
export {
|
|
452
548
|
OctavusChat,
|
|
549
|
+
createHttpTransport,
|
|
550
|
+
createSocketTransport,
|
|
453
551
|
isOtherThread,
|
|
454
552
|
parseSSEStream
|
|
455
553
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/chat.ts","../src/stream/reader.ts","../src/utils/message-helpers.ts"],"sourcesContent":["import {\n generateId,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type DisplayMode,\n} from '@octavus/core';\nimport { parseSSEStream } from './stream/reader';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error';\n\nexport type TriggerFunction = (\n triggerName: string,\n input?: Record<string, unknown>,\n) => Promise<Response>;\n\n/**\n * Input for creating a user message.\n * Currently supports text content. Will be extended to support attachments, images, etc.\n */\nexport interface UserMessageInput {\n /** Text content of the message */\n content: string;\n}\n\nexport interface OctavusChatOptions {\n /** Function to make the API call to trigger the agent */\n onTrigger: TriggerFunction;\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n /** Callback when streaming finishes */\n onFinish?: () => void;\n /** Callback when a resource is updated */\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\ninterface BlockState {\n blockId: string;\n blockName: string;\n blockType: string;\n display: DisplayMode;\n description?: string;\n outputToChat: boolean;\n thread?: string;\n reasoning: string;\n text: string;\n toolCalls: Map<string, UIToolCallPart>;\n}\n\ninterface StreamingState {\n messageId: string;\n parts: UIMessagePart[];\n activeBlock: BlockState | null;\n blocks: Map<string, BlockState>;\n // Track current text/reasoning part indices for delta updates\n currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nasync function parseErrorResponse(response: Response): Promise<string> {\n try {\n const data = (await response.json()) as { error?: string; message?: string };\n return data.error ?? data.message ?? `Request failed: ${response.status}`;\n } catch {\n return `Request failed: ${response.status}`;\n }\n}\n\nfunction createUserMessage(input: UserMessageInput): UIMessage {\n return {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: input.content, status: 'done' }],\n status: 'done',\n createdAt: new Date(),\n };\n}\n\nfunction createEmptyStreamingState(): StreamingState {\n return {\n messageId: generateId(),\n parts: [],\n activeBlock: null,\n blocks: new Map(),\n currentTextPartIndex: null,\n currentReasoningPartIndex: null,\n };\n}\n\nfunction buildMessageFromState(state: StreamingState, status: 'streaming' | 'done'): UIMessage {\n return {\n id: state.messageId,\n role: 'assistant',\n parts: [...state.parts],\n status,\n createdAt: new Date(),\n };\n}\n\n// =============================================================================\n// OctavusChat Class\n// =============================================================================\n\n/**\n * Framework-agnostic chat client for Octavus agents.\n * Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.\n *\n * @example\n * ```typescript\n * const chat = new OctavusChat({\n * onTrigger: (triggerName, input) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * });\n *\n * // Subscribe to updates\n * const unsubscribe = chat.subscribe(() => {\n * console.log('Messages:', chat.messages);\n * console.log('Status:', chat.status);\n * });\n *\n * // Send a message\n * await chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });\n *\n * // Cleanup\n * unsubscribe();\n * ```\n */\nexport class OctavusChat {\n // Private state\n private _messages: UIMessage[];\n private _status: ChatStatus = 'idle';\n private _error: Error | null = null;\n private options: OctavusChatOptions;\n private abortController: AbortController | null = null;\n private streamingState: StreamingState | null = null;\n\n // Listener sets for reactive frameworks\n private listeners = new Set<Listener>();\n\n constructor(options: OctavusChatOptions) {\n this.options = options;\n this._messages = options.initialMessages ?? [];\n }\n\n // =========================================================================\n // Public Getters\n // =========================================================================\n\n get messages(): UIMessage[] {\n return this._messages;\n }\n\n get status(): ChatStatus {\n return this._status;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n // =========================================================================\n // Subscription Methods (for reactive frameworks)\n // =========================================================================\n\n /**\n * Subscribe to state changes. The callback is called whenever messages, status, or error changes.\n * @returns Unsubscribe function\n */\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((l) => l());\n }\n\n // =========================================================================\n // Private Setters (notify listeners)\n // =========================================================================\n\n private setMessages(messages: UIMessage[]): void {\n this._messages = messages;\n this.notifyListeners();\n }\n\n private setStatus(status: ChatStatus): void {\n this._status = status;\n this.notifyListeners();\n }\n\n private setError(error: Error | null): void {\n this._error = error;\n this.notifyListeners();\n }\n\n // =========================================================================\n // Public Methods\n // =========================================================================\n\n /**\n * Trigger the agent and optionally add a user message to the chat.\n *\n * @param triggerName - The trigger name defined in the agent's protocol.yaml\n * @param input - Input parameters for the trigger (variable substitutions)\n * @param options.userMessage - If provided, adds a user message to the chat before triggering\n */\n async send(\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ): Promise<void> {\n // Abort any previous request\n this.abortController?.abort();\n\n const abortController = new AbortController();\n this.abortController = abortController;\n\n // Add user message if provided\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage);\n this.setMessages([...this._messages, userMsg]);\n }\n\n // Reset state\n this.setStatus('streaming');\n this.setError(null);\n this.streamingState = createEmptyStreamingState();\n\n try {\n const response = await this.options.onTrigger(triggerName, input);\n\n if (!response.ok) {\n const errorMessage = await parseErrorResponse(response);\n throw new Error(errorMessage);\n }\n\n for await (const event of parseSSEStream(response)) {\n if (abortController.signal.aborted) {\n break;\n }\n\n const state: StreamingState | null = this.streamingState;\n if (!state) break;\n\n switch (event.type) {\n case 'start':\n break;\n\n case 'block-start': {\n const block: BlockState = {\n blockId: event.blockId,\n blockName: event.blockName,\n blockType: event.blockType,\n display: event.display,\n description: event.description,\n outputToChat: event.outputToChat ?? true,\n thread: event.thread,\n reasoning: '',\n text: '',\n toolCalls: new Map(),\n };\n state.blocks.set(event.blockId, block);\n state.activeBlock = block;\n\n // Create operation part for internal operation blocks (if not hidden)\n const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);\n const isHidden = event.display === 'hidden';\n if (isOperation && !isHidden) {\n const thread = event.thread;\n const operationPart: UIOperationPart = {\n type: 'operation',\n operationId: event.blockId,\n name: event.description || event.blockName,\n operationType: event.blockType,\n status: 'running',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(operationPart);\n }\n\n state.currentTextPartIndex = null;\n state.currentReasoningPartIndex = null;\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'block-end': {\n // Mark operation part as done if it exists\n const operationPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'operation' && p.operationId === event.blockId,\n );\n if (operationPartIndex >= 0) {\n const part = state.parts[operationPartIndex] as UIOperationPart;\n state.parts[operationPartIndex] = { ...part, status: 'done' };\n }\n\n if (state.activeBlock?.blockId === event.blockId) {\n state.activeBlock = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-start': {\n const thread = state.activeBlock?.thread;\n const reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(reasoningPart);\n state.currentReasoningPartIndex = state.parts.length - 1;\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-delta': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.text += event.delta;\n state.parts[state.currentReasoningPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.reasoning += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-end': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.status = 'done';\n state.parts[state.currentReasoningPartIndex] = { ...part };\n state.currentReasoningPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-start': {\n const thread = state.activeBlock?.thread;\n const isOtherThread = Boolean(thread && thread !== 'main');\n const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread;\n\n if (shouldAddPart) {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread: isOtherThread ? thread : undefined,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.text += event.delta;\n state.parts[state.currentTextPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.text += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-end': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.status = 'done';\n state.parts[state.currentTextPartIndex] = { ...part };\n state.currentTextPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-start': {\n const thread = state.activeBlock?.thread;\n const toolPart: UIToolCallPart = {\n type: 'tool-call',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n displayName: event.title,\n args: {},\n status: 'pending',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(toolPart);\n\n if (state.activeBlock) {\n state.activeBlock.toolCalls.set(event.toolCallId, toolPart);\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-delta': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n try {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = JSON.parse(event.inputTextDelta) as Record<string, unknown>;\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n } catch {\n // Partial JSON, ignore\n }\n }\n break;\n }\n\n case 'tool-input-end':\n break;\n\n case 'tool-input-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = event.input as Record<string, unknown>;\n part.status = 'running';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.result = event.output;\n part.status = 'done';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-error': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.error = event.errorText;\n part.status = 'error';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'resource-update':\n this.options.onResourceUpdate?.(event.name, event.value);\n break;\n\n case 'finish': {\n const finalMessage = buildMessageFromState(state, 'done');\n\n finalMessage.parts = finalMessage.parts.map((part) => {\n if (part.type === 'text' || part.type === 'reasoning') {\n return { ...part, status: 'done' as const };\n }\n return part;\n });\n\n if (finalMessage.parts.length > 0) {\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.setStatus('idle');\n this.streamingState = null;\n this.options.onFinish?.();\n break;\n }\n\n case 'error':\n throw new Error(event.errorText);\n\n case 'tool-request':\n // Handled by server-sdk\n break;\n }\n }\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error('Unknown error');\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n } finally {\n this.abortController = null;\n }\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n this.abortController?.abort();\n this.abortController = null;\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n const finalMessage = buildMessageFromState(state, 'done');\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.streamingState = null;\n this.setStatus('idle');\n }\n\n // =========================================================================\n // Private Helpers\n // =========================================================================\n\n private updateStreamingMessage(): void {\n const state = this.streamingState;\n if (!state) return;\n\n const msg = buildMessageFromState(state, 'streaming');\n const messages = [...this._messages];\n\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = msg;\n } else {\n messages.push(msg);\n }\n\n this.setMessages(messages);\n }\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events\n */\nexport async function* parseSSEStream(\n response: Response,\n): AsyncGenerator<StreamEvent, void, unknown> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Response body is not readable');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n let reading = true;\n while (reading) {\n const { done, value } = await reader.read();\n\n if (done) {\n reading = false;\n continue;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ') && line !== 'data: [DONE]') {\n try {\n const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));\n if (parsed.success) {\n yield parsed.data;\n }\n // Skip malformed events silently\n } catch {\n // Skip malformed JSON - no logging in production\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { UIMessagePart } from '@octavus/core';\n\n/** Non-main thread content (e.g., \"summary\") is typically displayed differently. */\nexport function isOtherThread(part: UIMessagePart): boolean {\n return Boolean(part.thread && part.thread !== 'main');\n}\n"],"mappings":"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","names":["isOtherThread"]}
|
|
1
|
+
{"version":3,"sources":["../src/chat.ts","../src/stream/reader.ts","../src/utils/message-helpers.ts","../src/transports/http.ts","../src/transports/socket.ts"],"sourcesContent":["import {\n generateId,\n type UIMessage,\n type UIMessagePart,\n type UITextPart,\n type UIReasoningPart,\n type UIToolCallPart,\n type UIOperationPart,\n type DisplayMode,\n type StreamEvent,\n} from '@octavus/core';\nimport type { Transport } from './transports/types';\n\n/** Block types that are internal operations (not LLM-driven) */\nconst OPERATION_BLOCK_TYPES = new Set(['set-resource', 'serialize-thread']);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport type ChatStatus = 'idle' | 'streaming' | 'error';\n\n/**\n * Input for creating a user message.\n * Currently supports text content. Will be extended to support attachments, images, etc.\n */\nexport interface UserMessageInput {\n /** Text content of the message */\n content: string;\n}\n\nexport interface OctavusChatOptions {\n /**\n * Transport for streaming events.\n * Use `createHttpTransport` for HTTP/SSE or `createSocketTransport` for WebSocket/SockJS.\n */\n transport: Transport;\n\n /** Initial messages (for session refresh) */\n initialMessages?: UIMessage[];\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n /** Callback when streaming finishes */\n onFinish?: () => void;\n /** Callback when a resource is updated */\n onResourceUpdate?: (name: string, value: unknown) => void;\n}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\ninterface BlockState {\n blockId: string;\n blockName: string;\n blockType: string;\n display: DisplayMode;\n description?: string;\n outputToChat: boolean;\n thread?: string;\n reasoning: string;\n text: string;\n toolCalls: Map<string, UIToolCallPart>;\n}\n\ninterface StreamingState {\n messageId: string;\n parts: UIMessagePart[];\n activeBlock: BlockState | null;\n blocks: Map<string, BlockState>;\n // Track current text/reasoning part indices for delta updates\n currentTextPartIndex: number | null;\n currentReasoningPartIndex: number | null;\n}\n\ntype Listener = () => void;\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction createUserMessage(input: UserMessageInput): UIMessage {\n return {\n id: generateId(),\n role: 'user',\n parts: [{ type: 'text', text: input.content, status: 'done' }],\n status: 'done',\n createdAt: new Date(),\n };\n}\n\nfunction createEmptyStreamingState(): StreamingState {\n return {\n messageId: generateId(),\n parts: [],\n activeBlock: null,\n blocks: new Map(),\n currentTextPartIndex: null,\n currentReasoningPartIndex: null,\n };\n}\n\nfunction buildMessageFromState(state: StreamingState, status: 'streaming' | 'done'): UIMessage {\n return {\n id: state.messageId,\n role: 'assistant',\n parts: [...state.parts],\n status,\n createdAt: new Date(),\n };\n}\n\n// =============================================================================\n// OctavusChat Class\n// =============================================================================\n\n/**\n * Framework-agnostic chat client for Octavus agents.\n * Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.\n *\n * @example HTTP transport (Next.js, etc.)\n * ```typescript\n * import { OctavusChat, createHttpTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/trigger', {\n * method: 'POST',\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * }),\n * });\n * ```\n *\n * @example Socket transport (WebSocket, SockJS, Meteor)\n * ```typescript\n * import { OctavusChat, createSocketTransport } from '@octavus/client-sdk';\n *\n * const chat = new OctavusChat({\n * transport: createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * }),\n * });\n * ```\n */\nexport class OctavusChat {\n // Private state\n private _messages: UIMessage[];\n private _status: ChatStatus = 'idle';\n private _error: Error | null = null;\n private options: OctavusChatOptions;\n private transport: Transport;\n private streamingState: StreamingState | null = null;\n\n // Listener sets for reactive frameworks\n private listeners = new Set<Listener>();\n\n constructor(options: OctavusChatOptions) {\n this.options = options;\n this._messages = options.initialMessages ?? [];\n this.transport = options.transport;\n }\n\n // =========================================================================\n // Public Getters\n // =========================================================================\n\n get messages(): UIMessage[] {\n return this._messages;\n }\n\n get status(): ChatStatus {\n return this._status;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n // =========================================================================\n // Subscription Methods (for reactive frameworks)\n // =========================================================================\n\n /**\n * Subscribe to state changes. The callback is called whenever messages, status, or error changes.\n * @returns Unsubscribe function\n */\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n private notifyListeners(): void {\n this.listeners.forEach((l) => l());\n }\n\n // =========================================================================\n // Private Setters (notify listeners)\n // =========================================================================\n\n private setMessages(messages: UIMessage[]): void {\n this._messages = messages;\n this.notifyListeners();\n }\n\n private setStatus(status: ChatStatus): void {\n this._status = status;\n this.notifyListeners();\n }\n\n private setError(error: Error | null): void {\n this._error = error;\n this.notifyListeners();\n }\n\n // =========================================================================\n // Public Methods\n // =========================================================================\n\n /**\n * Trigger the agent and optionally add a user message to the chat.\n *\n * @param triggerName - The trigger name defined in the agent's protocol.yaml\n * @param input - Input parameters for the trigger (variable substitutions)\n * @param options.userMessage - If provided, adds a user message to the chat before triggering\n */\n async send(\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ): Promise<void> {\n this.transport.stop();\n\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage);\n this.setMessages([...this._messages, userMsg]);\n }\n\n this.setStatus('streaming');\n this.setError(null);\n this.streamingState = createEmptyStreamingState();\n\n try {\n for await (const event of this.transport.trigger(triggerName, input)) {\n if (this.streamingState === null) break;\n\n this.handleStreamEvent(event, this.streamingState);\n }\n } catch (err) {\n const errorObj = err instanceof Error ? err : new Error('Unknown error');\n this.setError(errorObj);\n this.setStatus('error');\n this.streamingState = null;\n this.options.onError?.(errorObj);\n }\n }\n\n /** Stop the current streaming and finalize any partial message */\n stop(): void {\n this.transport.stop();\n\n const state = this.streamingState;\n if (state && state.parts.length > 0) {\n const finalMessage = buildMessageFromState(state, 'done');\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.streamingState = null;\n this.setStatus('idle');\n }\n\n // =========================================================================\n // Private Helpers\n // =========================================================================\n\n private handleStreamEvent(event: StreamEvent, state: StreamingState): void {\n switch (event.type) {\n case 'start':\n break;\n\n case 'block-start': {\n const block: BlockState = {\n blockId: event.blockId,\n blockName: event.blockName,\n blockType: event.blockType,\n display: event.display,\n description: event.description,\n outputToChat: event.outputToChat ?? true,\n thread: event.thread,\n reasoning: '',\n text: '',\n toolCalls: new Map(),\n };\n state.blocks.set(event.blockId, block);\n state.activeBlock = block;\n\n const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);\n const isHidden = event.display === 'hidden';\n if (isOperation && !isHidden) {\n const thread = event.thread;\n const operationPart: UIOperationPart = {\n type: 'operation',\n operationId: event.blockId,\n name: event.description ?? event.blockName,\n operationType: event.blockType,\n status: 'running',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(operationPart);\n }\n\n state.currentTextPartIndex = null;\n state.currentReasoningPartIndex = null;\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'block-end': {\n const operationPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'operation' && p.operationId === event.blockId,\n );\n if (operationPartIndex >= 0) {\n const part = state.parts[operationPartIndex] as UIOperationPart;\n state.parts[operationPartIndex] = { ...part, status: 'done' };\n }\n\n if (state.activeBlock?.blockId === event.blockId) {\n state.activeBlock = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-start': {\n const thread = state.activeBlock?.thread;\n const reasoningPart: UIReasoningPart = {\n type: 'reasoning',\n text: '',\n status: 'streaming',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(reasoningPart);\n state.currentReasoningPartIndex = state.parts.length - 1;\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-delta': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.text += event.delta;\n state.parts[state.currentReasoningPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.reasoning += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'reasoning-end': {\n if (state.currentReasoningPartIndex !== null) {\n const part = state.parts[state.currentReasoningPartIndex] as UIReasoningPart;\n part.status = 'done';\n state.parts[state.currentReasoningPartIndex] = { ...part };\n state.currentReasoningPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-start': {\n const thread = state.activeBlock?.thread;\n const isOtherThread = Boolean(thread && thread !== 'main');\n const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread;\n\n if (shouldAddPart) {\n const textPart: UITextPart = {\n type: 'text',\n text: '',\n status: 'streaming',\n thread: isOtherThread ? thread : undefined,\n };\n state.parts.push(textPart);\n state.currentTextPartIndex = state.parts.length - 1;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-delta': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.text += event.delta;\n state.parts[state.currentTextPartIndex] = { ...part };\n }\n\n if (state.activeBlock) {\n state.activeBlock.text += event.delta;\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'text-end': {\n if (state.currentTextPartIndex !== null) {\n const part = state.parts[state.currentTextPartIndex] as UITextPart;\n part.status = 'done';\n state.parts[state.currentTextPartIndex] = { ...part };\n state.currentTextPartIndex = null;\n }\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-start': {\n const thread = state.activeBlock?.thread;\n const toolPart: UIToolCallPart = {\n type: 'tool-call',\n toolCallId: event.toolCallId,\n toolName: event.toolName,\n displayName: event.title,\n args: {},\n status: 'pending',\n thread: thread && thread !== 'main' ? thread : undefined,\n };\n state.parts.push(toolPart);\n\n if (state.activeBlock) {\n state.activeBlock.toolCalls.set(event.toolCallId, toolPart);\n }\n\n this.updateStreamingMessage();\n break;\n }\n\n case 'tool-input-delta': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n try {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = JSON.parse(event.inputTextDelta) as Record<string, unknown>;\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n } catch {\n // Partial JSON, ignore\n }\n }\n break;\n }\n\n case 'tool-input-end':\n // Input streaming ended, wait for tool-input-available\n break;\n\n case 'tool-input-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.args = event.input as Record<string, unknown>;\n part.status = 'running';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-available': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.result = event.output;\n part.status = 'done';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'tool-output-error': {\n const toolPartIndex = state.parts.findIndex(\n (p: UIMessagePart) => p.type === 'tool-call' && p.toolCallId === event.toolCallId,\n );\n if (toolPartIndex >= 0) {\n const part = state.parts[toolPartIndex] as UIToolCallPart;\n part.error = event.errorText;\n part.status = 'error';\n state.parts[toolPartIndex] = { ...part };\n this.updateStreamingMessage();\n }\n break;\n }\n\n case 'resource-update':\n this.options.onResourceUpdate?.(event.name, event.value);\n break;\n\n case 'finish': {\n const finalMessage = buildMessageFromState(state, 'done');\n\n finalMessage.parts = finalMessage.parts.map((part) => {\n if (part.type === 'text' || part.type === 'reasoning') {\n return { ...part, status: 'done' as const };\n }\n return part;\n });\n\n if (finalMessage.parts.length > 0) {\n const messages = [...this._messages];\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = finalMessage;\n } else {\n messages.push(finalMessage);\n }\n this.setMessages(messages);\n }\n\n this.setStatus('idle');\n this.streamingState = null;\n this.options.onFinish?.();\n break;\n }\n\n case 'error':\n throw new Error(event.errorText);\n\n case 'tool-request':\n // Handled by server-sdk, not relevant for UI\n break;\n }\n }\n\n private updateStreamingMessage(): void {\n const state = this.streamingState;\n if (!state) return;\n\n const msg = buildMessageFromState(state, 'streaming');\n const messages = [...this._messages];\n\n const lastMsg = messages[messages.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n messages[messages.length - 1] = msg;\n } else {\n messages.push(msg);\n }\n\n this.setMessages(messages);\n }\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\n\n/**\n * Parse SSE stream events\n */\nexport async function* parseSSEStream(\n response: Response,\n): AsyncGenerator<StreamEvent, void, unknown> {\n const reader = response.body?.getReader();\n if (!reader) {\n throw new Error('Response body is not readable');\n }\n\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n let reading = true;\n while (reading) {\n const { done, value } = await reader.read();\n\n if (done) {\n reading = false;\n continue;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ') && line !== 'data: [DONE]') {\n try {\n const parsed = safeParseStreamEvent(JSON.parse(line.slice(6)));\n if (parsed.success) {\n yield parsed.data;\n }\n // Skip malformed events silently\n } catch {\n // Skip malformed JSON - no logging in production\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import type { UIMessagePart } from '@octavus/core';\n\n/** Non-main thread content (e.g., \"summary\") is typically displayed differently. */\nexport function isOtherThread(part: UIMessagePart): boolean {\n return Boolean(part.thread && part.thread !== 'main');\n}\n","import { parseSSEStream } from '@/stream/reader';\nimport type { Transport } from './types';\n\n/**\n * Options for creating an HTTP transport.\n */\nexport interface HttpTransportOptions {\n /**\n * Function to make the trigger request.\n * Called each time `send()` is invoked on the chat.\n *\n * @param triggerName - The trigger name (e.g., 'user-message')\n * @param input - Input parameters for the trigger\n * @returns Response with SSE stream body\n */\n triggerRequest: (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;\n}\n\n/**\n * Create an HTTP transport using native fetch() and SSE parsing.\n * This is the default transport for Next.js and other HTTP-based applications.\n *\n * @example\n * ```typescript\n * const transport = createHttpTransport({\n * triggerRequest: (triggerName, input) =>\n * fetch('/api/octavus', {\n * method: 'POST',\n * headers: { 'Content-Type': 'application/json' },\n * body: JSON.stringify({ sessionId, triggerName, input }),\n * }),\n * });\n * ```\n */\nexport function createHttpTransport(options: HttpTransportOptions): Transport {\n let abortController: AbortController | null = null;\n\n return {\n async *trigger(triggerName, input) {\n abortController = new AbortController();\n\n const response = await options.triggerRequest(triggerName, input);\n\n if (!response.ok) {\n const errorText = await response.text().catch(() => `Request failed: ${response.status}`);\n throw new Error(errorText);\n }\n\n if (!response.body) {\n throw new Error('Response body is empty');\n }\n\n for await (const event of parseSSEStream(response)) {\n if (abortController.signal.aborted) {\n break;\n }\n yield event;\n }\n },\n\n stop() {\n abortController?.abort();\n abortController = null;\n },\n };\n}\n","import { safeParseStreamEvent, type StreamEvent } from '@octavus/core';\nimport type { Transport } from './types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Socket interface compatible with both WebSocket and SockJS.\n *\n * Uses MessageEvent for the message handler, which both WebSocket and SockJS support.\n * The `| undefined` union accommodates SockJS's optional property typing.\n */\nexport interface SocketLike {\n send(data: string): void;\n close(): void;\n readyState: number;\n onmessage: ((event: MessageEvent) => void) | null | undefined;\n onclose: ((event: CloseEvent) => void) | null | undefined;\n}\n\n/** WebSocket readyState constants */\nconst SOCKET_OPEN = 1;\n\n// =============================================================================\n// Transport Options\n// =============================================================================\n\n/**\n * Options for creating a socket transport.\n */\nexport interface SocketTransportOptions {\n /**\n * Function to create and connect the socket.\n * Works directly with WebSocket and SockJS - no wrappers needed.\n *\n * @example Native WebSocket\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket('wss://api.example.com/stream?sessionId=xxx');\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n *\n * @example SockJS\n * ```typescript\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/chat-service');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * })\n * ```\n */\n connect: () => Promise<SocketLike>;\n\n /**\n * Called for every message received (parsed as JSON).\n * Use this to handle custom (non-Octavus) events.\n * Octavus StreamEvents are handled automatically by the transport.\n *\n * @example\n * ```typescript\n * onMessage: (data) => {\n * const msg = data as { type: string };\n * if (msg.type === 'typing-indicator') {\n * setIsTyping(true);\n * }\n * if (msg.type === 'presence-update') {\n * updatePresence(msg.users);\n * }\n * }\n * ```\n */\n onMessage?: (data: unknown) => void;\n\n /**\n * Called when the socket connection closes.\n * Use this for cleanup or reconnection logic.\n */\n onClose?: () => void;\n}\n\n/**\n * Create a socket transport that works with any WebSocket-like connection.\n * Supports native WebSocket, SockJS, or any compatible socket implementation.\n *\n * The server should send StreamEvent format (same as SSE) over the socket.\n * Unknown events are safely ignored using Zod validation.\n *\n * @example Basic usage with WebSocket\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const ws = new WebSocket(`wss://api.octavus.ai/stream?sessionId=${sessionId}`);\n * ws.onopen = () => resolve(ws);\n * ws.onerror = () => reject(new Error('Connection failed'));\n * }),\n * });\n * ```\n *\n * @example With SockJS and custom events\n * ```typescript\n * const transport = createSocketTransport({\n * connect: () => new Promise((resolve, reject) => {\n * const sock = new SockJS('/octavus-stream');\n * sock.onopen = () => resolve(sock);\n * sock.onerror = () => reject(new Error('Connection failed'));\n * }),\n * onMessage: (data) => {\n * const msg = data as { type: string };\n * if (msg.type === 'typing-indicator') {\n * setIsTyping(msg.isTyping);\n * }\n * },\n * onClose: () => {\n * console.log('Socket closed, attempting reconnect...');\n * },\n * });\n * ```\n */\nexport function createSocketTransport(options: SocketTransportOptions): Transport {\n let socket: SocketLike | null = null;\n let eventQueue: StreamEvent[] = [];\n let eventResolver: ((event: StreamEvent | null) => void) | null = null;\n let isStreaming = false;\n\n function enqueueEvent(event: StreamEvent) {\n if (eventResolver) {\n eventResolver(event);\n eventResolver = null;\n } else {\n eventQueue.push(event);\n }\n }\n\n function nextEvent(): Promise<StreamEvent | null> {\n if (eventQueue.length > 0) {\n return Promise.resolve(eventQueue.shift()!);\n }\n if (!isStreaming) {\n return Promise.resolve(null);\n }\n return new Promise((resolve) => {\n eventResolver = resolve;\n });\n }\n\n async function connect(): Promise<SocketLike> {\n const sock = await options.connect();\n\n sock.onmessage = (e: MessageEvent) => {\n try {\n const data: unknown = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;\n\n options.onMessage?.(data);\n\n const result = safeParseStreamEvent(data);\n if (result.success) {\n const event = result.data;\n enqueueEvent(event);\n\n if (event.type === 'finish' || event.type === 'error') {\n isStreaming = false;\n }\n }\n } catch {\n // Malformed JSON, skip\n }\n };\n\n sock.onclose = () => {\n options.onClose?.();\n\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n };\n\n return sock;\n }\n\n return {\n async *trigger(triggerName, input) {\n if (socket?.readyState !== SOCKET_OPEN) {\n socket = await connect();\n }\n\n eventQueue = [];\n isStreaming = true;\n\n socket.send(\n JSON.stringify({\n type: 'trigger',\n triggerName,\n input,\n }),\n );\n\n while (true) {\n const event = await nextEvent();\n if (event === null) break;\n yield event;\n if (event.type === 'finish' || event.type === 'error') break;\n }\n },\n\n stop() {\n if (socket?.readyState === SOCKET_OPEN) {\n socket.send(JSON.stringify({ type: 'stop' }));\n }\n isStreaming = false;\n if (eventResolver) {\n eventResolver(null);\n eventResolver = null;\n }\n },\n };\n}\n"],"mappings":"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","names":["isOtherThread","safeParseStreamEvent"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octavus/client-sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Framework-agnostic client SDK for Octavus agents",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"author": "Octavus AI <
|
|
6
|
+
"author": "Octavus AI <dev@octavus.ai>",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"octavus",
|
|
9
9
|
"ai",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"access": "public"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@octavus/core": "^0.0.
|
|
31
|
+
"@octavus/core": "^0.0.7"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"tsup": "^8.3.5",
|