@trusty-squire/mcp 1.1.13 → 1.1.14-rc.2

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 (42) hide show
  1. package/README.md +19 -11
  2. package/dist/bot/browser-process-owner.d.ts +75 -0
  3. package/dist/bot/browser-process-owner.d.ts.map +1 -0
  4. package/dist/bot/browser-process-owner.js +931 -0
  5. package/dist/bot/browser-process-owner.js.map +1 -0
  6. package/dist/bot/browser-process-runtime.d.ts +159 -0
  7. package/dist/bot/browser-process-runtime.d.ts.map +1 -0
  8. package/dist/bot/browser-process-runtime.js +905 -0
  9. package/dist/bot/browser-process-runtime.js.map +1 -0
  10. package/dist/bot/browser.d.ts +45 -194
  11. package/dist/bot/browser.d.ts.map +1 -1
  12. package/dist/bot/browser.js +666 -2358
  13. package/dist/bot/browser.js.map +1 -1
  14. package/dist/bot/compact-observation-v2.d.ts +9 -0
  15. package/dist/bot/compact-observation-v2.d.ts.map +1 -1
  16. package/dist/bot/compact-observation-v2.js +141 -82
  17. package/dist/bot/compact-observation-v2.js.map +1 -1
  18. package/dist/bot/identity-runtime.d.ts +56 -0
  19. package/dist/bot/identity-runtime.d.ts.map +1 -0
  20. package/dist/bot/identity-runtime.js +141 -0
  21. package/dist/bot/identity-runtime.js.map +1 -0
  22. package/dist/bot/owned-pages.d.ts +16 -0
  23. package/dist/bot/owned-pages.d.ts.map +1 -0
  24. package/dist/bot/owned-pages.js +58 -0
  25. package/dist/bot/owned-pages.js.map +1 -0
  26. package/dist/bot/page-driver.d.ts +35 -0
  27. package/dist/bot/page-driver.d.ts.map +1 -0
  28. package/dist/bot/page-driver.js +302 -0
  29. package/dist/bot/page-driver.js.map +1 -0
  30. package/dist/bot/provision-session.d.ts +4 -0
  31. package/dist/bot/provision-session.d.ts.map +1 -1
  32. package/dist/bot/provision-session.js +122 -29
  33. package/dist/bot/provision-session.js.map +1 -1
  34. package/dist/bot/session/lifecycle.d.ts.map +1 -1
  35. package/dist/bot/session/lifecycle.js +218 -14
  36. package/dist/bot/session/lifecycle.js.map +1 -1
  37. package/dist/bot/session/multisession-flag.d.ts +2 -0
  38. package/dist/bot/session/multisession-flag.d.ts.map +1 -0
  39. package/dist/bot/session/multisession-flag.js +23 -0
  40. package/dist/bot/session/multisession-flag.js.map +1 -0
  41. package/dist/tools/provision-drive.d.ts +12 -12
  42. package/package.json +1 -1
