@remcp/runtime 0.2.160 → 0.2.161

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remcp/runtime",
3
- "version": "0.2.160",
3
+ "version": "0.2.161",
4
4
  "description": "First-party ReMCP local device runtime: filesystem, terminal, computer-use, browser, diagnostics and document tools over MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -244,6 +244,24 @@ export function productionSessionKey(endpoint, target) {
244
244
  return `${endpoint.origin}|${String(target.id)}`;
245
245
  }
246
246
 
247
+ // A target returned by browser_tabs is already bound to a validated CDP
248
+ // WebSocket. Reuse its guarded live session for subsequent exact-ID actions
249
+ // instead of rediscovering every unrelated Chrome tab on each keypress/click.
250
+ export function findGuardedCachedTarget(args, endpoint) {
251
+ if (process.env.NODE_ENV !== 'production') return null;
252
+ const id = optionalString(args?.target_id || args?.targetId);
253
+ if (!id || args?.title || args?.url_contains || args?.urlContains) return null;
254
+ const key = endpoint.origin + '|' + id;
255
+ const record = productionBrowserSessions.get(key);
256
+ if (!record?.guardInstalled || !record.target || record.targetId !== id
257
+ || record.target.id !== id || record.session?.closed
258
+ || record.session?.socket?.readyState !== 1) return null;
259
+ try {
260
+ if (validatedTargetWebSocketUrl(record.target, endpoint) !== record.session.url) return null;
261
+ } catch { return null; }
262
+ return { key, record, target:record.target };
263
+ }
264
+
247
265
  export function closeProductionSession(key, record) {
248
266
  if (!record) return;
249
267
  if (record.timer) clearTimeout(record.timer);
@@ -257,10 +275,10 @@ export function closeProductionSession(key, record) {
257
275
  if (productionBrowserSessions.get(key) === record) productionBrowserSessions.delete(key);
258
276
  }
259
277
 
260
- export function retainProductionSession(key, session, removeGuard, targetId, endpoint, ownedTarget = false) {
278
+ export function retainProductionSession(key, session, removeGuard, targetId, endpoint, ownedTarget = false, target = null) {
261
279
  const existing = productionBrowserSessions.get(key);
262
280
  if (existing) closeProductionSession(key, existing);
263
- const record = { session, removeGuard, targetId, endpoint, ownedTarget, timer:null };
281
+ const record = { session, removeGuard, targetId, endpoint, ownedTarget, target, guardInstalled:true, timer:null };
264
282
  productionBrowserSessions.set(key, record);
265
283
  const schedule = () => {
266
284
  if (record.timer) clearTimeout(record.timer);
@@ -299,7 +317,7 @@ export async function createGuardedNewTab(url, args) {
299
317
  removeGuard = await installBrowserRequestGuard(session, { closeRootTarget:() => closeBrowserTarget(endpoint, target.id) });
300
318
  if (process.env.NODE_ENV === 'production') {
301
319
  productionKey = productionSessionKey(endpoint, target);
302
- productionRecord = retainProductionSession(productionKey, session, removeGuard, target.id, endpoint, true);
320
+ productionRecord = retainProductionSession(productionKey, session, removeGuard, target.id, endpoint, true, target);
303
321
  }
304
322
  await session.send('Page.enable');
305
323
  const loaded = session.waitFor('Page.loadEventFired', clamp(args.timeout_ms, 15_000, 500, 120_000)).catch(() => null);
@@ -325,15 +343,16 @@ export async function createGuardedNewTab(url, args) {
325
343
  }
326
344
  }
327
345
 
328
- export async function withTarget(args, callback) {
346
+ export async function withTarget(args = {}, callback) {
329
347
  // chooseTarget already validated this exact discovered URL during this call.
330
348
  // The actual page URL is independently checked before and after the action.
331
349
  // Avoid a redundant DNS check for the same target metadata.
332
- const target = await chooseTarget(args);
333
350
  const endpoint = endpointUrl(args.endpoint);
351
+ const cached = findGuardedCachedTarget(args, endpoint);
352
+ const target = cached?.target || await chooseTarget(args);
334
353
  const production = process.env.NODE_ENV === 'production';
335
354
  const key = production ? productionSessionKey(endpoint, target) : '';
336
- let record = production ? productionBrowserSessions.get(key) : null;
355
+ let record = cached?.record || (production ? productionBrowserSessions.get(key) : null);
337
356
  if (record?.session?.closed || (record?.session?.socket && record.session.socket.readyState !== 1)) {
338
357
  closeProductionSession(key, record);
339
358
  record = null;
@@ -344,7 +363,7 @@ export async function withTarget(args, callback) {
344
363
  try {
345
364
  session = await new CdpSession(target.webSocketDebuggerUrl, clamp(args.timeout_ms, 10_000, 100, 120_000)).open();
346
365
  removeGuard = await installBrowserRequestGuard(session);
347
- if (production) record = retainProductionSession(key, session, removeGuard, target.id, endpoint);
366
+ if (production) record = retainProductionSession(key, session, removeGuard, target.id, endpoint, false, target);
348
367
  } catch (error) {
349
368
  removeGuard();
350
369
  if (session) {
@@ -358,7 +377,9 @@ export async function withTarget(args, callback) {
358
377
  try {
359
378
  const currentUrl = await evaluate(session, 'location.href');
360
379
  await assertResolvedPageUrl(currentUrl);
361
- const result = await callback(session, target);
380
+ // A reused target's discovery metadata can be old after navigation.
381
+ // Pass the just-verified live URL without an extra CDP round-trip.
382
+ const result = await callback(session, { ...target, url:currentUrl });
362
383
  const finalUrl = await evaluate(session, 'location.href').catch(() => { throw new Error('Browser page URL could not be verified after the operation'); });
363
384
  await assertResolvedPageUrl(finalUrl);
364
385
  return result;