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 +16 -2
- package/package.json +1 -1
- package/server.js +14 -3
- package/static/js/client.js +16 -119
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
|
-
|
|
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
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,
|
package/static/js/client.js
CHANGED
|
@@ -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
|
-
|