@@ -0,0 +1,141 @@
1
+ // Step 3 of the multi-session browser broker migration: a Chrome lifetime
2
+ // that outlives any single session's reference to it. Production still admits
3
+ // exactly one session at a time (the exclusive profile lease in
4
+ // session/lifecycle.ts is unchanged) — this only separates "who owns Chrome's
5
+ // process lifetime" from "who is currently borrowing its tabs" so a later PR
6
+ // can attach a second session without re-deriving that boundary.
7
+ //
8
+ // Generic over the launched handle (BrowserController in production, a fake
9
+ // in tests) so this file never has to construct a real browser to be tested.
10
+ export class IncompatibleIdentityRuntimeSettingsError extends Error {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "IncompatibleIdentityRuntimeSettingsError";
14
+ }
15
+ }
16
+ function defaultSettingsCompatible(live, requested) {
17
+ return JSON.stringify(live) === JSON.stringify(requested);
18
+ }
19
+ /** Owns one Chrome for one identity/profile. Launch is single-flight; tab
20
+ * acquisition and Chrome shutdown are deliberately separate operations so
21
+ * releasing a session never has to mean tearing the browser down. */
22
+ export class IdentityRuntime {
23
+ handle = null;
24
+ settings = null;
25
+ pendingSettings = null;
26
+ epoch = 0;
27
+ launchPromise = null;
28
+ leaseCount = 0;
29
+ settingsCompatible;
30
+ constructor(opts = {}) {
31
+ this.settingsCompatible = opts.settingsCompatible ?? defaultSettingsCompatible;
32
+ }
33
+ currentEpoch() {
34
+ return this.epoch;
35
+ }
36
+ isLive() {
37
+ return this.handle !== null;
38
+ }
39
+ // True while some caller's launch() is in flight (single-flighted — see
40
+ // acquire()). Lets a caller that lost a synchronous race for the OS-level
41
+ // resource the launch itself protects (see session/lifecycle.ts's
42
+ // experimental-multisession fallback) tell "this identity is being (or
43
+ // already was) launched IN THIS PROCESS" from "busy for an unrelated
44
+ // reason" before deciding to join it.
45
+ isLaunching() {
46
+ return this.launchPromise !== null;
47
+ }
48
+ activeLeaseCount() {
49
+ return this.leaseCount;
50
+ }
51
+ // Detects a stale reference: true once the runtime has moved past the epoch
52
+ // a caller was handed at acquire time (a relaunch happened underneath it).
53
+ isEpochStale(epoch) {
54
+ return epoch !== this.epoch;
55
+ }
56
+ /**
57
+ * Acquires this identity's Chrome, launching it at most once for any number
58
+ * of concurrent callers. `launch` runs only for the caller that wins the
59
+ * single-flight race; every other concurrent caller (and every caller while
60
+ * a compatible instance is already live) awaits that one launch or reuses
61
+ * the live handle without relaunching.
62
+ *
63
+ * A request whose settings differ from the currently live (or in-flight)
64
+ * launch is rejected rather than silently applied to the shared context —
65
+ * the caller must forgetAfterShutdown() a live runtime before requesting
66
+ * different settings.
67
+ */
68
+ async acquire(settings, launch) {
69
+ const liveSettings = this.settings ?? this.pendingSettings;
70
+ if (liveSettings !== null && !this.settingsCompatible(liveSettings, settings)) {
71
+ throw new IncompatibleIdentityRuntimeSettingsError("IdentityRuntime.acquire: requested settings are incompatible with the live/in-flight " +
72
+ "identity. Refusing to mutate a shared browser context — call forgetAfterShutdown() " +
73
+ "after closing it, then acquire() again for a fresh identity.");
74
+ }
75
+ if (this.handle !== null) {
76
+ this.leaseCount += 1;
77
+ return {
78
+ handle: this.handle,
79
+ epoch: this.epoch,
80
+ reused: true,
81
+ releaseTabs: this.makeReleaseTabs(),
82
+ };
83
+ }
84
+ if (this.launchPromise === null) {
85
+ this.pendingSettings = settings;
86
+ this.launchPromise = launch(settings).finally(() => {
87
+ this.pendingSettings = null;
88
+ });
89
+ }
90
+ let handle;
91
+ try {
92
+ handle = await this.launchPromise;
93
+ }
94
+ finally {
95
+ this.launchPromise = null;
96
+ }
97
+ // Only the first continuation to resume after the shared launch settles
98
+ // records it as the live identity — every other concurrent waiter sees
99
+ // `this.handle !== null` below and joins as a reuse rather than
100
+ // double-counting the same launch as a second (re)launch.
101
+ const wonLaunch = this.handle === null;
102
+ if (wonLaunch) {
103
+ this.handle = handle;
104
+ this.settings = settings;
105
+ this.epoch += 1;
106
+ }
107
+ const resolvedHandle = this.handle;
108
+ if (resolvedHandle === null) {
109
+ throw new Error("IdentityRuntime: invariant violated — no live handle after acquire");
110
+ }
111
+ this.leaseCount += 1;
112
+ return {
113
+ handle: resolvedHandle,
114
+ epoch: this.epoch,
115
+ reused: !wonLaunch,
116
+ releaseTabs: this.makeReleaseTabs(),
117
+ };
118
+ }
119
+ makeReleaseTabs() {
120
+ let released = false;
121
+ return () => {
122
+ if (released)
123
+ return;
124
+ released = true;
125
+ this.leaseCount = Math.max(0, this.leaseCount - 1);
126
+ };
127
+ }
128
+ /**
129
+ * Forgets the current identity so the next acquire() launches fresh. Does
130
+ * NOT close Chrome — the caller closes the handle itself (its close()
131
+ * semantics, timeouts, and force-close fallbacks stay exactly where they
132
+ * are today) and calls this afterward, in a `finally`, regardless of
133
+ * outcome. Safe to call when nothing is live.
134
+ */
135
+ forgetAfterShutdown() {
136
+ this.handle = null;
137
+ this.settings = null;
138
+ this.leaseCount = 0;
139
+ }
140
+ }
141
+ //# sourceMappingURL=identity-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-runtime.js","sourceRoot":"","sources":["../../src/bot/identity-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { Page } from "playwright";
2
+ /** Session-local page ownership; context membership is never ownership proof. */
3
+ export declare class OwnedPages {
4
+ private readonly onPopup;
5
+ private readonly owner;
6
+ private readonly pages;
7
+ constructor(onPopup: (page: Page) => void);
8
+ /** Only call for a known primary/recovery page, or a proven popup below. */
9
+ register(page: Page): void;
10
+ has(page: Page): boolean;
11
+ /** True only when a DIFFERENT registry has positively claimed the page. */
12
+ claimedByAnother(page: Page): boolean;
13
+ live(): Page[];
14
+ dispose(): void;
15
+ }
16
+ //# sourceMappingURL=owned-pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-pages.d.ts","sourceRoot":"","sources":["../../src/bot/owned-pages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAMvC,iFAAiF;AACjF,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA+B;gBAExB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI;IAE1D,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IA0B1B,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAIxB,2EAA2E;IAC3E,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO;IAKrC,IAAI,IAAI,IAAI,EAAE;IAId,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,58 @@
1
+ // Claims survive registry disposal while the Page is reachable: a different
2
+ // controller must never acquire an old session's page by registering it again.
3
+ const owners = new WeakMap();
4
+ /** Session-local page ownership; context membership is never ownership proof. */
5
+ export class OwnedPages {
6
+ onPopup;
7
+ owner = Symbol("browser-session");
8
+ pages = new Map();
9
+ constructor(onPopup) {
10
+ this.onPopup = onPopup;
11
+ }
12
+ /** Only call for a known primary/recovery page, or a proven popup below. */
13
+ register(page) {
14
+ const owner = owners.get(page);
15
+ if (owner !== undefined && owner !== this.owner) {
16
+ throw new Error("Browser page already belongs to another session");
17
+ }
18
+ if (page.isClosed() || this.pages.has(page))
19
+ return;
20
+ owners.set(page, this.owner);
21
+ // Playwright emits this on the opener using lineage captured when the
22
+ // target was created. Do not resolve page.opener() later: it can disappear
23
+ // when the opener closes. No event/proven opener means no assignment.
24
+ const popup = (child) => {
25
+ if (!this.has(page) || child.context() !== page.context())
26
+ return;
27
+ if (owners.has(child))
28
+ return; // duplicate/foreign attribution fails closed
29
+ this.register(child);
30
+ if (this.has(child))
31
+ this.onPopup(child);
32
+ };
33
+ const dispose = () => {
34
+ page.off("popup", popup);
35
+ page.off("close", dispose);
36
+ this.pages.delete(page);
37
+ };
38
+ this.pages.set(page, dispose);
39
+ page.on("popup", popup);
40
+ page.on("close", dispose);
41
+ }
42
+ has(page) {
43
+ return this.pages.has(page) && !page.isClosed();
44
+ }
45
+ /** True only when a DIFFERENT registry has positively claimed the page. */
46
+ claimedByAnother(page) {
47
+ const owner = owners.get(page);
48
+ return owner !== undefined && owner !== this.owner;
49
+ }
50
+ live() {
51
+ return [...this.pages.keys()].filter((page) => this.has(page));
52
+ }
53
+ dispose() {
54
+ for (const dispose of this.pages.values())
55
+ dispose();
56
+ }
57
+ }
58
+ //# sourceMappingURL=owned-pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owned-pages.js","sourceRoot":"","sources":["../../src/bot/owned-pages.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,+EAA+E;AAC/E,MAAM,MAAM,GAAG,IAAI,OAAO,EAAgB,CAAC;AAE3C,iFAAiF;AACjF,MAAM,OAAO,UAAU;IAIQ;IAHZ,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAClC,KAAK,GAAG,IAAI,GAAG,EAAoB,CAAC;IAErD,YAA6B,OAA6B;QAA7B,YAAO,GAAP,OAAO,CAAsB;IAAG,CAAC;IAE9D,4EAA4E;IAC5E,QAAQ,CAAC,IAAU;QACjB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACrE,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QACpD,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,sEAAsE;QACtE,2EAA2E;QAC3E,sEAAsE;QACtE,MAAM,KAAK,GAAG,CAAC,KAAW,EAAQ,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,OAAO,EAAE;gBAAE,OAAO;YAClE,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,OAAO,CAAC,6CAA6C;YAC5E,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACrB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3C,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACzB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACxB,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5B,CAAC;IAED,GAAG,CAAC,IAAU;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClD,CAAC;IAED,2EAA2E;IAC3E,gBAAgB,CAAC,IAAU;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC;IACrD,CAAC;IAED,IAAI;QACF,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,OAAO;QACL,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,OAAO,EAAE,CAAC;IACvD,CAAC;CACF"}
@@ -0,0 +1,35 @@
1
+ import type { BrowserContext, Page } from "playwright";
2
+ import { OwnedPages } from "./owned-pages.js";
3
+ /** One session's page lifetime; never launches or terminates a browser process. */
4
+ export declare class PageDriver {
5
+ private readonly getContext;
6
+ private readonly humanize;
7
+ page: Page | null;
8
+ primaryPage: Page | null;
9
+ private openedTabs;
10
+ readonly ownedPages: OwnedPages;
11
+ private readonly documentSubscriptions;
12
+ private mainDocumentSequence;
13
+ private readonly mainDocumentIdentities;
14
+ private readonly trackedMainDocumentPages;
15
+ harnessAttachedPage: boolean;
16
+ oauthProductPage: Page | null;
17
+ oauthProviderPage: Page | null;
18
+ oauthProviderPageClosed: boolean;
19
+ constructor(getContext: () => BrowserContext | null, humanize: boolean);
20
+ private get context();
21
+ private sleep;
22
+ trackMainDocument(page: Page): void;
23
+ mainDocumentIdentity(): string;
24
+ trackOpenedTabs(page: Page): void;
25
+ currentUrl(): string;
26
+ recoverActivePage(): boolean;
27
+ armOpenedTabAdoption(): void;
28
+ adoptOpenedTab(graceMs?: number): Promise<string | null>;
29
+ private takeFollowableTab;
30
+ adoptLivePage(): boolean;
31
+ goto(url: string): Promise<void>;
32
+ reload(): Promise<void>;
33
+ disposeRegistrations(): void;
34
+ }
35
+ //# sourceMappingURL=page-driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-driver.d.ts","sourceRoot":"","sources":["../../src/bot/page-driver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,mFAAmF;AACnF,qBAAa,UAAU;IA8CnB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IA9C3B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAQ;IAMzB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAQhC,OAAO,CAAC,UAAU,CAAc;IAEhC,QAAQ,CAAC,UAAU,aAIhB;IAEH,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA+B;IAErE,OAAO,CAAC,oBAAoB,CAAK;IAEjC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA+B;IAEtE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuB;IAIhE,mBAAmB,UAAS;IAO5B,gBAAgB,EAAE,IAAI,GAAG,IAAI,CAAQ;IAErC,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEtC,uBAAuB,UAAS;gBAEb,UAAU,EAAE,MAAM,cAAc,GAAG,IAAI,EACvC,QAAQ,EAAE,OAAO;IAEpC,OAAO,KAAK,OAAO,GAElB;IACD,OAAO,CAAC,KAAK;IAIb,iBAAiB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IA4BnC,oBAAoB,IAAI,MAAM;IAS9B,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAOjC,UAAU,IAAI,MAAM;IAIpB,iBAAiB,IAAI,OAAO;IAS5B,oBAAoB,IAAI,IAAI;IAWtB,cAAc,CAAC,OAAO,SAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA8BzD,OAAO,CAAC,iBAAiB;IAiBzB,aAAa,IAAI,OAAO;IAmBlB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsGhC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ7B,oBAAoB,IAAI,IAAI;CAK7B"}
@@ -0,0 +1,302 @@
1
+ import { OwnedPages } from "./owned-pages.js";
2
+ /** One session's page lifetime; never launches or terminates a browser process. */
3
+ export class PageDriver {
4
+ getContext;
5
+ humanize;
6
+ page = null;
7
+ // The page start() configured with the controller's navigation/captcha
8
+ // handlers. OAuth may temporarily switch `this.page` to a popup, but session
9
+ // reuse must always restore this original page rather than adopting a popup
10
+ // whose lifecycle handlers were never installed.
11
+ primaryPage = null;
12
+ // Tabs the PAGE opened (target=_blank / window.open) since the operator armed
13
+ // adoption for an action, oldest first. A real user lands on the tab their
14
+ // click opened — an email magic-link button in Gmail is the case this exists
15
+ // for — so the operator has to follow it too. Reading the link's href instead
16
+ // is not an option: a single-use login token is sealed and must never be
17
+ // handed to the model as text.
18
+ openedTabs = [];
19
+ ownedPages = new OwnedPages((page) => {
20
+ this.trackMainDocument(page);
21
+ this.openedTabs.push(page);
22
+ if (this.openedTabs.length > 8)
23
+ this.openedTabs.splice(0, this.openedTabs.length - 8);
24
+ });
25
+ documentSubscriptions = new Map();
26
+ mainDocumentSequence = 0;
27
+ mainDocumentIdentities = new WeakMap();
28
+ trackedMainDocumentPages = new WeakSet();
29
+ // The replay harness owns this context so it can route the storefront from a
30
+ // HAR, then remove that route before checkout becomes live.
31
+ harnessAttachedPage = false;
32
+ // T6/T7 — OAuth handshake bookkeeping. Legacy startOAuth() adopts a
33
+ // popup window as the active page, so keep the product tab parked here
34
+ // until settleAfterOAuth() restores it. The operator's oauth_login action
35
+ // keeps the observed product page active for the click and opens a recovery
36
+ // tab before the provider can redirect or close either OAuth transport.
37
+ oauthProductPage = null;
38
+ oauthProviderPage = null;
39
+ oauthProviderPageClosed = false;
40
+ constructor(getContext, humanize) {
41
+ this.getContext = getContext;
42
+ this.humanize = humanize;
43
+ }
44
+ get context() {
45
+ return this.getContext();
46
+ }
47
+ sleep(ms) {
48
+ return new Promise((resolve) => setTimeout(resolve, ms));
49
+ }
50
+ trackMainDocument(page) {
51
+ if (page.isClosed() || this.trackedMainDocumentPages.has(page))
52
+ return;
53
+ this.trackedMainDocumentPages.add(page);
54
+ this.mainDocumentIdentities.set(page, ++this.mainDocumentSequence);
55
+ // A REPLACED main document advances the identity; a same-document History
56
+ // API navigation does not. Playwright emits `framenavigated` for both, so
57
+ // keying on it made every `history.replaceState` inside an SPA checkout
58
+ // retire every operator ref mid-form — the identity churned faster than a
59
+ // multi-field address block could be filled. `domcontentloaded` fires once
60
+ // per real main-frame document (playwright's client `Frame` gates it on
61
+ // `!this._parentFrame`), which is exactly the document-replacement signal.
62
+ // A same-document route change to a genuinely different logical page is
63
+ // still caught by the observation epoch's normalized origin+pathname fold
64
+ // (compactV2EpochDoc), which is the backstop this narrowing relies on.
65
+ const onDocument = () => {
66
+ this.mainDocumentIdentities.set(page, ++this.mainDocumentSequence);
67
+ };
68
+ const dispose = () => {
69
+ page.off("domcontentloaded", onDocument);
70
+ page.off("close", dispose);
71
+ this.documentSubscriptions.delete(page);
72
+ this.trackedMainDocumentPages.delete(page);
73
+ };
74
+ this.documentSubscriptions.set(page, dispose);
75
+ page.on("domcontentloaded", onDocument);
76
+ page.on("close", dispose);
77
+ }
78
+ mainDocumentIdentity() {
79
+ const page = this.page;
80
+ if (page === null)
81
+ return "none";
82
+ this.trackMainDocument(page);
83
+ return String(this.mainDocumentIdentities.get(page));
84
+ }
85
+ // Register only explicitly created primary/recovery pages. Popup enrollment
86
+ // follows their creation-time opener events, never context-wide page events.
87
+ trackOpenedTabs(page) {
88
+ this.ownedPages.register(page);
89
+ this.trackMainDocument(page);
90
+ }
91
+ // URL of the active page (the OAuth page mid-handshake, the product
92
+ // page otherwise). Cheap — no screenshot, unlike getState().
93
+ currentUrl() {
94
+ return this.page !== null ? this.page.url() : "";
95
+ }
96
+ recoverActivePage() {
97
+ return this.adoptLivePage();
98
+ }
99
+ // ───────────── new-tab adoption ─────────────
100
+ //
101
+ // Arm adoption immediately BEFORE an action that may open a tab. Anything
102
+ // already queued belonged to an earlier action and is not this action's to
103
+ // follow.
104
+ armOpenedTabAdoption() {
105
+ this.openedTabs.length = 0;
106
+ }
107
+ // Adopt the newest live tab opened since armOpenedTabAdoption() as the active
108
+ // page, so the next observe/act reads the tab the click actually opened.
109
+ // Returns the adopted URL, or null when the action opened no followable tab.
110
+ //
111
+ // `graceMs` covers the window between the click returning and Playwright
112
+ // delivering the context "page" event; a caller that has already waited for
113
+ // the page to settle passes 0 and just drains what arrived.
114
+ async adoptOpenedTab(graceMs = 0) {
115
+ const deadline = Date.now() + Math.max(0, graceMs);
116
+ let candidate = this.takeFollowableTab();
117
+ while (candidate === null && Date.now() < deadline) {
118
+ await this.sleep(50);
119
+ candidate = this.takeFollowableTab();
120
+ }
121
+ if (candidate === null)
122
+ return null;
123
+ this.openedTabs.length = 0;
124
+ // A window.open target starts at about:blank and is navigated a tick later.
125
+ // Adopting it while blank would report an empty page to the host, so wait
126
+ // (bounded) for the document it was opened for.
127
+ const blank = (url) => url === "" || url === "about:blank" || url === "about:srcdoc";
128
+ for (let i = 0; i < 40 && !candidate.isClosed() && blank(candidate.url()); i++) {
129
+ await this.sleep(50);
130
+ }
131
+ if (!this.ownedPages.has(candidate))
132
+ return null;
133
+ this.page = candidate;
134
+ this.trackMainDocument(candidate);
135
+ await candidate.bringToFront().catch(() => undefined);
136
+ await candidate
137
+ .waitForLoadState("domcontentloaded", { timeout: 15_000 })
138
+ .catch(() => undefined);
139
+ return candidate.isClosed() ? null : candidate.url();
140
+ }
141
+ // Newest owned popup the operator may follow. Explicit primary/recovery
142
+ // pages never enter the queue; the active page and OAuth transports retain
143
+ // their existing lifecycle handling.
144
+ takeFollowableTab() {
145
+ for (let i = this.openedTabs.length - 1; i >= 0; i--) {
146
+ const tab = this.openedTabs[i];
147
+ if (!this.ownedPages.has(tab))
148
+ continue;
149
+ if (tab === this.page ||
150
+ tab === this.primaryPage ||
151
+ tab === this.oauthProductPage ||
152
+ tab === this.oauthProviderPage) {
153
+ continue;
154
+ }
155
+ return tab;
156
+ }
157
+ return null;
158
+ }
159
+ adoptLivePage() {
160
+ if (this.page !== null && this.ownedPages.has(this.page))
161
+ return true;
162
+ if (this.context === null)
163
+ return false;
164
+ const pages = this.ownedPages.live();
165
+ if (pages.length === 0)
166
+ return false;
167
+ const product = this.oauthProductPage !== null && this.ownedPages.has(this.oauthProductPage)
168
+ ? this.oauthProductPage
169
+ : null;
170
+ const nonAuth = [...pages]
171
+ .reverse()
172
+ .find((p) => !/accounts\.google\.com|github\.com\/login|login\.microsoftonline\.com/i.test(p.url()));
173
+ this.page = nonAuth ?? product ?? pages[pages.length - 1] ?? null;
174
+ return this.page !== null;
175
+ }
176
+ async goto(url) {
177
+ if (!this.page)
178
+ throw new Error("Browser not started");
179
+ // Retry transient network/proxy drops. A residential SOCKS tunnel
180
+ // intermittently resets a connection mid-navigation (Chrome surfaces
181
+ // net::ERR_SOCKS_CONNECTION_FAILED / ERR_CONNECTION_RESET / ERR_NETWORK_
182
+ // CHANGED / ERR_TIMED_OUT), especially on heavy onboarding pages that
183
+ // open many subresource connections at once (algolia's dashboard_setup).
184
+ // The host is reachable on the next attempt — a single goto failure
185
+ // shouldn't fail the whole signup. Only retry these connection-level
186
+ // errors; HTTP statuses and selector/logic errors fall straight through.
187
+ // net::ERR_ABORTED — a navigation superseded by a redirect/JS-nav during
188
+ // the domcontentloaded wait. Usually transient (a redirect race on the
189
+ // first hit of an auth-gated portal — MEASURED 2026-06-11: defang's
190
+ // portal.defang.io aborted on the initial goto); a retry lands the
191
+ // settled page. Distinct from ERR_CONNECTION_ABORTED (a dropped socket).
192
+ const TRANSIENT_NET = /ERR_SOCKS_CONNECTION_FAILED|ERR_CONNECTION_(?:RESET|CLOSED|FAILED|ABORTED)|ERR_NETWORK_CHANGED|ERR_TIMED_OUT|ERR_NAME_NOT_RESOLVED|net::ERR_EMPTY_RESPONSE|net::ERR_ABORTED/i;
193
+ const MAX_GOTO_ATTEMPTS = 3;
194
+ const sameOriginPathAndSearch = (a, b) => {
195
+ try {
196
+ const left = new URL(a);
197
+ const right = new URL(b);
198
+ return (left.origin === right.origin &&
199
+ left.pathname === right.pathname &&
200
+ left.search === right.search);
201
+ }
202
+ catch {
203
+ return false;
204
+ }
205
+ };
206
+ const landedAuthGateForTarget = (landedRaw, targetRaw) => {
207
+ try {
208
+ const landed = new URL(landedRaw);
209
+ const target = new URL(targetRaw);
210
+ if (landed.origin !== target.origin)
211
+ return false;
212
+ return /\/(?:sign[_-]?in|login|log[_-]?in|auth)(?:\/|$)/i.test(landed.pathname);
213
+ }
214
+ catch {
215
+ return false;
216
+ }
217
+ };
218
+ for (let attempt = 1;; attempt++) {
219
+ try {
220
+ await this.page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 });
221
+ // A SOCKS/connection drop does NOT always throw: Chrome resolves
222
+ // domcontentloaded on its own `chrome-error://chromewebdata/`
223
+ // interstitial and goto returns cleanly. The bot then ran the whole
224
+ // planner on a dead error page and gave up after one round (MEASURED
225
+ // 2026-06-11: galileo/lancedb landed on chrome-error with the app
226
+ // host as the title, never retried). Treat a chrome-error landing as
227
+ // the same transient class and retry it like a thrown net error.
228
+ const landed = this.page.url();
229
+ if (landed.startsWith("chrome-error://")) {
230
+ if (attempt >= MAX_GOTO_ATTEMPTS) {
231
+ throw new Error(`net::navigation landed on a Chrome error page for ${url} ` +
232
+ `after ${attempt} attempts (transient proxy/host failure)`);
233
+ }
234
+ await this.sleep(1500 * attempt);
235
+ continue;
236
+ }
237
+ break;
238
+ }
239
+ catch (err) {
240
+ const msg = err instanceof Error ? err.message : String(err);
241
+ // Some client-routed apps commit the address bar to the requested SPA
242
+ // route but never fire the lifecycle event Playwright is waiting for.
243
+ // Treat that as a successful navigation: callers immediately inspect
244
+ // the DOM and have their own element-level waits.
245
+ if (/Timeout \d+ms exceeded/i.test(msg)) {
246
+ await this.sleep(500);
247
+ if (sameOriginPathAndSearch(this.page.url(), url))
248
+ break;
249
+ if (landedAuthGateForTarget(this.page.url(), url))
250
+ break;
251
+ await this.page
252
+ .waitForURL((landed) => sameOriginPathAndSearch(landed.toString(), url), {
253
+ timeout: 5000,
254
+ })
255
+ .then(() => undefined)
256
+ .catch(() => undefined);
257
+ if (sameOriginPathAndSearch(this.page.url(), url))
258
+ break;
259
+ if (landedAuthGateForTarget(this.page.url(), url))
260
+ break;
261
+ }
262
+ if (attempt >= MAX_GOTO_ATTEMPTS || !TRANSIENT_NET.test(msg))
263
+ throw err;
264
+ // Linear backoff — give the tunnel a moment to recover a slot.
265
+ await this.sleep(1500 * attempt);
266
+ }
267
+ }
268
+ // Post-load dwell. Cloudflare/reCAPTCHA scoring runs JS that
269
+ // collects behavior signals over a window (typically 500-2000ms);
270
+ // landing on a page and immediately interacting reads as bot-like.
271
+ // The "dwell" gives the scoring window enough wall-clock to settle
272
+ // and also gives any deferred JS time to register event listeners
273
+ // we'll later fire.
274
+ if (this.humanize) {
275
+ await this.sleep(rand(800, 2000));
276
+ }
277
+ }
278
+ // Reload the current page. Used by the post-verify flow to make a SPA
279
+ // re-read a server-side state change (email verified) that the client
280
+ // hasn't picked up yet. Best-effort: a reload failure is non-fatal — the
281
+ // caller re-reads the page state regardless.
282
+ async reload() {
283
+ if (!this.page)
284
+ throw new Error("Browser not started");
285
+ try {
286
+ await this.page.reload({ waitUntil: "domcontentloaded", timeout: 20_000 });
287
+ }
288
+ catch {
289
+ // reload failed (slow SPA / transient) — caller re-inspects anyway
290
+ }
291
+ }
292
+ disposeRegistrations() {
293
+ this.ownedPages.dispose();
294
+ for (const dispose of this.documentSubscriptions.values())
295
+ dispose();
296
+ this.openedTabs.length = 0;
297
+ }
298
+ }
299
+ function rand(min, max) {
300
+ return Math.floor(min + Math.random() * (max - min + 1));
301
+ }
302
+ //# sourceMappingURL=page-driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-driver.js","sourceRoot":"","sources":["../../src/bot/page-driver.ts"],"names":[],"mappings":"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"}
@@ -102,6 +102,10 @@ export interface Observation {
102
102
  state: "in_progress";
103
103
  provider_page: "closed_or_detached";
104
104
  next_action: "operate_observe";
105
+ } | {
106
+ state: "awaiting_human";
107
+ reason: string;
108
+ next_action: "operate_observe";
105
109
  };
106
110
  needs_user?: NeedsUserLogin;
107
111
  user_email?: string;