bloby-bot 0.21.5 → 0.21.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.21.5",
3
+ "version": "0.21.6",
4
4
  "releaseNotes": [
5
5
  "1. react router implemented",
6
6
  "2. new workspace design",
@@ -37,11 +37,7 @@ export function useChat(ws: WsClient | null) {
37
37
  const [streamBuffer, setStreamBuffer] = useState('');
38
38
  const [tools, setTools] = useState<ToolActivity[]>([]);
39
39
  const loaded = useRef(false);
40
- /** Count of user messages awaiting a bot response keeps typing indicator alive across turns */
41
- const pendingMessages = useRef(0);
42
- /** ID of a partial assistant message committed early (when user sends while bot is streaming) */
43
- const partialMsgId = useRef<string | null>(null);
44
- /** Ref to current streamBuffer value (accessible in callbacks without stale closures) */
40
+ /** Ref to current streamBuffer (avoids stale closures in callbacks) */
45
41
  const streamBufferRef = useRef('');
46
42
 
47
43
  // Load current conversation from DB on mount
@@ -130,11 +126,12 @@ export function useChat(ws: WsClient | null) {
130
126
 
131
127
  const unsubs = [
132
128
  ws.on('bot:typing', () => {
129
+ console.log('[useChat] bot:typing → streaming=true');
133
130
  setStreaming(true);
134
131
  setTools([]);
135
132
  }),
136
133
  ws.on('bot:token', (data: { token: string }) => {
137
- // Auto-detect new turn — if tokens arrive after a response cleared streaming
134
+ // Auto-detect new turn — tokens arriving means agent is working
138
135
  setStreaming(true);
139
136
  setStreamBuffer((buf) => {
140
137
  const next = buf + data.token;
@@ -150,44 +147,36 @@ export function useChat(ws: WsClient | null) {
150
147
  });
151
148
  }),
152
149
  ws.on('bot:response', (data: { conversationId: string; messageId?: string; content: string }) => {
150
+ console.log('[useChat] bot:response — adding message bubble');
153
151
  setConversationId(data.conversationId);
154
152
 
155
- if (partialMsgId.current) {
156
- // A partial message was committed — update it with the full response
157
- const pid = partialMsgId.current;
158
- partialMsgId.current = null;
159
- setMessages((msgs) => msgs.map((m) =>
160
- m.id === pid ? { ...m, content: data.content } : m,
161
- ));
162
- } else {
163
- // Normal: add new message bubble
164
- setMessages((msgs) => [
165
- ...msgs,
166
- {
167
- id: data.messageId || Date.now().toString(),
168
- role: 'assistant',
169
- content: data.content,
170
- timestamp: new Date().toISOString(),
171
- },
172
- ]);
173
- }
153
+ // Always add as a new bubble
154
+ setMessages((msgs) => [
155
+ ...msgs,
156
+ {
157
+ id: data.messageId || Date.now().toString(),
158
+ role: 'assistant',
159
+ content: data.content,
160
+ timestamp: new Date().toISOString(),
161
+ },
162
+ ]);
174
163
 
175
164
  setStreamBuffer('');
176
165
  streamBufferRef.current = '';
177
166
  setTools([]);
178
- // Only stop streaming when all pending messages have been answered
179
- pendingMessages.current = Math.max(0, pendingMessages.current - 1);
180
- if (pendingMessages.current === 0) {
181
- setStreaming(false);
182
- }
167
+ // Don't clear streaming here wait for bot:idle from the server
168
+ }),
169
+ ws.on('bot:idle', () => {
170
+ // Server confirmed agent is idle — safe to stop streaming
171
+ console.log('[useChat] bot:idle → streaming=false');
172
+ setStreaming(false);
183
173
  }),
184
174
  ws.on('bot:error', (data: { error: string }) => {
175
+ console.log('[useChat] bot:error');
185
176
  setStreamBuffer('');
177
+ streamBufferRef.current = '';
178
+ setStreaming(false);
186
179
  setTools([]);
187
- pendingMessages.current = Math.max(0, pendingMessages.current - 1);
188
- if (pendingMessages.current === 0) {
189
- setStreaming(false);
190
- }
191
180
  setMessages((msgs) => [
192
181
  ...msgs,
193
182
  {
@@ -222,28 +211,28 @@ export function useChat(ws: WsClient | null) {
222
211
  audioData: audioData ? (audioData.startsWith('data:') ? audioData : `data:audio/webm;base64,${audioData}`) : undefined,
223
212
  attachments: optimisticAttachments,
224
213
  };
225
- // If bot is currently streaming, commit the partial response as a message
226
- // so the user's new message appears BELOW the bot's in-progress text
227
- if (streamBufferRef.current) {
228
- const pid = 'partial-' + Date.now();
229
- partialMsgId.current = pid;
230
- const partialContent = streamBufferRef.current;
214
+
215
+ // If bot is currently streaming, commit the partial response as a completed message
216
+ // so the user's new message appears BELOW the bot's in-progress text (chronological order)
217
+ const partialContent = streamBufferRef.current;
218
+ if (partialContent) {
219
+ console.log('[useChat] Committing partial stream buffer as message before user send');
231
220
  setMessages((msgs) => [
232
221
  ...msgs,
233
222
  {
234
- id: pid,
223
+ id: 'partial-' + Date.now(),
235
224
  role: 'assistant' as const,
236
225
  content: partialContent,
237
226
  timestamp: new Date().toISOString(),
238
227
  },
228
+ userMsg,
239
229
  ]);
240
230
  setStreamBuffer('');
241
231
  streamBufferRef.current = '';
232
+ } else {
233
+ setMessages((msgs) => [...msgs, userMsg]);
242
234
  }
243
235
 
244
- setMessages((msgs) => [...msgs, userMsg]);
245
- pendingMessages.current += 1;
246
-
247
236
  // Build WS payload
248
237
  const payload: any = { conversationId, content };
249
238
  if (audioData) {
@@ -273,8 +262,6 @@ export function useChat(ws: WsClient | null) {
273
262
  setStreamBuffer('');
274
263
  streamBufferRef.current = '';
275
264
  setTools([]);
276
- pendingMessages.current = 0;
277
- partialMsgId.current = null;
278
265
  }, [ws, conversationId]);
279
266
 
280
267
  const clearContext = useCallback(() => {
@@ -284,8 +271,6 @@ export function useChat(ws: WsClient | null) {
284
271
  streamBufferRef.current = '';
285
272
  setStreaming(false);
286
273
  setTools([]);
287
- pendingMessages.current = 0;
288
- partialMsgId.current = null;
289
274
  prevConvId.current = null;
290
275
  loaded.current = false;
291
276
  fetch('/api/context/clear', { method: 'POST' }).catch(() => {});
@@ -1167,7 +1167,7 @@ ${!connected ? '<script>setTimeout(()=>location.reload(),4000)</script>' : ''}
1167
1167
  waChunkBuf = '';
1168
1168
  }
1169
1169
 
1170
- // Agent finished a turn — handle backend restart + state cleanup
1170
+ // Agent finished a turn — handle backend restart + notify client
1171
1171
  if (type === 'bot:turn-complete') {
1172
1172
  log.info(`[orchestrator] ──── TURN COMPLETE ────`);
1173
1173
  log.info(`[orchestrator] File tools used: ${eventData.usedFileTools}`);
@@ -1186,7 +1186,9 @@ ${!connected ? '<script>setTimeout(()=>location.reload(),4000)</script>' : ''}
1186
1186
  pendingUpdate = false;
1187
1187
  runDeferredUpdate();
1188
1188
  }
1189
- return; // don't forward to client
1189
+ // Tell the client the agent is idle — streaming can stop
1190
+ broadcastBloby('bot:idle', { conversationId: convId });
1191
+ return;
1190
1192
  }
1191
1193
 
1192
1194
  // Conversation ended (query loop exited)