@octavus/client-sdk 0.0.5 → 0.0.6
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 +61 -14
- package/dist/index.js +341 -286
- package/dist/index.js.map +1 -1
- package/package.json +3 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { UIMessage, UIMessagePart } from '@octavus/core';
|
|
1
|
+
import { UIMessage, StreamEvent, UIMessagePart } from '@octavus/core';
|
|
2
2
|
export * from '@octavus/core';
|
|
3
3
|
|
|
4
4
|
type ChatStatus = 'idle' | 'streaming' | 'error';
|
|
@@ -11,7 +11,7 @@ interface UserMessageInput {
|
|
|
11
11
|
/** Text content of the message */
|
|
12
12
|
content: string;
|
|
13
13
|
}
|
|
14
|
-
interface
|
|
14
|
+
interface OctavusChatOptions {
|
|
15
15
|
/** Function to make the API call to trigger the agent */
|
|
16
16
|
onTrigger: TriggerFunction;
|
|
17
17
|
/** Initial messages (for session refresh) */
|
|
@@ -23,13 +23,55 @@ interface UseOctavusChatOptions {
|
|
|
23
23
|
/** Callback when a resource is updated */
|
|
24
24
|
onResourceUpdate?: (name: string, value: unknown) => void;
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
type Listener = () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Framework-agnostic chat client for Octavus agents.
|
|
29
|
+
* Manages chat state and streaming, allowing reactive frameworks to subscribe to updates.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const chat = new OctavusChat({
|
|
34
|
+
* onTrigger: (triggerName, input) =>
|
|
35
|
+
* fetch('/api/trigger', {
|
|
36
|
+
* method: 'POST',
|
|
37
|
+
* body: JSON.stringify({ sessionId, triggerName, input }),
|
|
38
|
+
* }),
|
|
39
|
+
* });
|
|
40
|
+
*
|
|
41
|
+
* // Subscribe to updates
|
|
42
|
+
* const unsubscribe = chat.subscribe(() => {
|
|
43
|
+
* console.log('Messages:', chat.messages);
|
|
44
|
+
* console.log('Status:', chat.status);
|
|
45
|
+
* });
|
|
46
|
+
*
|
|
47
|
+
* // Send a message
|
|
48
|
+
* await chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });
|
|
49
|
+
*
|
|
50
|
+
* // Cleanup
|
|
51
|
+
* unsubscribe();
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
declare class OctavusChat {
|
|
55
|
+
private _messages;
|
|
56
|
+
private _status;
|
|
57
|
+
private _error;
|
|
58
|
+
private options;
|
|
59
|
+
private abortController;
|
|
60
|
+
private streamingState;
|
|
61
|
+
private listeners;
|
|
62
|
+
constructor(options: OctavusChatOptions);
|
|
63
|
+
get messages(): UIMessage[];
|
|
64
|
+
get status(): ChatStatus;
|
|
65
|
+
get error(): Error | null;
|
|
66
|
+
/**
|
|
67
|
+
* Subscribe to state changes. The callback is called whenever messages, status, or error changes.
|
|
68
|
+
* @returns Unsubscribe function
|
|
69
|
+
*/
|
|
70
|
+
subscribe(listener: Listener): () => void;
|
|
71
|
+
private notifyListeners;
|
|
72
|
+
private setMessages;
|
|
73
|
+
private setStatus;
|
|
74
|
+
private setError;
|
|
33
75
|
/**
|
|
34
76
|
* Trigger the agent and optionally add a user message to the chat.
|
|
35
77
|
*
|
|
@@ -37,15 +79,20 @@ interface UseOctavusChatReturn {
|
|
|
37
79
|
* @param input - Input parameters for the trigger (variable substitutions)
|
|
38
80
|
* @param options.userMessage - If provided, adds a user message to the chat before triggering
|
|
39
81
|
*/
|
|
40
|
-
send
|
|
82
|
+
send(triggerName: string, input?: Record<string, unknown>, sendOptions?: {
|
|
41
83
|
userMessage?: UserMessageInput;
|
|
42
|
-
})
|
|
84
|
+
}): Promise<void>;
|
|
43
85
|
/** Stop the current streaming and finalize any partial message */
|
|
44
|
-
stop
|
|
86
|
+
stop(): void;
|
|
87
|
+
private updateStreamingMessage;
|
|
45
88
|
}
|
|
46
|
-
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Parse SSE stream events
|
|
92
|
+
*/
|
|
93
|
+
declare function parseSSEStream(response: Response): AsyncGenerator<StreamEvent, void, unknown>;
|
|
47
94
|
|
|
48
95
|
/** Non-main thread content (e.g., "summary") is typically displayed differently. */
|
|
49
96
|
declare function isOtherThread(part: UIMessagePart): boolean;
|
|
50
97
|
|
|
51
|
-
export { type ChatStatus,
|
|
98
|
+
export { type ChatStatus, OctavusChat, type OctavusChatOptions, type TriggerFunction, type UserMessageInput, isOtherThread, parseSSEStream };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import { useState, useCallback, useRef } from "react";
|
|
1
|
+
// src/chat.ts
|
|
3
2
|
import {
|
|
4
3
|
generateId
|
|
5
4
|
} from "@octavus/core";
|
|
@@ -41,7 +40,7 @@ async function* parseSSEStream(response) {
|
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
// src/
|
|
43
|
+
// src/chat.ts
|
|
45
44
|
var OPERATION_BLOCK_TYPES = /* @__PURE__ */ new Set(["set-resource", "serialize-thread"]);
|
|
46
45
|
async function parseErrorResponse(response) {
|
|
47
46
|
try {
|
|
@@ -79,323 +78,379 @@ function buildMessageFromState(state, status) {
|
|
|
79
78
|
createdAt: /* @__PURE__ */ new Date()
|
|
80
79
|
};
|
|
81
80
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
var OctavusChat = class {
|
|
82
|
+
// Private state
|
|
83
|
+
_messages;
|
|
84
|
+
_status = "idle";
|
|
85
|
+
_error = null;
|
|
86
|
+
options;
|
|
87
|
+
abortController = null;
|
|
88
|
+
streamingState = null;
|
|
89
|
+
// Listener sets for reactive frameworks
|
|
90
|
+
listeners = /* @__PURE__ */ new Set();
|
|
91
|
+
constructor(options) {
|
|
92
|
+
this.options = options;
|
|
93
|
+
this._messages = options.initialMessages ?? [];
|
|
94
|
+
}
|
|
95
|
+
// =========================================================================
|
|
96
|
+
// Public Getters
|
|
97
|
+
// =========================================================================
|
|
98
|
+
get messages() {
|
|
99
|
+
return this._messages;
|
|
100
|
+
}
|
|
101
|
+
get status() {
|
|
102
|
+
return this._status;
|
|
103
|
+
}
|
|
104
|
+
get error() {
|
|
105
|
+
return this._error;
|
|
106
|
+
}
|
|
107
|
+
// =========================================================================
|
|
108
|
+
// Subscription Methods (for reactive frameworks)
|
|
109
|
+
// =========================================================================
|
|
110
|
+
/**
|
|
111
|
+
* Subscribe to state changes. The callback is called whenever messages, status, or error changes.
|
|
112
|
+
* @returns Unsubscribe function
|
|
113
|
+
*/
|
|
114
|
+
subscribe(listener) {
|
|
115
|
+
this.listeners.add(listener);
|
|
116
|
+
return () => this.listeners.delete(listener);
|
|
117
|
+
}
|
|
118
|
+
notifyListeners() {
|
|
119
|
+
this.listeners.forEach((l) => l());
|
|
120
|
+
}
|
|
121
|
+
// =========================================================================
|
|
122
|
+
// Private Setters (notify listeners)
|
|
123
|
+
// =========================================================================
|
|
124
|
+
setMessages(messages) {
|
|
125
|
+
this._messages = messages;
|
|
126
|
+
this.notifyListeners();
|
|
127
|
+
}
|
|
128
|
+
setStatus(status) {
|
|
129
|
+
this._status = status;
|
|
130
|
+
this.notifyListeners();
|
|
131
|
+
}
|
|
132
|
+
setError(error) {
|
|
133
|
+
this._error = error;
|
|
134
|
+
this.notifyListeners();
|
|
135
|
+
}
|
|
136
|
+
// =========================================================================
|
|
137
|
+
// Public Methods
|
|
138
|
+
// =========================================================================
|
|
139
|
+
/**
|
|
140
|
+
* Trigger the agent and optionally add a user message to the chat.
|
|
141
|
+
*
|
|
142
|
+
* @param triggerName - The trigger name defined in the agent's protocol.yaml
|
|
143
|
+
* @param input - Input parameters for the trigger (variable substitutions)
|
|
144
|
+
* @param options.userMessage - If provided, adds a user message to the chat before triggering
|
|
145
|
+
*/
|
|
146
|
+
async send(triggerName, input, sendOptions) {
|
|
147
|
+
this.abortController?.abort();
|
|
148
|
+
const abortController = new AbortController();
|
|
149
|
+
this.abortController = abortController;
|
|
150
|
+
if (sendOptions?.userMessage !== void 0) {
|
|
151
|
+
const userMsg = createUserMessage(sendOptions.userMessage);
|
|
152
|
+
this.setMessages([...this._messages, userMsg]);
|
|
153
|
+
}
|
|
154
|
+
this.setStatus("streaming");
|
|
155
|
+
this.setError(null);
|
|
156
|
+
this.streamingState = createEmptyStreamingState();
|
|
157
|
+
try {
|
|
158
|
+
const response = await this.options.onTrigger(triggerName, input);
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
const errorMessage = await parseErrorResponse(response);
|
|
161
|
+
throw new Error(errorMessage);
|
|
109
162
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
const response = await onTrigger(triggerName, input);
|
|
115
|
-
if (!response.ok) {
|
|
116
|
-
const errorMessage = await parseErrorResponse(response);
|
|
117
|
-
throw new Error(errorMessage);
|
|
163
|
+
for await (const event of parseSSEStream(response)) {
|
|
164
|
+
if (abortController.signal.aborted) {
|
|
165
|
+
break;
|
|
118
166
|
}
|
|
119
|
-
|
|
120
|
-
|
|
167
|
+
const state = this.streamingState;
|
|
168
|
+
if (!state) break;
|
|
169
|
+
switch (event.type) {
|
|
170
|
+
case "start":
|
|
121
171
|
break;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const operationPart = {
|
|
148
|
-
type: "operation",
|
|
149
|
-
operationId: event.blockId,
|
|
150
|
-
name: event.description || event.blockName,
|
|
151
|
-
operationType: event.blockType,
|
|
152
|
-
status: "running",
|
|
153
|
-
thread: thread && thread !== "main" ? thread : void 0
|
|
154
|
-
};
|
|
155
|
-
state.parts.push(operationPart);
|
|
156
|
-
}
|
|
157
|
-
state.currentTextPartIndex = null;
|
|
158
|
-
state.currentReasoningPartIndex = null;
|
|
159
|
-
updateStreamingMessage();
|
|
160
|
-
break;
|
|
161
|
-
}
|
|
162
|
-
case "block-end": {
|
|
163
|
-
const operationPartIndex = state.parts.findIndex(
|
|
164
|
-
(p) => p.type === "operation" && p.operationId === event.blockId
|
|
165
|
-
);
|
|
166
|
-
if (operationPartIndex >= 0) {
|
|
167
|
-
const part = state.parts[operationPartIndex];
|
|
168
|
-
state.parts[operationPartIndex] = { ...part, status: "done" };
|
|
169
|
-
}
|
|
170
|
-
if (state.activeBlock?.blockId === event.blockId) {
|
|
171
|
-
state.activeBlock = null;
|
|
172
|
-
}
|
|
173
|
-
updateStreamingMessage();
|
|
174
|
-
break;
|
|
175
|
-
}
|
|
176
|
-
case "reasoning-start": {
|
|
177
|
-
const thread = state.activeBlock?.thread;
|
|
178
|
-
const reasoningPart = {
|
|
179
|
-
type: "reasoning",
|
|
180
|
-
text: "",
|
|
181
|
-
status: "streaming",
|
|
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",
|
|
182
197
|
thread: thread && thread !== "main" ? thread : void 0
|
|
183
198
|
};
|
|
184
|
-
state.parts.push(
|
|
185
|
-
state.currentReasoningPartIndex = state.parts.length - 1;
|
|
186
|
-
updateStreamingMessage();
|
|
187
|
-
break;
|
|
199
|
+
state.parts.push(operationPart);
|
|
188
200
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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" };
|
|
200
213
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
const part = state.parts[state.currentReasoningPartIndex];
|
|
204
|
-
part.status = "done";
|
|
205
|
-
state.parts[state.currentReasoningPartIndex] = { ...part };
|
|
206
|
-
state.currentReasoningPartIndex = null;
|
|
207
|
-
}
|
|
208
|
-
updateStreamingMessage();
|
|
209
|
-
break;
|
|
214
|
+
if (state.activeBlock?.blockId === event.blockId) {
|
|
215
|
+
state.activeBlock = null;
|
|
210
216
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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 };
|
|
227
238
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const part = state.parts[state.currentTextPartIndex];
|
|
231
|
-
part.text += event.delta;
|
|
232
|
-
state.parts[state.currentTextPartIndex] = { ...part };
|
|
233
|
-
}
|
|
234
|
-
if (state.activeBlock) {
|
|
235
|
-
state.activeBlock.text += event.delta;
|
|
236
|
-
}
|
|
237
|
-
updateStreamingMessage();
|
|
238
|
-
break;
|
|
239
|
+
if (state.activeBlock) {
|
|
240
|
+
state.activeBlock.reasoning += event.delta;
|
|
239
241
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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;
|
|
249
251
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
260
265
|
};
|
|
261
|
-
state.parts.push(
|
|
262
|
-
|
|
263
|
-
state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
|
|
264
|
-
}
|
|
265
|
-
updateStreamingMessage();
|
|
266
|
-
break;
|
|
266
|
+
state.parts.push(textPart);
|
|
267
|
+
state.currentTextPartIndex = state.parts.length - 1;
|
|
267
268
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
state.parts[toolPartIndex] = { ...part };
|
|
277
|
-
updateStreamingMessage();
|
|
278
|
-
} catch {
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
break;
|
|
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 };
|
|
282
277
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
case "tool-input-available": {
|
|
286
|
-
const toolPartIndex = state.parts.findIndex(
|
|
287
|
-
(p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
|
|
288
|
-
);
|
|
289
|
-
if (toolPartIndex >= 0) {
|
|
290
|
-
const part = state.parts[toolPartIndex];
|
|
291
|
-
part.args = event.input;
|
|
292
|
-
part.status = "running";
|
|
293
|
-
state.parts[toolPartIndex] = { ...part };
|
|
294
|
-
updateStreamingMessage();
|
|
295
|
-
}
|
|
296
|
-
break;
|
|
278
|
+
if (state.activeBlock) {
|
|
279
|
+
state.activeBlock.text += event.delta;
|
|
297
280
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
updateStreamingMessage();
|
|
308
|
-
}
|
|
309
|
-
break;
|
|
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;
|
|
310
290
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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 {
|
|
316
318
|
const part = state.parts[toolPartIndex];
|
|
317
|
-
part.
|
|
318
|
-
part.status = "error";
|
|
319
|
+
part.args = JSON.parse(event.inputTextDelta);
|
|
319
320
|
state.parts[toolPartIndex] = { ...part };
|
|
320
|
-
updateStreamingMessage();
|
|
321
|
+
this.updateStreamingMessage();
|
|
322
|
+
} catch {
|
|
321
323
|
}
|
|
322
|
-
break;
|
|
323
324
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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" };
|
|
343
376
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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);
|
|
348
388
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
389
|
+
this.setStatus("idle");
|
|
390
|
+
this.streamingState = null;
|
|
391
|
+
this.options.onFinish?.();
|
|
392
|
+
break;
|
|
353
393
|
}
|
|
394
|
+
case "error":
|
|
395
|
+
throw new Error(event.errorText);
|
|
396
|
+
case "tool-request":
|
|
397
|
+
break;
|
|
354
398
|
}
|
|
355
|
-
} catch (err) {
|
|
356
|
-
const errorObj = err instanceof Error ? err : new Error("Unknown error");
|
|
357
|
-
setError(errorObj);
|
|
358
|
-
setStatus("error");
|
|
359
|
-
streamingStateRef.current = null;
|
|
360
|
-
onError?.(errorObj);
|
|
361
|
-
} finally {
|
|
362
|
-
abortControllerRef.current = null;
|
|
363
399
|
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
400
|
+
} catch (err) {
|
|
401
|
+
const errorObj = err instanceof Error ? err : new Error("Unknown error");
|
|
402
|
+
this.setError(errorObj);
|
|
403
|
+
this.setStatus("error");
|
|
404
|
+
this.streamingState = null;
|
|
405
|
+
this.options.onError?.(errorObj);
|
|
406
|
+
} finally {
|
|
407
|
+
this.abortController = null;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/** Stop the current streaming and finalize any partial message */
|
|
411
|
+
stop() {
|
|
412
|
+
this.abortController?.abort();
|
|
413
|
+
this.abortController = null;
|
|
414
|
+
const state = this.streamingState;
|
|
371
415
|
if (state && state.parts.length > 0) {
|
|
372
416
|
const finalMessage = buildMessageFromState(state, "done");
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
417
|
+
const messages = [...this._messages];
|
|
418
|
+
const lastMsg = messages[messages.length - 1];
|
|
419
|
+
if (lastMsg && lastMsg.id === state.messageId) {
|
|
420
|
+
messages[messages.length - 1] = finalMessage;
|
|
421
|
+
} else {
|
|
422
|
+
messages.push(finalMessage);
|
|
423
|
+
}
|
|
424
|
+
this.setMessages(messages);
|
|
380
425
|
}
|
|
381
|
-
|
|
382
|
-
setStatus("idle");
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
426
|
+
this.streamingState = null;
|
|
427
|
+
this.setStatus("idle");
|
|
428
|
+
}
|
|
429
|
+
// =========================================================================
|
|
430
|
+
// Private Helpers
|
|
431
|
+
// =========================================================================
|
|
432
|
+
updateStreamingMessage() {
|
|
433
|
+
const state = this.streamingState;
|
|
434
|
+
if (!state) return;
|
|
435
|
+
const msg = buildMessageFromState(state, "streaming");
|
|
436
|
+
const messages = [...this._messages];
|
|
437
|
+
const lastMsg = messages[messages.length - 1];
|
|
438
|
+
if (lastMsg && lastMsg.id === state.messageId) {
|
|
439
|
+
messages[messages.length - 1] = msg;
|
|
440
|
+
} else {
|
|
441
|
+
messages.push(msg);
|
|
442
|
+
}
|
|
443
|
+
this.setMessages(messages);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
392
446
|
|
|
393
447
|
// src/utils/message-helpers.ts
|
|
394
448
|
function isOtherThread(part) {
|
|
395
449
|
return Boolean(part.thread && part.thread !== "main");
|
|
396
450
|
}
|
|
397
451
|
export {
|
|
452
|
+
OctavusChat,
|
|
398
453
|
isOtherThread,
|
|
399
|
-
|
|
454
|
+
parseSSEStream
|
|
400
455
|
};
|
|
401
456
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks/use-octavus-chat.ts","../src/stream/reader.ts","../src/utils/message-helpers.ts"],"sourcesContent":["'use client';\n\nimport { useState, useCallback, useRef } from 'react';\nimport {\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 UseOctavusChatOptions {\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\nexport interface UseOctavusChatReturn {\n /** All messages including the currently streaming one */\n messages: UIMessage[];\n /** Current status of the chat */\n status: ChatStatus;\n /** Error if status is 'error' */\n error: Error | null;\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 send: (\n triggerName: string,\n input?: Record<string, unknown>,\n options?: { userMessage?: UserMessageInput },\n ) => Promise<void>;\n /** Stop the current streaming and finalize any partial message */\n stop: () => 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\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// Hook Implementation\n// =============================================================================\n\nexport function useOctavusChat(options: UseOctavusChatOptions): UseOctavusChatReturn {\n const { onTrigger, initialMessages = [], onError, onFinish, onResourceUpdate } = options;\n\n const [messages, setMessages] = useState<UIMessage[]>(initialMessages);\n const [status, setStatus] = useState<ChatStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const streamingStateRef = useRef<StreamingState | null>(null);\n\n const updateStreamingMessage = useCallback(() => {\n const state = streamingStateRef.current;\n if (!state) return;\n\n const msg = buildMessageFromState(state, 'streaming');\n setMessages((prev) => {\n const lastMsg = prev[prev.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n // Update existing streaming message\n return [...prev.slice(0, -1), msg];\n }\n // Add new streaming message\n return [...prev, msg];\n });\n }, []);\n\n const send = useCallback(\n async (\n triggerName: string,\n input?: Record<string, unknown>,\n sendOptions?: { userMessage?: UserMessageInput },\n ) => {\n // Abort any previous request\n abortControllerRef.current?.abort();\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n // Add user message if provided\n if (sendOptions?.userMessage !== undefined) {\n const userMsg = createUserMessage(sendOptions.userMessage);\n setMessages((prev) => [...prev, userMsg]);\n }\n\n // Reset state\n setStatus('streaming');\n setError(null);\n streamingStateRef.current = createEmptyStreamingState();\n\n try {\n const response = await 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 = streamingStateRef.current;\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 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 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 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 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 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 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 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 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 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 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 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 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 updateStreamingMessage();\n }\n break;\n }\n\n case 'resource-update':\n 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 setMessages((prev) => {\n const lastMsg = prev[prev.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n return [...prev.slice(0, -1), finalMessage];\n }\n return [...prev, finalMessage];\n });\n }\n\n setStatus('idle');\n streamingStateRef.current = null;\n 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 setError(errorObj);\n setStatus('error');\n streamingStateRef.current = null;\n onError?.(errorObj);\n } finally {\n abortControllerRef.current = null;\n }\n },\n [onTrigger, onError, onFinish, onResourceUpdate, updateStreamingMessage],\n );\n\n const stop = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n\n const state = streamingStateRef.current;\n if (state && state.parts.length > 0) {\n const finalMessage = buildMessageFromState(state, 'done');\n setMessages((prev) => {\n const lastMsg = prev[prev.length - 1];\n if (lastMsg && lastMsg.id === state.messageId) {\n return [...prev.slice(0, -1), finalMessage];\n }\n return [...prev, finalMessage];\n });\n }\n\n streamingStateRef.current = null;\n setStatus('idle');\n }, []);\n\n return {\n messages,\n status,\n error,\n send,\n stop,\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"],"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"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octavus/client-sdk",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.0.6",
|
|
4
|
+
"description": "Framework-agnostic client SDK for Octavus agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Octavus AI <hello@octavus.ai>",
|
|
7
7
|
"keywords": [
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"ai",
|
|
10
10
|
"agents",
|
|
11
11
|
"sdk",
|
|
12
|
-
"react",
|
|
13
12
|
"streaming"
|
|
14
13
|
],
|
|
15
14
|
"type": "module",
|
|
@@ -29,14 +28,9 @@
|
|
|
29
28
|
"access": "public"
|
|
30
29
|
},
|
|
31
30
|
"dependencies": {
|
|
32
|
-
"@octavus/core": "^0.0.
|
|
33
|
-
},
|
|
34
|
-
"peerDependencies": {
|
|
35
|
-
"react": ">=18.0.0"
|
|
31
|
+
"@octavus/core": "^0.0.6"
|
|
36
32
|
},
|
|
37
33
|
"devDependencies": {
|
|
38
|
-
"@types/react": "^19.0.0",
|
|
39
|
-
"react": "^19.0.0",
|
|
40
34
|
"tsup": "^8.3.5",
|
|
41
35
|
"typescript": "^5.6.3"
|
|
42
36
|
},
|