agentgui 1.0.597 → 1.0.598
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/lib/checkpoint-manager.js +31 -0
- package/package.json +1 -1
- package/server.js +35 -19
|
@@ -8,6 +8,7 @@ class CheckpointManager {
|
|
|
8
8
|
constructor(queries) {
|
|
9
9
|
this.queries = queries;
|
|
10
10
|
this._injectedSessions = new Set(); // Track which sessions already had checkpoints injected
|
|
11
|
+
this._pendingCheckpoints = new Map(); // Store checkpoints to inject on subscribe: { conversationId -> checkpoint }
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
@@ -146,6 +147,36 @@ class CheckpointManager {
|
|
|
146
147
|
reset() {
|
|
147
148
|
this._injectedSessions.clear();
|
|
148
149
|
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Store a checkpoint to be injected when client subscribes
|
|
153
|
+
* (Used during server startup when no clients are connected yet)
|
|
154
|
+
*/
|
|
155
|
+
storeCheckpointForDelay(conversationId, checkpoint) {
|
|
156
|
+
if (checkpoint && checkpoint.events && checkpoint.events.length > 0) {
|
|
157
|
+
this._pendingCheckpoints.set(conversationId, checkpoint);
|
|
158
|
+
console.log(`[checkpoint] Stored checkpoint for ${conversationId} to inject on subscribe (${checkpoint.events.length} events, ${checkpoint.chunks.length} chunks)`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get and remove a pending checkpoint (called when client subscribes)
|
|
164
|
+
*/
|
|
165
|
+
getPendingCheckpoint(conversationId) {
|
|
166
|
+
const checkpoint = this._pendingCheckpoints.get(conversationId);
|
|
167
|
+
if (checkpoint) {
|
|
168
|
+
this._pendingCheckpoints.delete(conversationId);
|
|
169
|
+
console.log(`[checkpoint] Retrieved pending checkpoint for ${conversationId}`);
|
|
170
|
+
}
|
|
171
|
+
return checkpoint;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Check if there's a pending checkpoint for a conversation
|
|
176
|
+
*/
|
|
177
|
+
hasPendingCheckpoint(conversationId) {
|
|
178
|
+
return this._pendingCheckpoints.has(conversationId);
|
|
179
|
+
}
|
|
149
180
|
}
|
|
150
181
|
|
|
151
182
|
export default CheckpointManager;
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -4242,6 +4242,38 @@ wsRouter.onLegacy((data, ws) => {
|
|
|
4242
4242
|
conversationId: data.conversationId,
|
|
4243
4243
|
timestamp: Date.now()
|
|
4244
4244
|
}));
|
|
4245
|
+
|
|
4246
|
+
// Inject pending checkpoint events if this is a conversation subscription
|
|
4247
|
+
if (data.conversationId && checkpointManager.hasPendingCheckpoint(data.conversationId)) {
|
|
4248
|
+
const checkpoint = checkpointManager.getPendingCheckpoint(data.conversationId);
|
|
4249
|
+
if (checkpoint) {
|
|
4250
|
+
console.log(`[checkpoint] Injecting ${checkpoint.events.length} events to client for ${data.conversationId}`);
|
|
4251
|
+
|
|
4252
|
+
// Get the session to use for the injection
|
|
4253
|
+
const latestSession = queries.getLatestSession(data.conversationId);
|
|
4254
|
+
if (latestSession) {
|
|
4255
|
+
// Send streaming_resumed event first
|
|
4256
|
+
ws.send(JSON.stringify({
|
|
4257
|
+
type: 'streaming_resumed',
|
|
4258
|
+
sessionId: latestSession.id,
|
|
4259
|
+
conversationId: data.conversationId,
|
|
4260
|
+
resumeFrom: checkpoint.sessionId,
|
|
4261
|
+
eventCount: checkpoint.events.length,
|
|
4262
|
+
chunkCount: checkpoint.chunks.length,
|
|
4263
|
+
timestamp: Date.now()
|
|
4264
|
+
}));
|
|
4265
|
+
|
|
4266
|
+
// Inject each checkpoint event
|
|
4267
|
+
checkpointManager.injectCheckpointEvents(latestSession.id, checkpoint, (evt) => {
|
|
4268
|
+
ws.send(JSON.stringify({
|
|
4269
|
+
...evt,
|
|
4270
|
+
sessionId: latestSession.id,
|
|
4271
|
+
conversationId: data.conversationId
|
|
4272
|
+
}));
|
|
4273
|
+
});
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4245
4277
|
} else if (data.type === 'unsubscribe') {
|
|
4246
4278
|
if (data.sessionId) {
|
|
4247
4279
|
ws.subscriptions.delete(data.sessionId);
|
|
@@ -4585,27 +4617,11 @@ async function resumeInterruptedStreams() {
|
|
|
4585
4617
|
timestamp: Date.now()
|
|
4586
4618
|
});
|
|
4587
4619
|
|
|
4588
|
-
//
|
|
4620
|
+
// Store checkpoint to inject when client subscribes (not now, since no clients connected yet)
|
|
4589
4621
|
if (checkpoint) {
|
|
4590
|
-
|
|
4591
|
-
type: 'streaming_resumed',
|
|
4592
|
-
sessionId: session.id,
|
|
4593
|
-
conversationId: conv.id,
|
|
4594
|
-
resumeFrom: previousSessionId,
|
|
4595
|
-
eventCount: checkpoint.events.length,
|
|
4596
|
-
chunkCount: checkpoint.chunks.length,
|
|
4597
|
-
timestamp: Date.now()
|
|
4598
|
-
});
|
|
4599
|
-
|
|
4600
|
-
checkpointManager.injectCheckpointEvents(session.id, checkpoint, (evt) => {
|
|
4601
|
-
broadcastSync({
|
|
4602
|
-
...evt,
|
|
4603
|
-
sessionId: session.id,
|
|
4604
|
-
conversationId: conv.id
|
|
4605
|
-
});
|
|
4606
|
-
});
|
|
4607
|
-
|
|
4622
|
+
checkpointManager.storeCheckpointForDelay(conv.id, checkpoint);
|
|
4608
4623
|
checkpointManager.markSessionResumed(previousSessionId);
|
|
4624
|
+
console.log(`[RESUME] Checkpoint stored for ${conv.id}, will inject on next client subscribe`);
|
|
4609
4625
|
}
|
|
4610
4626
|
|
|
4611
4627
|
const messageId = lastMsg?.id || null;
|