@@ -57,6 +57,7 @@ export class AgentConnection {
57
57
58
58
private nextMessageId = 0 ;
59
59
private pendingMessages = new Map < number , PendingPitcherMessage < any , any > > ( ) ;
60
+ private messageQueue : PendingPitcherMessage < any , any > [ ] = [ ] ;
60
61
private notificationListeners : Record <
61
62
string ,
62
63
SliceList < ( params : any ) => void >
@@ -174,18 +175,29 @@ export class AgentConnection {
174
175
options : IRequestOptions = { }
175
176
) {
176
177
if ( this . _isDisposed ) {
177
- throw new Error ( "Cannot perform operation: SandboxClient has been disposed" ) ;
178
+ throw new Error (
179
+ "Cannot perform operation: SandboxClient has been disposed"
180
+ ) ;
178
181
}
179
-
182
+
180
183
const { timeoutMs } = options ;
181
184
const request = this . createRequest ( pitcherRequest , timeoutMs ) ;
182
185
186
+ // If not connected, queue the message for later
187
+ if ( this . state !== "CONNECTED" ) {
188
+ this . messageQueue . push ( request ) ;
189
+ return request . unwrap ( ) ;
190
+ }
191
+
183
192
try {
184
193
// This will throw if we are not in the right connection state
185
194
this . connection . send ( request . message ) ;
186
195
187
196
return request . unwrap ( ) ;
188
197
} catch ( error ) {
198
+ // If send fails, queue the message for retry on reconnect
199
+ this . messageQueue . push ( request ) ;
200
+
189
201
this . errorEmitter . fire ( {
190
202
message : ( error as Error ) . message ,
191
203
extras : {
@@ -195,9 +207,8 @@ export class AgentConnection {
195
207
} ,
196
208
} ) ;
197
209
198
- // We always want to return a promise from the method so it does not matter if the error is related to disconnect
199
- // or Pitcher giving an error. It all ends up in the `catch` of the unwrapped promise
200
- return Promise . reject ( error ) ;
210
+ // Return the queued message's promise instead of rejecting
211
+ return request . unwrap ( ) ;
201
212
}
202
213
}
203
214
@@ -267,6 +278,28 @@ export class AgentConnection {
267
278
} ) ;
268
279
269
280
this . state = "CONNECTED" ;
281
+
282
+ // Flush the message queue after successful reconnection
283
+ await this . flushMessageQueue ( ) ;
284
+ }
285
+
286
+ private async flushMessageQueue ( ) {
287
+ const queuedMessages = [ ...this . messageQueue ] ;
288
+ this . messageQueue = [ ] ;
289
+
290
+ for ( const message of queuedMessages ) {
291
+ if ( message . isDisposed ) {
292
+ // Skip messages that have already been disposed (timed out)
293
+ continue ;
294
+ }
295
+
296
+ try {
297
+ this . connection . send ( message . message ) ;
298
+ } catch ( error ) {
299
+ // If send still fails, reject the message
300
+ message . reject ( error as Error ) ;
301
+ }
302
+ }
270
303
}
271
304
272
305
dispose ( ) : void {
@@ -275,7 +308,15 @@ export class AgentConnection {
275
308
this . messageEmitter . dispose ( ) ;
276
309
this . connection . dispose ( ) ;
277
310
this . disposePendingMessages ( ) ;
311
+ this . disposeQueuedMessages ( ) ;
278
312
this . pendingMessages . clear ( ) ;
313
+ this . messageQueue = [ ] ;
279
314
this . notificationListeners = { } ;
280
315
}
316
+
317
+ private disposeQueuedMessages ( ) {
318
+ this . messageQueue . forEach ( ( queuedMessage ) => {
319
+ queuedMessage . dispose ( "Client disposed" ) ;
320
+ } ) ;
321
+ }
281
322
}
0 commit comments