bloby-bot 0.21.4 → 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.4",
3
+ "version": "0.21.6",
4
4
  "releaseNotes": [
5
5
  "1. react router implemented",
6
6
  "2. new workspace design",
@@ -37,8 +37,8 @@ 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);
40
+ /** Ref to current streamBuffer (avoids stale closures in callbacks) */
41
+ const streamBufferRef = useRef('');
42
42
 
43
43
  // Load current conversation from DB on mount
44
44
  useEffect(() => {
@@ -126,13 +126,18 @@ export function useChat(ws: WsClient | null) {
126
126
 
127
127
  const unsubs = [
128
128
  ws.on('bot:typing', () => {
129
+ console.log('[useChat] bot:typing → streaming=true');
129
130
  setStreaming(true);
130
131
  setTools([]);
131
132
  }),
132
133
  ws.on('bot:token', (data: { token: string }) => {
133
- // Auto-detect new turn — if tokens arrive after a response cleared streaming
134
+ // Auto-detect new turn — tokens arriving means agent is working
134
135
  setStreaming(true);
135
- setStreamBuffer((buf) => buf + data.token);
136
+ setStreamBuffer((buf) => {
137
+ const next = buf + data.token;
138
+ streamBufferRef.current = next;
139
+ return next;
140
+ });
136
141
  }),
137
142
  ws.on('bot:tool', (data: { name: string; input?: any; status?: string }) => {
138
143
  setTools((prev) => {
@@ -142,7 +147,10 @@ export function useChat(ws: WsClient | null) {
142
147
  });
143
148
  }),
144
149
  ws.on('bot:response', (data: { conversationId: string; messageId?: string; content: string }) => {
150
+ console.log('[useChat] bot:response — adding message bubble');
145
151
  setConversationId(data.conversationId);
152
+
153
+ // Always add as a new bubble
146
154
  setMessages((msgs) => [
147
155
  ...msgs,
148
156
  {
@@ -152,21 +160,23 @@ export function useChat(ws: WsClient | null) {
152
160
  timestamp: new Date().toISOString(),
153
161
  },
154
162
  ]);
163
+
155
164
  setStreamBuffer('');
165
+ streamBufferRef.current = '';
156
166
  setTools([]);
157
- // Only stop streaming when all pending messages have been answered
158
- pendingMessages.current = Math.max(0, pendingMessages.current - 1);
159
- if (pendingMessages.current === 0) {
160
- setStreaming(false);
161
- }
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);
162
173
  }),
163
174
  ws.on('bot:error', (data: { error: string }) => {
175
+ console.log('[useChat] bot:error');
164
176
  setStreamBuffer('');
177
+ streamBufferRef.current = '';
178
+ setStreaming(false);
165
179
  setTools([]);
166
- pendingMessages.current = Math.max(0, pendingMessages.current - 1);
167
- if (pendingMessages.current === 0) {
168
- setStreaming(false);
169
- }
170
180
  setMessages((msgs) => [
171
181
  ...msgs,
172
182
  {
@@ -201,8 +211,27 @@ export function useChat(ws: WsClient | null) {
201
211
  audioData: audioData ? (audioData.startsWith('data:') ? audioData : `data:audio/webm;base64,${audioData}`) : undefined,
202
212
  attachments: optimisticAttachments,
203
213
  };
204
- setMessages((msgs) => [...msgs, userMsg]);
205
- pendingMessages.current += 1;
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');
220
+ setMessages((msgs) => [
221
+ ...msgs,
222
+ {
223
+ id: 'partial-' + Date.now(),
224
+ role: 'assistant' as const,
225
+ content: partialContent,
226
+ timestamp: new Date().toISOString(),
227
+ },
228
+ userMsg,
229
+ ]);
230
+ setStreamBuffer('');
231
+ streamBufferRef.current = '';
232
+ } else {
233
+ setMessages((msgs) => [...msgs, userMsg]);
234
+ }
206
235
 
207
236
  // Build WS payload
208
237
  const payload: any = { conversationId, content };
@@ -231,17 +260,17 @@ export function useChat(ws: WsClient | null) {
231
260
  ws.send('user:stop', { conversationId });
232
261
  setStreaming(false);
233
262
  setStreamBuffer('');
263
+ streamBufferRef.current = '';
234
264
  setTools([]);
235
- pendingMessages.current = 0;
236
265
  }, [ws, conversationId]);
237
266
 
238
267
  const clearContext = useCallback(() => {
239
268
  setMessages([]);
240
269
  setConversationId(null);
241
270
  setStreamBuffer('');
271
+ streamBufferRef.current = '';
242
272
  setStreaming(false);
243
273
  setTools([]);
244
- pendingMessages.current = 0;
245
274
  prevConvId.current = null;
246
275
  loaded.current = false;
247
276
  fetch('/api/context/clear', { method: 'POST' }).catch(() => {});
@@ -80,7 +80,7 @@ const SW_JS = `// Service worker — app-shell caching + push notifications
80
80
  // JS/CSS modules → stale-while-revalidate
81
81
  // API, WebSocket, Vite internals → network-only (no cache)
82
82
 
83
- var CACHE = 'bloby-v5';
83
+ var CACHE = 'bloby-v6';
84
84
  var HASHED_RE = new RegExp('/assets/.+-[a-zA-Z0-9]{6,}[.](js|css)$');
85
85
 
86
86
  // Precache the HTML shell on install so the cache is never empty.
@@ -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)