@@ -65,6 +65,82 @@ describe('executeTool', () => {
6565 expect ( second . result ) . toMatchObject ( { tabs : [ ] } )
6666 } )
6767
68+ it ( 'keeps tool queues and tab state isolated by chat scope' , async ( ) => {
69+ await driver . executeTool ( 'chat-a' , 'browser_open_tab' , { } )
70+ await driver . executeTool ( 'chat-a' , 'browser_open_tab' , { } )
71+ await driver . executeTool ( 'chat-b' , 'browser_open_tab' , { } )
72+
73+ const chatA = await driver . executeTool ( 'chat-a' , 'browser_list_tabs' , { } )
74+ const chatB = await driver . executeTool ( 'chat-b' , 'browser_list_tabs' , { } )
75+
76+ expect ( chatA ) . toMatchObject ( {
77+ ok : true ,
78+ result : { scopeId : 'chat-a' , activeTabId : '2' , tabs : [ { tabId : '1' } , { tabId : '2' } ] } ,
79+ } )
80+ expect ( chatB ) . toMatchObject ( {
81+ ok : true ,
82+ result : { scopeId : 'chat-b' , activeTabId : '1' , tabs : [ { tabId : '1' } ] } ,
83+ } )
84+ } )
85+
86+ it ( 'adopts pending tabs over an activation-only durable destination' , async ( ) => {
87+ await driver . executeTool ( 'pending:new-chat' , 'browser_open_tab' , { } )
88+ driver . activateBrowserScope ( 'chat-real' )
89+
90+ expect ( driver . migrateBrowserScope ( 'pending:new-chat' , 'chat-real' ) ) . toBe ( true )
91+ await expect ( driver . executeTool ( 'chat-real' , 'browser_list_tabs' , { } ) ) . resolves . toMatchObject ( {
92+ ok : true ,
93+ result : { scopeId : 'chat-real' , tabs : [ { tabId : '1' } ] } ,
94+ } )
95+
96+ await driver . executeTool ( 'pending:other-chat' , 'browser_open_tab' , { } )
97+ await driver . executeTool ( 'chat-occupied' , 'browser_open_tab' , { } )
98+ expect ( driver . migrateBrowserScope ( 'pending:other-chat' , 'chat-occupied' ) ) . toBe ( false )
99+ } )
100+
101+ it ( 'keeps activation lazy, then restores and disposes through the driver API' , async ( ) => {
102+ const snapshot : session . BrowserSessionSnapshotV1 = {
103+ v : 1 ,
104+ tabs : [ { url : 'https://restored.example/' , pinned : false } ] ,
105+ activeIndex : 0 ,
106+ }
107+ const load = vi . fn ( ( ) => snapshot )
108+ const disposeScope = vi . fn ( )
109+ driver . initDriver (
110+ {
111+ onPageState : vi . fn ( ) ,
112+ onTabsState : vi . fn ( ) ,
113+ onSessionStatus : vi . fn ( ) ,
114+ onFillAvailability : vi . fn ( ) ,
115+ } ,
116+ ( ) => null ,
117+ undefined ,
118+ {
119+ load,
120+ save : vi . fn ( ) ,
121+ migrateScope : vi . fn ( ) ,
122+ disposeScope,
123+ }
124+ )
125+
126+ driver . activateBrowserScope ( 'chat-restored' )
127+ expect ( load ) . not . toHaveBeenCalled ( )
128+ expect ( session . withBrowserScope ( 'chat-restored' , ( ) => session . peekTabsState ( ) . tabs ) ) . toEqual (
129+ [ ]
130+ )
131+
132+ const listed = driver . restoreBrowserScope ( 'chat-restored' )
133+ expect ( load ) . toHaveBeenCalledWith ( 'chat-restored' )
134+ expect ( listed ) . toMatchObject ( {
135+ tabs : [ { url : 'https://restored.example/' } ] ,
136+ } )
137+ const restoredTab = session . withBrowserScope ( 'chat-restored' , ( ) => session . activeTab ( ) )
138+
139+ driver . disposeBrowserScope ( 'chat-restored' )
140+ expect ( restoredTab ?. view . webContents . close ) . toHaveBeenCalled ( )
141+ expect ( disposeScope ) . toHaveBeenCalledWith ( 'chat-restored' )
142+ } )
143+
68144 it . each ( [ '' , 'about:blank' ] ) (
69145 'fails page tools immediately and releases queued tab listing when the URL is %j' ,
70146 async ( url ) => {
0 commit comments