agentgui 1.0.598 → 1.0.600

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/database.js CHANGED
@@ -949,7 +949,15 @@ export const queries = {
949
949
  return result.changes || 0;
950
950
  },
951
951
 
952
- createEvent(type, data, conversationId = null, sessionId = null) {
952
+ createEvent(type, data, conversationId = null, sessionId = null, idempotencyKey = null) {
953
+ if (idempotencyKey) {
954
+ const cached = this.getIdempotencyKey(idempotencyKey);
955
+ if (cached) {
956
+ console.log(`[event-idempotency] Event already exists for key ${idempotencyKey}, returning cached`);
957
+ return JSON.parse(cached);
958
+ }
959
+ }
960
+
953
961
  const id = generateId('evt');
954
962
  const now = Date.now();
955
963
 
@@ -958,7 +966,7 @@ export const queries = {
958
966
  );
959
967
  stmt.run(id, type, conversationId, sessionId, JSON.stringify(data), now);
960
968
 
961
- return {
969
+ const event = {
962
970
  id,
963
971
  type,
964
972
  conversationId,
@@ -966,6 +974,12 @@ export const queries = {
966
974
  data,
967
975
  created_at: now
968
976
  };
977
+
978
+ if (idempotencyKey) {
979
+ this.setIdempotencyKey(idempotencyKey, event);
980
+ }
981
+
982
+ return event;
969
983
  },
970
984
 
971
985
  getEvent(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.598",
3
+ "version": "1.0.600",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -4243,16 +4243,28 @@ wsRouter.onLegacy((data, ws) => {
4243
4243
  timestamp: Date.now()
4244
4244
  }));
4245
4245
 
4246
+ // Notify client if this conversation has an active streaming execution
4247
+ if (data.conversationId && activeExecutions.has(data.conversationId)) {
4248
+ const execution = activeExecutions.get(data.conversationId);
4249
+ const conv = queries.getConversation(data.conversationId);
4250
+ ws.send(JSON.stringify({
4251
+ type: 'streaming_start',
4252
+ sessionId: execution.sessionId,
4253
+ conversationId: data.conversationId,
4254
+ agentId: conv?.agentType || conv?.agentId || 'claude-code',
4255
+ resumed: true,
4256
+ timestamp: Date.now()
4257
+ }));
4258
+ }
4259
+
4246
4260
  // Inject pending checkpoint events if this is a conversation subscription
4247
4261
  if (data.conversationId && checkpointManager.hasPendingCheckpoint(data.conversationId)) {
4248
4262
  const checkpoint = checkpointManager.getPendingCheckpoint(data.conversationId);
4249
4263
  if (checkpoint) {
4250
4264
  console.log(`[checkpoint] Injecting ${checkpoint.events.length} events to client for ${data.conversationId}`);
4251
4265
 
4252
- // Get the session to use for the injection
4253
4266
  const latestSession = queries.getLatestSession(data.conversationId);
4254
4267
  if (latestSession) {
4255
- // Send streaming_resumed event first
4256
4268
  ws.send(JSON.stringify({
4257
4269
  type: 'streaming_resumed',
4258
4270
  sessionId: latestSession.id,
@@ -4263,7 +4275,6 @@ wsRouter.onLegacy((data, ws) => {
4263
4275
  timestamp: Date.now()
4264
4276
  }));
4265
4277
 
4266
- // Inject each checkpoint event
4267
4278
  checkpointManager.injectCheckpointEvents(latestSession.id, checkpoint, (evt) => {
4268
4279
  ws.send(JSON.stringify({
4269
4280
  ...evt,
@@ -696,6 +696,9 @@ class AgentGUIClient {
696
696
  case 'streaming_start':
697
697
  this.handleStreamingStart(data).catch(e => console.error('handleStreamingStart error:', e));
698
698
  break;
699
+ case 'streaming_resumed':
700
+ this.handleStreamingResumed(data).catch(e => console.error('handleStreamingResumed error:', e));
701
+ break;
699
702
  case 'streaming_progress':
700
703
  this.handleStreamingProgress(data);
701
704
  break;
@@ -920,6 +923,19 @@ class AgentGUIClient {
920
923
  this.emit('streaming:start', data);
921
924
  }
922
925
 
926
+ async handleStreamingResumed(data) {
927
+ console.log('Streaming resumed:', data);
928
+ const conv = this.state.currentConversation || { id: data.conversationId };
929
+ await this.handleStreamingStart({
930
+ type: 'streaming_start',
931
+ sessionId: data.sessionId,
932
+ conversationId: data.conversationId,
933
+ agentId: conv.agentType || conv.agentId || 'claude-code',
934
+ resumed: true,
935
+ timestamp: data.timestamp
936
+ });
937
+ }
938
+
923
939
  handleStreamingProgress(data) {
924
940
  // NOTE: With chunk-based architecture, blocks are rendered from polling
925
941
  // This handler is kept for backward compatibility and to trigger polling updates
@@ -3284,122 +3300,3 @@ document.addEventListener('DOMContentLoaded', async () => {
3284
3300
  if (typeof module !== 'undefined' && module.exports) {
3285
3301
  module.exports = AgentGUIClient;
3286
3302
  }
3287
- // PHASE 2: Generate unique request ID
3288
- _generateRequestId() {
3289
- return ++this._currentRequestId;
3290
- }
3291
-
3292
- // PHASE 2: Make a load request with tracking
3293
- _makeLoadRequest(conversationId) {
3294
- const requestId = this._generateRequestId();
3295
- const abortController = new AbortController();
3296
-
3297
- // Cancel previous request for this conversation if exists
3298
- const prev = this._loadInProgress[conversationId];
3299
- if (prev?.abortController) {
3300
- prev.abortController.abort();
3301
- }
3302
-
3303
- this._loadInProgress[conversationId] = {
3304
- requestId,
3305
- abortController,
3306
- timestamp: Date.now(),
3307
- conversationId
3308
- };
3309
-
3310
- return { requestId, abortController };
3311
- }
3312
-
3313
- // PHASE 2: Verify request is current before rendering
3314
- _isCurrentRequest(conversationId, requestId) {
3315
- const current = this._loadInProgress[conversationId];
3316
- return current?.requestId === requestId;
3317
- }
3318
-
3319
- // PHASE 3: Queue WebSocket message based on priority
3320
- _queueWebSocketMessage(data) {
3321
- const highPriorityTypes = ['conversation_deleted', 'all_conversations_deleted'];
3322
-
3323
- if (highPriorityTypes.includes(data.type)) {
3324
- this._highPriorityQueue.push(data);
3325
- } else {
3326
- this._lowPriorityQueue.push(data);
3327
- }
3328
- }
3329
-
3330
- // PHASE 3: Process queued WebSocket messages
3331
- _drainMessageQueues() {
3332
- // Process high-priority first (deletions)
3333
- while (this._highPriorityQueue.length > 0) {
3334
- const msg = this._highPriorityQueue.shift();
3335
- this._processWebSocketMessageDirect(msg);
3336
- }
3337
-
3338
- // Then process low-priority (metadata)
3339
- while (this._lowPriorityQueue.length > 0) {
3340
- const msg = this._lowPriorityQueue.shift();
3341
- this._processWebSocketMessageDirect(msg);
3342
- }
3343
- }
3344
-
3345
- // PHASE 3: Direct WebSocket message processing (extracted from switch)
3346
- _processWebSocketMessageDirect(data) {
3347
- switch (data.type) {
3348
- case 'streaming_start':
3349
- this.handleStreamingStart(data).catch(e => console.error('handleStreamingStart error:', e));
3350
- break;
3351
- case 'streaming_progress':
3352
- this.handleStreamingProgress(data);
3353
- break;
3354
- case 'streaming_complete':
3355
- this.handleStreamingComplete(data);
3356
- break;
3357
- case 'streaming_error':
3358
- this.handleStreamingError(data);
3359
- break;
3360
- case 'conversation_created':
3361
- this.handleConversationCreated(data);
3362
- break;
3363
- case 'conversation_deleted':
3364
- this.handleConversationDeleted(data);
3365
- break;
3366
- case 'all_conversations_deleted':
3367
- this.handleAllConversationsDeleted(data);
3368
- break;
3369
- case 'message_created':
3370
- this.handleMessageCreated(data);
3371
- break;
3372
- case 'conversation_updated':
3373
- this.handleConversationUpdated(data);
3374
- break;
3375
- case 'message_updated':
3376
- this.handleMessageUpdated(data);
3377
- break;
3378
- default:
3379
- // Other types handled elsewhere
3380
- break;
3381
- }
3382
- }
3383
-
3384
- // PHASE 4: Track streaming event sequence
3385
- _recordStreamingSequence(sessionId, sequence) {
3386
- this._lastProcessedSequence[sessionId] = sequence;
3387
- }
3388
-
3389
- // PHASE 4: Verify streaming event is current and in-order
3390
- _isValidStreamingEvent(event) {
3391
- // Must be for current session
3392
- if (event.sessionId !== this.state.currentSession?.id) {
3393
- return false;
3394
- }
3395
-
3396
- // Check sequence number
3397
- const lastSeq = this._lastProcessedSequence[event.sessionId] || -1;
3398
- if (event.sequence !== undefined && event.sequence <= lastSeq) {
3399
- return false; // Duplicate or out-of-order
3400
- }
3401
-
3402
- return true;
3403
- }
3404
-
3405
-