@mastra/agent-browser 0.3.0 → 0.3.1

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/dist/index.d.cts CHANGED
@@ -474,6 +474,8 @@ declare class AgentBrowser extends MastraBrowser {
474
474
  private defaultTimeout;
475
475
  /** Pending PID lookups — awaited in disconnect handlers to avoid racing. */
476
476
  private pidLookups;
477
+ private readonly pendingCloseReasons;
478
+ private readonly activeUrlChangeSources;
477
479
  /** Thread manager - narrowed type from base class */
478
480
  protected threadManager: AgentBrowserThreadManager;
479
481
  private browserConfig;
@@ -499,6 +501,7 @@ declare class AgentBrowser extends MastraBrowser {
499
501
  */
500
502
  protected setupCloseListenerForSharedScope(manager: BrowserManager): void;
501
503
  protected doClose(): Promise<void>;
504
+ closeThreadSession(threadId: string): Promise<void>;
502
505
  /**
503
506
  * Check if the browser is still alive by verifying the page is connected.
504
507
  * Called by base class ensureReady() to detect externally closed browsers.
@@ -509,6 +512,12 @@ declare class AgentBrowser extends MastraBrowser {
509
512
  * Returns 16 flat tools for browser automation.
510
513
  */
511
514
  getTools(): Record<string, Tool<any, any>>;
515
+ private browserStateKey;
516
+ markBrowserCloseReason(reason: 'agent' | 'user' | 'process_restart' | 'error', threadId?: string): void;
517
+ private markActiveUrlChangeSource;
518
+ private getCloseReason;
519
+ private getActiveUrlChangeSource;
520
+ private rememberClosedBrowserState;
512
521
  /**
513
522
  * Get the page for the current thread.
514
523
  * Uses thread scope if enabled, otherwise returns the shared page.
package/dist/index.d.ts CHANGED
@@ -474,6 +474,8 @@ declare class AgentBrowser extends MastraBrowser {
474
474
  private defaultTimeout;
475
475
  /** Pending PID lookups — awaited in disconnect handlers to avoid racing. */
476
476
  private pidLookups;
477
+ private readonly pendingCloseReasons;
478
+ private readonly activeUrlChangeSources;
477
479
  /** Thread manager - narrowed type from base class */
478
480
  protected threadManager: AgentBrowserThreadManager;
479
481
  private browserConfig;
@@ -499,6 +501,7 @@ declare class AgentBrowser extends MastraBrowser {
499
501
  */
500
502
  protected setupCloseListenerForSharedScope(manager: BrowserManager): void;
501
503
  protected doClose(): Promise<void>;
504
+ closeThreadSession(threadId: string): Promise<void>;
502
505
  /**
503
506
  * Check if the browser is still alive by verifying the page is connected.
504
507
  * Called by base class ensureReady() to detect externally closed browsers.
@@ -509,6 +512,12 @@ declare class AgentBrowser extends MastraBrowser {
509
512
  * Returns 16 flat tools for browser automation.
510
513
  */
511
514
  getTools(): Record<string, Tool<any, any>>;
515
+ private browserStateKey;
516
+ markBrowserCloseReason(reason: 'agent' | 'user' | 'process_restart' | 'error', threadId?: string): void;
517
+ private markActiveUrlChangeSource;
518
+ private getCloseReason;
519
+ private getActiveUrlChangeSource;
520
+ private rememberClosedBrowserState;
512
521
  /**
513
522
  * Get the page for the current thread.
514
523
  * Uses thread scope if enabled, otherwise returns the shared page.
package/dist/index.js CHANGED
@@ -314,13 +314,16 @@ function createCloseTool(browser) {
314
314
  inputSchema: closeInputSchema,
315
315
  execute: async (_input, { agent }) => {
316
316
  const threadId = agent?.threadId;
317
+ browser.setCurrentThread(threadId);
317
318
  if (browser.getScope() !== "shared") {
318
319
  if (!threadId) {
319
320
  throw new Error("browser_close requires agent.threadId when browser scope is not shared");
320
321
  }
322
+ browser.markBrowserCloseReason("agent", threadId);
321
323
  await browser.closeThreadSession(threadId);
322
324
  return { success: true, hint: "Thread's browser session closed. A new session will be created on next use." };
323
325
  }
326
+ browser.markBrowserCloseReason("agent");
324
327
  await browser.close();
325
328
  return { success: true, hint: "Browser closed. It will be re-launched automatically on next use." };
326
329
  }
@@ -571,6 +574,8 @@ var AgentBrowser = class extends MastraBrowser {
571
574
  defaultTimeout = 3e4;
572
575
  /** Pending PID lookups — awaited in disconnect handlers to avoid racing. */
573
576
  pidLookups = /* @__PURE__ */ new Set();
577
+ pendingCloseReasons = /* @__PURE__ */ new Map();
578
+ activeUrlChangeSources = /* @__PURE__ */ new Map();
574
579
  browserConfig;
575
580
  constructor(config = {}) {
576
581
  super(config);
@@ -639,6 +644,8 @@ var AgentBrowser = class extends MastraBrowser {
639
644
  // Lifecycle
640
645
  // ---------------------------------------------------------------------------
641
646
  async doLaunch() {
647
+ this.pendingCloseReasons.clear();
648
+ this.activeUrlChangeSources.clear();
642
649
  const scope = this.threadManager.getScope();
643
650
  if (scope === "thread") {
644
651
  this.sharedManager = new BrowserManager();
@@ -675,6 +682,7 @@ var AgentBrowser = class extends MastraBrowser {
675
682
  const handleDisconnect = () => {
676
683
  if (disconnectHandled) return;
677
684
  disconnectHandled = true;
685
+ this.rememberClosedBrowserState(manager, "user");
678
686
  void pidLookup.catch(() => void 0).then(() => this.handleBrowserDisconnected());
679
687
  };
680
688
  const context = manager.getContext();
@@ -704,6 +712,14 @@ var AgentBrowser = class extends MastraBrowser {
704
712
  }
705
713
  this.sharedManager = null;
706
714
  }
715
+ async closeThreadSession(threadId) {
716
+ const manager = this.threadManager.getExistingManagerForThread(threadId);
717
+ if (manager) {
718
+ const state = this.getBrowserStateForManager(manager, threadId);
719
+ if (state) this.threadManager.updateBrowserState(threadId, state);
720
+ }
721
+ await super.closeThreadSession(threadId);
722
+ }
707
723
  /**
708
724
  * Check if the browser is still alive by verifying the page is connected.
709
725
  * Called by base class ensureReady() to detect externally closed browsers.
@@ -754,6 +770,32 @@ var AgentBrowser = class extends MastraBrowser {
754
770
  // ---------------------------------------------------------------------------
755
771
  // Helpers
756
772
  // ---------------------------------------------------------------------------
773
+ browserStateKey(threadId) {
774
+ return threadId ?? this.getCurrentThread() ?? DEFAULT_THREAD_ID;
775
+ }
776
+ markBrowserCloseReason(reason, threadId) {
777
+ this.pendingCloseReasons.set(this.browserStateKey(threadId), reason);
778
+ }
779
+ markActiveUrlChangeSource(source, url, threadId) {
780
+ this.activeUrlChangeSources.set(this.browserStateKey(threadId), { url, source });
781
+ }
782
+ getCloseReason(threadId) {
783
+ return this.pendingCloseReasons.get(this.browserStateKey(threadId)) ?? this.pendingCloseReasons.get(DEFAULT_THREAD_ID);
784
+ }
785
+ getActiveUrlChangeSource(activeUrl, threadId) {
786
+ const entry = this.activeUrlChangeSources.get(this.browserStateKey(threadId));
787
+ return entry && entry.url === activeUrl ? entry.source : void 0;
788
+ }
789
+ rememberClosedBrowserState(manager, reason, threadId) {
790
+ const state = this.getBrowserStateForManager(manager, threadId);
791
+ if (!state || state.tabs.length === 0) return;
792
+ const closedState = { ...state, closeReason: this.getCloseReason(threadId) ?? reason };
793
+ if (threadId) {
794
+ this.threadManager.updateBrowserState(threadId, closedState);
795
+ } else {
796
+ this.lastBrowserState = closedState;
797
+ }
798
+ }
757
799
  /**
758
800
  * Get the page for the current thread.
759
801
  * Uses thread scope if enabled, otherwise returns the shared page.
@@ -796,6 +838,7 @@ var AgentBrowser = class extends MastraBrowser {
796
838
  const handleDisconnect = () => {
797
839
  if (disconnectHandled) return;
798
840
  disconnectHandled = true;
841
+ this.rememberClosedBrowserState(manager, "user", threadId);
799
842
  void pidLookup.catch(() => void 0).then(() => this.handleThreadBrowserDisconnected(threadId));
800
843
  };
801
844
  const context = manager.getContext();
@@ -927,12 +970,12 @@ var AgentBrowser = class extends MastraBrowser {
927
970
  * Get the current browser state (all tabs and active tab index).
928
971
  */
929
972
  async getBrowserState(threadId) {
930
- if (!this.isBrowserRunning()) {
973
+ if (!this.isBrowserRunning(threadId)) {
931
974
  return null;
932
975
  }
933
976
  try {
934
977
  const manager = await this.getManagerForThread(threadId);
935
- return this.getBrowserStateForManager(manager);
978
+ return this.getBrowserStateForManager(manager, threadId);
936
979
  } catch {
937
980
  return null;
938
981
  }
@@ -945,22 +988,32 @@ var AgentBrowser = class extends MastraBrowser {
945
988
  const effectiveThreadId = threadId ?? this.getCurrentThread() ?? DEFAULT_THREAD_ID;
946
989
  const manager = this.threadManager.getExistingManagerForThread(effectiveThreadId);
947
990
  if (!manager) return null;
948
- return this.getBrowserStateForManager(manager);
991
+ return this.getBrowserStateForManager(manager, effectiveThreadId);
949
992
  }
950
993
  /**
951
994
  * Get browser state from a specific manager instance.
952
995
  */
953
- getBrowserStateForManager(manager) {
996
+ getBrowserStateForManager(manager, threadId) {
954
997
  try {
998
+ const stateKey = this.browserStateKey(threadId);
955
999
  const pages = manager.getPages();
956
1000
  const activeIndex = manager.getActiveIndex();
957
1001
  const tabs = pages.map((page) => ({
958
1002
  url: page.url()
959
1003
  }));
960
- return {
1004
+ const activeUrl = tabs[activeIndex]?.url;
1005
+ const previousState = this.threadManager.getSavedBrowserState(stateKey) ?? this.lastBrowserState;
1006
+ const previousUrl = previousState?.tabs[previousState.activeTabIndex]?.url;
1007
+ const activeUrlChangeSource = this.getActiveUrlChangeSource(activeUrl, stateKey) ?? (previousUrl && activeUrl !== previousUrl ? "user" : void 0);
1008
+ const state = {
961
1009
  tabs,
962
- activeTabIndex: activeIndex
1010
+ activeTabIndex: activeIndex,
1011
+ ...this.getCloseReason(stateKey) ? { closeReason: this.getCloseReason(stateKey) } : {},
1012
+ ...activeUrlChangeSource ? { activeUrlChangeSource } : {}
963
1013
  };
1014
+ this.threadManager.updateBrowserState(stateKey, state);
1015
+ this.lastBrowserState = state;
1016
+ return state;
964
1017
  } catch {
965
1018
  return null;
966
1019
  }
@@ -1015,9 +1068,11 @@ var AgentBrowser = class extends MastraBrowser {
1015
1068
  timeout: input.timeout ?? this.defaultTimeout,
1016
1069
  waitUntil: input.waitUntil ?? "domcontentloaded"
1017
1070
  });
1071
+ const url = page.url();
1072
+ this.markActiveUrlChangeSource("agent", url, threadId);
1018
1073
  return {
1019
1074
  success: true,
1020
- url: page.url(),
1075
+ url,
1021
1076
  title: await page.title(),
1022
1077
  hint: "Take a snapshot to see interactive elements and get refs."
1023
1078
  };
@@ -1304,9 +1359,11 @@ var AgentBrowser = class extends MastraBrowser {
1304
1359
  try {
1305
1360
  const page = await this.getPage(threadId);
1306
1361
  await page.goBack({ timeout: this.defaultTimeout });
1362
+ const url = page.url();
1363
+ this.markActiveUrlChangeSource("agent", url, threadId);
1307
1364
  return {
1308
1365
  success: true,
1309
- url: page.url(),
1366
+ url,
1310
1367
  title: await page.title(),
1311
1368
  hint: "Take a new snapshot to see the previous page."
1312
1369
  };
@@ -1437,6 +1494,7 @@ var AgentBrowser = class extends MastraBrowser {
1437
1494
  if (input.url) {
1438
1495
  const page = await this.getPage(threadId);
1439
1496
  await page.goto(input.url);
1497
+ this.markActiveUrlChangeSource("agent", page.url(), threadId);
1440
1498
  }
1441
1499
  this.updateSessionBrowserState(threadId);
1442
1500
  return {
@@ -1457,6 +1515,7 @@ var AgentBrowser = class extends MastraBrowser {
1457
1515
  await this.reconnectScreencastForThread(threadId, "tab switch");
1458
1516
  const page = browser.getPage();
1459
1517
  const pageUrl = page.url();
1518
+ this.markActiveUrlChangeSource("agent", pageUrl, threadId);
1460
1519
  const streamKey = this.getStreamKey(threadId);
1461
1520
  const stream = this.activeScreencastStreams.get(streamKey);
1462
1521
  if (pageUrl && stream?.isActive()) {