@mastra/core 1.22.0-alpha.2 → 1.22.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/agent/agent.d.ts.map +1 -1
  3. package/dist/agent/index.cjs +8 -8
  4. package/dist/agent/index.js +1 -1
  5. package/dist/browser/browser.d.ts +155 -6
  6. package/dist/browser/browser.d.ts.map +1 -1
  7. package/dist/browser/index.cjs +292 -69
  8. package/dist/browser/index.cjs.map +1 -1
  9. package/dist/browser/index.d.ts +1 -1
  10. package/dist/browser/index.d.ts.map +1 -1
  11. package/dist/browser/index.js +292 -69
  12. package/dist/browser/index.js.map +1 -1
  13. package/dist/browser/thread-manager.d.ts +41 -6
  14. package/dist/browser/thread-manager.d.ts.map +1 -1
  15. package/dist/channels/index.cjs +4 -4
  16. package/dist/channels/index.js +1 -1
  17. package/dist/{chunk-T5EPN63U.cjs → chunk-4CUUOZHE.cjs} +9 -9
  18. package/dist/{chunk-T5EPN63U.cjs.map → chunk-4CUUOZHE.cjs.map} +1 -1
  19. package/dist/{chunk-DOBLNWJ4.js → chunk-7RUFTPIY.js} +4 -4
  20. package/dist/{chunk-DOBLNWJ4.js.map → chunk-7RUFTPIY.js.map} +1 -1
  21. package/dist/{chunk-OBEZWZWL.cjs → chunk-AUJ7TUEZ.cjs} +185 -185
  22. package/dist/{chunk-OBEZWZWL.cjs.map → chunk-AUJ7TUEZ.cjs.map} +1 -1
  23. package/dist/{chunk-27O5T2VT.cjs → chunk-CXQ3QR5K.cjs} +48 -48
  24. package/dist/{chunk-27O5T2VT.cjs.map → chunk-CXQ3QR5K.cjs.map} +1 -1
  25. package/dist/{chunk-YEAJ4WNU.js → chunk-DRITAH5L.js} +3 -3
  26. package/dist/{chunk-YEAJ4WNU.js.map → chunk-DRITAH5L.js.map} +1 -1
  27. package/dist/{chunk-7BCOOVHZ.js → chunk-GYQ22SLZ.js} +4 -4
  28. package/dist/{chunk-7BCOOVHZ.js.map → chunk-GYQ22SLZ.js.map} +1 -1
  29. package/dist/{chunk-CMAULWNV.js → chunk-HPVNBI7J.js} +3 -3
  30. package/dist/{chunk-CMAULWNV.js.map → chunk-HPVNBI7J.js.map} +1 -1
  31. package/dist/{chunk-6RXPK4SK.js → chunk-HXX4JDIB.js} +3 -3
  32. package/dist/{chunk-6RXPK4SK.js.map → chunk-HXX4JDIB.js.map} +1 -1
  33. package/dist/{chunk-XQLIIZJB.cjs → chunk-KLHUYSDF.cjs} +6 -6
  34. package/dist/{chunk-XQLIIZJB.cjs.map → chunk-KLHUYSDF.cjs.map} +1 -1
  35. package/dist/{chunk-NKRYR7II.js → chunk-NLN4DDI6.js} +6 -6
  36. package/dist/{chunk-NKRYR7II.js.map → chunk-NLN4DDI6.js.map} +1 -1
  37. package/dist/{chunk-ELXVJWPF.js → chunk-NMRSL5GT.js} +9 -9
  38. package/dist/{chunk-ELXVJWPF.js.map → chunk-NMRSL5GT.js.map} +1 -1
  39. package/dist/{chunk-RSJKFDKP.cjs → chunk-NVMXENQS.cjs} +17 -17
  40. package/dist/{chunk-RSJKFDKP.cjs.map → chunk-NVMXENQS.cjs.map} +1 -1
  41. package/dist/{chunk-KAEFU5PR.cjs → chunk-PJERQ3YG.cjs} +3 -3
  42. package/dist/chunk-PJERQ3YG.cjs.map +1 -0
  43. package/dist/{chunk-WOFPOL3K.cjs → chunk-QFVPZL25.cjs} +5 -5
  44. package/dist/{chunk-WOFPOL3K.cjs.map → chunk-QFVPZL25.cjs.map} +1 -1
  45. package/dist/{chunk-XW5P6CHZ.cjs → chunk-RHKFYYB6.cjs} +26 -24
  46. package/dist/chunk-RHKFYYB6.cjs.map +1 -0
  47. package/dist/{chunk-O4GMYDMB.js → chunk-TULT7C36.js} +6 -4
  48. package/dist/chunk-TULT7C36.js.map +1 -0
  49. package/dist/{chunk-KYUKC4QI.cjs → chunk-UZVGJ26K.cjs} +77 -77
  50. package/dist/{chunk-KYUKC4QI.cjs.map → chunk-UZVGJ26K.cjs.map} +1 -1
  51. package/dist/{chunk-PQGC24JT.js → chunk-V5FLTMXK.js} +3 -3
  52. package/dist/chunk-V5FLTMXK.js.map +1 -0
  53. package/dist/{chunk-HLG6JE5B.cjs → chunk-VDXTOWVO.cjs} +16 -14
  54. package/dist/chunk-VDXTOWVO.cjs.map +1 -0
  55. package/dist/{chunk-BE4GXCOK.cjs → chunk-VRH5HTH5.cjs} +7 -7
  56. package/dist/{chunk-BE4GXCOK.cjs.map → chunk-VRH5HTH5.cjs.map} +1 -1
  57. package/dist/{chunk-X4JB5HFS.js → chunk-WTVDTW4F.js} +14 -12
  58. package/dist/chunk-WTVDTW4F.js.map +1 -0
  59. package/dist/{chunk-XZJRR6NR.js → chunk-Y7KNCAZY.js} +3 -3
  60. package/dist/{chunk-XZJRR6NR.js.map → chunk-Y7KNCAZY.js.map} +1 -1
  61. package/dist/datasets/index.cjs +11 -11
  62. package/dist/datasets/index.js +1 -1
  63. package/dist/docs/SKILL.md +1 -1
  64. package/dist/docs/assets/SOURCE_MAP.json +154 -154
  65. package/dist/evals/index.cjs +5 -5
  66. package/dist/evals/index.js +2 -2
  67. package/dist/evals/scoreTraces/index.cjs +3 -3
  68. package/dist/evals/scoreTraces/index.js +1 -1
  69. package/dist/harness/index.cjs +7 -7
  70. package/dist/harness/index.js +5 -5
  71. package/dist/index.cjs +2 -2
  72. package/dist/index.js +1 -1
  73. package/dist/llm/index.cjs +20 -20
  74. package/dist/llm/index.js +5 -5
  75. package/dist/llm/model/provider-types.generated.d.ts +3 -2
  76. package/dist/loop/index.cjs +14 -14
  77. package/dist/loop/index.js +1 -1
  78. package/dist/mastra/index.cjs +2 -2
  79. package/dist/mastra/index.js +1 -1
  80. package/dist/mastra-33MCZUH4.cjs +12 -0
  81. package/dist/{mastra-FLOYM4EW.cjs.map → mastra-33MCZUH4.cjs.map} +1 -1
  82. package/dist/mastra-RY543V4U.js +3 -0
  83. package/dist/{mastra-YQRFVXLA.js.map → mastra-RY543V4U.js.map} +1 -1
  84. package/dist/memory/index.cjs +17 -17
  85. package/dist/memory/index.js +1 -1
  86. package/dist/models-dev-2BJQPEQJ.js +3 -0
  87. package/dist/{models-dev-URH3VX3L.js.map → models-dev-2BJQPEQJ.js.map} +1 -1
  88. package/dist/models-dev-OZGJCJIG.cjs +12 -0
  89. package/dist/{models-dev-2W4M2TXF.cjs.map → models-dev-OZGJCJIG.cjs.map} +1 -1
  90. package/dist/netlify-N7PUN3NA.js +3 -0
  91. package/dist/{netlify-S7UHC335.js.map → netlify-N7PUN3NA.js.map} +1 -1
  92. package/dist/netlify-TSDN7D7F.cjs +12 -0
  93. package/dist/{netlify-LCIM6BYA.cjs.map → netlify-TSDN7D7F.cjs.map} +1 -1
  94. package/dist/processor-provider/index.cjs +10 -10
  95. package/dist/processor-provider/index.js +1 -1
  96. package/dist/processors/index.cjs +44 -44
  97. package/dist/processors/index.js +1 -1
  98. package/dist/provider-registry-DBWCMPP3.cjs +40 -0
  99. package/dist/{provider-registry-GK7MD55F.cjs.map → provider-registry-DBWCMPP3.cjs.map} +1 -1
  100. package/dist/provider-registry-EINTM27D.js +3 -0
  101. package/dist/{provider-registry-PN4KYENK.js.map → provider-registry-EINTM27D.js.map} +1 -1
  102. package/dist/provider-registry.json +6 -4
  103. package/dist/relevance/index.cjs +3 -3
  104. package/dist/relevance/index.js +1 -1
  105. package/dist/stream/index.cjs +8 -8
  106. package/dist/stream/index.js +1 -1
  107. package/dist/tool-loop-agent/index.cjs +4 -4
  108. package/dist/tool-loop-agent/index.js +1 -1
  109. package/dist/workflows/evented/index.cjs +10 -10
  110. package/dist/workflows/evented/index.js +1 -1
  111. package/dist/workflows/index.cjs +24 -24
  112. package/dist/workflows/index.js +1 -1
  113. package/package.json +9 -9
  114. package/src/llm/model/provider-types.generated.d.ts +3 -2
  115. package/dist/chunk-HLG6JE5B.cjs.map +0 -1
  116. package/dist/chunk-KAEFU5PR.cjs.map +0 -1
  117. package/dist/chunk-O4GMYDMB.js.map +0 -1
  118. package/dist/chunk-PQGC24JT.js.map +0 -1
  119. package/dist/chunk-X4JB5HFS.js.map +0 -1
  120. package/dist/chunk-XW5P6CHZ.cjs.map +0 -1
  121. package/dist/mastra-FLOYM4EW.cjs +0 -12
  122. package/dist/mastra-YQRFVXLA.js +0 -3
  123. package/dist/models-dev-2W4M2TXF.cjs +0 -12
  124. package/dist/models-dev-URH3VX3L.js +0 -3
  125. package/dist/netlify-LCIM6BYA.cjs +0 -12
  126. package/dist/netlify-S7UHC335.js +0 -3
  127. package/dist/provider-registry-GK7MD55F.cjs +0 -40
  128. package/dist/provider-registry-PN4KYENK.js +0 -3
