teamshare-bridge 0.26.20 → 0.26.21

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 (46) hide show
  1. package/dist/bridge/daemon.js +60 -0
  2. package/dist/bridge/daemon.js.map +1 -1
  3. package/dist/bridge/local-server.js +29 -11
  4. package/dist/bridge/local-server.js.map +1 -1
  5. package/dist/bridge/session-relay.d.ts +14 -0
  6. package/dist/bridge/session-relay.js +53 -2
  7. package/dist/bridge/session-relay.js.map +1 -1
  8. package/dist/cli/commands/research.d.ts +7 -3
  9. package/dist/cli/commands/research.js +67 -15
  10. package/dist/cli/commands/research.js.map +1 -1
  11. package/dist/cli/commands/usage.js +85 -32
  12. package/dist/cli/commands/usage.js.map +1 -1
  13. package/dist/cli/index.js +1 -1
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/lib/brief.js +3 -2
  16. package/dist/lib/brief.js.map +1 -1
  17. package/dist/lib/browser/batch.d.ts +23 -0
  18. package/dist/lib/browser/batch.js +33 -0
  19. package/dist/lib/browser/batch.js.map +1 -1
  20. package/dist/lib/browser/manager.d.ts +1 -0
  21. package/dist/lib/browser/manager.js +1 -0
  22. package/dist/lib/browser/manager.js.map +1 -1
  23. package/dist/lib/browser/network-log.d.ts +28 -3
  24. package/dist/lib/browser/network-log.js +74 -2
  25. package/dist/lib/browser/network-log.js.map +1 -1
  26. package/dist/lib/browser/profile-sweep.d.ts +15 -0
  27. package/dist/lib/browser/profile-sweep.js +69 -0
  28. package/dist/lib/browser/profile-sweep.js.map +1 -0
  29. package/dist/lib/browser/screen-change.d.ts +5 -0
  30. package/dist/lib/browser/screen-change.js +22 -1
  31. package/dist/lib/browser/screen-change.js.map +1 -1
  32. package/dist/lib/browser/session-pool.d.ts +40 -0
  33. package/dist/lib/browser/session-pool.js +62 -1
  34. package/dist/lib/browser/session-pool.js.map +1 -1
  35. package/dist/lib/browser/session.d.ts +51 -8
  36. package/dist/lib/browser/session.js +144 -15
  37. package/dist/lib/browser/session.js.map +1 -1
  38. package/dist/lib/llm/usage-reader.d.ts +91 -4
  39. package/dist/lib/llm/usage-reader.js +750 -111
  40. package/dist/lib/llm/usage-reader.js.map +1 -1
  41. package/dist/lib/llm/usage.d.ts +9 -9
  42. package/dist/lib/llm/usage.js +9 -8
  43. package/dist/lib/llm/usage.js.map +1 -1
  44. package/dist/lib/orchestrator/handlers/browser.js +342 -56
  45. package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
  46. package/package.json +1 -1
@@ -41,4 +41,44 @@ export declare function pickDebugProfileDir(opts: {
41
41
  sessionKey: string;
42
42
  usedDirs: Iterable<string>;
43
43
  namedDir?: string;
44
+ /** Force an isolated per-session profile, never the shared one (2026-11). */
45
+ forceIsolated?: boolean;
44
46
  }): ProfileChoice;
47
+ /** Normalize a test-account label for comparison (2026-11). */
48
+ export declare function normalizeActor(raw: unknown): string | null;
49
+ /**
50
+ * Resolve the session a no-sessionId command runs against (2026-11).
51
+ *
52
+ * Explicit > the caller's remembered names > the derived identity default.
53
+ * Only the CALLER's windows count: another agent's Chrome on the same machine
54
+ * must never make my action ambiguous. With two or more of MY live windows
55
+ * and no name, the command is AMBIGUOUS — acting on "whichever was touched
56
+ * last" would drive the wrong Chrome.
57
+ */
58
+ export declare function resolveSessionForCommand(input: {
59
+ explicit?: string | null;
60
+ remembered: string[];
61
+ derived: string;
62
+ live: Array<{
63
+ id: string;
64
+ actor?: string | null;
65
+ }>;
66
+ }): {
67
+ sessionId: string;
68
+ } | {
69
+ ambiguous: Array<{
70
+ id: string;
71
+ actor?: string | null;
72
+ }>;
73
+ };
74
+ /**
75
+ * Actor gate for tagged windows (2026-11). A window never changes hands:
76
+ * a login in the Admin window can only be the Admin.
77
+ */
78
+ export declare function assertActorForLogin(input: {
79
+ sessionActor?: string | null;
80
+ callActor?: string | null;
81
+ }): null | {
82
+ code: 'ACTOR_REQUIRED' | 'WRONG_SESSION_ACTOR';
83
+ message: string;
84
+ };
@@ -6,6 +6,9 @@ exports.deriveSessionKey = deriveSessionKey;
6
6
  exports.sessionSlug = sessionSlug;
