agentgui 1.0.140 → 1.0.141

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/static/js/client.js +104 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.140",
3
+ "version": "1.0.141",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -348,7 +348,7 @@ class AgentGUIClient {
348
348
 
349
349
  switch (data.type) {
350
350
  case 'streaming_start':
351
- this.handleStreamingStart(data);
351
+ this.handleStreamingStart(data).catch(e => console.error('handleStreamingStart error:', e));
352
352
  break;
353
353
  case 'streaming_progress':
354
354
  this.handleStreamingProgress(data);
@@ -388,7 +388,7 @@ class AgentGUIClient {
388
388
  }
389
389
  }
390
390
 
391
- handleStreamingStart(data) {
391
+ async handleStreamingStart(data) {
392
392
  console.log('Streaming started:', data);
393
393
 
394
394
  // If this streaming event is for a different conversation than what we are viewing,
@@ -420,8 +420,60 @@ class AgentGUIClient {
420
420
  if (outputEl) {
421
421
  let messagesEl = outputEl.querySelector('.conversation-messages');
422
422
  if (!messagesEl) {
423
- outputEl.innerHTML = '<div class="conversation-messages"></div>';
423
+ // Load existing conversation history before starting the stream
424
+ const conv = this.state.currentConversation;
425
+ const wdInfo = conv?.workingDirectory ? ` - ${this.escapeHtml(conv.workingDirectory)}` : '';
426
+ outputEl.innerHTML = `
427
+ <div class="conversation-header">
428
+ <h2>${this.escapeHtml(conv?.title || 'Conversation')}</h2>
429
+ <p class="text-secondary">${conv?.agentType || 'unknown'} - ${new Date(conv?.created_at || Date.now()).toLocaleDateString()}${wdInfo}</p>
430
+ </div>
431
+ <div class="conversation-messages"></div>
432
+ `;
424
433
  messagesEl = outputEl.querySelector('.conversation-messages');
434
+ // Load prior messages into the container
435
+ try {
436
+ const msgResp = await fetch(window.__BASE_URL + `/api/conversations/${data.conversationId}/messages`);
437
+ if (msgResp.ok) {
438
+ const msgData = await msgResp.json();
439
+ const priorChunks = await this.fetchChunks(data.conversationId, 0);
440
+ if (priorChunks.length > 0) {
441
+ const userMsgs = (msgData.messages || []).filter(m => m.role === 'user');
442
+ const sessionOrder = [];
443
+ const sessionGroups = {};
444
+ priorChunks.forEach(c => {
445
+ if (!sessionGroups[c.sessionId]) { sessionGroups[c.sessionId] = []; sessionOrder.push(c.sessionId); }
446
+ sessionGroups[c.sessionId].push(c);
447
+ });
448
+ let ui = 0;
449
+ sessionOrder.forEach(sid => {
450
+ const sList = sessionGroups[sid];
451
+ const sStart = sList[0].created_at;
452
+ while (ui < userMsgs.length && userMsgs[ui].created_at <= sStart) {
453
+ const m = userMsgs[ui++];
454
+ messagesEl.insertAdjacentHTML('beforeend', `<div class="message message-user" data-msg-id="${m.id}"><div class="message-role">User</div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div></div>`);
455
+ }
456
+ const mDiv = document.createElement('div');
457
+ mDiv.className = 'message message-assistant';
458
+ mDiv.id = `message-${sid}`;
459
+ mDiv.innerHTML = '<div class="message-role">Assistant</div><div class="message-blocks streaming-blocks"></div>';
460
+ const bEl = mDiv.querySelector('.message-blocks');
461
+ sList.forEach(chunk => { if (chunk.block?.type) { const el = this.renderer.renderBlock(chunk.block, chunk); if (el) bEl.appendChild(el); } });
462
+ const ts = document.createElement('div'); ts.className = 'message-timestamp'; ts.textContent = new Date(sList[sList.length - 1].created_at).toLocaleString();
463
+ mDiv.appendChild(ts);
464
+ messagesEl.appendChild(mDiv);
465
+ });
466
+ while (ui < userMsgs.length) {
467
+ const m = userMsgs[ui++];
468
+ messagesEl.insertAdjacentHTML('beforeend', `<div class="message message-user" data-msg-id="${m.id}"><div class="message-role">User</div>${this.renderMessageContent(m.content)}<div class="message-timestamp">${new Date(m.created_at).toLocaleString()}</div></div>`);
469
+ }
470
+ } else {
471
+ messagesEl.innerHTML = this.renderMessages(msgData.messages || []);
472
+ }
473
+ }
474
+ } catch (e) {
475
+ console.warn('Failed to load prior messages for streaming view:', e);
476
+ }
425
477
  }
426
478
  const streamingDiv = document.createElement('div');
427
479
  streamingDiv.className = 'message message-assistant streaming-message';
@@ -1181,17 +1233,48 @@ class AgentGUIClient {
1181
1233
  // Render all chunks
1182
1234
  const messagesEl = outputEl.querySelector('.conversation-messages');
1183
1235
  if (chunks.length > 0) {
1184
- // Group chunks by session
1236
+ // Fetch user messages to interleave with session chunks
1237
+ let userMessages = [];
1238
+ try {
1239
+ const msgResp = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/messages`);
1240
+ if (msgResp.ok) {
1241
+ const msgData = await msgResp.json();
1242
+ userMessages = (msgData.messages || []).filter(m => m.role === 'user');
1243
+ }
1244
+ } catch (_) {}
1245
+
1246
+ // Group chunks by session, preserving order
1247
+ const sessionOrder = [];
1185
1248
  const sessionChunks = {};
1186
1249
  chunks.forEach(chunk => {
1187
1250
  if (!sessionChunks[chunk.sessionId]) {
1188
1251
  sessionChunks[chunk.sessionId] = [];
1252
+ sessionOrder.push(chunk.sessionId);
1189
1253
  }
1190
1254
  sessionChunks[chunk.sessionId].push(chunk);
1191
1255
  });
1192
1256
 
1193
- // Render each session's chunks
1194
- Object.entries(sessionChunks).forEach(([sessionId, sessionChunkList]) => {
1257
+ // Build a timeline: match user messages to sessions by timestamp
1258
+ let userMsgIdx = 0;
1259
+ sessionOrder.forEach((sessionId) => {
1260
+ const sessionChunkList = sessionChunks[sessionId];
1261
+ const sessionStart = sessionChunkList[0].created_at;
1262
+
1263
+ // Render user messages that came before this session
1264
+ while (userMsgIdx < userMessages.length && userMessages[userMsgIdx].created_at <= sessionStart) {
1265
+ const msg = userMessages[userMsgIdx];
1266
+ const userDiv = document.createElement('div');
1267
+ userDiv.className = 'message message-user';
1268
+ userDiv.setAttribute('data-msg-id', msg.id);
1269
+ userDiv.innerHTML = `
1270
+ <div class="message-role">User</div>
1271
+ ${this.renderMessageContent(msg.content)}
1272
+ <div class="message-timestamp">${new Date(msg.created_at).toLocaleString()}</div>
1273
+ `;
1274
+ messagesEl.appendChild(userDiv);
1275
+ userMsgIdx++;
1276
+ }
1277
+
1195
1278
  const isCurrentActiveSession = shouldResumeStreaming && latestSession && latestSession.id === sessionId;
1196
1279
  const messageDiv = document.createElement('div');
1197
1280
  messageDiv.className = `message message-assistant${isCurrentActiveSession ? ' streaming-message' : ''}`;
@@ -1208,7 +1291,6 @@ class AgentGUIClient {
1208
1291
  }
1209
1292
  });
1210
1293
 
1211
- // Add streaming indicator for active session
1212
1294
  if (isCurrentActiveSession) {
1213
1295
  const indicatorDiv = document.createElement('div');
1214
1296
  indicatorDiv.className = 'streaming-indicator';
@@ -1227,6 +1309,21 @@ class AgentGUIClient {
1227
1309
 
1228
1310
  messagesEl.appendChild(messageDiv);
1229
1311
  });
1312
+
1313
+ // Render any remaining user messages after the last session
1314
+ while (userMsgIdx < userMessages.length) {
1315
+ const msg = userMessages[userMsgIdx];
1316
+ const userDiv = document.createElement('div');
1317
+ userDiv.className = 'message message-user';
1318
+ userDiv.setAttribute('data-msg-id', msg.id);
1319
+ userDiv.innerHTML = `
1320
+ <div class="message-role">User</div>
1321
+ ${this.renderMessageContent(msg.content)}
1322
+ <div class="message-timestamp">${new Date(msg.created_at).toLocaleString()}</div>
1323
+ `;
1324
+ messagesEl.appendChild(userDiv);
1325
+ userMsgIdx++;
1326
+ }
1230
1327
  } else {
1231
1328
  // Fall back to messages if no chunks
1232
1329
  const messagesResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/messages`);