@@ -1,3 +1,4 @@
1
+ import { isProcessorWorkflow } from '../chunk-TULT7C36.js';
1
2
  import { MastraBase } from '../chunk-DNW46JHP.js';
2
3
  import { RegisteredLogger } from '../chunk-M3LRSDLL.js';
3
4
  import { EventEmitter } from 'events';
@@ -14,6 +15,68 @@ function createError(code, message, hint) {
14
15
  };
15
16
  }
16
17
 
18
+ // src/browser/processor.ts
19
+ var BrowserContextProcessor = class {
20
+ id = "browser-context";
21
+ processInput(args) {
22
+ const ctx = args.requestContext?.get("browser");
23
+ if (!ctx) return args.messageList;
24
+ const lines = [`You have access to a browser (${ctx.provider}).`];
25
+ if (ctx.headless === false) {
26
+ lines.push("The browser is running in visible mode (not headless).");
27
+ }
28
+ if (ctx.sessionId) {
29
+ lines.push(`Session ID: ${ctx.sessionId}`);
30
+ }
31
+ const systemMessages = [...args.systemMessages, { role: "system", content: lines.join(" ") }];
32
+ return { messages: args.messages, systemMessages };
33
+ }
34
+ processInputStep(args) {
35
+ if (args.stepNumber !== 0) return;
36
+ const ctx = args.requestContext?.get("browser");
37
+ if (!ctx) return;
38
+ const parts = [];
39
+ if (ctx.currentUrl) {
40
+ parts.push(`Current URL: ${ctx.currentUrl}`);
41
+ }
42
+ if (ctx.pageTitle) {
43
+ parts.push(`Page title: ${ctx.pageTitle}`);
44
+ }
45
+ if (ctx.isRunning === false) {
46
+ parts.push("Browser is not currently running.");
47
+ }
48
+ if (parts.length === 0) return;
49
+ const reminder = `<system-reminder>${parts.join(" | ")}</system-reminder>
50
+
51
+ `;
52
+ const messages = [...args.messages];
53
+ for (let i = messages.length - 1; i >= 0; i--) {
54
+ const msg = messages[i];
55
+ if (msg.role === "user") {
56
+ const content = msg.content;
57
+ const existingParts = content.parts ?? [];
58
+ const firstTextIdx = existingParts.findIndex((p) => p.type === "text");
59
+ if (firstTextIdx >= 0) {
60
+ const textPart = existingParts[firstTextIdx];
61
+ const newParts = [...existingParts];
62
+ newParts[firstTextIdx] = { ...textPart, text: reminder + textPart.text };
63
+ messages[i] = { ...msg, content: { ...content, parts: newParts } };
64
+ } else {
65
+ messages[i] = {
66
+ ...msg,
67
+ content: {
68
+ ...content,
69
+ parts: [{ type: "text", text: reminder }, ...existingParts]
70
+ }
71
+ };
72
+ }
73
+ break;
74
+ }
75
+ }
76
+ return { messages };
77
+ }
78
+ };
79
+
17
80
  // src/browser/thread-manager.ts