7
7
  exports.pickDebugPort = pickDebugPort;
8
8
  exports.pickDebugProfileDir = pickDebugProfileDir;
9
+ exports.normalizeActor = normalizeActor;
10
+ exports.resolveSessionForCommand = resolveSessionForCommand;
11
+ exports.assertActorForLogin = assertActorForLogin;
9
12
  /**
10
13
  * Browser session identity + port/profile allocation (2026-10).
11
14
  *
@@ -116,7 +119,7 @@ function pickDebugProfileDir(opts) {
116
119
  }
117
120
  return { dir: opts.namedDir, shared: false };
118
121
  }
119
- if (!used.has(key(opts.baseDir))) {
122
+ if (!used.has(key(opts.baseDir)) && !opts.forceIsolated) {
120
123
  return { dir: opts.baseDir, shared: true };
121
124
  }
122
125
  const dir = (0, node_path_1.join)((0, node_path_1.dirname)(opts.baseDir), `${(0, node_path_1.basename)(opts.baseDir)}-sessions`, sessionSlug(opts.sessionKey));
@@ -125,4 +128,62 @@ function pickDebugProfileDir(opts) {
125
128
  }
126
129
  return { dir, shared: false };
127
130
  }
131
+ /** Normalize a test-account label for comparison (2026-11). */
132
+ function normalizeActor(raw) {
133
+ if (typeof raw !== 'string')
134
+ return null;
135
+ const trimmed = raw.trim();
136
+ return trimmed ? trimmed.toLowerCase() : null;
137
+ }
138
+ /**
139
+ * Resolve the session a no-sessionId command runs against (2026-11).
140
+ *
141
+ * Explicit > the caller's remembered names > the derived identity default.
142
+ * Only the CALLER's windows count: another agent's Chrome on the same machine
143
+ * must never make my action ambiguous. With two or more of MY live windows
144
+ * and no name, the command is AMBIGUOUS — acting on "whichever was touched
145
+ * last" would drive the wrong Chrome.
146
+ */
147
+ function resolveSessionForCommand(input) {
148
+ if (input.explicit)
149
+ return { sessionId: input.explicit };
150
+ const liveById = new Map(input.live.map((s) => [s.id, s]));
151
+ const owned = input.remembered.filter((id) => liveById.has(id));
152
+ if (owned.length >= 2) {
153
+ return { ambiguous: owned.map((id) => liveById.get(id)) };
154
+ }
155
+ if (owned.length === 1)
156
+ return { sessionId: owned[0] };
157
+ // None of my remembered windows is live: fall back to my derived default.
158
+ // When it is not live either, the action fails with "No active browser
159
+ // session <derived>" — which is the correct answer, never another agent's
160
+ // window and never a guess.
161
+ return { sessionId: input.derived };
162
+ }
163
+ /**
164
+ * Actor gate for tagged windows (2026-11). A window never changes hands:
165
+ * a login in the Admin window can only be the Admin.
166
+ */
167
+ function assertActorForLogin(input) {
168
+ const session = normalizeActor(input.sessionActor);
169
+ const call = normalizeActor(input.callActor);
170
+ if (!session)
171
+ return null;
172
+ if (!call) {
173
+ return {
174
+ code: 'ACTOR_REQUIRED',
175
+ message: `ACTOR_REQUIRED: this browser window is tagged for "${input.sessionActor}". ` +
176
+ `Pass actor:"${input.sessionActor}" on login (and sessionId on every action).`,
177
+ };
178
+ }
179
+ if (session !== call) {
180
+ return {
181
+ code: 'WRONG_SESSION_ACTOR',
182
+ message: `WRONG_SESSION_ACTOR: this window belongs to "${input.sessionActor}" — ` +
183
+ `it cannot sign in as "${input.callActor}". Open that account's own window ` +
184
+ `with browser_open({ sessionId, actor:"${input.callActor}" }) instead.`,
185
+ };
186
+ }
187
+ return null;
188
+ }
128
189
  //# sourceMappingURL=session-pool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"session-pool.js","sourceRoot":"","sources":["../../../src/lib/browser/session-pool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-pool.js","sourceRoot":"","sources":["../../../src/lib/browser/session-pool.ts"],"names":[],"mappings":"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"}
