@@ -49,6 +49,7 @@ let refreshChat: typeof import("./app-chat.ts").refreshChat;
4949let refreshChatAvatar : typeof import ( "./app-chat.ts" ) . refreshChatAvatar ;
5050let clearPendingQueueItemsForRun : typeof import ( "./app-chat.ts" ) . clearPendingQueueItemsForRun ;
5151let removeQueuedMessage : typeof import ( "./app-chat.ts" ) . removeQueuedMessage ;
52+ let markQueuedChatSendsWaitingForReconnect : typeof import ( "./app-chat.ts" ) . markQueuedChatSendsWaitingForReconnect ;
5253
5354async function loadChatHelpers ( ) : Promise < void > {
5455 ( {
@@ -61,6 +62,7 @@ async function loadChatHelpers(): Promise<void> {
6162 refreshChatAvatar,
6263 clearPendingQueueItemsForRun,
6364 removeQueuedMessage,
65+ markQueuedChatSendsWaitingForReconnect,
6466 } = await import ( "./app-chat.ts" ) ) ;
6567}
6668
@@ -132,6 +134,7 @@ function makeHost(overrides?: Partial<ChatHost>): ChatHost {
132134 chatDraftBeforeHistory : null ,
133135 chatAttachments : [ ] ,
134136 chatQueue : [ ] ,
137+ chatQueueBySession : { } ,
135138 chatRunId : null ,
136139 chatSending : false ,
137140 lastError : null ,
@@ -1292,6 +1295,44 @@ describe("handleSendChat", () => {
12921295 expect ( userMessage . role ) . toBe ( "user" ) ;
12931296 } ) ;
12941297
1298+ it ( "keeps delayed chat.send ACK effects scoped to the submitted session" , async ( ) => {
1299+ const sent = createDeferred < unknown > ( ) ;
1300+ const request = vi . fn ( ( method : string ) => {
1301+ if ( method === "chat.send" ) {
1302+ return sent . promise ;
1303+ }
1304+ throw new Error ( `Unexpected request: ${ method } ` ) ;
1305+ } ) ;
1306+ const host = makeHost ( {
1307+ client : { request } as unknown as ChatHost [ "client" ] ,
1308+ chatMessage : "stay with session A" ,
1309+ sessionKey : "agent:a" ,
1310+ } ) ;
1311+
1312+ const send = handleSendChat ( host ) ;
1313+ await Promise . resolve ( ) ;
1314+
1315+ const queuedRunId = host . chatQueue [ 0 ] ?. sendRunId ;
1316+ expect ( queuedRunId ) . toEqual ( expect . any ( String ) ) ;
1317+
1318+ host . chatQueueBySession = { "agent:a" : [ ...host . chatQueue ] } ;
1319+ host . chatQueue = [ ] ;
1320+ host . sessionKey = "agent:b" ;
1321+ host . chatMessages = [ ] ;
1322+ host . chatRunId = null ;
1323+ host . chatStream = null ;
1324+
1325+ sent . resolve ( { runId : queuedRunId , status : "started" } ) ;
1326+ await send ;
1327+
1328+ expect ( host . sessionKey ) . toBe ( "agent:b" ) ;
1329+ expect ( host . chatMessages ) . toStrictEqual ( [ ] ) ;
1330+ expect ( host . chatRunId ) . toBeNull ( ) ;
1331+ expect ( host . chatStream ) . toBeNull ( ) ;
1332+ expect ( host . chatQueue ) . toStrictEqual ( [ ] ) ;
1333+ expect ( host . chatQueueBySession ?. [ "agent:a" ] ) . toBeUndefined ( ) ;
1334+ } ) ;
1335+
12951336 it ( "keeps a pre-ack socket close recoverable with the same run id" , async ( ) => {
12961337 const request = vi . fn ( ( method : string ) => {
12971338 if ( method === "chat.send" ) {
@@ -1335,6 +1376,31 @@ describe("handleSendChat", () => {
13351376 expect ( host . chatQueue [ 0 ] ?. sendRunId ) . toEqual ( expect . any ( String ) ) ;
13361377 } ) ;
13371378
1379+ it ( "marks saved session queued sends waiting after a disconnect" , ( ) => {
1380+ const host = makeHost ( {
1381+ chatQueue : [ ] ,
1382+ chatQueueBySession : {
1383+ "agent:a" : [
1384+ {
1385+ id : "pending-send-a" ,
1386+ text : "pending" ,
1387+ createdAt : 1 ,
1388+ sendRunId : "run-a" ,
1389+ sendState : "sending" ,
1390+ sessionKey : "agent:a" ,
1391+ } ,
1392+ ] ,
1393+ } ,
1394+ } ) ;
1395+
1396+ markQueuedChatSendsWaitingForReconnect ( host ) ;
1397+
1398+ expect ( host . chatQueueBySession ?. [ "agent:a" ] ?. [ 0 ] ) . toMatchObject ( {
1399+ sendRunId : "run-a" ,
1400+ sendState : "waiting-reconnect" ,
1401+ } ) ;
1402+ } ) ;
1403+
13381404 it ( "marks validation failures visible and restores the composer" , async ( ) => {
13391405 const request = vi . fn ( ( method : string ) => {
13401406 if ( method === "chat.send" ) {
0 commit comments