18
81
  var DEFAULT_THREAD_ID = "__default__";
19
82
  var ThreadManager = class {
@@ -23,6 +86,10 @@ var ThreadManager = class {
23
86
  activeThreadId = DEFAULT_THREAD_ID;
24
87
  /** Preserved browser state that survives session clears (for browser restore) */
25
88
  savedBrowserStates = /* @__PURE__ */ new Map();
89
+ /** Shared manager instance (used for 'shared' scope) */
90
+ sharedManager = null;
91
+ /** Map of thread ID to dedicated manager instance (for 'thread' scope) */
92
+ threadManagers = /* @__PURE__ */ new Map();
26
93
  onSessionCreated;
27
94
  onSessionDestroyed;
28
95
  constructor(config) {
@@ -43,6 +110,49 @@ var ThreadManager = class {
43
110
  getActiveThreadId() {
44
111
  return this.activeThreadId;
45
112
  }
113
+ /**
114
+ * Set the shared manager instance (called after browser launch).
115
+ */
116
+ setSharedManager(manager) {
117
+ this.sharedManager = manager;
118
+ }
119
+ /**
120
+ * Clear the shared manager instance (called when browser disconnects).
121
+ */
122
+ clearSharedManager() {
123
+ this.sharedManager = null;
124
+ }
125
+ /**
126
+ * Get the manager for an existing thread session without creating a new one.
127
+ *
128
+ * For 'thread' scope: Returns the thread-specific manager, or null if no session exists.
129
+ * For 'shared' scope: Returns the shared manager (all threads use the same instance).
130
+ *
131
+ * @param threadId - Thread identifier (defaults to DEFAULT_THREAD_ID)
132
+ * @returns The manager for the thread, or null if not found (thread scope only)
133
+ */
134
+ getExistingManagerForThread(threadId) {
135
+ const effectiveThreadId = threadId ?? DEFAULT_THREAD_ID;
136
+ if (this.scope === "thread") {
137
+ return this.threadManagers.get(effectiveThreadId) ?? null;
138
+ }
139
+ return this.sharedManager;
140
+ }
141
+ /**
142
+ * Check if any thread managers are still running (for 'thread' scope).
143
+ */
144
+ hasActiveThreadManagers() {
145
+ return this.threadManagers.size > 0;
146
+ }
147
+ /**
148
+ * Clear all session tracking without closing managers.
149
+ * Used when browsers have been externally closed and we just need to reset state.
150
+ */
151
+ clearAllSessions() {
152
+ this.threadManagers.clear();
153
+ this.sessions.clear();
154
+ this.activeThreadId = DEFAULT_THREAD_ID;
155
+ }
46
156
  /**
47
157
  * Get a session by thread ID.
48
158
  */
@@ -87,8 +197,6 @@ var ThreadManager = class {
87
197
  this.sessions.set(effectiveThreadId, session);
88
198
  this.logger?.debug?.(`Created thread session: ${effectiveThreadId}`);
89
199
  this.onSessionCreated?.(session);
90
- } else if (this.activeThreadId !== effectiveThreadId) {
91
- await this.switchToSession(session);
92
200
  }
93
201
  this.activeThreadId = effectiveThreadId;
94
202
  return this.getManagerForSession(session);
@@ -104,6 +212,7 @@ var ThreadManager = class {
104
212
  return;
105
213
  }
106
214
  await this.doDestroySession(session);
215
+ this.threadManagers.delete(threadId);
107
216
  this.sessions.delete(threadId);
108
217
  this.logger?.debug?.(`Destroyed thread session: ${threadId}`);
109
218
  this.onSessionDestroyed?.(threadId);
@@ -150,6 +259,37 @@ var ThreadManager = class {
150
259
  }
151
260
  return this.savedBrowserStates.get(threadId);
152
261
  }
262
+ /**
263
+ * Clear a specific thread's session without closing the browser.
264
+ * Used when a thread's browser has been externally closed.
265
+ * Preserves the browser state for potential restoration.
266
+ *
267
+ * @param threadId - The thread ID to clear
268
+ */
269
+ clearSession(threadId) {
270
+ const session = this.sessions.get(threadId);
271
+ if (session?.browserState) {
272
+ this.savedBrowserStates.set(threadId, session.browserState);
273
+ }
274
+ this.threadManagers.delete(threadId);
275
+ this.sessions.delete(threadId);
276
+ if (this.activeThreadId === threadId) {
277
+ this.activeThreadId = DEFAULT_THREAD_ID;
278
+ }
279
+ }
280
+ // ---------------------------------------------------------------------------
281
+ // Abstract methods to be implemented by subclasses
282
+ // ---------------------------------------------------------------------------
283
+ /**
284
+ * Get the shared browser manager (used for 'shared' scope and default thread).
285
+ * @throws Error if shared manager is not initialized
286
+ */
287
+ getSharedManager() {
288
+ if (!this.sharedManager) {
289
+ throw new Error("Browser not launched");
290
+ }
291
+ return this.sharedManager;
292
+ }
153
293
  };
154
294
 
155
295
  // src/browser/browser.ts
@@ -170,6 +310,12 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
170
310
  }
171
311
  /** Last known browser state before browser was closed (for restore on relaunch) */
172
312
  lastBrowserState;
313
+ /**
314
+ * Shared manager instance for 'shared' scope mode.
315
+ * Type varies by provider (e.g., BrowserManager for agent-browser, Stagehand for stagehand).
316
+ * Providers should cast this to their specific type when accessing.
317
+ */
318
+ sharedManager = null;
173
319
  /** Configuration */
174
320
  config;
175
321
  /**
@@ -183,6 +329,69 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
183
329
  */
184
330
  currentThreadId = DEFAULT_THREAD_ID;
185
331
  // ---------------------------------------------------------------------------
332
+ // Screencast State
333
+ // ---------------------------------------------------------------------------
334
+ /** Default key for shared scope screencast streams */
335
+ static SHARED_STREAM_KEY = "__shared__";
336
+ /** Active screencast streams per thread (for triggering reconnects on tab changes) */
337
+ activeScreencastStreams = /* @__PURE__ */ new Map();
338
+ /**
339
+ * Get the stream key for a thread (or shared key for shared scope).
340
+ * @param threadId - Optional thread ID
341
+ * @returns The stream key to use for the screencast streams map
342
+ */
343
+ getStreamKey(threadId) {
344
+ return threadId || _MastraBrowser.SHARED_STREAM_KEY;
345
+ }
346
+ /**
347
+ * Reconnect the active screencast for a specific thread.
348
+ * Called internally when tabs are switched or closed.
349
+ */
350
+ async reconnectScreencastForThread(threadId, reason) {
351
+ const streamKey = this.getStreamKey(threadId);
352
+ const stream = this.activeScreencastStreams.get(streamKey);
353
+ if (!stream || !stream.isActive()) {
354
+ return;
355
+ }
356
+ if (!this.isBrowserRunning()) {
357
+ this.logger.debug?.("Skipping screencast reconnect - browser not running");
358
+ return;
359
+ }
360
+ const scope = this.getScope();
361
+ if (scope === "thread" && threadId && !this.threadManager?.getExistingManagerForThread(threadId)) {
362
+ this.logger.debug?.(`Skipping screencast reconnect - no session for thread ${threadId}`);
363
+ return;
364
+ }
365
+ this.logger.debug?.(`Reconnecting screencast: ${reason}`);
366
+ try {
367
+ await new Promise((resolve) => setTimeout(resolve, 150));
368
+ await stream.reconnect();
369
+ const activePage = await this.getActivePage(threadId);
370
+ if (activePage) {
371
+ const url = activePage.url();
372
+ if (url) {
373
+ stream.emitUrl(url);
374
+ }
375
+ }
376
+ } catch (error) {
377
+ this.logger.debug?.("Screencast reconnect failed", error);
378
+ }
379
+ }
380
+ /**
381
+ * Update the browser state in the thread session.
382
+ * Called on navigation, tab open/close to keep state fresh.
383
+ */
384
+ updateSessionBrowserState(threadId) {
385
+ try {
386
+ const effectiveThreadId = threadId ?? this.getCurrentThread() ?? DEFAULT_THREAD_ID;
387
+ const state = this.getBrowserStateForThread(effectiveThreadId);
388
+ if (state) {
389
+ this.threadManager?.updateBrowserState(effectiveThreadId, state);
390
+ }
391
+ } catch {
392
+ }
393
+ }
394
+ // ---------------------------------------------------------------------------
186
395
  // Lifecycle Promise Tracking (prevents race conditions)
187
396
  // ---------------------------------------------------------------------------
188
397
  _launchPromise;
@@ -193,6 +402,19 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
193
402
  constructor(config = {}) {
194
403
  super({ name: "MastraBrowser", component: RegisteredLogger.BROWSER });
195
404
  this.config = config;
405
+ const scope = config.scope;
406
+ if (config.cdpUrl && scope === "thread") {
407
+ throw new Error(
408
+ `Invalid browser configuration: "cdpUrl" and "scope: 'thread'" cannot be used together.
409
+
410
+ \u2022 cdpUrl connects to a single existing browser instance (all threads share it)
411
+ \u2022 scope: "thread" requires spawning separate browser instances per thread
412
+
413
+ To fix this, either:
414
+ 1. Remove cdpUrl to let the provider spawn separate browser instances (supports thread isolation)
415
+ 2. Use scope: "shared" when connecting via cdpUrl (all threads share one browser)`
416
+ );
417
+ }
196
418
  }
197
419
  /**
198
420
  * Launch the browser.
@@ -401,13 +623,25 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
401
623
  /**
402
624
  * Handle browser disconnection by updating status and notifying listeners.
403
625
  * Called when browser is detected as externally closed.
404
- * Subclasses should call this and also clear their internal instance references.
626
+ *
627
+ * For 'thread' scope: clears only the specific thread's session (other threads unaffected)
628
+ * For 'shared' scope: clears the shared manager and updates global status
405
629
  */
406
630
  handleBrowserDisconnected() {
407
- if (this.status !== "closed") {
408
- this.status = "closed";
409
- this.logger.debug?.("Browser was externally closed, status set to closed");
410
- this.notifyBrowserClosed();
631
+ const scope = this.threadManager?.getScope();
632
+ const threadId = this.getCurrentThread();
633
+ if (scope === "thread" && threadId !== DEFAULT_THREAD_ID) {
634
+ this.threadManager.clearSession(threadId);
635
+ this.logger.debug?.(`Cleared browser session for thread: ${threadId}`);
636
+ this.notifyBrowserClosed(threadId);
637
+ } else {
638
+ this.sharedManager = null;
639
+ this.threadManager?.clearSharedManager();
640
+ if (this.status !== "closed") {
641
+ this.status = "closed";
642
+ this.logger.debug?.("Browser was externally closed, status set to closed");
643
+ this.notifyBrowserClosed();
644
+ }
411
645
  }
412
646
  }
413
647
  /**
@@ -698,6 +932,37 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
698
932
  }
699
933
  return this.threadManager.hasSession(threadId);
700
934
  }
935
+ /**
936
+ * Close a specific thread's browser session.
937
+ * Delegates to ThreadManager and notifies registered callbacks.
938
+ *
939
+ * For 'thread' scope, this closes only that thread's browser instance.
940
+ * For 'shared' scope, this is a no-op (use close() to close the shared browser).
941
+ *
942
+ * @param threadId - The thread ID whose session should be closed
943
+ */
944
+ async closeThreadSession(threadId) {
945
+ if (!this.threadManager) {
946
+ return;
947
+ }
948
+ await this.threadManager.destroySession(threadId);
949
+ this.notifyBrowserClosed(threadId);
950
+ }
951
+ /**
952
+ * Handle browser disconnection for a specific thread.
953
+ * Called when a thread's browser is closed externally (e.g., user closes browser window).
954
+ * Clears the thread session and notifies registered callbacks.
955
+ *
956
+ * @param threadId - The thread ID whose session was disconnected
957
+ */
958
+ handleThreadBrowserDisconnected(threadId) {
959
+ if (!this.threadManager) {
960
+ return;
961
+ }
962
+ this.threadManager.clearSession(threadId);
963
+ this.logger.debug?.(`Cleared browser session for thread: ${threadId}`);
964
+ this.notifyBrowserClosed(threadId);
965
+ }
701
966
  /**
702
967
  * Get a session identifier for a specific thread.
703
968
  * In thread scope, returns a composite ID (browser:threadId).
@@ -755,6 +1020,26 @@ var MastraBrowser = class _MastraBrowser extends MastraBase {
755
1020
  async injectKeyboardEvent(_event, _threadId) {
756
1021
  throw new Error("Keyboard event injection not supported by this provider");
757
1022
  }
1023
+ // ---------------------------------------------------------------------------
1024
+ // Input Processors
1025
+ // ---------------------------------------------------------------------------
1026
+ /**
1027
+ * Returns browser input processors (e.g., BrowserContextProcessor for context injection).
1028
+ * Skips if the user already added a processor with the same id.
1029
+ *
1030
+ * This method is similar to AgentChannels.getInputProcessors() and allows
1031
+ * browser implementations to provide their own processors.
1032
+ *
1033
+ * @param configuredProcessors - Processors already configured by the user (for deduplication)
1034
+ * @returns Array of input processors for this browser instance
1035
+ */
1036
+ getInputProcessors(configuredProcessors = []) {
1037
+ const hasProcessor = configuredProcessors.some(
1038
+ (p) => !isProcessorWorkflow(p) && "id" in p && p.id === "browser-context"
1039
+ );
1040
+ if (hasProcessor) return [];
1041
+ return [new BrowserContextProcessor()];
1042
+ }
758
1043
  };
759
1044
 
760
1045
  // src/browser/screencast/types.ts
@@ -927,68 +1212,6 @@ var ScreencastStream = class extends EventEmitter {
927
1212
  }
928
1213
  };
