@mastra/stagehand 0.1.0-alpha.0 → 0.1.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 +40 -0
- package/dist/index.cjs +62 -266
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -116
- package/dist/index.d.ts +50 -116
- package/dist/index.js +62 -266
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -5,71 +5,27 @@ import { z } from 'zod';
|
|
|
5
5
|
|
|
6
6
|
// src/stagehand-browser.ts
|
|
7
7
|
var StagehandThreadManager = class extends ThreadManager {
|
|
8
|
-
sharedStagehand = null;
|
|
9
8
|
sessions = /* @__PURE__ */ new Map();
|
|
10
9
|
createStagehand;
|
|
11
10
|
onBrowserCreated;
|
|
12
|
-
/** Map of thread ID to dedicated Stagehand instance (for 'thread' mode) */
|
|
13
|
-
threadStagehands = /* @__PURE__ */ new Map();
|
|
14
11
|
constructor(config) {
|
|
15
12
|
super(config);
|
|
16
13
|
this.createStagehand = config.createStagehand;
|
|
17
14
|
this.onBrowserCreated = config.onBrowserCreated;
|
|
18
15
|
}
|
|
19
|
-
/**
|
|
20
|
-
* Set the shared Stagehand instance (called after browser launch).
|
|
21
|
-
*/
|
|
22
|
-
setStagehand(instance) {
|
|
23
|
-
this.sharedStagehand = instance;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Clear the shared Stagehand instance (called when browser disconnects).
|
|
27
|
-
*/
|
|
28
|
-
clearStagehand() {
|
|
29
|
-
this.sharedStagehand = null;
|
|
30
|
-
}
|
|
31
16
|
/**
|
|
32
17
|
* Set the factory function for creating new Stagehand instances.
|
|
33
|
-
* Required for '
|
|
18
|
+
* Required for 'thread' scope mode.
|
|
34
19
|
*/
|
|
35
20
|
setCreateStagehand(factory) {
|
|
36
21
|
this.createStagehand = factory;
|
|
37
22
|
}
|
|
38
23
|
/**
|
|
39
|
-
* Get the
|
|
40
|
-
*/
|
|
41
|
-
getSharedStagehand() {
|
|
42
|
-
if (!this.sharedStagehand) {
|
|
43
|
-
throw new Error("Stagehand not initialized");
|
|
44
|
-
}
|
|
45
|
-
return this.sharedStagehand;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Get the Stagehand instance for a specific thread.
|
|
49
|
-
* In 'shared' mode, returns the shared instance.
|
|
50
|
-
* In 'thread' mode, returns the thread's dedicated instance.
|
|
51
|
-
*/
|
|
52
|
-
getStagehandForThread(threadId) {
|
|
53
|
-
if (this.scope === "thread") {
|
|
54
|
-
const session = this.sessions.get(threadId);
|
|
55
|
-
return session?.stagehand;
|
|
56
|
-
}
|
|
57
|
-
return this.sharedStagehand ?? void 0;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get the Stagehand page for a thread.
|
|
61
|
-
* Returns the active page from the thread's Stagehand instance.
|
|
62
|
-
*/
|
|
63
|
-
getPageForThread(threadId) {
|
|
64
|
-
const stagehand = this.getStagehandForThread(threadId);
|
|
65
|
-
return stagehand?.context?.activePage();
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Get the shared manager - returns the active page or the Stagehand instance.
|
|
24
|
+
* Get the page for a specific thread, creating session if needed.
|
|
69
25
|
*/
|
|
70
|
-
|
|
71
|
-
const stagehand = this.
|
|
72
|
-
return stagehand
|
|
26
|
+
async getPageForThread(threadId) {
|
|
27
|
+
const stagehand = await this.getManagerForThread(threadId);
|
|
28
|
+
return stagehand?.context?.activePage() ?? null;
|
|
73
29
|
}
|
|
74
30
|
/**
|
|
75
31
|
* Create a new session for a thread.
|
|
@@ -88,7 +44,7 @@ var StagehandThreadManager = class extends ThreadManager {
|
|
|
88
44
|
this.logger?.debug?.(`Creating dedicated Stagehand instance for thread ${threadId}`);
|
|
89
45
|
const stagehand = await this.createStagehand();
|
|
90
46
|
session.stagehand = stagehand;
|
|
91
|
-
this.
|
|
47
|
+
this.threadManagers.set(threadId, stagehand);
|
|
92
48
|
if (savedState && savedState.tabs.length > 0) {
|
|
93
49
|
this.logger?.debug?.(`Restoring browser state for thread ${threadId}: ${savedState.tabs.length} tabs`);
|
|
94
50
|
await this.restoreBrowserState(stagehand, savedState);
|
|
@@ -127,18 +83,11 @@ var StagehandThreadManager = class extends ThreadManager {
|
|
|
127
83
|
}
|
|
128
84
|
}
|
|
129
85
|
/**
|
|
130
|
-
*
|
|
131
|
-
* For 'thread' mode, no switching needed - each thread has its own instance.
|
|
132
|
-
* For 'shared' mode, nothing to switch.
|
|
133
|
-
*/
|
|
134
|
-
async switchToSession(_session) {
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Get the manager for a specific session.
|
|
86
|
+
* Get the manager (Stagehand instance) for a specific session.
|
|
138
87
|
*/
|
|
139
88
|
getManagerForSession(session) {
|
|
140
89
|
if (this.scope === "thread" && session.stagehand) {
|
|
141
|
-
return session.stagehand
|
|
90
|
+
return session.stagehand;
|
|
142
91
|
}
|
|
143
92
|
return this.getSharedManager();
|
|
144
93
|
}
|
|
@@ -153,50 +102,14 @@ var StagehandThreadManager = class extends ThreadManager {
|
|
|
153
102
|
} catch (error) {
|
|
154
103
|
this.logger?.warn?.(`Failed to close Stagehand for thread ${session.threadId}: ${error}`);
|
|
155
104
|
}
|
|
156
|
-
this.threadStagehands.delete(session.threadId);
|
|
157
105
|
}
|
|
158
106
|
}
|
|
159
107
|
/**
|
|
160
|
-
*
|
|
108
|
+
* Destroy all sessions (called during browser close).
|
|
109
|
+
* doDestroySession handles closing individual Stagehand instances.
|
|
161
110
|
*/
|
|
162
|
-
async
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
await stagehand.close();
|
|
166
|
-
} catch {
|
|
167
|
-
this.logger?.debug?.(`Failed to close Stagehand for thread: ${threadId}`);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
this.threadStagehands.clear();
|
|
171
|
-
this.sessions.clear();
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Check if any thread Stagehands are still running.
|
|
175
|
-
*/
|
|
176
|
-
hasActiveThreadStagehands() {
|
|
177
|
-
return this.threadStagehands.size > 0;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Clear all session tracking without closing browsers.
|
|
181
|
-
* Used when browsers have been externally closed and we just need to reset state.
|
|
182
|
-
*/
|
|
183
|
-
clearAllSessions() {
|
|
184
|
-
this.threadStagehands.clear();
|
|
185
|
-
this.sessions.clear();
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Clear a specific thread's session without closing the browser.
|
|
189
|
-
* Used when a thread's browser has been externally closed.
|
|
190
|
-
* Preserves the browser state for potential restoration.
|
|
191
|
-
* @param threadId - The thread ID to clear
|
|
192
|
-
*/
|
|
193
|
-
clearSession(threadId) {
|
|
194
|
-
const session = this.sessions.get(threadId);
|
|
195
|
-
if (session?.browserState) {
|
|
196
|
-
this.savedBrowserStates.set(threadId, session.browserState);
|
|
197
|
-
}
|
|
198
|
-
this.threadStagehands.delete(threadId);
|
|
199
|
-
this.sessions.delete(threadId);
|
|
111
|
+
async destroyAllSessions() {
|
|
112
|
+
await super.destroyAllSessions();
|
|
200
113
|
}
|
|
201
114
|
};
|
|
202
115
|
var actInputSchema = z.object({
|
|
@@ -367,29 +280,18 @@ function createStagehandTools(browser) {
|
|
|
367
280
|
}
|
|
368
281
|
|
|
369
282
|
// src/stagehand-browser.ts
|
|
370
|
-
var StagehandBrowser = class
|
|
283
|
+
var StagehandBrowser = class extends MastraBrowser {
|
|
371
284
|
id;
|
|
372
285
|
name = "StagehandBrowser";
|
|
373
286
|
provider = "browserbase/stagehand";
|
|
374
|
-
stagehand = null;
|
|
375
287
|
stagehandConfig;
|
|
376
|
-
/** Active screencast streams per thread (for reconnection on tab changes) */
|
|
377
|
-
activeScreencastStreams = /* @__PURE__ */ new Map();
|
|
378
288
|
/** Debounce timers per thread for tab change reconnection */
|
|
379
289
|
tabChangeDebounceTimers = /* @__PURE__ */ new Map();
|
|
380
|
-
/** Default key for shared scope */
|
|
381
|
-
static SHARED_STREAM_KEY = "__shared__";
|
|
382
290
|
constructor(config = {}) {
|
|
383
291
|
super(config);
|
|
384
292
|
this.id = `stagehand-${Date.now()}`;
|
|
385
293
|
this.stagehandConfig = config;
|
|
386
|
-
|
|
387
|
-
if (config.cdpUrl && effectiveScope === "thread") {
|
|
388
|
-
this.logger.warn?.(
|
|
389
|
-
'Browser scope "thread" is not supported when connecting via cdpUrl. Falling back to "shared" (shared browser connection).'
|
|
390
|
-
);
|
|
391
|
-
effectiveScope = "shared";
|
|
392
|
-
}
|
|
294
|
+
const effectiveScope = config.cdpUrl ? config.scope ?? "shared" : config.scope ?? "thread";
|
|
393
295
|
this.threadManager = new StagehandThreadManager({
|
|
394
296
|
scope: effectiveScope,
|
|
395
297
|
logger: this.logger,
|
|
@@ -403,15 +305,6 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
403
305
|
}
|
|
404
306
|
});
|
|
405
307
|
}
|
|
406
|
-
/**
|
|
407
|
-
* Close a specific thread's browser session.
|
|
408
|
-
* For 'thread' scope, this closes only that thread's Stagehand instance.
|
|
409
|
-
* For 'shared' scope, this is a no-op (use close() to close the shared browser).
|
|
410
|
-
*/
|
|
411
|
-
async closeThreadSession(threadId) {
|
|
412
|
-
await this.threadManager.destroySession(threadId);
|
|
413
|
-
this.notifyBrowserClosed(threadId);
|
|
414
|
-
}
|
|
415
308
|
/**
|
|
416
309
|
* Ensure browser is ready and thread session exists.
|
|
417
310
|
* For 'thread' scope, this creates a dedicated Stagehand instance for the thread.
|
|
@@ -422,7 +315,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
422
315
|
const scope = this.getScope();
|
|
423
316
|
const threadId = this.getCurrentThread();
|
|
424
317
|
if (scope === "thread" && threadId && threadId !== DEFAULT_THREAD_ID) {
|
|
425
|
-
await this.
|
|
318
|
+
await this.getManagerForThread(threadId);
|
|
426
319
|
}
|
|
427
320
|
}
|
|
428
321
|
// ---------------------------------------------------------------------------
|
|
@@ -469,7 +362,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
469
362
|
}
|
|
470
363
|
/**
|
|
471
364
|
* Create a new Stagehand instance with the current config.
|
|
472
|
-
* Used by thread manager for '
|
|
365
|
+
* Used by thread manager for 'thread' scope.
|
|
473
366
|
*/
|
|
474
367
|
async createStagehandInstance() {
|
|
475
368
|
const stagehandOptions = await this.buildStagehandOptions();
|
|
@@ -483,15 +376,15 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
483
376
|
if (scope === "thread") {
|
|
484
377
|
return;
|
|
485
378
|
}
|
|
486
|
-
this.
|
|
487
|
-
this.threadManager.
|
|
488
|
-
this.
|
|
379
|
+
this.sharedManager = await this.createStagehandInstance();
|
|
380
|
+
this.threadManager.setSharedManager(this.sharedManager);
|
|
381
|
+
this.setupCloseListenerForSharedScope(this.sharedManager);
|
|
489
382
|
}
|
|
490
383
|
/**
|
|
491
|
-
* Set up close event listener for a Stagehand instance.
|
|
384
|
+
* Set up close event listener for a shared Stagehand instance.
|
|
492
385
|
* Listens to both context and page close events for robust detection.
|
|
493
386
|
*/
|
|
494
|
-
|
|
387
|
+
setupCloseListenerForSharedScope(stagehand) {
|
|
495
388
|
let disconnectHandled = false;
|
|
496
389
|
const handleDisconnect = () => {
|
|
497
390
|
if (disconnectHandled) return;
|
|
@@ -565,20 +458,11 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
565
458
|
} catch {
|
|
566
459
|
}
|
|
567
460
|
}
|
|
568
|
-
/**
|
|
569
|
-
* Handle browser disconnection for a specific thread.
|
|
570
|
-
* Called when a thread's browser is closed externally.
|
|
571
|
-
*/
|
|
572
|
-
handleThreadBrowserDisconnected(threadId) {
|
|
573
|
-
this.threadManager.clearSession(threadId);
|
|
574
|
-
this.logger.debug?.(`Cleared Stagehand session for thread: ${threadId}`);
|
|
575
|
-
this.notifyBrowserClosed(threadId);
|
|
576
|
-
}
|
|
577
461
|
async doClose() {
|
|
578
|
-
await this.threadManager.
|
|
579
|
-
if (this.
|
|
580
|
-
await this.
|
|
581
|
-
this.
|
|
462
|
+
await this.threadManager.destroyAllSessions();
|
|
463
|
+
if (this.sharedManager) {
|
|
464
|
+
await this.sharedManager.close();
|
|
465
|
+
this.sharedManager = null;
|
|
582
466
|
}
|
|
583
467
|
this.setCurrentThread(void 0);
|
|
584
468
|
}
|
|
@@ -589,13 +473,13 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
589
473
|
async checkBrowserAlive() {
|
|
590
474
|
const scope = this.getScope();
|
|
591
475
|
if (scope === "thread") {
|
|
592
|
-
return this.threadManager.
|
|
476
|
+
return this.threadManager.hasActiveThreadManagers();
|
|
593
477
|
}
|
|
594
|
-
if (!this.
|
|
478
|
+
if (!this.sharedManager) {
|
|
595
479
|
return false;
|
|
596
480
|
}
|
|
597
481
|
try {
|
|
598
|
-
const context = this.
|
|
482
|
+
const context = this.sharedManager.context;
|
|
599
483
|
if (!context) {
|
|
600
484
|
return false;
|
|
601
485
|
}
|
|
@@ -605,7 +489,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
605
489
|
}
|
|
606
490
|
const url = pages[0]?.url();
|
|
607
491
|
if (url && url !== "about:blank") {
|
|
608
|
-
const state = this.getBrowserStateFromStagehand(this.
|
|
492
|
+
const state = this.getBrowserStateFromStagehand(this.sharedManager);
|
|
609
493
|
if (state) {
|
|
610
494
|
this.lastBrowserState = state;
|
|
611
495
|
}
|
|
@@ -619,24 +503,6 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
619
503
|
return false;
|
|
620
504
|
}
|
|
621
505
|
}
|
|
622
|
-
/**
|
|
623
|
-
* Handle browser disconnection by clearing internal state.
|
|
624
|
-
* For 'thread' scope, only notifies the specific thread's callbacks.
|
|
625
|
-
* For 'shared' scope, notifies all callbacks.
|
|
626
|
-
*/
|
|
627
|
-
handleBrowserDisconnected() {
|
|
628
|
-
const scope = this.threadManager.getScope();
|
|
629
|
-
const threadId = this.getCurrentThread();
|
|
630
|
-
if (scope === "thread" && threadId !== DEFAULT_THREAD_ID) {
|
|
631
|
-
this.threadManager.clearSession(threadId);
|
|
632
|
-
this.logger.debug?.(`Cleared Stagehand session for thread: ${threadId}`);
|
|
633
|
-
this.notifyBrowserClosed(threadId);
|
|
634
|
-
} else {
|
|
635
|
-
this.stagehand = null;
|
|
636
|
-
this.threadManager.clearStagehand();
|
|
637
|
-
super.handleBrowserDisconnected();
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
506
|
/**
|
|
641
507
|
* Create an error response from an exception.
|
|
642
508
|
* Extends base class to add Stagehand-specific error handling.
|
|
@@ -657,21 +523,21 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
657
523
|
// ---------------------------------------------------------------------------
|
|
658
524
|
/**
|
|
659
525
|
* Get the Stagehand instance for a thread, creating it if needed.
|
|
660
|
-
* For '
|
|
661
|
-
* For '
|
|
526
|
+
* For 'thread' scope, this creates a dedicated Stagehand instance.
|
|
527
|
+
* For 'shared' scope, returns the shared instance.
|
|
662
528
|
*/
|
|
663
|
-
async
|
|
529
|
+
async getManagerForThread(threadId) {
|
|
664
530
|
const scope = this.getScope();
|
|
665
531
|
if (scope === "shared") {
|
|
666
|
-
return this.
|
|
532
|
+
return this.sharedManager;
|
|
667
533
|
}
|
|
668
534
|
if (!threadId || threadId === DEFAULT_THREAD_ID) {
|
|
669
|
-
return this.
|
|
535
|
+
return this.sharedManager;
|
|
670
536
|
}
|
|
671
|
-
let stagehand = this.threadManager.
|
|
537
|
+
let stagehand = this.threadManager.getExistingManagerForThread(threadId);
|
|
672
538
|
if (!stagehand) {
|
|
673
539
|
await this.threadManager.getManagerForThread(threadId);
|
|
674
|
-
stagehand = this.threadManager.
|
|
540
|
+
stagehand = this.threadManager.getExistingManagerForThread(threadId);
|
|
675
541
|
}
|
|
676
542
|
return stagehand ?? null;
|
|
677
543
|
}
|
|
@@ -683,14 +549,14 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
683
549
|
*/
|
|
684
550
|
requireStagehand(explicitThreadId) {
|
|
685
551
|
const threadId = explicitThreadId ?? this.getCurrentThread();
|
|
686
|
-
const stagehand = this.threadManager.
|
|
552
|
+
const stagehand = this.threadManager.getExistingManagerForThread(threadId) ?? this.sharedManager;
|
|
687
553
|
if (!stagehand) {
|
|
688
554
|
throw new Error("Browser not launched");
|
|
689
555
|
}
|
|
690
556
|
return stagehand;
|
|
691
557
|
}
|
|
692
558
|
/**
|
|
693
|
-
* Get the current page from Stagehand v3, respecting thread
|
|
559
|
+
* Get the current page from Stagehand v3, respecting thread scope.
|
|
694
560
|
* @param explicitThreadId - Optional thread ID to use instead of getCurrentThread()
|
|
695
561
|
* Use this to avoid race conditions in concurrent tool calls.
|
|
696
562
|
*/
|
|
@@ -698,15 +564,15 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
698
564
|
const scope = this.getScope();
|
|
699
565
|
const threadId = explicitThreadId ?? this.getCurrentThread();
|
|
700
566
|
if (scope === "thread" && threadId && threadId !== DEFAULT_THREAD_ID) {
|
|
701
|
-
const stagehand = this.threadManager.
|
|
567
|
+
const stagehand = this.threadManager.getExistingManagerForThread(threadId);
|
|
702
568
|
if (stagehand?.context) {
|
|
703
569
|
return stagehand.context.activePage();
|
|
704
570
|
}
|
|
705
571
|
return null;
|
|
706
572
|
}
|
|
707
|
-
if (!this.
|
|
573
|
+
if (!this.sharedManager) return null;
|
|
708
574
|
try {
|
|
709
|
-
const context = this.
|
|
575
|
+
const context = this.sharedManager.context;
|
|
710
576
|
if (context) {
|
|
711
577
|
const activePage = context.activePage();
|
|
712
578
|
if (activePage) {
|
|
@@ -722,18 +588,10 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
722
588
|
return null;
|
|
723
589
|
}
|
|
724
590
|
/**
|
|
725
|
-
* Get the page for a
|
|
591
|
+
* Get the active page for a thread (implements abstract method from base class).
|
|
726
592
|
*/
|
|
727
|
-
async
|
|
728
|
-
|
|
729
|
-
if (scope === "shared") {
|
|
730
|
-
return this.getPage();
|
|
731
|
-
}
|
|
732
|
-
const stagehand = await this.getStagehandForThread(threadId);
|
|
733
|
-
if (stagehand?.context) {
|
|
734
|
-
return stagehand.context.activePage();
|
|
735
|
-
}
|
|
736
|
-
return null;
|
|
593
|
+
async getActivePage(threadId) {
|
|
594
|
+
return this.getPage(threadId);
|
|
737
595
|
}
|
|
738
596
|
/**
|
|
739
597
|
* Get a CDP session for a specific page.
|
|
@@ -984,7 +842,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
984
842
|
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
985
843
|
const scope = this.threadManager.getScope();
|
|
986
844
|
if (scope === "thread" && effectiveThreadId) {
|
|
987
|
-
const stagehand = this.threadManager.
|
|
845
|
+
const stagehand = this.threadManager.getExistingManagerForThread(effectiveThreadId);
|
|
988
846
|
if (!stagehand?.context) {
|
|
989
847
|
return null;
|
|
990
848
|
}
|
|
@@ -1003,7 +861,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1003
861
|
try {
|
|
1004
862
|
const url = page.url();
|
|
1005
863
|
if (url && url !== "about:blank") {
|
|
1006
|
-
const state = this.getBrowserStateFromStagehand(this.
|
|
864
|
+
const state = this.getBrowserStateFromStagehand(this.sharedManager);
|
|
1007
865
|
if (state) {
|
|
1008
866
|
this.lastBrowserState = state;
|
|
1009
867
|
}
|
|
@@ -1038,15 +896,24 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1038
896
|
const scope = this.threadManager.getScope();
|
|
1039
897
|
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
1040
898
|
if (scope === "thread" && effectiveThreadId) {
|
|
1041
|
-
const stagehand = this.threadManager.
|
|
899
|
+
const stagehand = this.threadManager.getExistingManagerForThread(effectiveThreadId);
|
|
1042
900
|
if (!stagehand) return null;
|
|
1043
901
|
return this.getBrowserStateFromStagehand(stagehand);
|
|
1044
902
|
}
|
|
1045
|
-
return this.getBrowserStateFromStagehand(this.
|
|
903
|
+
return this.getBrowserStateFromStagehand(this.sharedManager);
|
|
1046
904
|
} catch {
|
|
1047
905
|
return null;
|
|
1048
906
|
}
|
|
1049
907
|
}
|
|
908
|
+
/**
|
|
909
|
+
* Get browser state for a thread (implements abstract method from base class).
|
|
910
|
+
* Sync version that uses existing manager lookup without creating sessions.
|
|
911
|
+
*/
|
|
912
|
+
getBrowserStateForThread(threadId) {
|
|
913
|
+
const effectiveThreadId = threadId ?? this.getCurrentThread() ?? DEFAULT_THREAD_ID;
|
|
914
|
+
const stagehand = this.threadManager.getExistingManagerForThread(effectiveThreadId);
|
|
915
|
+
return this.getBrowserStateFromStagehand(stagehand);
|
|
916
|
+
}
|
|
1050
917
|
/**
|
|
1051
918
|
* Get browser state from a specific Stagehand instance.
|
|
1052
919
|
*/
|
|
@@ -1084,44 +951,15 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1084
951
|
const state = await this.getBrowserState(threadId);
|
|
1085
952
|
return state?.activeTabIndex ?? 0;
|
|
1086
953
|
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Update the browser state in the thread session.
|
|
1089
|
-
* Called on navigation, tab open/close to keep state fresh.
|
|
1090
|
-
*/
|
|
1091
|
-
updateSessionBrowserState(threadId) {
|
|
1092
|
-
try {
|
|
1093
|
-
const effectiveThreadId = threadId ?? this.getCurrentThread() ?? DEFAULT_THREAD_ID;
|
|
1094
|
-
const scope = this.threadManager.getScope();
|
|
1095
|
-
let stagehand = null;
|
|
1096
|
-
if (scope === "thread") {
|
|
1097
|
-
stagehand = this.threadManager.getStagehandForThread(effectiveThreadId);
|
|
1098
|
-
} else {
|
|
1099
|
-
stagehand = this.stagehand;
|
|
1100
|
-
}
|
|
1101
|
-
if (stagehand) {
|
|
1102
|
-
const state = this.getBrowserStateFromStagehand(stagehand);
|
|
1103
|
-
if (state) {
|
|
1104
|
-
this.threadManager.updateBrowserState(effectiveThreadId, state);
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
} catch {
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
954
|
// ---------------------------------------------------------------------------
|
|
1111
955
|
// Screencast (for Studio live view)
|
|
1112
956
|
// Uses Stagehand v3's native CDP access
|
|
1113
957
|
// ---------------------------------------------------------------------------
|
|
1114
|
-
/**
|
|
1115
|
-
* Get the stream key for a thread (or shared key for shared scope).
|
|
1116
|
-
*/
|
|
1117
|
-
getStreamKey(threadId) {
|
|
1118
|
-
return threadId || _StagehandBrowser.SHARED_STREAM_KEY;
|
|
1119
|
-
}
|
|
1120
958
|
async startScreencast(options) {
|
|
1121
959
|
const threadId = options?.threadId;
|
|
1122
960
|
const provider = {
|
|
1123
961
|
getCdpSession: async () => {
|
|
1124
|
-
const page = await this.getPageForThread(threadId
|
|
962
|
+
const page = await this.threadManager.getPageForThread(threadId);
|
|
1125
963
|
if (!page) {
|
|
1126
964
|
throw new Error("No page available for screencast");
|
|
1127
965
|
}
|
|
@@ -1155,7 +993,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1155
993
|
* Uses CDP Target events since Stagehand doesn't expose page lifecycle events.
|
|
1156
994
|
*/
|
|
1157
995
|
async setupTabChangeDetection(threadId, stream) {
|
|
1158
|
-
const stagehand = await this.
|
|
996
|
+
const stagehand = await this.getManagerForThread(threadId);
|
|
1159
997
|
if (!stagehand?.context) return;
|
|
1160
998
|
const connection = stagehand.context.conn;
|
|
1161
999
|
if (!connection) {
|
|
@@ -1217,7 +1055,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1217
1055
|
if (newPage && stagehand.context) {
|
|
1218
1056
|
stagehand.context.setActivePage(newPage);
|
|
1219
1057
|
}
|
|
1220
|
-
void this.
|
|
1058
|
+
void this.reconnectScreencastForThread(threadId, "manual tab tracked");
|
|
1221
1059
|
void setupPageNavigationListener();
|
|
1222
1060
|
} else {
|
|
1223
1061
|
this.logger.debug?.("Stagehand did not register the page (non-injectable URL)");
|
|
@@ -1300,48 +1138,6 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1300
1138
|
this.logger.debug?.("Failed to set up tab change detection", error);
|
|
1301
1139
|
}
|
|
1302
1140
|
}
|
|
1303
|
-
/**
|
|
1304
|
-
* Reconnect the active screencast for a specific thread.
|
|
1305
|
-
*/
|
|
1306
|
-
async reconnectScreencastForThread(threadId, reason) {
|
|
1307
|
-
const streamKey = this.getStreamKey(threadId);
|
|
1308
|
-
const stream = this.activeScreencastStreams.get(streamKey);
|
|
1309
|
-
if (!stream || !stream.isActive()) {
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
if (!this.isBrowserRunning()) {
|
|
1313
|
-
this.logger.debug?.("Skipping screencast reconnect - browser not running");
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
const scope = this.getScope();
|
|
1317
|
-
if (scope === "thread" && threadId && !this.threadManager.getStagehandForThread(threadId)) {
|
|
1318
|
-
this.logger.debug?.(`Skipping screencast reconnect - no session for thread ${threadId}`);
|
|
1319
|
-
return;
|
|
1320
|
-
}
|
|
1321
|
-
this.logger.debug?.(`Reconnecting screencast: ${reason}`);
|
|
1322
|
-
try {
|
|
1323
|
-
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
1324
|
-
await stream.reconnect();
|
|
1325
|
-
const stagehand = await this.getStagehandForThread(threadId);
|
|
1326
|
-
const activePage = stagehand?.context?.activePage();
|
|
1327
|
-
if (activePage) {
|
|
1328
|
-
const url = activePage.url();
|
|
1329
|
-
if (url) {
|
|
1330
|
-
stream.emitUrl(url);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
} catch (error) {
|
|
1334
|
-
this.logger.debug?.("Screencast reconnect failed", error);
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
/**
|
|
1338
|
-
* Reconnect the active screencast for the current thread.
|
|
1339
|
-
* Wrapper for reconnectScreencastForThread using getCurrentThread().
|
|
1340
|
-
*/
|
|
1341
|
-
async reconnectScreencast(reason) {
|
|
1342
|
-
const threadId = this.getCurrentThread();
|
|
1343
|
-
await this.reconnectScreencastForThread(threadId, reason);
|
|
1344
|
-
}
|
|
1345
1141
|
// NOTE: Manual tab switching in browser UI is not fully supported.
|
|
1346
1142
|
// Stagehand v3 does not track pages opened via browser UI (only pages created through its API).
|
|
1347
1143
|
// We've requested this feature from Browserbase - see Notion doc for details.
|
|
@@ -1350,7 +1146,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1350
1146
|
// ---------------------------------------------------------------------------
|
|
1351
1147
|
async injectMouseEvent(event, threadId) {
|
|
1352
1148
|
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
1353
|
-
const page = await this.getPageForThread(effectiveThreadId
|
|
1149
|
+
const page = await this.threadManager.getPageForThread(effectiveThreadId);
|
|
1354
1150
|
const cdpSession = this.getCdpSessionForPage(page);
|
|
1355
1151
|
if (!cdpSession) {
|
|
1356
1152
|
throw new Error("No CDP session available");
|
|
@@ -1376,7 +1172,7 @@ var StagehandBrowser = class _StagehandBrowser extends MastraBrowser {
|
|
|
1376
1172
|
}
|
|
1377
1173
|
async injectKeyboardEvent(event, threadId) {
|
|
1378
1174
|
const effectiveThreadId = threadId ?? this.getCurrentThread();
|
|
1379
|
-
const page = await this.getPageForThread(effectiveThreadId
|
|
1175
|
+
const page = await this.threadManager.getPageForThread(effectiveThreadId);
|
|
1380
1176
|
const cdpSession = this.getCdpSessionForPage(page);
|
|
1381
1177
|
if (!cdpSession) {
|
|
1382
1178
|
throw new Error("No CDP session available");
|