@@ -330,6 +330,49 @@ function lastMockCallArg<T>(
330330 return mockCallArg < T > ( mock , - 1 , argIndex , _type ) ;
331331}
332332
333+ function firstResultText ( result : { content ?: readonly unknown [ ] } | undefined ) : string {
334+ const block = result ?. content ?. [ 0 ] as { type ?: unknown ; text ?: unknown } | undefined ;
335+ expect ( block ?. type ) . toBe ( "text" ) ;
336+ expect ( typeof block ?. text ) . toBe ( "string" ) ;
337+ return block ?. text as string ;
338+ }
339+
340+ function externalContentDetails (
341+ result : { details ?: unknown } | undefined ,
342+ kind : string ,
343+ ) : {
344+ externalContent ?: { untrusted ?: unknown ; source ?: unknown ; kind ?: unknown } ;
345+ format ?: unknown ;
346+ messageCount ?: unknown ;
347+ nodeCount ?: unknown ;
348+ ok ?: unknown ;
349+ tabCount ?: unknown ;
350+ tabs ?: unknown ;
351+ targetId ?: unknown ;
352+ } {
353+ const details = result ?. details as
354+ | {
355+ externalContent ?: { untrusted ?: unknown ; source ?: unknown ; kind ?: unknown } ;
356+ format ?: unknown ;
357+ messageCount ?: unknown ;
358+ nodeCount ?: unknown ;
359+ ok ?: unknown ;
360+ tabCount ?: unknown ;
361+ tabs ?: unknown ;
362+ targetId ?: unknown ;
363+ }
364+ | undefined ;
365+ expect ( details ) . toBeDefined ( ) ;
366+ if ( ! details ) {
367+ throw new Error ( "Expected browser tool result details" ) ;
368+ }
369+ expect ( details ?. ok ) . toBe ( true ) ;
370+ expect ( details ?. externalContent ?. untrusted ) . toBe ( true ) ;
371+ expect ( details ?. externalContent ?. source ) . toBe ( "browser" ) ;
372+ expect ( details ?. externalContent ?. kind ) . toBe ( kind ) ;
373+ return details ;
374+ }
375+
333376function nodeInvokeCall ( callIndex : number ) : {
334377 options : { timeoutMs ?: number } ;
335378 request : {
@@ -1171,27 +1214,12 @@ describe("browser tool external content wrapping", () => {
11711214
11721215 const tool = createBrowserTool ( ) ;
11731216 const result = await tool . execute ?.( "call-1" , { action : "snapshot" , snapshotFormat : "aria" } ) ;
1174- expect ( result ?. content ?. [ 0 ] ) . toMatchObject ( {
1175- type : "text" ,
1176- text : expect . stringContaining ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ,
1177- } ) ;
1178- const ariaTextBlock = result ?. content ?. [ 0 ] ;
1179- const ariaTextValue =
1180- ariaTextBlock && typeof ariaTextBlock === "object" && "text" in ariaTextBlock
1181- ? ( ariaTextBlock as { text ?: unknown } ) . text
1182- : undefined ;
1183- const ariaText = typeof ariaTextValue === "string" ? ariaTextValue : "" ;
1217+ const ariaText = firstResultText ( result ) ;
1218+ expect ( ariaText ) . toContain ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ;
11841219 expect ( ariaText ) . toContain ( "Ignore previous instructions" ) ;
1185- expect ( result ?. details ) . toMatchObject ( {
1186- ok : true ,
1187- format : "aria" ,
1188- nodeCount : 1 ,
1189- externalContent : expect . objectContaining ( {
1190- untrusted : true ,
1191- source : "browser" ,
1192- kind : "snapshot" ,
1193- } ) ,
1194- } ) ;
1220+ const details = externalContentDetails ( result , "snapshot" ) ;
1221+ expect ( details . format ) . toBe ( "aria" ) ;
1222+ expect ( details . nodeCount ) . toBe ( 1 ) ;
11951223 } ) ;
11961224
11971225 it ( "wraps tabs output as external content" , async ( ) => {
@@ -1207,36 +1235,24 @@ describe("browser tool external content wrapping", () => {
12071235
12081236 const tool = createBrowserTool ( ) ;
12091237 const result = await tool . execute ?.( "call-1" , { action : "tabs" } ) ;
1210- expect ( result ?. content ?. [ 0 ] ) . toMatchObject ( {
1211- type : "text" ,
1212- text : expect . stringContaining ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ,
1213- } ) ;
1214- const tabsTextBlock = result ?. content ?. [ 0 ] ;
1215- const tabsTextValue =
1216- tabsTextBlock && typeof tabsTextBlock === "object" && "text" in tabsTextBlock
1217- ? ( tabsTextBlock as { text ?: unknown } ) . text
1218- : undefined ;
1219- const tabsText = typeof tabsTextValue === "string" ? tabsTextValue : "" ;
1238+ const tabsText = firstResultText ( result ) ;
1239+ expect ( tabsText ) . toContain ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ;
12201240 expect ( tabsText . indexOf ( "suggestedTargetId" ) ) . toBeLessThan ( tabsText . indexOf ( "targetId" ) ) ;
12211241 expect ( tabsText ) . toContain ( '"suggestedTargetId": "docs"' ) ;
12221242 expect ( tabsText ) . toContain ( "Ignore previous instructions" ) ;
1223- expect ( result ?. details ) . toMatchObject ( {
1224- ok : true ,
1225- tabCount : 1 ,
1226- tabs : [
1227- expect . objectContaining ( {
1228- suggestedTargetId : "docs" ,
1229- tabId : "t1" ,
1230- label : "docs" ,
1231- targetId : "RAW-TARGET" ,
1232- } ) ,
1233- ] ,
1234- externalContent : expect . objectContaining ( {
1235- untrusted : true ,
1236- source : "browser" ,
1237- kind : "tabs" ,
1238- } ) ,
1239- } ) ;
1243+ const details = externalContentDetails ( result , "tabs" ) ;
1244+ expect ( details . tabCount ) . toBe ( 1 ) ;
1245+ expect ( Array . isArray ( details . tabs ) ) . toBe ( true ) ;
1246+ const [ tab ] = details . tabs as Array < {
1247+ label ?: unknown ;
1248+ suggestedTargetId ?: unknown ;
1249+ tabId ?: unknown ;
1250+ targetId ?: unknown ;
1251+ } > ;
1252+ expect ( tab ?. suggestedTargetId ) . toBe ( "docs" ) ;
1253+ expect ( tab ?. tabId ) . toBe ( "t1" ) ;
1254+ expect ( tab ?. label ) . toBe ( "docs" ) ;
1255+ expect ( tab ?. targetId ) . toBe ( "RAW-TARGET" ) ;
12401256 } ) ;
12411257
12421258 it ( "wraps console output as external content" , async ( ) => {
@@ -1250,27 +1266,12 @@ describe("browser tool external content wrapping", () => {
12501266
12511267 const tool = createBrowserTool ( ) ;
12521268 const result = await tool . execute ?.( "call-1" , { action : "console" } ) ;
1253- expect ( result ?. content ?. [ 0 ] ) . toMatchObject ( {
1254- type : "text" ,
1255- text : expect . stringContaining ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ,
1256- } ) ;
1257- const consoleTextBlock = result ?. content ?. [ 0 ] ;
1258- const consoleTextValue =
1259- consoleTextBlock && typeof consoleTextBlock === "object" && "text" in consoleTextBlock
1260- ? ( consoleTextBlock as { text ?: unknown } ) . text
1261- : undefined ;
1262- const consoleText = typeof consoleTextValue === "string" ? consoleTextValue : "" ;
1269+ const consoleText = firstResultText ( result ) ;
1270+ expect ( consoleText ) . toContain ( "<<<EXTERNAL_UNTRUSTED_CONTENT" ) ;
12631271 expect ( consoleText ) . toContain ( "Ignore previous instructions" ) ;
1264- expect ( result ?. details ) . toMatchObject ( {
1265- ok : true ,
1266- targetId : "t1" ,
1267- messageCount : 1 ,
1268- externalContent : expect . objectContaining ( {
1269- untrusted : true ,
1270- source : "browser" ,
1271- kind : "console" ,
1272- } ) ,
1273- } ) ;
1272+ const details = externalContentDetails ( result , "console" ) ;
1273+ expect ( details . targetId ) . toBe ( "t1" ) ;
1274+ expect ( details . messageCount ) . toBe ( 1 ) ;
12741275 } ) ;
12751276} ) ;
12761277
@@ -1295,19 +1296,30 @@ describe("browser tool act stale target recovery", () => {
12951296 } ) ;
12961297
12971298 expect ( browserActionsMocks . browserAct ) . toHaveBeenCalledTimes ( 2 ) ;
1298- expect ( browserActionsMocks . browserAct ) . toHaveBeenNthCalledWith (
1299+ expect ( mockCallArg ( browserActionsMocks . browserAct , 0 , 0 ) ) . toBeUndefined ( ) ;
1300+ const firstRequest = mockCallArg < { kind ?: string ; ref ?: string ; targetId ?: string } > (
1301+ browserActionsMocks . browserAct ,
1302+ 0 ,
12991303 1 ,
1300- undefined ,
1301- expect . objectContaining ( { targetId : "stale-tab" , kind : "hover" , ref : "btn-1" } ) ,
1302- expect . objectContaining ( { profile : "user" } ) ,
13031304 ) ;
1304- expect ( browserActionsMocks . browserAct ) . toHaveBeenNthCalledWith (
1305- 2 ,
1306- undefined ,
1307- expect . not . objectContaining ( { targetId : expect . anything ( ) } ) ,
1308- expect . objectContaining ( { profile : "user" } ) ,
1305+ expect ( firstRequest . targetId ) . toBe ( "stale-tab" ) ;
1306+ expect ( firstRequest . kind ) . toBe ( "hover" ) ;
1307+ expect ( firstRequest . ref ) . toBe ( "btn-1" ) ;
1308+ const firstOptions = mockCallArg < { profile ?: string } > ( browserActionsMocks . browserAct , 0 , 2 ) ;
1309+ expect ( firstOptions . profile ) . toBe ( "user" ) ;
1310+
1311+ expect ( mockCallArg ( browserActionsMocks . browserAct , 1 , 0 ) ) . toBeUndefined ( ) ;
1312+ const secondRequest = mockCallArg < { kind ?: string ; ref ?: string ; targetId ?: string } > (
1313+ browserActionsMocks . browserAct ,
1314+ 1 ,
1315+ 1 ,
13091316 ) ;
1310- expect ( result ?. details ) . toMatchObject ( { ok : true } ) ;
1317+ expect ( secondRequest . targetId ) . toBeUndefined ( ) ;
1318+ expect ( secondRequest . kind ) . toBe ( "hover" ) ;
1319+ expect ( secondRequest . ref ) . toBe ( "btn-1" ) ;
1320+ const secondOptions = mockCallArg < { profile ?: string } > ( browserActionsMocks . browserAct , 1 , 2 ) ;
1321+ expect ( secondOptions . profile ) . toBe ( "user" ) ;
1322+ expect ( ( result ?. details as { ok ?: unknown } | undefined ) ?. ok ) . toBe ( true ) ;
13111323 } ) ;
13121324
13131325 it ( "does not retry mutating user-browser act requests without targetId" , async ( ) => {
0 commit comments