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
|
@@ -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
|
-
/**
|
|
41
|
-
const
|
|
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 —
|
|
134
|
+
// Auto-detect new turn — tokens arriving means agent is working
|
|
134
135
|
setStreaming(true);
|
|
135
|
-
setStreamBuffer((buf) =>
|
|
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
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
205
|
-
|
|
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(() => {});
|
package/supervisor/index.ts
CHANGED
|
@@ -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-
|
|
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 +
|
|
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
|
-
|
|
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)
|