@@ -45,6 +45,10 @@ export interface BrowserSessionOptions {
45
45
  headless?: boolean;
46
46
  /** Persistent user-data-dir when auto-launching (default the debug profile). */
47
47
  profileDir?: string;
48
+ /** Test-account label this window belongs to (2026-11, one window per identity). */
49
+ actor?: string | null;
50
+ /** Force an isolated per-session profile, never shared (2026-11). */
51
+ isolated?: boolean;
48
52
  /** Extra Chrome args for auto-launch. */
49
53
  chromeArgs?: string[];
50
54
  /**
@@ -94,6 +98,8 @@ export declare class BrowserSession {
94
98
  * started (or the auto-answer browser) is never killed by us.
95
99
  */
96
100
  private _ownedByBridge;
101
+ /** Test-account label this window is tagged for (2026-11). Never changes hands. */
102
+ private _actor;
97
103
  /**
98
104
  * Request/response + console evidence for the whole session (QA). Attached
99
105
  * on both connect paths; the agent reads it with `browser_action
@@ -112,6 +118,10 @@ export declare class BrowserSession {
112
118
  get sessionKey(): string;
113
119
  /** The user-data-dir this session launches Chrome with. */
114
120
  get profileDir(): string;
121
+ /** The test-account label this window is tagged for (2026-11). */
122
+ get actor(): string | null;
123
+ /** Tag an untagged window. A tagged window never changes hands. */
124
+ setActor(actor: string | null | undefined): void;
115
125
  /**
116
126
  * Reconfigure a DISCONNECTED session before reconnecting it (2026-10).
117
127
  *
@@ -259,6 +269,19 @@ export declare class BrowserSession {
259
269
  stoppedBecause: string;
260
270
  }>;
261
271
  navigate(url: string): Promise<PageInfo>;
272
+ /**
273
+ * Set the browser viewport before a design-verification screenshot
274
+ * (2026-11): the design's viewport IS the contract — a pixel diff at any
275
+ * other size measures the window, not the design. Applies
276
+ * Emulation.setDeviceMetricsOverride on the live target and reads the
277
+ * viewport back, so the caller knows what the page actually reports
278
+ * (a clamped value is still a successful, truthful set).
279
+ */
280
+ setViewport(width: number, height: number, deviceScaleFactor?: number): Promise<{
281
+ width: number;
282
+ height: number;
283
+ deviceScaleFactor: number;
284
+ }>;
262
285
  /**
263
286
  * Capture a screenshot. Returns base64-encoded data.
264
287
  */
@@ -339,12 +362,18 @@ export declare class BrowserSession {
339
362
  * role, accessible name, state and CSS-pixel centre. Includes elements
340
363
  * inside portalled menus/popovers/dialogs, which is the whole point: a kebab
341
364
  * menu that just opened is findable without guessing a selector.
365
+ *
366
+ * `filter` (2026-11) narrows by substring over role + name + tag, so
367
+ * `getRefs {filter:"button"}` answers "all buttons" without a second scan.
368
+ * Refs are ALWAYS the full-scan numbering — a filtered view never renumbers,
369
+ * or a ref from the list would not resolve.
342
370
  */
343
- getRefs(): Promise<{
371
+ getRefs(filter?: string): Promise<{
344
372
  at: number;
345
373
  count: number;
346
374
  refs: RefElement[];
347
375
  lines: string[];
376
+ filter?: string;
348
377
  }>;
349
378
  /**
350
379
  * Resolve a ref to a clickable point. A ref whose node left the DOM is
@@ -626,14 +655,28 @@ export declare class BrowserSession {
626
655
  nativeDialog: NativeDialogState | null;
627
656
  }>;
628
657
  /**
629
- * Is the page actually usable right now? (2026-10)
658
+ * Wait for the screen to settle after an action (2026-11, qaVision only).
630
659
  *
631
- * `connected` only says the CDP socket is open, but the TAB can be dead
632
- * while the socket lives (renderer crash / "Aw, Snap!" after Chrome was
633
- * killed externally). One cheap evaluate distinguishes the two, so a batch
634
- * can re-attach instead of failing every step with raw CDP errors.
635
- */
636
- probeAlive(): Promise<boolean>;
660
+ * The auto-answer loop re-captures context + screenshot on every iteration
661
+ * and aborts its vision call on material change; the QA browser used to
662
+ * return the screen MID-TRANSITION (click → compactSnapshot immediately).
663
+ * This waits for a real navigation, an SPA route change, or a material DOM
664
+ * change, then for a short quiet window (no new mutations, network idle-ish)
665
+ * before the caller reads the screen. Bounded (~4 s default) and
666
+ * best-effort: a timeout resolves with what is known, never a failure.
667
+ */
668
+ waitForScreenSettle(opts?: {
669
+ since?: number;
670
+ quietMs?: number;
671
+ timeoutMs?: number;
672
+ fromUrl?: string;
673
+ }): Promise<{
674
+ changed: boolean;
675
+ navigated: boolean;
676
+ fromUrl: string | null;
677
+ toUrl: string | null;
678
+ elapsedMs: number;
679
+ }>;
637
680
  /**
638
681
  * Re-attach to the debug Chrome after the page died (2026-10).
639
682
  *
@@ -115,6 +115,8 @@ class BrowserSession {
115
115
  * started (or the auto-answer browser) is never killed by us.
116
116
  */
117
117
  _ownedByBridge = false;
118
+ /** Test-account label this window is tagged for (2026-11). Never changes hands. */
119
+ _actor;
118
120
  /**
119
121
  * Request/response + console evidence for the whole session (QA). Attached
120
122
  * on both connect paths; the agent reads it with `browser_action
@@ -134,6 +136,10 @@ class BrowserSession {
134
136
  this._autoLaunch = opts?.autoLaunch ?? true;
135
137
  this._headless = opts?.headless ?? false;
136
138
  this._profileDir = opts?.profileDir ?? exports.DEBUG_PROFILE_DIR;
139
+ this._actor =
140
+ typeof opts?.actor === 'string' && opts.actor.trim()
141
+ ? opts.actor.trim()
142
+ : null;
137
143
  this._chromeArgs = opts?.chromeArgs ?? [];
138
144
  this._allowCredentials = opts?.allowCredentials ?? false;
139
145
  this._qaVision = opts?.qaVision ?? false;
@@ -146,6 +152,17 @@ class BrowserSession {
146
152
  get profileDir() {
147
153
  return this._profileDir;
148
154
  }
155
+ /** The test-account label this window is tagged for (2026-11). */
156
+ get actor() {
157
+ return this._actor;
158
+ }
159
+ /** Tag an untagged window. A tagged window never changes hands. */
160
+ setActor(actor) {
161
+ if (this._actor)
162
+ return;
163
+ if (typeof actor === 'string' && actor.trim())
164
+ this._actor = actor.trim();
165
+ }
149
166
  /**
150
167
  * Reconfigure a DISCONNECTED session before reconnecting it (2026-10).
151
168
  *
@@ -166,6 +183,8 @@ class BrowserSession {
166
183
  this._headless = opts.headless;
167
184
  if (opts.profileDir)
168
185
  this._profileDir = opts.profileDir;
186
+ if (opts.actor !== undefined)
187
+ this.setActor(opts.actor);
169
188
  if (opts.chromeArgs)
170
189
  this._chromeArgs = opts.chromeArgs;
171
190
  if (opts.allowCredentials !== undefined) {
@@ -383,8 +402,12 @@ class BrowserSession {
383
402
  (0, cdp_1.onCdpEvent)(page, 'Page.frameNavigated', (params) => {
384
403
  if (!(0, screen_change_1.isMainFrameNavigation)(params))
385
404
  return;
405
+ // Stamp the load on the observer record (2026-11): the full-load half of
406
+ // the settle signal. The stamp lands after the new document exists, so a
407
+ // settle that started before the navigation still sees it.
386
408
  setTimeout(() => {
387
409
  void install();
410
+ void (0, cdp_1.evaluateJs)(page, '(() => { try { const o = window.__tsScreenObs; if (o) { o.navAt = Date.now(); o.url = location.href; o.urlAt = Date.now(); } } catch {} return true; })()').catch(() => undefined);
388
411
  }, 600);
389
412
  });
390
413
  }
@@ -700,6 +723,32 @@ class BrowserSession {
700
723
  this.ensureConnected();
701
724
  return (0, cdp_1.navigateTo)(this.page, url);
702
725
  }
726
+ /**
727
+ * Set the browser viewport before a design-verification screenshot
728
+ * (2026-11): the design's viewport IS the contract — a pixel diff at any
729
+ * other size measures the window, not the design. Applies
730
+ * Emulation.setDeviceMetricsOverride on the live target and reads the
731
+ * viewport back, so the caller knows what the page actually reports
732
+ * (a clamped value is still a successful, truthful set).
733
+ */
734
+ async setViewport(width, height, deviceScaleFactor = 1) {
735
+ this.ensureConnected();
736
+ const w = Math.max(320, Math.min(8192, Math.floor(width)));
737
+ const h = Math.max(320, Math.min(8192, Math.floor(height)));
738
+ const dpr = Math.max(1, Math.min(3, deviceScaleFactor || 1));
739
+ await (0, cdp_1.cdpSend)(this.page, 'Emulation.setDeviceMetricsOverride', {
740
+ width: w,
741
+ height: h,
742
+ deviceScaleFactor: dpr,
743
+ mobile: false,
744
+ });
745
+ const actual = await (0, cdp_1.viewportOf)(this.page);
746
+ return {
747
+ width: actual?.width ?? w,
748
+ height: actual?.height ?? h,
749
+ deviceScaleFactor: actual?.dpr ?? dpr,
750
+ };
751
+ }
703
752
  /**
704
753
  * Capture a screenshot. Returns base64-encoded data.
705
754
  */
@@ -834,12 +883,28 @@ class BrowserSession {
834
883
  * role, accessible name, state and CSS-pixel centre. Includes elements
835
884
  * inside portalled menus/popovers/dialogs, which is the whole point: a kebab
836
885
  * menu that just opened is findable without guessing a selector.
886
+ *
887
+ * `filter` (2026-11) narrows by substring over role + name + tag, so
888
+ * `getRefs {filter:"button"}` answers "all buttons" without a second scan.
889
+ * Refs are ALWAYS the full-scan numbering — a filtered view never renumbers,
890
+ * or a ref from the list would not resolve.
837
891
  */
838
- async getRefs() {
892
+ async getRefs(filter) {
839
893
  this.ensureConnected();
840
894
  const { at, list } = await (0, refs_1.scanRefs)(this.page);
841
895
  this._lastRefs = list;
842
- return { at, count: list.length, refs: list, lines: (0, refs_1.formatRefs)(list) };
896
+ const needle = (filter ?? '').trim().toLowerCase();
897
+ if (!needle) {
898
+ return { at, count: list.length, refs: list, lines: (0, refs_1.formatRefs)(list) };
899
+ }
900
+ const kept = list.filter((r) => `${r.role} ${r.name} ${r.tag}`.toLowerCase().includes(needle));
901
+ return {
902
+ at,
903
+ count: kept.length,
904
+ refs: kept,
905
+ lines: (0, refs_1.formatRefs)(kept),
906
+ filter,
907
+ };
843
908
  }
844
909
  /**
845
910
  * Resolve a ref to a clickable point. A ref whose node left the DOM is
@@ -1571,22 +1636,86 @@ class BrowserSession {
1571
1636
  };
1572
1637
  }
1573
1638
  /**
1574
- * Is the page actually usable right now? (2026-10)
1639
+ * Wait for the screen to settle after an action (2026-11, qaVision only).
1575
1640
  *
1576
- * `connected` only says the CDP socket is open, but the TAB can be dead
1577
- * while the socket lives (renderer crash / "Aw, Snap!" after Chrome was
1578
- * killed externally). One cheap evaluate distinguishes the two, so a batch
1579
- * can re-attach instead of failing every step with raw CDP errors.
1641
+ * The auto-answer loop re-captures context + screenshot on every iteration
1642
+ * and aborts its vision call on material change; the QA browser used to
1643
+ * return the screen MID-TRANSITION (click → compactSnapshot immediately).
1644
+ * This waits for a real navigation, an SPA route change, or a material DOM
1645
+ * change, then for a short quiet window (no new mutations, network idle-ish)
1646
+ * before the caller reads the screen. Bounded (~4 s default) and
1647
+ * best-effort: a timeout resolves with what is known, never a failure.
1580
1648
  */
1581
- async probeAlive() {
1582
- try {
1583
- this.ensureConnected();
1584
- await (0, cdp_1.evaluateJs)(this.page, '1');
1585
- return true;
1586
- }
1587
- catch {
1588
- return false;
1649
+ async waitForScreenSettle(opts) {
1650
+ const started = Date.now();
1651
+ const since = opts?.since ?? started;
1652
+ const quietMs = Math.min(Math.max(opts?.quietMs ?? 300, 100), 2_000);
1653
+ const timeout = Math.min(Math.max(opts?.timeoutMs ?? 4_000, 500), 15_000);
1654
+ const deadline = started + timeout;
1655
+ const fromUrl = opts?.fromUrl ?? null;
1656
+ let toUrl = null;
1657
+ let navigated = false;
1658
+ let changed = false;
1659
+ let quietSince = Date.now();
1660
+ let lastMutated = 0;
1661
+ while (Date.now() < deadline) {
1662
+ if (this._nativeDialog)
1663
+ break;
1664
+ let seen = null;
1665
+ let href = null;
1666
+ try {
1667
+ const raw = (await (0, cdp_1.evaluateJs)(this.page, '(() => { const o = window.__tsScreenObs; const h = location.href; return { obs: o ? { mutated: o.mutated || 0, changed: o.changed || 0, url: o.url || null, urlAt: o.urlAt || 0, navAt: o.navAt || 0 } : null, href: h }; })()'));
1668
+ seen = raw?.obs ?? null;
1669
+ href = raw?.href ?? null;
1670
+ }
1671
+ catch {
1672
+ // Navigation destroyed the execution context: the page IS changing.
1673
+ // Wait for it to come back instead of reporting a settled screen.
1674
+ await new Promise((resolve) => setTimeout(resolve, 250));
1675
+ continue;
1676
+ }
1677
+ if (href)
1678
+ toUrl = href;
1679
+ const frameNav = (seen?.navAt ?? 0) >= since;
1680
+ const spaNav = (seen?.urlAt ?? 0) >= since ||
1681
+ (fromUrl !== null && href !== null && href !== fromUrl);
1682
+ if (frameNav || spaNav)
1683
+ navigated = true;
1684
+ if ((seen?.changed ?? 0) >= since || (seen?.mutated ?? 0) >= since) {
1685
+ changed = true;
1686
+ }
1687
+ const mutated = seen?.mutated ?? 0;
1688
+ if (mutated > lastMutated) {
1689
+ lastMutated = mutated;
1690
+ quietSince = Date.now();
1691
+ }
1692
+ const quietFor = Date.now() - quietSince;
1693
+ const networkQuiet = this.network.pendingCount() === 0;
1694
+ // Resolve when SOMETHING happened and the page went quiet — or when
1695
+ // nothing happened at all for the quiet window (a no-op click settles
1696
+ // immediately instead of burning the full timeout).
1697
+ if ((navigated || changed) && quietFor >= quietMs && networkQuiet)
1698
+ break;
1699
+ // Minimum observation window (2026-11): the observer debounces at
1700
+ // 500 ms and a click's mutations take a moment to arrive, so exiting on
1701
+ // the first quiet poll (~300 ms in) reported "settled" while the page
1702
+ // was still mid-transition. A genuinely idle page still exits after
1703
+ // ~700 ms, not the full timeout.
1704
+ if (!navigated &&
1705
+ !changed &&
1706
+ quietFor >= quietMs &&
1707
+ networkQuiet &&
1708
+ Date.now() - started >= Math.max(quietMs, 700))
1709
+ break;
1710
+ await new Promise((resolve) => setTimeout(resolve, Math.min(120, Math.max(0, deadline - Date.now()))));
1589
1711
  }
1712
+ return {
1713
+ changed,
1714
+ navigated,
1715
+ fromUrl,
1716
+ toUrl,
1717
+ elapsedMs: Date.now() - started,
1718
+ };
1590
1719
  }
1591
1720
  /**
1592
1721
  * Re-attach to the debug Chrome after the page died (2026-10).