@mastra/agent-browser 0.1.0-alpha.0 → 0.2.0-alpha.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.
- package/CHANGELOG.md +46 -0
- package/README.md +138 -0
- package/dist/index.cjs +122 -219
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -85
- package/dist/index.d.ts +58 -85
- package/dist/index.js +122 -220
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -7,12 +7,9 @@ var zod = require('zod');
|
|
|
7
7
|
|
|
8
8
|
// src/agent-browser.ts
|
|
9
9
|
var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
10
|
-
sharedManager = null;
|
|
11
10
|
browserConfig;
|
|
12
11
|
resolveCdpUrl;
|
|
13
12
|
onBrowserCreated;
|
|
14
|
-
/** Map of thread ID to dedicated browser manager (for 'thread' scope) */
|
|
15
|
-
threadBrowsers = /* @__PURE__ */ new Map();
|
|
16
13
|
constructor(config) {
|
|
17
14
|
super(config);
|
|
18
15
|
this.browserConfig = config.browserConfig;
|
|
@@ -20,25 +17,11 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
20
17
|
this.onBrowserCreated = config.onBrowserCreated;
|
|
21
18
|
}
|
|
22
19
|
/**
|
|
23
|
-
*
|
|
20
|
+
* Get the page for a specific thread, creating session if needed.
|
|
24
21
|
*/
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Clear the shared browser manager (called when browser disconnects).
|
|
30
|
-
*/
|
|
31
|
-
clearSharedManager() {
|
|
32
|
-
this.sharedManager = null;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Get the shared browser manager.
|
|
36
|
-
*/
|
|
37
|
-
getSharedManager() {
|
|
38
|
-
if (!this.sharedManager) {
|
|
39
|
-
throw new Error("Browser not launched");
|
|
40
|
-
}
|
|
41
|
-
return this.sharedManager;
|
|
22
|
+
async getPageForThread(threadId) {
|
|
23
|
+
const manager = await this.getManagerForThread(threadId);
|
|
24
|
+
return manager.getPage();
|
|
42
25
|
}
|
|
43
26
|
/**
|
|
44
27
|
* Create a new session for a thread.
|
|
@@ -54,7 +37,10 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
54
37
|
const manager = new agentBrowser.BrowserManager();
|
|
55
38
|
const launchOptions = {
|
|
56
39
|
headless: this.browserConfig.headless ?? true,
|
|
57
|
-
viewport: this.browserConfig.viewport
|
|
40
|
+
viewport: this.browserConfig.viewport,
|
|
41
|
+
profile: this.browserConfig.profile,
|
|
42
|
+
executablePath: this.browserConfig.executablePath,
|
|
43
|
+
storageState: this.browserConfig.storageState
|
|
58
44
|
};
|
|
59
45
|
if (this.browserConfig.cdpUrl && this.resolveCdpUrl) {
|
|
60
46
|
launchOptions.cdpUrl = await this.resolveCdpUrl(this.browserConfig.cdpUrl);
|
|
@@ -69,7 +55,7 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
69
55
|
throw error;
|
|
70
56
|
}
|
|
71
57
|
session.manager = manager;
|
|
72
|
-
this.
|
|
58
|
+
this.threadManagers.set(threadId, manager);
|
|
73
59
|
try {
|
|
74
60
|
if (savedState && savedState.tabs.length > 0) {
|
|
75
61
|
this.logger?.debug?.(`Restoring browser state for thread ${threadId}: ${savedState.tabs.length} tabs`);
|
|
@@ -77,7 +63,7 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
77
63
|
}
|
|
78
64
|
this.onBrowserCreated?.(manager, threadId);
|
|
79
65
|
} catch (error) {
|
|
80
|
-
this.
|
|
66
|
+
this.threadManagers.delete(threadId);
|
|
81
67
|
session.manager = void 0;
|
|
82
68
|
try {
|
|
83
69
|
await manager.close();
|
|
@@ -117,13 +103,6 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
117
103
|
this.logger?.warn?.(`Failed to restore browser state: ${error}`);
|
|
118
104
|
}
|
|
119
105
|
}
|
|
120
|
-
/**
|
|
121
|
-
* Switch to an existing session.
|
|
122
|
-
* For 'thread' scope, no switching needed - each thread has its own manager.
|
|
123
|
-
* For 'shared' scope, nothing to switch.
|
|
124
|
-
*/
|
|
125
|
-
async switchToSession(_session) {
|
|
126
|
-
}
|
|
127
106
|
/**
|
|
128
107
|
* Get the browser manager for a specific session.
|
|
129
108
|
*/
|
|
@@ -139,61 +118,15 @@ var AgentBrowserThreadManager = class extends browser.ThreadManager {
|
|
|
139
118
|
async doDestroySession(session) {
|
|
140
119
|
if (this.scope === "thread" && session.manager) {
|
|
141
120
|
await session.manager.close();
|
|
142
|
-
this.threadBrowsers.delete(session.threadId);
|
|
143
121
|
}
|
|
144
122
|
}
|
|
145
123
|
/**
|
|
146
124
|
* Destroy all sessions (called during browser close).
|
|
125
|
+
* doDestroySession handles closing individual browser managers.
|
|
147
126
|
*/
|
|
148
127
|
async destroyAllSessions() {
|
|
149
|
-
for (const [threadId, manager] of this.threadBrowsers) {
|
|
150
|
-
try {
|
|
151
|
-
await manager.close();
|
|
152
|
-
} catch {
|
|
153
|
-
this.logger?.debug?.(`Failed to close browser for thread: ${threadId}`);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
this.threadBrowsers.clear();
|
|
157
128
|
await super.destroyAllSessions();
|
|
158
129
|
}
|
|
159
|
-
/**
|
|
160
|
-
* Check if any thread browsers are still running.
|
|
161
|
-
*/
|
|
162
|
-
hasActiveThreadBrowsers() {
|
|
163
|
-
return this.threadBrowsers.size > 0;
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Get the browser manager for an existing thread session without creating a new one.
|
|
167
|
-
* Returns null if no session exists for the thread.
|
|
168
|
-
*/
|
|
169
|
-
getExistingManagerForThread(threadId) {
|
|
170
|
-
if (this.scope === "thread") {
|
|
171
|
-
return this.threadBrowsers.get(threadId) ?? null;
|
|
172
|
-
}
|
|
173
|
-
return this.sharedManager;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Clear all session tracking without closing browsers.
|
|
177
|
-
* Used when browsers have been externally closed and we just need to reset state.
|
|
178
|
-
*/
|
|
179
|
-
clearAllSessions() {
|
|
180
|
-
this.threadBrowsers.clear();
|
|
181
|
-
this.sessions.clear();
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Clear a specific thread's session without closing the browser.
|
|
185
|
-
* Used when a thread's browser has been externally closed.
|
|
186
|
-
* Preserves the browser state for potential restoration.
|
|
187
|
-
* @param threadId - The thread ID to clear
|
|
188
|
-
*/
|
|
189
|
-
clearSession(threadId) {
|
|
190
|
-
const session = this.sessions.get(threadId);
|
|
191
|
-
if (session?.browserState) {
|
|
192
|
-
this.savedBrowserStates.set(threadId, session.browserState);
|
|
193
|
-
}
|
|
194
|
-
this.threadBrowsers.delete(threadId);
|
|
195
|
-
this.sessions.delete(threadId);
|
|
196
|
-
}
|
|
197
130
|
};
|
|
198
131
|
var gotoInputSchema = zod.z.object({
|
|
199
132
|
url: zod.z.string().describe("The URL to navigate to"),
|
|
@@ -561,31 +494,43 @@ function createAgentBrowserTools(browser) {
|
|
|
561
494
|
};
|
|
562
495
|
}
|
|
563
496
|
|
|
497
|
+
// src/utils.ts
|
|
498
|
+
async function getBrowserPid(manager) {
|
|
499
|
+
try {
|
|
500
|
+
let browser = manager.getBrowser();
|
|
501
|
+
if (!browser) {
|
|
502
|
+
const ctx = manager.getContext();
|
|
503
|
+
browser = ctx?.browser?.() ?? null;
|
|
504
|
+
}
|
|
505
|
+
if (!browser) return void 0;
|
|
506
|
+
const cdp = await browser.newBrowserCDPSession();
|
|
507
|
+
try {
|
|
508
|
+
const info = await cdp.send("SystemInfo.getProcessInfo");
|
|
509
|
+
const browserProcess = info.processInfo?.find((p) => p.type === "browser");
|
|
510
|
+
return browserProcess?.id;
|
|
511
|
+
} finally {
|
|
512
|
+
await cdp.detach().catch(() => void 0);
|
|
513
|
+
}
|
|
514
|
+
} catch {
|
|
515
|
+
return void 0;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
564
519
|
// src/agent-browser.ts
|
|
565
|
-
var AgentBrowser = class
|
|
520
|
+
var AgentBrowser = class extends browser.MastraBrowser {
|
|
566
521
|
id;
|
|
567
522
|
name = "AgentBrowser";
|
|
568
523
|
provider = "vercel-labs/agent-browser";
|
|
569
|
-
/** Primary browser manager (for 'none' mode, also used as fallback) */
|
|
570
|
-
browserManager = null;
|
|
571
524
|
defaultTimeout = 3e4;
|
|
572
|
-
/**
|
|
573
|
-
|
|
574
|
-
/** Default key for shared scope */
|
|
575
|
-
static SHARED_STREAM_KEY = "__shared__";
|
|
525
|
+
/** Pending PID lookups — awaited in disconnect handlers to avoid racing. */
|
|
526
|
+
pidLookups = /* @__PURE__ */ new Set();
|
|
576
527
|
constructor(config = {}) {
|
|
577
528
|
super(config);
|
|
578
529
|
this.id = `agent-browser-${Date.now()}`;
|
|
579
530
|
if (config.timeout) {
|
|
580
531
|
this.defaultTimeout = config.timeout;
|
|
581
532
|
}
|
|
582
|
-
|
|
583
|
-
if (config.cdpUrl && effectiveScope === "thread") {
|
|
584
|
-
this.logger.warn?.(
|
|
585
|
-
'Browser scope "thread" is not supported when connecting via cdpUrl. Falling back to "shared" (shared browser connection).'
|
|
586
|
-
);
|
|
587
|
-
effectiveScope = "shared";
|
|
588
|
-
}
|
|
533
|
+
const effectiveScope = config.cdpUrl ? config.scope ?? "shared" : config.scope ?? "thread";
|
|
589
534
|
this.threadManager = new AgentBrowserThreadManager({
|
|
590
535
|
scope: effectiveScope,
|
|
591
536
|
browserConfig: config,
|
|
@@ -602,13 +547,13 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
602
547
|
});
|
|
603
548
|
}
|
|
604
549
|
// ---------------------------------------------------------------------------
|
|
605
|
-
// Thread
|
|
550
|
+
// Thread Scope (delegated to ThreadManager)
|
|
606
551
|
// ---------------------------------------------------------------------------
|
|
607
552
|
/**
|
|
608
553
|
* Ensure browser is ready and thread session exists.
|
|
609
554
|
* Creates a new page/context for the current thread if needed.
|
|
610
555
|
*
|
|
611
|
-
* For '
|
|
556
|
+
* For 'thread' scope, we need to create the thread session BEFORE
|
|
612
557
|
* calling super.ensureReady() because the base class's ensureReady() will
|
|
613
558
|
* call checkBrowserAlive(), which needs at least one thread browser to exist.
|
|
614
559
|
*/
|
|
@@ -616,17 +561,17 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
616
561
|
const scope = this.threadManager.getScope();
|
|
617
562
|
const threadId = this.getCurrentThread();
|
|
618
563
|
const existingSession = this.threadManager.hasSession(threadId);
|
|
619
|
-
if (scope === "thread" &&
|
|
564
|
+
if (scope === "thread" && !existingSession) {
|
|
620
565
|
await this.getManagerForThread(threadId);
|
|
621
566
|
}
|
|
622
567
|
await super.ensureReady();
|
|
623
|
-
if (scope === "thread" &&
|
|
568
|
+
if (scope === "thread" && existingSession) {
|
|
624
569
|
await this.getManagerForThread(threadId);
|
|
625
570
|
}
|
|
626
571
|
}
|
|
627
572
|
/**
|
|
628
573
|
* Get the browser manager for the current thread.
|
|
629
|
-
* Delegates to ThreadManager for
|
|
574
|
+
* Delegates to ThreadManager for scope handling.
|
|
630
575
|
*/
|
|
631
576
|
async getManagerForThread(threadId) {
|
|
632
577
|
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
@@ -639,56 +584,47 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
639
584
|
}
|
|
640
585
|
return this.threadManager.getManagerForThread(effectiveThreadId);
|
|
641
586
|
}
|
|
642
|
-
/**
|
|
643
|
-
* Get the page for a specific thread.
|
|
644
|
-
* For thread-isolated modes, ensures we're on the correct context/page.
|
|
645
|
-
*/
|
|
646
|
-
async getPageForThread(threadId) {
|
|
647
|
-
const manager = await this.getManagerForThread(threadId);
|
|
648
|
-
return manager.getPage();
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* Close a specific thread's browser session.
|
|
652
|
-
* Delegates to ThreadManager and notifies registered callbacks.
|
|
653
|
-
*/
|
|
654
|
-
async closeThreadSession(threadId) {
|
|
655
|
-
await this.threadManager.destroySession(threadId);
|
|
656
|
-
this.notifyBrowserClosed(threadId);
|
|
657
|
-
}
|
|
658
587
|
// ---------------------------------------------------------------------------
|
|
659
588
|
// Lifecycle
|
|
660
589
|
// ---------------------------------------------------------------------------
|
|
661
590
|
async doLaunch() {
|
|
662
591
|
const scope = this.threadManager.getScope();
|
|
663
592
|
if (scope === "thread") {
|
|
664
|
-
this.
|
|
665
|
-
this.threadManager.setSharedManager(this.
|
|
593
|
+
this.sharedManager = new agentBrowser.BrowserManager();
|
|
594
|
+
this.threadManager.setSharedManager(this.sharedManager);
|
|
666
595
|
return;
|
|
667
596
|
}
|
|
668
|
-
this.
|
|
597
|
+
this.sharedManager = new agentBrowser.BrowserManager();
|
|
669
598
|
const localConfig = this.config;
|
|
670
599
|
const launchOptions = {
|
|
671
600
|
headless: localConfig.headless ?? true,
|
|
672
|
-
viewport: localConfig.viewport
|
|
601
|
+
viewport: localConfig.viewport,
|
|
602
|
+
profile: localConfig.profile,
|
|
603
|
+
executablePath: localConfig.executablePath,
|
|
604
|
+
storageState: localConfig.storageState
|
|
673
605
|
};
|
|
674
606
|
if (localConfig.cdpUrl) {
|
|
675
607
|
launchOptions.cdpUrl = await this.resolveCdpUrl(localConfig.cdpUrl);
|
|
676
608
|
}
|
|
677
|
-
await this.
|
|
678
|
-
this.threadManager.setSharedManager(this.
|
|
679
|
-
this.
|
|
609
|
+
await this.sharedManager.launch(launchOptions);
|
|
610
|
+
this.threadManager.setSharedManager(this.sharedManager);
|
|
611
|
+
this.setupCloseListenerForSharedScope(this.sharedManager);
|
|
680
612
|
}
|
|
681
613
|
/**
|
|
682
|
-
* Set up close event listeners for '
|
|
614
|
+
* Set up close event listeners for 'shared' scope browser.
|
|
683
615
|
* This handles the case where the shared browser is closed externally.
|
|
684
616
|
*/
|
|
685
|
-
|
|
617
|
+
setupCloseListenerForSharedScope(manager) {
|
|
686
618
|
try {
|
|
619
|
+
const pidLookup = getBrowserPid(manager).then((pid) => {
|
|
620
|
+
if (pid && this.sharedManager === manager) this.sharedBrowserPid = pid;
|
|
621
|
+
}).finally(() => this.pidLookups.delete(pidLookup));
|
|
622
|
+
this.pidLookups.add(pidLookup);
|
|
687
623
|
let disconnectHandled = false;
|
|
688
624
|
const handleDisconnect = () => {
|
|
689
625
|
if (disconnectHandled) return;
|
|
690
626
|
disconnectHandled = true;
|
|
691
|
-
this.handleBrowserDisconnected();
|
|
627
|
+
void pidLookup.catch(() => void 0).then(() => this.handleBrowserDisconnected());
|
|
692
628
|
};
|
|
693
629
|
const context = manager.getContext();
|
|
694
630
|
if (context) {
|
|
@@ -707,13 +643,15 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
707
643
|
}
|
|
708
644
|
}
|
|
709
645
|
async doClose() {
|
|
646
|
+
await Promise.allSettled([...this.pidLookups]);
|
|
647
|
+
this.pidLookups.clear();
|
|
710
648
|
await this.threadManager.destroyAllSessions();
|
|
711
649
|
this.setCurrentThread(void 0);
|
|
712
650
|
const scope = this.threadManager.getScope();
|
|
713
|
-
if (scope === "shared" && this.
|
|
714
|
-
await this.
|
|
651
|
+
if (scope === "shared" && this.sharedManager) {
|
|
652
|
+
await this.sharedManager.close();
|
|
715
653
|
}
|
|
716
|
-
this.
|
|
654
|
+
this.sharedManager = null;
|
|
717
655
|
}
|
|
718
656
|
/**
|
|
719
657
|
* Check if the browser is still alive by verifying the page is connected.
|
|
@@ -722,13 +660,13 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
722
660
|
async checkBrowserAlive() {
|
|
723
661
|
const scope = this.threadManager.getScope();
|
|
724
662
|
if (scope === "thread") {
|
|
725
|
-
return this.threadManager.
|
|
663
|
+
return this.threadManager.hasActiveThreadManagers();
|
|
726
664
|
}
|
|
727
|
-
if (!this.
|
|
665
|
+
if (!this.sharedManager) {
|
|
728
666
|
return false;
|
|
729
667
|
}
|
|
730
668
|
try {
|
|
731
|
-
const page = this.
|
|
669
|
+
const page = this.sharedManager.getPage();
|
|
732
670
|
const url = page.url();
|
|
733
671
|
if (url && url !== "about:blank") {
|
|
734
672
|
const state = await this.getBrowserState();
|
|
@@ -767,28 +705,21 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
767
705
|
async getPage(explicitThreadId) {
|
|
768
706
|
const scope = this.getScope();
|
|
769
707
|
const threadId = explicitThreadId ?? this.getCurrentThread();
|
|
770
|
-
if (scope === "thread"
|
|
771
|
-
return this.getPageForThread(threadId);
|
|
708
|
+
if (scope === "thread") {
|
|
709
|
+
return this.threadManager.getPageForThread(threadId);
|
|
772
710
|
}
|
|
773
|
-
if (!this.
|
|
774
|
-
return this.
|
|
711
|
+
if (!this.sharedManager) throw new Error("Browser not launched");
|
|
712
|
+
return this.sharedManager.getPage();
|
|
775
713
|
}
|
|
776
714
|
/**
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
* For 'shared' scope, notifies all callbacks.
|
|
715
|
+
* Get the active page for a thread (implements abstract method from base class).
|
|
716
|
+
* Returns null if no page is available, unlike getPage which throws.
|
|
780
717
|
*/
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
this.logger.debug?.(`Cleared browser session for thread: ${threadId}`);
|
|
787
|
-
this.notifyBrowserClosed(threadId);
|
|
788
|
-
} else {
|
|
789
|
-
this.browserManager = null;
|
|
790
|
-
this.threadManager.clearSharedManager();
|
|
791
|
-
super.handleBrowserDisconnected();
|
|
718
|
+
async getActivePage(threadId) {
|
|
719
|
+
try {
|
|
720
|
+
return await this.getPage(threadId);
|
|
721
|
+
} catch {
|
|
722
|
+
return null;
|
|
792
723
|
}
|
|
793
724
|
}
|
|
794
725
|
/**
|
|
@@ -797,11 +728,17 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
797
728
|
*/
|
|
798
729
|
setupCloseListenerForThread(manager, threadId) {
|
|
799
730
|
try {
|
|
731
|
+
const pidLookup = getBrowserPid(manager).then((pid) => {
|
|
732
|
+
if (pid && this.threadManager?.getExistingManagerForThread(threadId) === manager) {
|
|
733
|
+
this.threadBrowserPids.set(threadId, pid);
|
|
734
|
+
}
|
|
735
|
+
}).finally(() => this.pidLookups.delete(pidLookup));
|
|
736
|
+
this.pidLookups.add(pidLookup);
|
|
800
737
|
let disconnectHandled = false;
|
|
801
738
|
const handleDisconnect = () => {
|
|
802
739
|
if (disconnectHandled) return;
|
|
803
740
|
disconnectHandled = true;
|
|
804
|
-
this.handleThreadBrowserDisconnected(threadId);
|
|
741
|
+
void pidLookup.catch(() => void 0).then(() => this.handleThreadBrowserDisconnected(threadId));
|
|
805
742
|
};
|
|
806
743
|
const context = manager.getContext();
|
|
807
744
|
if (context) {
|
|
@@ -819,15 +756,6 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
819
756
|
} catch {
|
|
820
757
|
}
|
|
821
758
|
}
|
|
822
|
-
/**
|
|
823
|
-
* Handle browser disconnection for a specific thread.
|
|
824
|
-
* Called when a thread's browser is closed externally.
|
|
825
|
-
*/
|
|
826
|
-
handleThreadBrowserDisconnected(threadId) {
|
|
827
|
-
this.threadManager.clearSession(threadId);
|
|
828
|
-
this.logger.debug?.(`Cleared browser session for thread: ${threadId}`);
|
|
829
|
-
this.notifyBrowserClosed(threadId);
|
|
830
|
-
}
|
|
831
759
|
/**
|
|
832
760
|
* Create an error response from an exception.
|
|
833
761
|
* Extends base class to add agent-browser specific error handling.
|
|
@@ -951,6 +879,16 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
951
879
|
return null;
|
|
952
880
|
}
|
|
953
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* Get browser state for a thread (implements abstract method from base class).
|
|
884
|
+
* Sync version that uses existing manager lookup without creating sessions.
|
|
885
|
+
*/
|
|
886
|
+
getBrowserStateForThread(threadId) {
|
|
887
|
+
const effectiveThreadId = threadId ?? this.getCurrentThread() ?? browser.DEFAULT_THREAD_ID;
|
|
888
|
+
const manager = this.threadManager.getExistingManagerForThread(effectiveThreadId);
|
|
889
|
+
if (!manager) return null;
|
|
890
|
+
return this.getBrowserStateForManager(manager);
|
|
891
|
+
}
|
|
954
892
|
/**
|
|
955
893
|
* Get browser state from a specific manager instance.
|
|
956
894
|
*/
|
|
@@ -991,27 +929,23 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
991
929
|
}
|
|
992
930
|
}
|
|
993
931
|
/**
|
|
994
|
-
*
|
|
995
|
-
*
|
|
932
|
+
* Export the current browser session's storage state (cookies, localStorage) to a JSON file.
|
|
933
|
+
* This can later be loaded via the `storageState` config option to restore the session.
|
|
934
|
+
*
|
|
935
|
+
* @param path - File path to save the storage state JSON
|
|
936
|
+
* @param threadId - Optional thread ID (defaults to current thread)
|
|
996
937
|
*/
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
}
|
|
1007
|
-
if (manager) {
|
|
1008
|
-
const state = this.getBrowserStateForManager(manager);
|
|
1009
|
-
if (state) {
|
|
1010
|
-
this.threadManager.updateBrowserState(effectiveThreadId, state);
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
} catch {
|
|
938
|
+
async exportStorageState(path, threadId) {
|
|
939
|
+
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
940
|
+
const manager = this.threadManager.getExistingManagerForThread(effectiveThreadId);
|
|
941
|
+
if (!manager) {
|
|
942
|
+
throw new Error("No browser is running. Launch a browser first before exporting storage state.");
|
|
943
|
+
}
|
|
944
|
+
const context = manager.getContext();
|
|
945
|
+
if (!context) {
|
|
946
|
+
throw new Error("Browser context not available");
|
|
1014
947
|
}
|
|
948
|
+
await context.storageState({ path });
|
|
1015
949
|
}
|
|
1016
950
|
// ---------------------------------------------------------------------------
|
|
1017
951
|
// 1. browser_goto - Navigate to URL
|
|
@@ -1435,10 +1369,10 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
1435
1369
|
);
|
|
1436
1370
|
}
|
|
1437
1371
|
await browser.switchTo(input.index);
|
|
1438
|
-
await this.
|
|
1372
|
+
await this.reconnectScreencastForThread(threadId, "tab switch");
|
|
1439
1373
|
const page = browser.getPage();
|
|
1440
1374
|
const pageUrl = page.url();
|
|
1441
|
-
const streamKey = this.getStreamKey(
|
|
1375
|
+
const streamKey = this.getStreamKey(threadId);
|
|
1442
1376
|
const stream = this.activeScreencastStreams.get(streamKey);
|
|
1443
1377
|
if (pageUrl && stream?.isActive()) {
|
|
1444
1378
|
stream.emitUrl(pageUrl);
|
|
@@ -1461,7 +1395,7 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
1461
1395
|
);
|
|
1462
1396
|
}
|
|
1463
1397
|
await browser.closeTab(input.index);
|
|
1464
|
-
await this.
|
|
1398
|
+
await this.reconnectScreencastForThread(threadId, "tab close");
|
|
1465
1399
|
this.updateSessionBrowserState(threadId);
|
|
1466
1400
|
const tabsList = await browser.listTabs?.() ?? [];
|
|
1467
1401
|
return {
|
|
@@ -1557,47 +1491,15 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
1557
1491
|
// ---------------------------------------------------------------------------
|
|
1558
1492
|
// Screencast (for Studio live view)
|
|
1559
1493
|
// ---------------------------------------------------------------------------
|
|
1560
|
-
/**
|
|
1561
|
-
* Get the stream key for a thread (or shared key for shared scope).
|
|
1562
|
-
*/
|
|
1563
|
-
getStreamKey(threadId) {
|
|
1564
|
-
return threadId || _AgentBrowser.SHARED_STREAM_KEY;
|
|
1565
|
-
}
|
|
1566
|
-
/**
|
|
1567
|
-
* Trigger a screencast reconnect after tab changes.
|
|
1568
|
-
* Called internally when tabs are switched or closed.
|
|
1569
|
-
*/
|
|
1570
|
-
async reconnectScreencast(_reason) {
|
|
1571
|
-
const threadId = this.getCurrentThread();
|
|
1572
|
-
const streamKey = this.getStreamKey(threadId);
|
|
1573
|
-
const stream = this.activeScreencastStreams.get(streamKey);
|
|
1574
|
-
if (stream?.isActive()) {
|
|
1575
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
1576
|
-
if (stream?.isActive()) {
|
|
1577
|
-
try {
|
|
1578
|
-
await stream.reconnect();
|
|
1579
|
-
const manager = this.threadManager.getExistingManagerForThread(threadId) ?? this.browserManager;
|
|
1580
|
-
const activePage = manager?.getPage();
|
|
1581
|
-
if (activePage) {
|
|
1582
|
-
const url = activePage.url();
|
|
1583
|
-
if (url) {
|
|
1584
|
-
stream.emitUrl(url);
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
} catch (err) {
|
|
1588
|
-
console.error("[AgentBrowser] Failed to reconnect screencast:", err);
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
}
|
|
1593
1494
|
async startScreencast(_options) {
|
|
1594
|
-
const
|
|
1495
|
+
const requestedThreadId = _options?.threadId;
|
|
1496
|
+
const effectiveThreadId = this.getScope() === "thread" ? requestedThreadId ?? this.getCurrentThread() ?? browser.DEFAULT_THREAD_ID : requestedThreadId;
|
|
1595
1497
|
let browserManager;
|
|
1596
|
-
if (this.getScope() === "thread"
|
|
1597
|
-
browserManager = await this.getManagerForThread(
|
|
1498
|
+
if (this.getScope() === "thread") {
|
|
1499
|
+
browserManager = await this.getManagerForThread(effectiveThreadId);
|
|
1598
1500
|
} else {
|
|
1599
|
-
if (!this.
|
|
1600
|
-
browserManager = this.
|
|
1501
|
+
if (!this.sharedManager) throw new Error("Browser not launched");
|
|
1502
|
+
browserManager = this.sharedManager;
|
|
1601
1503
|
}
|
|
1602
1504
|
const provider = {
|
|
1603
1505
|
getCdpSession: async () => {
|
|
@@ -1611,7 +1513,7 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
1611
1513
|
isBrowserRunning: () => browserManager.isLaunched()
|
|
1612
1514
|
};
|
|
1613
1515
|
const stream = new browser.ScreencastStreamImpl(provider, _options);
|
|
1614
|
-
const streamKey = this.getStreamKey(
|
|
1516
|
+
const streamKey = this.getStreamKey(effectiveThreadId);
|
|
1615
1517
|
this.activeScreencastStreams.set(streamKey, stream);
|
|
1616
1518
|
const context = browserManager.getContext();
|
|
1617
1519
|
if (context) {
|
|
@@ -1630,7 +1532,7 @@ var AgentBrowser = class _AgentBrowser extends browser.MastraBrowser {
|
|
|
1630
1532
|
const onFrameNavigated = (frame) => {
|
|
1631
1533
|
if (!frame.parentFrame()) {
|
|
1632
1534
|
stream.emitUrl(frame.url());
|
|
1633
|
-
this.updateSessionBrowserState(
|
|
1535
|
+
this.updateSessionBrowserState(effectiveThreadId);
|
|
1634
1536
|
}
|
|
1635
1537
|
};
|
|
1636
1538
|
page.on("framenavigated", onFrameNavigated);
|
|
@@ -1723,6 +1625,7 @@ exports.createAgentBrowserTools = createAgentBrowserTools;
|
|
|
1723
1625
|
exports.dialogInputSchema = dialogInputSchema;
|
|
1724
1626
|
exports.dragInputSchema = dragInputSchema;
|
|
1725
1627
|
exports.evaluateInputSchema = evaluateInputSchema;
|
|
1628
|
+
exports.getBrowserPid = getBrowserPid;
|
|
1726
1629
|
exports.gotoInputSchema = gotoInputSchema;
|
|
1727
1630
|
exports.hoverInputSchema = hoverInputSchema;
|
|
1728
1631
|
exports.pressInputSchema = pressInputSchema;
|