craftdriver 1.7.0 → 1.8.0

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.
Files changed (175) hide show
  1. package/CHANGELOG.md +23 -11
  2. package/README.md +13 -4
  3. package/dist/cli/agentSession.d.ts +62 -0
  4. package/dist/cli/agentSession.d.ts.map +1 -0
  5. package/dist/cli/agentSession.js +100 -0
  6. package/dist/cli/agentSession.js.map +1 -0
  7. package/dist/cli/artifactPaths.d.ts +42 -0
  8. package/dist/cli/artifactPaths.d.ts.map +1 -0
  9. package/dist/cli/artifactPaths.js +144 -0
  10. package/dist/cli/artifactPaths.js.map +1 -0
  11. package/dist/cli/bounds.d.ts +88 -0
  12. package/dist/cli/bounds.d.ts.map +1 -0
  13. package/dist/cli/bounds.js +128 -0
  14. package/dist/cli/bounds.js.map +1 -0
  15. package/dist/cli/client.d.ts +3 -0
  16. package/dist/cli/client.d.ts.map +1 -1
  17. package/dist/cli/client.js +24 -8
  18. package/dist/cli/client.js.map +1 -1
  19. package/dist/cli/daemon.d.ts +3 -0
  20. package/dist/cli/daemon.d.ts.map +1 -1
  21. package/dist/cli/daemon.js +152 -39
  22. package/dist/cli/daemon.js.map +1 -1
  23. package/dist/cli/defaults.d.ts +23 -3
  24. package/dist/cli/defaults.d.ts.map +1 -1
  25. package/dist/cli/defaults.js +50 -5
  26. package/dist/cli/defaults.js.map +1 -1
  27. package/dist/cli/dispatcher.d.ts +54 -11
  28. package/dist/cli/dispatcher.d.ts.map +1 -1
  29. package/dist/cli/dispatcher.js +851 -54
  30. package/dist/cli/dispatcher.js.map +1 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +305 -56
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/init.d.ts +14 -14
  35. package/dist/cli/init.d.ts.map +1 -1
  36. package/dist/cli/init.js +328 -180
  37. package/dist/cli/init.js.map +1 -1
  38. package/dist/cli/journal.d.ts +153 -0
  39. package/dist/cli/journal.d.ts.map +1 -0
  40. package/dist/cli/journal.js +252 -0
  41. package/dist/cli/journal.js.map +1 -0
  42. package/dist/cli/lineReader.d.ts +33 -0
  43. package/dist/cli/lineReader.d.ts.map +1 -0
  44. package/dist/cli/lineReader.js +66 -0
  45. package/dist/cli/lineReader.js.map +1 -0
  46. package/dist/cli/locatorCandidates.d.ts +44 -0
  47. package/dist/cli/locatorCandidates.d.ts.map +1 -0
  48. package/dist/cli/locatorCandidates.js +190 -0
  49. package/dist/cli/locatorCandidates.js.map +1 -0
  50. package/dist/cli/mcp/artifacts.d.ts +39 -1
  51. package/dist/cli/mcp/artifacts.d.ts.map +1 -1
  52. package/dist/cli/mcp/artifacts.js +104 -9
  53. package/dist/cli/mcp/artifacts.js.map +1 -1
  54. package/dist/cli/mcp/bounds.d.ts +65 -0
  55. package/dist/cli/mcp/bounds.d.ts.map +1 -0
  56. package/dist/cli/mcp/bounds.js +124 -0
  57. package/dist/cli/mcp/bounds.js.map +1 -0
  58. package/dist/cli/mcp/params.d.ts +38 -0
  59. package/dist/cli/mcp/params.d.ts.map +1 -0
  60. package/dist/cli/mcp/params.js +174 -0
  61. package/dist/cli/mcp/params.js.map +1 -0
  62. package/dist/cli/mcp/server.d.ts +94 -16
  63. package/dist/cli/mcp/server.d.ts.map +1 -1
  64. package/dist/cli/mcp/server.js +304 -150
  65. package/dist/cli/mcp/server.js.map +1 -1
  66. package/dist/cli/mcp/tools.d.ts +62 -17
  67. package/dist/cli/mcp/tools.d.ts.map +1 -1
  68. package/dist/cli/mcp/tools.js +373 -158
  69. package/dist/cli/mcp/tools.js.map +1 -1
  70. package/dist/cli/pageSemantics.d.ts +42 -0
  71. package/dist/cli/pageSemantics.d.ts.map +1 -0
  72. package/dist/cli/pageSemantics.js +194 -0
  73. package/dist/cli/pageSemantics.js.map +1 -0
  74. package/dist/cli/parseArgs.d.ts +6 -18
  75. package/dist/cli/parseArgs.d.ts.map +1 -1
  76. package/dist/cli/parseArgs.js +689 -45
  77. package/dist/cli/parseArgs.js.map +1 -1
  78. package/dist/cli/protocol.d.ts +8 -0
  79. package/dist/cli/protocol.d.ts.map +1 -1
  80. package/dist/cli/sessionRegistry.d.ts +42 -0
  81. package/dist/cli/sessionRegistry.d.ts.map +1 -0
  82. package/dist/cli/sessionRegistry.js +99 -0
  83. package/dist/cli/sessionRegistry.js.map +1 -0
  84. package/dist/cli/snapshot.d.ts +100 -18
  85. package/dist/cli/snapshot.d.ts.map +1 -1
  86. package/dist/cli/snapshot.js +248 -102
  87. package/dist/cli/snapshot.js.map +1 -1
  88. package/dist/cli/stateStore.d.ts +50 -0
  89. package/dist/cli/stateStore.d.ts.map +1 -0
  90. package/dist/cli/stateStore.js +122 -0
  91. package/dist/cli/stateStore.js.map +1 -0
  92. package/dist/index.d.ts +1 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js.map +1 -1
  95. package/dist/lib/bidi/index.d.ts +1 -1
  96. package/dist/lib/bidi/index.d.ts.map +1 -1
  97. package/dist/lib/bidi/index.js +2 -2
  98. package/dist/lib/bidi/index.js.map +1 -1
  99. package/dist/lib/bidi/logs.d.ts +3 -0
  100. package/dist/lib/bidi/logs.d.ts.map +1 -1
  101. package/dist/lib/bidi/logs.js +7 -0
  102. package/dist/lib/bidi/logs.js.map +1 -1
  103. package/dist/lib/bidi/network.d.ts +14 -1
  104. package/dist/lib/bidi/network.d.ts.map +1 -1
  105. package/dist/lib/bidi/network.js +45 -7
  106. package/dist/lib/bidi/network.js.map +1 -1
  107. package/dist/lib/bidi/storage.d.ts +30 -6
  108. package/dist/lib/bidi/storage.d.ts.map +1 -1
  109. package/dist/lib/bidi/storage.js +190 -45
  110. package/dist/lib/bidi/storage.js.map +1 -1
  111. package/dist/lib/bidi/types.d.ts +7 -2
  112. package/dist/lib/bidi/types.d.ts.map +1 -1
  113. package/dist/lib/browser.d.ts +6 -3
  114. package/dist/lib/browser.d.ts.map +1 -1
  115. package/dist/lib/browser.js +140 -30
  116. package/dist/lib/browser.js.map +1 -1
  117. package/dist/lib/browserContext.d.ts +54 -18
  118. package/dist/lib/browserContext.d.ts.map +1 -1
  119. package/dist/lib/browserContext.js +295 -92
  120. package/dist/lib/browserContext.js.map +1 -1
  121. package/dist/lib/by.d.ts +3 -3
  122. package/dist/lib/by.d.ts.map +1 -1
  123. package/dist/lib/by.js +7 -3
  124. package/dist/lib/by.js.map +1 -1
  125. package/dist/lib/driver.d.ts +7 -0
  126. package/dist/lib/driver.d.ts.map +1 -1
  127. package/dist/lib/driver.js +12 -0
  128. package/dist/lib/driver.js.map +1 -1
  129. package/dist/lib/errors.d.ts +8 -0
  130. package/dist/lib/errors.d.ts.map +1 -1
  131. package/dist/lib/errors.js +8 -0
  132. package/dist/lib/errors.js.map +1 -1
  133. package/dist/lib/initScript.d.ts +9 -0
  134. package/dist/lib/initScript.d.ts.map +1 -0
  135. package/dist/lib/initScript.js +14 -0
  136. package/dist/lib/initScript.js.map +1 -0
  137. package/dist/lib/page.d.ts +22 -0
  138. package/dist/lib/page.d.ts.map +1 -1
  139. package/dist/lib/page.js +48 -0
  140. package/dist/lib/page.js.map +1 -1
  141. package/dist/lib/secureFile.d.ts +8 -0
  142. package/dist/lib/secureFile.d.ts.map +1 -0
  143. package/dist/lib/secureFile.js +67 -0
  144. package/dist/lib/secureFile.js.map +1 -0
  145. package/dist/lib/sessionStateValidation.d.ts +35 -0
  146. package/dist/lib/sessionStateValidation.d.ts.map +1 -0
  147. package/dist/lib/sessionStateValidation.js +223 -0
  148. package/dist/lib/sessionStateValidation.js.map +1 -0
  149. package/dist/lib/tracing.d.ts +2 -1
  150. package/dist/lib/tracing.d.ts.map +1 -1
  151. package/dist/lib/tracing.js +12 -1
  152. package/dist/lib/tracing.js.map +1 -1
  153. package/docs/agents.md +56 -61
  154. package/docs/api-reference.md +2 -1
  155. package/docs/browser-api.md +24 -4
  156. package/docs/browser-context.md +18 -12
  157. package/docs/cli.md +346 -26
  158. package/docs/error-codes.md +5 -4
  159. package/docs/getting-started.md +6 -1
  160. package/docs/index.md +1 -1
  161. package/docs/mcp.md +129 -62
  162. package/docs/public/examples/agent-actions.html +48 -0
  163. package/docs/public/examples/agent-debug.html +86 -0
  164. package/docs/public/examples/agent-late-mutation.html +18 -0
  165. package/docs/public/examples/agent-ref-shift.html +33 -0
  166. package/docs/recipes/login-once-reuse-session.md +32 -4
  167. package/docs/session-management.md +64 -3
  168. package/docs/why-craftdriver.md +1 -1
  169. package/package.json +3 -1
  170. package/skills/craftdriver/SKILL.md +59 -75
  171. package/skills/craftdriver/agents/openai.yaml +4 -0
  172. package/skills/craftdriver/cheatsheet.md +18 -4
  173. package/skills/craftdriver/cli.md +194 -68
  174. package/skills/craftdriver/patterns.md +14 -8
  175. package/skills/craftdriver/workflow.md +149 -0
