@octavus/client-sdk 0.0.4 → 0.0.5

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 CHANGED
@@ -1,83 +1,51 @@
1
- import { ToolCallInfo, DisplayMode, MessagePart } from '@octavus/core';
1
+ import { UIMessage, UIMessagePart } from '@octavus/core';
2
2
  export * from '@octavus/core';
3
3
 
4
- interface ToolCallWithDescription extends ToolCallInfo {
5
- description?: string;
6
- display?: DisplayMode;
7
- }
8
- interface ExecutionStep {
9
- id: string;
10
- name: string;
11
- type: string;
12
- display: DisplayMode;
13
- description?: string;
14
- outputToChat: boolean;
15
- summary?: string;
16
- fullOutput?: string;
17
- reasoning?: string;
18
- toolCalls?: ToolCallWithDescription[];
19
- thread?: string;
20
- }
21
- interface Message {
22
- id: string;
23
- role: 'user' | 'assistant' | 'system';
4
+ type ChatStatus = 'idle' | 'streaming' | 'error';
5
+ type TriggerFunction = (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;
6
+ /**
7
+ * Input for creating a user message.
8
+ * Currently supports text content. Will be extended to support attachments, images, etc.
9
+ */
10
+ interface UserMessageInput {
11
+ /** Text content of the message */
24
12
  content: string;
25
- toolCalls?: ToolCallWithDescription[];
26
- executionSteps?: ExecutionStep[];
27
- /** If false, message is not shown to user (internal directive) */
28
- visible?: boolean;
29
- /** Ordered message parts - single source of truth for display */
30
- parts?: MessagePart[];
31
- reasoning?: string;
32
- createdAt: Date;
33
13
  }
34
- type BlockStatus = 'pending' | 'running' | 'completed' | 'error';
35
- interface ExecutionBlock {
36
- id: string;
37
- name: string;
38
- type: string;
39
- status: BlockStatus;
40
- display: DisplayMode;
41
- description?: string;
42
- /** False for independent blocks */
43
- outputToChat: boolean;
44
- thread?: string;
45
- streamingText: string;
46
- summary?: string;
47
- reasoning?: string;
48
- toolCalls: ToolCallWithDescription[];
49
- startedAt: Date;
50
- completedAt?: Date;
51
- }
52
- type ChatStatus = 'idle' | 'loading' | 'streaming' | 'error';
53
- type TriggerFunction = (triggerName: string, input?: Record<string, unknown>) => Promise<Response>;
54
14
  interface UseOctavusChatOptions {
15
+ /** Function to make the API call to trigger the agent */
55
16
  onTrigger: TriggerFunction;
56
- initialMessages?: Message[];
57
- onMessage?: (message: Message) => void;
17
+ /** Initial messages (for session refresh) */
18
+ initialMessages?: UIMessage[];
19
+ /** Callback when an error occurs */
58
20
  onError?: (error: Error) => void;
59
- onDone?: () => void;
21
+ /** Callback when streaming finishes */
22
+ onFinish?: () => void;
23
+ /** Callback when a resource is updated */
60
24
  onResourceUpdate?: (name: string, value: unknown) => void;
61
- onBlockStart?: (block: ExecutionBlock) => void;
62
- onBlockEnd?: (block: ExecutionBlock) => void;
63
25
  }
64
26
  interface UseOctavusChatReturn {
65
- messages: Message[];
27
+ /** All messages including the currently streaming one */
28
+ messages: UIMessage[];
29
+ /** Current status of the chat */
66
30
  status: ChatStatus;
67
- isLoading: boolean;
31
+ /** Error if status is 'error' */
68
32
  error: Error | null;
69
33
  /**
70
- * Add a user message to the UI state.
71
- * Does NOT trigger the agent - call triggerAction() after.
34
+ * Trigger the agent and optionally add a user message to the chat.
35
+ *
36
+ * @param triggerName - The trigger name defined in the agent's protocol.yaml
37
+ * @param input - Input parameters for the trigger (variable substitutions)
38
+ * @param options.userMessage - If provided, adds a user message to the chat before triggering
72
39
  */
73
- addUserMessage: (content: string) => void;
74
- triggerAction: (triggerName: string, input?: Record<string, unknown>) => Promise<void>;
75
- streamingText: string;
76
- /** Streaming parts in MessagePart format - single source of truth */
77
- streamingParts: MessagePart[];
78
- executionBlocks: ExecutionBlock[];
79
- reasoningText: string;
40
+ send: (triggerName: string, input?: Record<string, unknown>, options?: {
41
+ userMessage?: UserMessageInput;
42
+ }) => Promise<void>;
43
+ /** Stop the current streaming and finalize any partial message */
44
+ stop: () => void;
80
45
  }
81
46
  declare function useOctavusChat(options: UseOctavusChatOptions): UseOctavusChatReturn;
82
47
 
83
- export { type BlockStatus, type ChatStatus, type ExecutionBlock, type ExecutionStep, type Message, type ToolCallWithDescription, type TriggerFunction, type UseOctavusChatOptions, type UseOctavusChatReturn, useOctavusChat };
48
+ /** Non-main thread content (e.g., "summary") is typically displayed differently. */
49
+ declare function isOtherThread(part: UIMessagePart): boolean;
50
+
51
+ export { type ChatStatus, type TriggerFunction, type UseOctavusChatOptions, type UseOctavusChatReturn, type UserMessageInput, isOtherThread, useOctavusChat };
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  // src/hooks/use-octavus-chat.ts
2
2
  import { useState, useCallback, useRef } from "react";
3
- import { generateId } from "@octavus/core";
3
+ import {
4
+ generateId
5
+ } from "@octavus/core";
4
6
 
5
7
  // src/stream/reader.ts
6
8
  import { safeParseStreamEvent } from "@octavus/core";
@@ -40,299 +42,282 @@ async function* parseSSEStream(response) {
40
42
  }
41
43
 
42
44
  // src/hooks/use-octavus-chat.ts
45
+ var OPERATION_BLOCK_TYPES = /* @__PURE__ */ new Set(["set-resource", "serialize-thread"]);
43
46
  async function parseErrorResponse(response) {
44
47
  try {
45
48
  const data = await response.json();
46
- return data.error ?? data.errorText ?? data.message ?? `Request failed: ${response.status}`;
49
+ return data.error ?? data.message ?? `Request failed: ${response.status}`;
47
50
  } catch {
48
51
  return `Request failed: ${response.status}`;
49
52
  }
50
53
  }
51
- function findOrCreateTextPart(parts, thread) {
52
- for (let i = parts.length - 1; i >= 0; i--) {
53
- const part = parts[i];
54
- if (!part) continue;
55
- if (part.type === "text" && part.thread === thread) {
56
- return { part, index: i };
57
- }
58
- if (part.type !== "text") break;
59
- }
60
- const newPart = {
61
- type: "text",
62
- visible: true,
63
- content: "",
64
- thread
54
+ function createUserMessage(input) {
55
+ return {
56
+ id: generateId(),
57
+ role: "user",
58
+ parts: [{ type: "text", text: input.content, status: "done" }],
59
+ status: "done",
60
+ createdAt: /* @__PURE__ */ new Date()
61
+ };
62
+ }
63
+ function createEmptyStreamingState() {
64
+ return {
65
+ messageId: generateId(),
66
+ parts: [],
67
+ activeBlock: null,
68
+ blocks: /* @__PURE__ */ new Map(),
69
+ currentTextPartIndex: null,
70
+ currentReasoningPartIndex: null
71
+ };
72
+ }
73
+ function buildMessageFromState(state, status) {
74
+ return {
75
+ id: state.messageId,
76
+ role: "assistant",
77
+ parts: [...state.parts],
78
+ status,
79
+ createdAt: /* @__PURE__ */ new Date()
65
80
  };
66
- parts.push(newPart);
67
- return { part: newPart, index: parts.length - 1 };
68
81
  }
69
82
  function useOctavusChat(options) {
70
- const {
71
- onTrigger,
72
- initialMessages = [],
73
- onMessage,
74
- onError,
75
- onDone,
76
- onResourceUpdate,
77
- onBlockStart,
78
- onBlockEnd
79
- } = options;
83
+ const { onTrigger, initialMessages = [], onError, onFinish, onResourceUpdate } = options;
80
84
  const [messages, setMessages] = useState(initialMessages);
81
85
  const [status, setStatus] = useState("idle");
82
86
  const [error, setError] = useState(null);
83
- const [streamingText, setStreamingText] = useState("");
84
- const [streamingParts, setStreamingParts] = useState([]);
85
- const [executionBlocks, setExecutionBlocks] = useState([]);
86
- const [reasoningText, setReasoningText] = useState("");
87
87
  const abortControllerRef = useRef(null);
88
- const triggerAction = useCallback(
89
- async (triggerName, input) => {
88
+ const streamingStateRef = useRef(null);
89
+ const updateStreamingMessage = useCallback(() => {
90
+ const state = streamingStateRef.current;
91
+ if (!state) return;
92
+ const msg = buildMessageFromState(state, "streaming");
93
+ setMessages((prev) => {
94
+ const lastMsg = prev[prev.length - 1];
95
+ if (lastMsg && lastMsg.id === state.messageId) {
96
+ return [...prev.slice(0, -1), msg];
97
+ }
98
+ return [...prev, msg];
99
+ });
100
+ }, []);
101
+ const send = useCallback(
102
+ async (triggerName, input, sendOptions) => {
90
103
  abortControllerRef.current?.abort();
91
- abortControllerRef.current = null;
92
104
  const abortController = new AbortController();
93
105
  abortControllerRef.current = abortController;
94
- setStatus("loading");
106
+ if (sendOptions?.userMessage !== void 0) {
107
+ const userMsg = createUserMessage(sendOptions.userMessage);
108
+ setMessages((prev) => [...prev, userMsg]);
109
+ }
110
+ setStatus("streaming");
95
111
  setError(null);
96
- setStreamingText("");
97
- setStreamingParts([]);
98
- setExecutionBlocks([]);
99
- setReasoningText("");
100
- const blocks = [];
101
- let activeBlock = null;
102
- const toolCalls = [];
103
- let visibleFullText = "";
104
- let executionReasoningText = "";
105
- const reasoningStack = [];
106
- const getCurrentReasoning = () => reasoningStack[reasoningStack.length - 1];
107
- const parts = [];
112
+ streamingStateRef.current = createEmptyStreamingState();
108
113
  try {
109
114
  const response = await onTrigger(triggerName, input);
110
115
  if (!response.ok) {
111
116
  const errorMessage = await parseErrorResponse(response);
112
117
  throw new Error(errorMessage);
113
118
  }
114
- setStatus("streaming");
115
119
  for await (const event of parseSSEStream(response)) {
116
120
  if (abortController.signal.aborted) {
117
121
  break;
118
122
  }
123
+ const state = streamingStateRef.current;
124
+ if (!state) break;
119
125
  switch (event.type) {
120
126
  case "start":
121
127
  break;
122
128
  case "block-start": {
123
- const newBlock = {
124
- id: event.blockId,
125
- name: event.blockName,
126
- type: event.blockType,
127
- status: "running",
129
+ const block = {
130
+ blockId: event.blockId,
131
+ blockName: event.blockName,
132
+ blockType: event.blockType,
128
133
  display: event.display,
129
134
  description: event.description,
130
- outputToChat: event.outputToChat ?? false,
135
+ outputToChat: event.outputToChat ?? true,
131
136
  thread: event.thread,
132
- streamingText: "",
133
- toolCalls: [],
134
- startedAt: /* @__PURE__ */ new Date()
137
+ reasoning: "",
138
+ text: "",
139
+ toolCalls: /* @__PURE__ */ new Map()
135
140
  };
136
- blocks.push(newBlock);
137
- activeBlock = newBlock;
138
- setExecutionBlocks([...blocks]);
139
- onBlockStart?.(newBlock);
141
+ state.blocks.set(event.blockId, block);
142
+ state.activeBlock = block;
143
+ const isOperation = OPERATION_BLOCK_TYPES.has(event.blockType);
144
+ const isHidden = event.display === "hidden";
145
+ if (isOperation && !isHidden) {
146
+ const thread = event.thread;
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();
140
160
  break;
141
161
  }
142
162
  case "block-end": {
143
- if (activeBlock && activeBlock.id === event.blockId) {
144
- activeBlock.status = "completed";
145
- activeBlock.summary = event.summary;
146
- activeBlock.completedAt = /* @__PURE__ */ new Date();
147
- setExecutionBlocks([...blocks]);
148
- onBlockEnd?.(activeBlock);
149
- activeBlock = null;
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" };
150
169
  }
151
- break;
152
- }
153
- case "text-delta":
154
- if (activeBlock) {
155
- activeBlock.streamingText += event.delta;
156
- setExecutionBlocks([...blocks]);
157
- const isNonMainThread = activeBlock.thread !== void 0 && activeBlock.thread !== "main";
158
- const shouldCaptureText = activeBlock.outputToChat || isNonMainThread;
159
- if (shouldCaptureText) {
160
- const { part, index } = findOrCreateTextPart(parts, activeBlock.thread);
161
- part.content = (part.content ?? "") + event.delta;
162
- parts[index] = { ...part };
163
- if (activeBlock.outputToChat) {
164
- visibleFullText += event.delta;
165
- setStreamingText(visibleFullText);
166
- }
167
- setStreamingParts([...parts]);
168
- }
170
+ if (state.activeBlock?.blockId === event.blockId) {
171
+ state.activeBlock = null;
169
172
  }
173
+ updateStreamingMessage();
170
174
  break;
171
- case "text-start":
172
- case "text-end":
173
- break;
175
+ }
174
176
  case "reasoning-start": {
175
- const isNonMainThread = activeBlock?.thread !== void 0 && activeBlock.thread !== "main";
176
- const shouldCapture = (activeBlock?.outputToChat ?? false) || isNonMainThread;
177
- const newPart = {
177
+ const thread = state.activeBlock?.thread;
178
+ const reasoningPart = {
178
179
  type: "reasoning",
179
- visible: false,
180
- content: "",
181
- thread: activeBlock?.thread
180
+ text: "",
181
+ status: "streaming",
182
+ thread: thread && thread !== "main" ? thread : void 0
182
183
  };
183
- parts.push(newPart);
184
- reasoningStack.push({
185
- id: event.id,
186
- outputToChat: shouldCapture,
187
- partIndex: parts.length - 1
188
- });
189
- setReasoningText("");
184
+ state.parts.push(reasoningPart);
185
+ state.currentReasoningPartIndex = state.parts.length - 1;
186
+ updateStreamingMessage();
190
187
  break;
191
188
  }
192
189
  case "reasoning-delta": {
193
- const ctx = getCurrentReasoning();
194
- const reasoningPart = ctx ? parts[ctx.partIndex] : void 0;
195
- if (ctx && reasoningPart) {
196
- reasoningPart.content = (reasoningPart.content ?? "") + event.delta;
197
- parts[ctx.partIndex] = { ...reasoningPart };
190
+ if (state.currentReasoningPartIndex !== null) {
191
+ const part = state.parts[state.currentReasoningPartIndex];
192
+ part.text += event.delta;
193
+ state.parts[state.currentReasoningPartIndex] = { ...part };
198
194
  }
199
- if (activeBlock?.outputToChat) {
200
- executionReasoningText += event.delta;
195
+ if (state.activeBlock) {
196
+ state.activeBlock.reasoning += event.delta;
201
197
  }
202
- setReasoningText((prev) => prev + event.delta);
203
- if (activeBlock) {
204
- activeBlock.reasoning = (activeBlock.reasoning ?? "") + event.delta;
205
- setExecutionBlocks([...blocks]);
198
+ updateStreamingMessage();
199
+ break;
200
+ }
201
+ case "reasoning-end": {
202
+ if (state.currentReasoningPartIndex !== null) {
203
+ const part = state.parts[state.currentReasoningPartIndex];
204
+ part.status = "done";
205
+ state.parts[state.currentReasoningPartIndex] = { ...part };
206
+ state.currentReasoningPartIndex = null;
206
207
  }
207
- if (ctx?.outputToChat) {
208
- setStreamingParts([...parts]);
208
+ updateStreamingMessage();
209
+ break;
210
+ }
211
+ case "text-start": {
212
+ const thread = state.activeBlock?.thread;
213
+ const isOtherThread2 = Boolean(thread && thread !== "main");
214
+ const shouldAddPart = state.activeBlock?.outputToChat !== false || isOtherThread2;
215
+ if (shouldAddPart) {
216
+ const textPart = {
217
+ type: "text",
218
+ text: "",
219
+ status: "streaming",
220
+ thread: isOtherThread2 ? thread : void 0
221
+ };
222
+ state.parts.push(textPart);
223
+ state.currentTextPartIndex = state.parts.length - 1;
209
224
  }
225
+ updateStreamingMessage();
210
226
  break;
211
227
  }
212
- case "reasoning-end": {
213
- const ctx = reasoningStack.pop();
214
- setReasoningText("");
215
- const endReasoningPart = ctx ? parts[ctx.partIndex] : void 0;
216
- if (ctx && endReasoningPart && !endReasoningPart.content?.trim()) {
217
- parts.splice(ctx.partIndex, 1);
228
+ case "text-delta": {
229
+ if (state.currentTextPartIndex !== null) {
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;
218
236
  }
219
- if (ctx?.outputToChat) {
220
- setStreamingParts([...parts]);
237
+ updateStreamingMessage();
238
+ break;
239
+ }
240
+ case "text-end": {
241
+ if (state.currentTextPartIndex !== null) {
242
+ const part = state.parts[state.currentTextPartIndex];
243
+ part.status = "done";
244
+ state.parts[state.currentTextPartIndex] = { ...part };
245
+ state.currentTextPartIndex = null;
221
246
  }
247
+ updateStreamingMessage();
222
248
  break;
223
249
  }
224
250
  case "tool-input-start": {
225
- const newToolCall = {
226
- id: event.toolCallId,
227
- name: event.toolName,
228
- arguments: {},
229
- status: "streaming",
230
- description: event.title
251
+ const thread = state.activeBlock?.thread;
252
+ const toolPart = {
253
+ type: "tool-call",
254
+ toolCallId: event.toolCallId,
255
+ toolName: event.toolName,
256
+ displayName: event.title,
257
+ args: {},
258
+ status: "pending",
259
+ thread: thread && thread !== "main" ? thread : void 0
231
260
  };
232
- toolCalls.push(newToolCall);
233
- if (activeBlock) {
234
- activeBlock.toolCalls = [...activeBlock.toolCalls, newToolCall];
235
- setExecutionBlocks([...blocks]);
236
- }
237
- const isVisibleBlock = activeBlock?.display !== "hidden";
238
- if (isVisibleBlock) {
239
- const toolPart = {
240
- type: "tool-call",
241
- visible: true,
242
- toolCall: newToolCall,
243
- thread: activeBlock?.thread
244
- };
245
- parts.push(toolPart);
246
- setStreamingParts([...parts]);
261
+ state.parts.push(toolPart);
262
+ if (state.activeBlock) {
263
+ state.activeBlock.toolCalls.set(event.toolCallId, toolPart);
247
264
  }
265
+ updateStreamingMessage();
248
266
  break;
249
267
  }
250
268
  case "tool-input-delta": {
251
- const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);
252
- if (toolCall) {
269
+ const toolPartIndex = state.parts.findIndex(
270
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
271
+ );
272
+ if (toolPartIndex >= 0) {
253
273
  try {
254
- toolCall.arguments = JSON.parse(event.inputTextDelta);
274
+ const part = state.parts[toolPartIndex];
275
+ part.args = JSON.parse(event.inputTextDelta);
276
+ state.parts[toolPartIndex] = { ...part };
277
+ updateStreamingMessage();
255
278
  } catch {
256
279
  }
257
- const argsPart = parts.find(
258
- (p) => p.type === "tool-call" && p.toolCall?.id === event.toolCallId
259
- );
260
- if (argsPart?.toolCall) {
261
- argsPart.toolCall = { ...argsPart.toolCall, arguments: toolCall.arguments };
262
- setStreamingParts([...parts]);
263
- }
264
- if (activeBlock) {
265
- setExecutionBlocks([...blocks]);
266
- }
267
280
  }
268
281
  break;
269
282
  }
270
- case "tool-input-end": {
283
+ case "tool-input-end":
271
284
  break;
272
- }
273
285
  case "tool-input-available": {
274
- const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);
275
- if (toolCall) {
276
- toolCall.arguments = event.input;
277
- toolCall.status = "available";
278
- const inputPart = parts.find(
279
- (p) => p.type === "tool-call" && p.toolCall?.id === event.toolCallId
280
- );
281
- if (inputPart?.toolCall) {
282
- inputPart.toolCall = {
283
- ...inputPart.toolCall,
284
- arguments: toolCall.arguments,
285
- status: "available"
286
- };
287
- setStreamingParts([...parts]);
288
- }
289
- if (activeBlock) {
290
- setExecutionBlocks([...blocks]);
291
- }
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();
292
295
  }
293
296
  break;
294
297
  }
295
298
  case "tool-output-available": {
296
- const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);
297
- if (toolCall) {
298
- toolCall.status = "available";
299
- toolCall.result = event.output;
300
- const resultPart = parts.find(
301
- (p) => p.type === "tool-call" && p.toolCall?.id === event.toolCallId
302
- );
303
- if (resultPart?.toolCall) {
304
- resultPart.toolCall = {
305
- ...resultPart.toolCall,
306
- status: "available",
307
- result: event.output
308
- };
309
- setStreamingParts([...parts]);
310
- }
311
- if (activeBlock) {
312
- setExecutionBlocks([...blocks]);
313
- }
299
+ const toolPartIndex = state.parts.findIndex(
300
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
301
+ );
302
+ if (toolPartIndex >= 0) {
303
+ const part = state.parts[toolPartIndex];
304
+ part.result = event.output;
305
+ part.status = "done";
306
+ state.parts[toolPartIndex] = { ...part };
307
+ updateStreamingMessage();
314
308
  }
315
309
  break;
316
310
  }
317
311
  case "tool-output-error": {
318
- const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);
319
- if (toolCall) {
320
- toolCall.status = "error";
321
- toolCall.error = event.errorText;
322
- const errorPart = parts.find(
323
- (p) => p.type === "tool-call" && p.toolCall?.id === event.toolCallId
324
- );
325
- if (errorPart?.toolCall) {
326
- errorPart.toolCall = {
327
- ...errorPart.toolCall,
328
- status: "error",
329
- error: event.errorText
330
- };
331
- setStreamingParts([...parts]);
332
- }
333
- if (activeBlock) {
334
- setExecutionBlocks([...blocks]);
335
- }
312
+ const toolPartIndex = state.parts.findIndex(
313
+ (p) => p.type === "tool-call" && p.toolCallId === event.toolCallId
314
+ );
315
+ if (toolPartIndex >= 0) {
316
+ const part = state.parts[toolPartIndex];
317
+ part.error = event.errorText;
318
+ part.status = "error";
319
+ state.parts[toolPartIndex] = { ...part };
320
+ updateStreamingMessage();
336
321
  }
337
322
  break;
338
323
  }
@@ -340,46 +325,25 @@ function useOctavusChat(options) {
340
325
  onResourceUpdate?.(event.name, event.value);
341
326
  break;
342
327
  case "finish": {
343
- while (reasoningStack.length > 0) {
344
- const ctx = reasoningStack.pop();
345
- const remainingPart = ctx ? parts[ctx.partIndex] : void 0;
346
- if (ctx && remainingPart && !remainingPart.content?.trim()) {
347
- parts.splice(ctx.partIndex, 1);
328
+ const finalMessage = buildMessageFromState(state, "done");
329
+ finalMessage.parts = finalMessage.parts.map((part) => {
330
+ if (part.type === "text" || part.type === "reasoning") {
331
+ return { ...part, status: "done" };
348
332
  }
349
- }
350
- const executionSteps = blocks.filter((b) => b.status === "completed").map((b) => ({
351
- id: b.id,
352
- name: b.name,
353
- type: b.type,
354
- display: b.display,
355
- description: b.description,
356
- outputToChat: b.outputToChat,
357
- summary: b.summary,
358
- fullOutput: b.streamingText || void 0,
359
- reasoning: b.reasoning || void 0,
360
- toolCalls: b.toolCalls.length > 0 ? b.toolCalls : void 0,
361
- thread: b.thread
362
- }));
363
- if (visibleFullText || executionSteps.length > 0 || executionReasoningText) {
364
- const assistantMessage = {
365
- id: generateId(),
366
- role: "assistant",
367
- content: visibleFullText,
368
- toolCalls: toolCalls.filter((tc) => tc.status === "available"),
369
- executionSteps: executionSteps.length > 0 ? executionSteps : void 0,
370
- parts: parts.length > 0 ? parts : void 0,
371
- reasoning: executionReasoningText || void 0,
372
- createdAt: /* @__PURE__ */ new Date()
373
- };
374
- setMessages((prev) => [...prev, assistantMessage]);
375
- onMessage?.(assistantMessage);
333
+ return part;
334
+ });
335
+ if (finalMessage.parts.length > 0) {
336
+ setMessages((prev) => {
337
+ const lastMsg = prev[prev.length - 1];
338
+ if (lastMsg && lastMsg.id === state.messageId) {
339
+ return [...prev.slice(0, -1), finalMessage];
340
+ }
341
+ return [...prev, finalMessage];
342
+ });
376
343
  }
377
344
  setStatus("idle");
378
- setStreamingText("");
379
- setStreamingParts([]);
380
- setExecutionBlocks([]);
381
- setReasoningText("");
382
- onDone?.();
345
+ streamingStateRef.current = null;
346
+ onFinish?.();
383
347
  break;
384
348
  }
385
349
  case "error":
@@ -392,44 +356,46 @@ function useOctavusChat(options) {
392
356
  const errorObj = err instanceof Error ? err : new Error("Unknown error");
393
357
  setError(errorObj);
394
358
  setStatus("error");
395
- if (activeBlock) {
396
- activeBlock.status = "error";
397
- setExecutionBlocks([...blocks]);
398
- }
359
+ streamingStateRef.current = null;
399
360
  onError?.(errorObj);
400
361
  } finally {
401
362
  abortControllerRef.current = null;
402
363
  }
403
364
  },
404
- [onTrigger, onMessage, onError, onDone, onResourceUpdate, onBlockStart, onBlockEnd]
405
- );
406
- const addUserMessage = useCallback(
407
- (content) => {
408
- const userMessage = {
409
- id: generateId(),
410
- role: "user",
411
- content,
412
- createdAt: /* @__PURE__ */ new Date()
413
- };
414
- setMessages((prev) => [...prev, userMessage]);
415
- onMessage?.(userMessage);
416
- },
417
- [onMessage]
365
+ [onTrigger, onError, onFinish, onResourceUpdate, updateStreamingMessage]
418
366
  );
367
+ const stop = useCallback(() => {
368
+ abortControllerRef.current?.abort();
369
+ abortControllerRef.current = null;
370
+ const state = streamingStateRef.current;
371
+ if (state && state.parts.length > 0) {
372
+ const finalMessage = buildMessageFromState(state, "done");
373
+ setMessages((prev) => {
374
+ const lastMsg = prev[prev.length - 1];
375
+ if (lastMsg && lastMsg.id === state.messageId) {
376
+ return [...prev.slice(0, -1), finalMessage];
377
+ }
378
+ return [...prev, finalMessage];
379
+ });
380
+ }
381
+ streamingStateRef.current = null;
382
+ setStatus("idle");
383
+ }, []);
419
384
  return {
420
385
  messages,
421
386
  status,
422
- isLoading: status === "loading" || status === "streaming",
423
387
  error,
424
- addUserMessage,
425
- triggerAction,
426
- streamingText,
427
- streamingParts,
428
- executionBlocks,
429
- reasoningText
388
+ send,
389
+ stop
430
390
  };
431
391
  }
392
+
393
+ // src/utils/message-helpers.ts
394
+ function isOtherThread(part) {
395
+ return Boolean(part.thread && part.thread !== "main");
396
+ }
432
397
  export {
398
+ isOtherThread,
433
399
  useOctavusChat
434
400
  };
435
401
  //# 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"],"sourcesContent":["'use client';\n\nimport { useState, useCallback, useRef } from 'react';\nimport { generateId, type ToolCallInfo, type DisplayMode, type MessagePart } from '@octavus/core';\nimport { parseSSEStream } from '@/stream/reader';\n\nexport interface ToolCallWithDescription extends ToolCallInfo {\n description?: string;\n display?: DisplayMode;\n}\n\nexport interface ExecutionStep {\n id: string;\n name: string;\n type: string;\n display: DisplayMode;\n description?: string;\n outputToChat: boolean;\n summary?: string;\n fullOutput?: string;\n reasoning?: string;\n toolCalls?: ToolCallWithDescription[];\n thread?: string;\n}\n\nexport interface Message {\n id: string;\n role: 'user' | 'assistant' | 'system';\n content: string;\n toolCalls?: ToolCallWithDescription[];\n executionSteps?: ExecutionStep[];\n /** If false, message is not shown to user (internal directive) */\n visible?: boolean;\n /** Ordered message parts - single source of truth for display */\n parts?: MessagePart[];\n reasoning?: string;\n createdAt: Date;\n}\n\nexport type BlockStatus = 'pending' | 'running' | 'completed' | 'error';\n\nexport interface ExecutionBlock {\n id: string;\n name: string;\n type: string;\n status: BlockStatus;\n display: DisplayMode;\n description?: string;\n /** False for independent blocks */\n outputToChat: boolean;\n thread?: string;\n streamingText: string;\n summary?: string;\n reasoning?: string;\n toolCalls: ToolCallWithDescription[];\n startedAt: Date;\n completedAt?: Date;\n}\n\nexport type ChatStatus = 'idle' | 'loading' | 'streaming' | 'error';\n\nexport type TriggerFunction = (\n triggerName: string,\n input?: Record<string, unknown>,\n) => Promise<Response>;\n\nexport interface UseOctavusChatOptions {\n onTrigger: TriggerFunction;\n initialMessages?: Message[];\n onMessage?: (message: Message) => void;\n onError?: (error: Error) => void;\n onDone?: () => void;\n onResourceUpdate?: (name: string, value: unknown) => void;\n onBlockStart?: (block: ExecutionBlock) => void;\n onBlockEnd?: (block: ExecutionBlock) => void;\n}\n\nexport interface UseOctavusChatReturn {\n messages: Message[];\n status: ChatStatus;\n isLoading: boolean;\n error: Error | null;\n /**\n * Add a user message to the UI state.\n * Does NOT trigger the agent - call triggerAction() after.\n */\n addUserMessage: (content: string) => void;\n triggerAction: (triggerName: string, input?: Record<string, unknown>) => Promise<void>;\n streamingText: string;\n /** Streaming parts in MessagePart format - single source of truth */\n streamingParts: MessagePart[];\n executionBlocks: ExecutionBlock[];\n reasoningText: string;\n}\n\ninterface ErrorResponse {\n error?: string;\n message?: string;\n errorText?: string;\n}\n\nasync function parseErrorResponse(response: Response): Promise<string> {\n try {\n const data = (await response.json()) as ErrorResponse;\n return data.error ?? data.errorText ?? data.message ?? `Request failed: ${response.status}`;\n } catch {\n return `Request failed: ${response.status}`;\n }\n}\n\n/**\n * Helper to find or create a text part for the current block\n */\nfunction findOrCreateTextPart(\n parts: MessagePart[],\n thread: string | undefined,\n): { part: MessagePart; index: number } {\n // Look for an existing text part for this thread at the end\n for (let i = parts.length - 1; i >= 0; i--) {\n const part = parts[i];\n if (!part) continue;\n if (part.type === 'text' && part.thread === thread) {\n return { part, index: i };\n }\n // If we hit a different part type, stop looking\n if (part.type !== 'text') break;\n }\n // Create new text part\n const newPart: MessagePart = {\n type: 'text',\n visible: true,\n content: '',\n thread,\n };\n parts.push(newPart);\n return { part: newPart, index: parts.length - 1 };\n}\n\nexport function useOctavusChat(options: UseOctavusChatOptions): UseOctavusChatReturn {\n const {\n onTrigger,\n initialMessages = [],\n onMessage,\n onError,\n onDone,\n onResourceUpdate,\n onBlockStart,\n onBlockEnd,\n } = options;\n\n const [messages, setMessages] = useState<Message[]>(initialMessages);\n const [status, setStatus] = useState<ChatStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n const [streamingText, setStreamingText] = useState('');\n const [streamingParts, setStreamingParts] = useState<MessagePart[]>([]);\n const [executionBlocks, setExecutionBlocks] = useState<ExecutionBlock[]>([]);\n const [reasoningText, setReasoningText] = useState('');\n\n const abortControllerRef = useRef<AbortController | null>(null);\n\n const triggerAction = useCallback(\n async (triggerName: string, input?: Record<string, unknown>) => {\n // Abort any previous request\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n setStatus('loading');\n setError(null);\n setStreamingText('');\n setStreamingParts([]);\n setExecutionBlocks([]);\n setReasoningText('');\n\n const blocks: ExecutionBlock[] = [];\n let activeBlock: ExecutionBlock | null = null;\n const toolCalls: ToolCallInfo[] = [];\n\n let visibleFullText = '';\n let executionReasoningText = '';\n\n interface ReasoningContext {\n id: string;\n outputToChat: boolean;\n partIndex: number;\n }\n const reasoningStack: ReasoningContext[] = [];\n const getCurrentReasoning = () => reasoningStack[reasoningStack.length - 1];\n\n // Build MessagePart[] directly during streaming\n const parts: MessagePart[] = [];\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 setStatus('streaming');\n\n for await (const event of parseSSEStream(response)) {\n if (abortController.signal.aborted) {\n break;\n }\n\n switch (event.type) {\n case 'start':\n break;\n\n case 'block-start': {\n const newBlock: ExecutionBlock = {\n id: event.blockId,\n name: event.blockName,\n type: event.blockType,\n status: 'running',\n display: event.display,\n description: event.description,\n outputToChat: event.outputToChat ?? false,\n thread: event.thread,\n streamingText: '',\n toolCalls: [],\n startedAt: new Date(),\n };\n blocks.push(newBlock);\n activeBlock = newBlock;\n setExecutionBlocks([...blocks]);\n onBlockStart?.(newBlock);\n break;\n }\n\n case 'block-end': {\n if (activeBlock && activeBlock.id === event.blockId) {\n activeBlock.status = 'completed';\n activeBlock.summary = event.summary;\n activeBlock.completedAt = new Date();\n setExecutionBlocks([...blocks]);\n onBlockEnd?.(activeBlock);\n activeBlock = null;\n }\n break;\n }\n\n case 'text-delta':\n if (activeBlock) {\n activeBlock.streamingText += event.delta;\n setExecutionBlocks([...blocks]);\n\n // Capture text if outputToChat OR non-main thread\n const isNonMainThread =\n activeBlock.thread !== undefined && activeBlock.thread !== 'main';\n const shouldCaptureText = activeBlock.outputToChat || isNonMainThread;\n\n if (shouldCaptureText) {\n const { part, index } = findOrCreateTextPart(parts, activeBlock.thread);\n part.content = (part.content ?? '') + event.delta;\n parts[index] = { ...part };\n\n if (activeBlock.outputToChat) {\n visibleFullText += event.delta;\n setStreamingText(visibleFullText);\n }\n\n setStreamingParts([...parts]);\n }\n }\n break;\n\n case 'text-start':\n case 'text-end':\n break;\n\n case 'reasoning-start': {\n // Non-main threads always capture content (shown as orange cards)\n const isNonMainThread =\n activeBlock?.thread !== undefined && activeBlock.thread !== 'main';\n const shouldCapture = (activeBlock?.outputToChat ?? false) || isNonMainThread;\n\n const newPart: MessagePart = {\n type: 'reasoning',\n visible: false,\n content: '',\n thread: activeBlock?.thread,\n };\n parts.push(newPart);\n\n reasoningStack.push({\n id: event.id,\n outputToChat: shouldCapture,\n partIndex: parts.length - 1,\n });\n\n setReasoningText('');\n break;\n }\n\n case 'reasoning-delta': {\n const ctx = getCurrentReasoning();\n const reasoningPart = ctx ? parts[ctx.partIndex] : undefined;\n if (ctx && reasoningPart) {\n reasoningPart.content = (reasoningPart.content ?? '') + event.delta;\n parts[ctx.partIndex] = { ...reasoningPart };\n }\n\n if (activeBlock?.outputToChat) {\n executionReasoningText += event.delta;\n }\n setReasoningText((prev) => prev + event.delta);\n\n if (activeBlock) {\n activeBlock.reasoning = (activeBlock.reasoning ?? '') + event.delta;\n setExecutionBlocks([...blocks]);\n }\n\n if (ctx?.outputToChat) {\n setStreamingParts([...parts]);\n }\n break;\n }\n\n case 'reasoning-end': {\n const ctx = reasoningStack.pop();\n setReasoningText('');\n\n const endReasoningPart = ctx ? parts[ctx.partIndex] : undefined;\n if (ctx && endReasoningPart && !endReasoningPart.content?.trim()) {\n parts.splice(ctx.partIndex, 1);\n }\n\n // Update streaming parts\n if (ctx?.outputToChat) {\n setStreamingParts([...parts]);\n }\n break;\n }\n\n case 'tool-input-start': {\n const newToolCall: ToolCallWithDescription = {\n id: event.toolCallId,\n name: event.toolName,\n arguments: {},\n status: 'streaming',\n description: event.title,\n };\n toolCalls.push(newToolCall);\n\n if (activeBlock) {\n activeBlock.toolCalls = [...activeBlock.toolCalls, newToolCall];\n setExecutionBlocks([...blocks]);\n }\n\n // Add tool call part (visible unless hidden block)\n const isVisibleBlock = activeBlock?.display !== 'hidden';\n if (isVisibleBlock) {\n const toolPart: MessagePart = {\n type: 'tool-call',\n visible: true,\n toolCall: newToolCall,\n thread: activeBlock?.thread,\n };\n parts.push(toolPart);\n setStreamingParts([...parts]);\n }\n break;\n }\n\n case 'tool-input-delta': {\n const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);\n if (toolCall) {\n try {\n toolCall.arguments = JSON.parse(event.inputTextDelta) as Record<string, unknown>;\n } catch {\n // Partial args may not be valid JSON yet\n }\n\n const argsPart = parts.find(\n (p) => p.type === 'tool-call' && p.toolCall?.id === event.toolCallId,\n );\n if (argsPart?.toolCall) {\n argsPart.toolCall = { ...argsPart.toolCall, arguments: toolCall.arguments };\n setStreamingParts([...parts]);\n }\n\n if (activeBlock) {\n setExecutionBlocks([...blocks]);\n }\n }\n break;\n }\n\n case 'tool-input-end': {\n // Tool input streaming has ended - no action needed\n // The full input is available in tool-input-available\n break;\n }\n\n case 'tool-input-available': {\n const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);\n if (toolCall) {\n toolCall.arguments = event.input as Record<string, unknown>;\n toolCall.status = 'available';\n\n const inputPart = parts.find(\n (p) => p.type === 'tool-call' && p.toolCall?.id === event.toolCallId,\n );\n if (inputPart?.toolCall) {\n inputPart.toolCall = {\n ...inputPart.toolCall,\n arguments: toolCall.arguments,\n status: 'available',\n };\n setStreamingParts([...parts]);\n }\n\n if (activeBlock) {\n setExecutionBlocks([...blocks]);\n }\n }\n break;\n }\n\n case 'tool-output-available': {\n const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);\n if (toolCall) {\n toolCall.status = 'available';\n toolCall.result = event.output;\n\n const resultPart = parts.find(\n (p) => p.type === 'tool-call' && p.toolCall?.id === event.toolCallId,\n );\n if (resultPart?.toolCall) {\n resultPart.toolCall = {\n ...resultPart.toolCall,\n status: 'available',\n result: event.output,\n };\n setStreamingParts([...parts]);\n }\n\n if (activeBlock) {\n setExecutionBlocks([...blocks]);\n }\n }\n break;\n }\n\n case 'tool-output-error': {\n const toolCall = toolCalls.find((tc) => tc.id === event.toolCallId);\n if (toolCall) {\n toolCall.status = 'error';\n toolCall.error = event.errorText;\n\n const errorPart = parts.find(\n (p) => p.type === 'tool-call' && p.toolCall?.id === event.toolCallId,\n );\n if (errorPart?.toolCall) {\n errorPart.toolCall = {\n ...errorPart.toolCall,\n status: 'error',\n error: event.errorText,\n };\n setStreamingParts([...parts]);\n }\n\n if (activeBlock) {\n setExecutionBlocks([...blocks]);\n }\n }\n break;\n }\n\n case 'resource-update':\n onResourceUpdate?.(event.name, event.value);\n break;\n\n case 'finish': {\n while (reasoningStack.length > 0) {\n const ctx = reasoningStack.pop();\n const remainingPart = ctx ? parts[ctx.partIndex] : undefined;\n if (ctx && remainingPart && !remainingPart.content?.trim()) {\n parts.splice(ctx.partIndex, 1);\n }\n }\n\n const executionSteps: ExecutionStep[] = blocks\n .filter((b) => b.status === 'completed')\n .map((b) => ({\n id: b.id,\n name: b.name,\n type: b.type,\n display: b.display,\n description: b.description,\n outputToChat: b.outputToChat,\n summary: b.summary,\n fullOutput: b.streamingText || undefined,\n reasoning: b.reasoning || undefined,\n toolCalls: b.toolCalls.length > 0 ? b.toolCalls : undefined,\n thread: b.thread,\n }));\n\n if (visibleFullText || executionSteps.length > 0 || executionReasoningText) {\n const assistantMessage: Message = {\n id: generateId(),\n role: 'assistant',\n content: visibleFullText,\n toolCalls: toolCalls.filter((tc) => tc.status === 'available'),\n executionSteps: executionSteps.length > 0 ? executionSteps : undefined,\n parts: parts.length > 0 ? parts : undefined,\n reasoning: executionReasoningText || undefined,\n createdAt: new Date(),\n };\n setMessages((prev) => [...prev, assistantMessage]);\n onMessage?.(assistantMessage);\n }\n setStatus('idle');\n setStreamingText('');\n setStreamingParts([]);\n setExecutionBlocks([]);\n setReasoningText('');\n onDone?.();\n break;\n }\n\n case 'error':\n throw new Error(event.errorText);\n\n case 'tool-request':\n // Handled by server-sdk, should not reach client\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\n if (activeBlock) {\n activeBlock.status = 'error';\n setExecutionBlocks([...blocks]);\n }\n\n onError?.(errorObj);\n } finally {\n abortControllerRef.current = null;\n }\n },\n [onTrigger, onMessage, onError, onDone, onResourceUpdate, onBlockStart, onBlockEnd],\n );\n\n const addUserMessage = useCallback(\n (content: string) => {\n const userMessage: Message = {\n id: generateId(),\n role: 'user',\n content,\n createdAt: new Date(),\n };\n setMessages((prev) => [...prev, userMessage]);\n onMessage?.(userMessage);\n },\n [onMessage],\n );\n\n return {\n messages,\n status,\n isLoading: status === 'loading' || status === 'streaming',\n error,\n addUserMessage,\n triggerAction,\n streamingText,\n streamingParts,\n executionBlocks,\n reasoningText,\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"],"mappings":"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","names":[]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octavus/client-sdk",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Client SDK for streaming Octavus agent responses",
5
5
  "license": "MIT",
6
6
  "author": "Octavus AI <hello@octavus.ai>",
@@ -29,7 +29,7 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@octavus/core": "^0.0.4"
32
+ "@octavus/core": "^0.0.5"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": ">=18.0.0"