929
1214
 
930
- // src/browser/processor.ts
931
- var BrowserContextProcessor = class {
932
- id = "browser-context";
933
- processInput(args) {
934
- const ctx = args.requestContext?.get("browser");
935
- if (!ctx) return args.messageList;
936
- const lines = [`You have access to a browser (${ctx.provider}).`];
937
- if (ctx.headless === false) {
938
- lines.push("The browser is running in visible mode (not headless).");
939
- }
940
- if (ctx.sessionId) {
941
- lines.push(`Session ID: ${ctx.sessionId}`);
942
- }
943
- const systemMessages = [...args.systemMessages, { role: "system", content: lines.join(" ") }];
944
- return { messages: args.messages, systemMessages };
945
- }
946
- processInputStep(args) {
947
- if (args.stepNumber !== 0) return;
948
- const ctx = args.requestContext?.get("browser");
949
- if (!ctx) return;
950
- const parts = [];
951
- if (ctx.currentUrl) {
952
- parts.push(`Current URL: ${ctx.currentUrl}`);
953
- }
954
- if (ctx.pageTitle) {
955
- parts.push(`Page title: ${ctx.pageTitle}`);
956
- }
957
- if (ctx.isRunning === false) {
958
- parts.push("Browser is not currently running.");
959
- }
960
- if (parts.length === 0) return;
961
- const reminder = `<system-reminder>${parts.join(" | ")}</system-reminder>
962
-
963
- `;
964
- const messages = [...args.messages];
965
- for (let i = messages.length - 1; i >= 0; i--) {
966
- const msg = messages[i];
967
- if (msg.role === "user") {
968
- const content = msg.content;
969
- const existingParts = content.parts ?? [];
970
- const firstTextIdx = existingParts.findIndex((p) => p.type === "text");
971
- if (firstTextIdx >= 0) {
972
- const textPart = existingParts[firstTextIdx];
973
- const newParts = [...existingParts];
974
- newParts[firstTextIdx] = { ...textPart, text: reminder + textPart.text };
975
- messages[i] = { ...msg, content: { ...content, parts: newParts } };
976
- } else {
977
- messages[i] = {
978
- ...msg,
979
- content: {
980
- ...content,
981
- parts: [{ type: "text", text: reminder }, ...existingParts]
982
- }
983
- };
984
- }
985
- break;
986
- }
987
- }
988
- return { messages };
989
- }
990
- };
991
-
992
1215
  export { BrowserContextProcessor, DEFAULT_THREAD_ID, MastraBrowser, SCREENCAST_DEFAULTS, ScreencastStream as ScreencastStreamImpl, ThreadManager, createError };
993
1216
  //# sourceMappingURL=index.js.map
994
1217
  //# sourceMappingURL=index.js.map