@@ -33,10 +33,12 @@ import { Tracer } from './tracing.js';
33
33
  import { A11y } from './a11y.js';
34
34
  import { Clock } from './clock.js';
35
35
  import { CraftdriverError, ErrorCode } from './errors.js';
36
+ import { parseSessionState, hasNonEmptySessionStorage, isSessionStateEmpty, storageStateDetail, } from './sessionStateValidation.js';
36
37
  import { clickWithFastPath } from './clickFastPath.js';
37
38
  import { fillWithFastPath } from './fillFastPath.js';
38
39
  import { clearWithFastPath } from './clearFastPath.js';
39
40
  import { runExpectScreenshot, shouldUpdateVisualBaselines } from './visual/index.js';
41
+ import { publicPageInitScript } from './initScript.js';
40
42
  /** Common mobile device presets */
41
43
  export const devices = {
42
44
  'iPhone 14': {
@@ -269,6 +271,7 @@ export class Browser {
269
271
  _topLevelContextTracking;
270
272
  _topLevelContextTrackingOffs = [];
271
273
  _topLevelContextCacheVersion = 0;
274
+ _destroyedTopLevelContextIds = new Set();
272
275
  _logs;
273
276
  _tracer;
274
277
  _storage;
@@ -481,9 +484,19 @@ export class Browser {
481
484
  if (wsUrl && bidiRequested) {
482
485
  await browser.initBiDi(wsUrl);
483
486
  }
484
- // Load session state if provided
485
- if (options.storageState) {
486
- await browser.loadState(options.storageState);
487
+ if (options.storageState !== undefined) {
488
+ const protocol = browser.bidiSession?.isConnected() ? 'bidi' : 'classic';
489
+ const errorContext = { operation: 'Browser.launch', browserName: name, protocol };
490
+ const state = await parseSessionState(options.storageState, errorContext);
491
+ if (browser.bidiSession?.isConnected()) {
492
+ await browser.defaultContext._loadStorageState(state, 'Browser.launch');
493
+ }
494
+ else if (!isSessionStateEmpty(state)) {
495
+ throw new CraftdriverError(ErrorCode.UNSUPPORTED, 'non-empty storageState cannot be restored at WebDriver Classic launch', {
496
+ detail: storageStateDetail(errorContext, 'capability', { partialApplied: false }),
497
+ hint: 'Enable BiDi, or launch first, navigate to the sole origin, then call browser.loadState().',
498
+ });
499
+ }
487
500
  }
488
501
  }
489
502
  catch (err) {
@@ -517,8 +530,23 @@ export class Browser {
517
530
  if (wsUrl && target.bidiRequested) {
518
531
  await browser.initBiDi(wsUrl);
519
532
  }
520
- if (options.storageState) {
521
- await browser.loadState(options.storageState);
533
+ if (options.storageState !== undefined) {
534
+ const protocol = browser.bidiSession?.isConnected() ? 'bidi' : 'classic';
535
+ const errorContext = {
536
+ operation: 'Browser.launch',
537
+ browserName: target.browserName,
538
+ protocol,
539
+ };
540
+ const state = await parseSessionState(options.storageState, errorContext);
541
+ if (browser.bidiSession?.isConnected()) {
542
+ await browser.defaultContext._loadStorageState(state, 'Browser.launch');
543
+ }
544
+ else if (!isSessionStateEmpty(state)) {
545
+ throw new CraftdriverError(ErrorCode.UNSUPPORTED, 'non-empty storageState cannot be restored at WebDriver Classic launch', {
546
+ detail: storageStateDetail(errorContext, 'capability', { partialApplied: false }),
547
+ hint: 'Enable BiDi, or launch first, navigate to the sole origin, then call browser.loadState().',
548
+ });
549
+ }
522
550
  }
523
551
  }
524
552
  catch (err) {
@@ -550,6 +578,8 @@ export class Browser {
550
578
  this._topLevelContextTracking = this._startTopLevelContextTracking(contexts);
551
579
  },
552
580
  });
581
+ session.network.setInternalContextPredicate((context) => this._isInternalContextId(context));
582
+ session.logs.setInternalContextPredicate((context) => this._isInternalContextId(context));
553
583
  await this._ensureTopLevelContextTracking().catch(() => { });
554
584
  return; // success
555
585
  }
@@ -618,7 +648,13 @@ export class Browser {
618
648
  */
619
649
  get storage() {
620
650
  if (!this._storage) {
621
- this._storage = new SessionStateManager(this.driver, this.bidiSession?.isConnected() ? this.bidiSession.getConnection() : null);
651
+ this._storage = new SessionStateManager(this.driver, this.bidiSession?.isConnected() ? this.bidiSession.getConnection() : null, undefined, {
652
+ browserName: this._browserName,
653
+ protocol: this.bidiSession?.isConnected() ? 'bidi' : 'classic',
654
+ runSerializedRestore: this.bidiSession?.isConnected()
655
+ ? (task) => this.defaultContext._runSerializedRestore(task)
656
+ : undefined,
657
+ });
622
658
  }
623
659
  return this._storage;
624
660
  }
@@ -681,8 +717,23 @@ export class Browser {
681
717
  /**
682
718
  * Load session state from file
683
719
  */
684
- async loadState(path) {
685
- return this.storage.loadState(path);
720
+ async loadState(source) {
721
+ const protocol = this.bidiSession?.isConnected() ? 'bidi' : 'classic';
722
+ const state = await parseSessionState(source, {
723
+ operation: 'browser.loadState',
724
+ browserName: this._browserName,
725
+ protocol,
726
+ });
727
+ // BiDi with no sessionStorage → the validated hydrator (multi-origin, no
728
+ // active page required). Otherwise (Classic, or sessionStorage present) take
729
+ // the active-page path, which restores a single origin matching the active
730
+ // page and can carry sessionStorage.
731
+ if (this.bidiSession?.isConnected() && !hasNonEmptySessionStorage(state)) {
732
+ await this.defaultContext._loadStorageState(state, 'browser.loadState');
733
+ }
734
+ else {
735
+ await this.storage.setState(state, 'browser.loadState');
736
+ }
686
737
  }
687
738
  async _runTracedAction(name, args, selector, run) {
688
739
  const actionIndex = this._tracer?.recordAction(name, args, selector);
@@ -1135,6 +1186,7 @@ export class Browser {
1135
1186
  this._topLevelContextTrackingOffs = [];
1136
1187
  this._topLevelContextTracking = undefined;
1137
1188
  this._topLevelContextUserContexts.clear();
1189
+ this._destroyedTopLevelContextIds.clear();
1138
1190
  this._topLevelContextCacheVersion++;
1139
1191
  // DELETE the WebDriver session — this tells the driver service to close the browser.
1140
1192
  await this.driver.quit().catch(() => { });
@@ -1179,7 +1231,7 @@ export class Browser {
1179
1231
  'Tracing relies on BiDi events; Classic WebDriver does not expose them.', { detail: { browserName: this._browserName, feature: 'startTrace' } });
1180
1232
  }
1181
1233
  if (!this._tracer) {
1182
- this._tracer = new Tracer(this, this.bidiSession.getConnection(), this._engine);
1234
+ this._tracer = new Tracer(this, this.bidiSession.getConnection(), this._engine, (context) => this._isInternalContextId(context));
1183
1235
  }
1184
1236
  await this._tracer.start(opts);
1185
1237
  }
@@ -1278,7 +1330,7 @@ export class Browser {
1278
1330
  const conn = this.bidiSession.getConnection();
1279
1331
  const fnSrc = typeof fnOrSrc === 'function' ? fnOrSrc.toString() : `() => { ${fnOrSrc} }`;
1280
1332
  const result = await conn.send('script.addPreloadScript', {
1281
- functionDeclaration: fnSrc,
1333
+ functionDeclaration: publicPageInitScript(fnSrc),
1282
1334
  });
1283
1335
  const scriptId = result.script;
1284
1336
  this._browserInitScriptIds.add(scriptId);
@@ -1611,7 +1663,14 @@ export class Browser {
1611
1663
  const tree = await conn.send('browsingContext.getTree', {
1612
1664
  maxDepth: 0,
1613
1665
  });
1614
- return (tree.contexts ?? []).map((ctx) => new Page(this.driver, ctx.context, this.getDefaultTimeout, conn, this._wrapContext(ctx.userContext ?? 'default')));
1666
+ const pages = [];
1667
+ for (const ctx of tree.contexts ?? []) {
1668
+ const owner = this._wrapContext(ctx.userContext ?? 'default');
1669
+ if (await owner._isInternalPageContext(ctx.context))
1670
+ continue;
1671
+ pages.push(new Page(this.driver, ctx.context, this.getDefaultTimeout, conn, owner));
1672
+ }
1673
+ return pages;
1615
1674
  }
1616
1675
  // Classic fallback: use window handles
1617
1676
  const handles = await this.driver.getWindowHandles();
@@ -1657,19 +1716,38 @@ export class Browser {
1657
1716
  // `BiDiSession.connect()` (which must have run for `isConnected()` to be
1658
1717
  // true), so no per-call `subscribe()` round trip is needed here.
1659
1718
  return new Promise((resolve, reject) => {
1719
+ let settled = false;
1660
1720
  const timer = setTimeout(() => {
1721
+ settled = true;
1661
1722
  off();
1662
1723
  reject(new Error(`waitForPage() timed out after ${timeout}ms`));
1663
1724
  }, timeout);
1664
1725
  const off = conn.on('browsingContext.contextCreated', (params) => {
1665
1726
  // Only top-level contexts have no parent
1666
1727
  if (!params.parent) {
1667
- clearTimeout(timer);
1668
- off();
1669
- resolve(new Page(this.driver, params.context, this.getDefaultTimeout, conn, this._wrapContext(params.userContext ?? 'default')));
1728
+ const id = params.context;
1729
+ const owner = this._wrapContext(params.userContext ?? 'default');
1730
+ void owner._isInternalPageContext(id).then((internal) => {
1731
+ if (internal || settled)
1732
+ return;
1733
+ settled = true;
1734
+ clearTimeout(timer);
1735
+ off();
1736
+ resolve(new Page(this.driver, id, this.getDefaultTimeout, conn, owner));
1737
+ }).catch((err) => {
1738
+ if (settled)
1739
+ return;
1740
+ settled = true;
1741
+ clearTimeout(timer);
1742
+ off();
1743
+ reject(err);
1744
+ });
1670
1745
  }
1671
1746
  });
1672
1747
  action().catch((err) => {
1748
+ if (settled)
1749
+ return;
1750
+ settled = true;
1673
1751
  clearTimeout(timer);
1674
1752
  off();
1675
1753
  reject(err);
@@ -1797,6 +1875,15 @@ export class Browser {
1797
1875
  });
1798
1876
  return ctx;
1799
1877
  }
1878
+ _isInternalContextId(context) {
1879
+ if (typeof context !== 'string')
1880
+ return false;
1881
+ for (const ctx of this._contextsById.values()) {
1882
+ if (ctx._isInternalPageId(context))
1883
+ return true;
1884
+ }
1885
+ return false;
1886
+ }
1800
1887
  async newContext(opts) {
1801
1888
  this.requireBiDi('newContext()', 'newContext() requires BiDi (enableBiDi: true). ' +
1802
1889
  'WebDriver Classic has no concept of user contexts.');
@@ -1806,17 +1893,25 @@ export class Browser {
1806
1893
  baseURL: opts?.baseURL,
1807
1894
  extraHTTPHeaders: opts?.extraHTTPHeaders,
1808
1895
  });
1809
- if (opts?.storageState !== undefined) {
1810
- await ctx.loadStorageState(opts.storageState);
1811
- }
1812
- // Apply identity & device emulation options. Each setter is `userContexts`-scoped,
1813
- // so future pages in this context inherit automatically.
1814
- if (opts?.locale !== undefined)
1815
- await ctx.setLocale(opts.locale);
1816
- if (opts?.timezoneId !== undefined)
1817
- await ctx.setTimezone(opts.timezoneId);
1818
- if (opts?.geolocation !== undefined)
1819
- await ctx.setGeolocation(opts.geolocation);
1896
+ try {
1897
+ if (opts?.storageState !== undefined) {
1898
+ await ctx._loadStorageState(opts.storageState, 'browser.newContext');
1899
+ }
1900
+ // Apply identity & device emulation options. Each setter is `userContexts`-scoped,
1901
+ // so future pages in this context inherit automatically.
1902
+ if (opts?.locale !== undefined)
1903
+ await ctx.setLocale(opts.locale);
1904
+ if (opts?.timezoneId !== undefined)
1905
+ await ctx.setTimezone(opts.timezoneId);
1906
+ if (opts?.geolocation !== undefined)
1907
+ await ctx.setGeolocation(opts.geolocation);
1908
+ }
1909
+ catch (err) {
1910
+ // Restore/emulation failed — tear the just-created context down so a
1911
+ // rejected newContext() never leaves an orphaned user context behind.
1912
+ await ctx.close().catch(() => { });
1913
+ throw err;
1914
+ }
1820
1915
  return ctx;
1821
1916
  }
1822
1917
  /**
@@ -1887,13 +1982,21 @@ export class Browser {
1887
1982
  const id = params.context;
1888
1983
  if (typeof id !== 'string')
1889
1984
  return;
1890
- this._topLevelContextUserContexts.set(id, typeof params.userContext === 'string' ? params.userContext : '');
1891
- this._topLevelContextCacheVersion++;
1985
+ const userContext = typeof params.userContext === 'string' ? params.userContext : 'default';
1986
+ this._destroyedTopLevelContextIds.delete(id);
1987
+ const owner = this._wrapContext(userContext);
1988
+ void owner._isInternalPageContext(id).then((internal) => {
1989
+ if (internal || this._destroyedTopLevelContextIds.has(id))
1990
+ return;
1991
+ this._topLevelContextUserContexts.set(id, userContext);
1992
+ this._topLevelContextCacheVersion++;
1993
+ }).catch(() => { });
1892
1994
  };
1893
1995
  const onDestroyed = (params) => {
1894
1996
  if (params.parent)
1895
1997
  return;
1896
1998
  if (typeof params.context === 'string') {
1999
+ this._destroyedTopLevelContextIds.add(params.context);
1897
2000
  this._topLevelContextUserContexts.delete(params.context);
1898
2001
  this._topLevelContextCacheVersion++;
1899
2002
  }
@@ -1922,6 +2025,9 @@ export class Browser {
1922
2025
  }
1923
2026
  for (const ctx of tree.contexts ?? []) {
1924
2027
  if (!ctx.parent) {
2028
+ const owner = this._wrapContext(ctx.userContext ?? 'default');
2029
+ if (await owner._isInternalPageContext(ctx.context))
2030
+ continue;
1925
2031
  this._topLevelContextUserContexts.set(ctx.context, ctx.userContext ?? 'default');
1926
2032
  }
1927
2033
  }
@@ -1929,7 +2035,7 @@ export class Browser {
1929
2035
  }
1930
2036
  _firstDefaultTopLevelContext() {
1931
2037
  for (const [id, userContext] of this._topLevelContextUserContexts) {
1932
- if (userContext === 'default')
2038
+ if (userContext === 'default' && !this._isInternalContextId(id))
1933
2039
  return id;
1934
2040
  }
1935
2041
  return undefined;
@@ -1958,13 +2064,17 @@ export class Browser {
1958
2064
  const conn = this.bidiSession.getConnection();
1959
2065
  const handle = await this.driver.getCurrentWindowHandle().catch(() => '');
1960
2066
  await this._ensureTopLevelContextTracking();
1961
- if (handle && this._topLevelContextUserContexts.get(handle) === 'default') {
2067
+ if (handle &&
2068
+ this._topLevelContextUserContexts.get(handle) === 'default' &&
2069
+ !this._isInternalContextId(handle)) {
1962
2070
  return new Page(this.driver, handle, this.getDefaultTimeout, conn, this.defaultContext);
1963
2071
  }
1964
2072
  // Events should keep the cache warm, but a cheap top-level sync covers
1965
2073
  // missed startup events and direct protocol/browser changes.
1966
2074
  await this._refreshTopLevelContextCache(conn);
1967
- if (handle && this._topLevelContextUserContexts.get(handle) === 'default') {
2075
+ if (handle &&
2076
+ this._topLevelContextUserContexts.get(handle) === 'default' &&
2077
+ !this._isInternalContextId(handle)) {
1968
2078
  return new Page(this.driver, handle, this.getDefaultTimeout, conn, this.defaultContext);
1969
2079
  }
1970
2080
  const fallback = this._firstDefaultTopLevelContext();