@omercnet/paseo-shared-browser 3.0.0-next.284.1 → 3.0.0-next.292.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/OVERVIEW.md CHANGED
@@ -13,7 +13,7 @@ New agents whose provider accepts external MCP servers receive a local MCP serve
13
13
 
14
14
  ## Setup
15
15
 
16
- - Paseo ^0.12.0 || ^0.13.0, and Node.js 24 or newer on the daemon host.
16
+ - Paseo ^0.13.0, and Node.js 24 or newer on the daemon host.
17
17
  - Chromium is downloaded when the plugin is installed and stored in the Paseo data directory. On Linux ARM64 nothing is downloaded, and you install a non-Snap Chromium at `/usr/bin/chromium` first. `PASEO_SHARED_BROWSER_CHROMIUM_EXECUTABLE` points the plugin at a different Chromium.
18
18
 
19
19
  ## Limits
package/README.md CHANGED
@@ -183,7 +183,9 @@ User-supplied `AGENT_BROWSER_*` variables are deliberately ignored.
183
183
  within the pane, scroll long lists, and dismiss with an outside press, Escape or native Back.
184
184
  - The monitor menu offers **Fit to panel** (default) and **Actual size (100%)**. Actual size
185
185
  uses browser layout pixels, not the larger JPEG dimensions of sharper phone captures.
186
- This choice affects only the current viewer, without resizing the shared browser.
186
+ This choice affects only the current viewer, without resizing the shared browser. Each
187
+ Shared Browser tab remembers its choice and its selected browser page across app reloads;
188
+ a remembered page that no longer exists falls back to the default page.
187
189
  Desktop local scrollbars reveal overflow while wheel events on a controlled frame go to
188
190
  the remote page. Phone viewers can pan while observing; controlling sends swipes to the
189
191
  page instead. Fitting images stay centered without empty scroll ranges; a scrollbar on
@@ -422,7 +424,7 @@ runs the actual Paseo compiler on both plugin entries.
422
424
  Release Please maintains the version, changelog, component tag, and GitHub release from
423
425
  Conventional Commits in the monorepo.
424
426
 
425
- Both the Paseo daemon and app must satisfy `^0.12.0 || ^0.13.0`. Encoded video is opt-in on the daemon and
427
+ Both the Paseo daemon and app must satisfy `^0.13.0`. Encoded video is opt-in on the daemon and
426
428
  requires WebCodecs in a web client; JPEG is always available. The client surface uses React
427
429
  Native primitives and works in desktop, web, iOS, and Android Paseo clients.
428
430
 
@@ -1,6 +1,6 @@
1
1
  /** Public themed panel controls and notices. Leaves own only focus/hover styling, never browser authority. */
2
2
  import type { PluginWorkspacePanelProps } from "@getpaseo/plugin/client";
3
- import { Icon, TextInput } from "@getpaseo/plugin/client/react-native";
3
+ import { Icon, TextInput } from "@getpaseo/plugin/client/ui";
4
4
  import { type ReactNode, useCallback, useState } from "react";
5
5
  import {
6
6
  ActivityIndicator,
@@ -33,7 +33,7 @@ vi.mock("react", () => ({
33
33
  hooks.effects.push(effect);
34
34
  },
35
35
  }));
36
- vi.mock("@getpaseo/plugin/client/react-native", () => {
36
+ vi.mock("@getpaseo/plugin/client/ui", () => {
37
37
  const Modal = Object.assign(() => null, { Content: () => null });
38
38
  return { Modal, TextInput: () => null };
39
39
  });
@@ -1,6 +1,6 @@
1
1
  /** Explicit Compose draft UI, with original control/document ownership pinned until Done. */
2
2
  import type { PluginWorkspacePanelProps } from "@getpaseo/plugin/client";
3
- import { Modal, TextInput } from "@getpaseo/plugin/client/react-native";
3
+ import { Modal, TextInput } from "@getpaseo/plugin/client/ui";
4
4
  import { useEffect, useRef, useState } from "react";
5
5
  import { Text, View } from "react-native";
6
6
  import { ControlButton } from "./browser-chrome";
@@ -1,5 +1,5 @@
1
1
  import type { PluginWorkspacePanelProps } from "@getpaseo/plugin/client";
2
- import { Icon } from "@getpaseo/plugin/client/react-native";
2
+ import { Icon } from "@getpaseo/plugin/client/ui";
3
3
  import { useCallback, useState } from "react";
4
4
  import { Pressable, Text, type TextStyle, type ViewStyle } from "react-native";
5
5
 
@@ -1,6 +1,6 @@
1
1
  /** Confirm a one-time control request before the selected tab action continues. */
2
2
  import type { PluginWorkspacePanelProps } from "@getpaseo/plugin/client";
3
- import { Icon, Modal } from "@getpaseo/plugin/client/react-native";
3
+ import { Icon, Modal } from "@getpaseo/plugin/client/ui";
4
4
  import type { BrowserState } from "../shared/browser";
5
5
  import { ControlButton } from "./browser-chrome";
6
6
  import type { ControlRequest } from "./browser-control-request";
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { browserPanelStateSchema, DEFAULT_BROWSER_PANEL_STATE } from "./browser-panel-state";
3
+
4
+ describe("persisted browser panel state", () => {
5
+ it("round-trips a remembered tab and display mode", () => {
6
+ const state = { scaleMode: "actual", selectedTabId: "page-2" };
7
+ expect(browserPanelStateSchema.parse(state)).toEqual(state);
8
+ });
9
+
10
+ it("accepts its own default", () => {
11
+ expect(browserPanelStateSchema.safeParse(DEFAULT_BROWSER_PANEL_STATE).success).toBe(true);
12
+ });
13
+
14
+ it("rejects state that a newer or older plugin version could have written", () => {
15
+ expect(browserPanelStateSchema.safeParse(undefined).success).toBe(false);
16
+ expect(
17
+ browserPanelStateSchema.safeParse({ scaleMode: "zoom", selectedTabId: null }).success,
18
+ ).toBe(false);
19
+ expect(browserPanelStateSchema.safeParse({ scaleMode: "fit", selectedTabId: 3 }).success).toBe(
20
+ false,
21
+ );
22
+ });
23
+ });
@@ -0,0 +1,14 @@
1
+ /** Per-tab state Paseo persists across tab restoration and app reloads. */
2
+ import { z } from "zod";
3
+ import { browserTabIdSchema } from "../shared/browser";
4
+
5
+ export const browserPanelStateSchema = z.object({
6
+ scaleMode: z.enum(["fit", "actual"]),
7
+ selectedTabId: browserTabIdSchema.nullable(),
8
+ });
9
+ export type BrowserPanelState = z.infer<typeof browserPanelStateSchema>;
10
+
11
+ export const DEFAULT_BROWSER_PANEL_STATE: BrowserPanelState = {
12
+ scaleMode: "fit",
13
+ selectedTabId: null,
14
+ };
@@ -1,6 +1,6 @@
1
1
  /** Grouped resolution choices with independent host favorites and capture quality. */
2
2
  import type { PluginHostProps } from "@getpaseo/plugin/client";
3
- import { Icon } from "@getpaseo/plugin/client/react-native";
3
+ import { Icon } from "@getpaseo/plugin/client/ui";
4
4
  import { Pressable, StyleSheet, Text, View } from "react-native";
5
5
  import type { BrowserDisplayPreferences } from "../shared/browser-display-preferences";
6
6
  import type { DevicePresetId } from "../shared/device-presets";
@@ -4,7 +4,7 @@
4
4
  * host internals. Placement stays inside the pane, including compact clients.
5
5
  */
6
6
  import type { PluginHostProps } from "@getpaseo/plugin/client";
7
- import { Icon } from "@getpaseo/plugin/client/react-native";
7
+ import { Icon } from "@getpaseo/plugin/client/ui";
8
8
  import {
9
9
  type ReactNode,
10
10
  type RefObject,
@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
3
3
  import {
4
4
  createBrowserViewerRecovery,
5
5
  isBrowserClosedError,
6
+ isBrowserTabUnavailableError,
6
7
  isExpiredBrowserViewerError,
7
8
  } from "./browser-viewer-recovery";
8
9
 
@@ -109,3 +110,26 @@ it("the actual capture query observer discards old token errors when its token k
109
110
  client.clear();
110
111
  }
111
112
  });
113
+
114
+ describe("remembered tab recovery", () => {
115
+ it("recognizes only the unavailable-tab rejection", () => {
116
+ const unavailable = Object.assign(
117
+ new Error(
118
+ "Request failed: Workspace runtime identity failed: Browser tab is no longer available requestType=plugin.rpc.invoke.request code=handler_error",
119
+ ),
120
+ { code: "handler_error" },
121
+ );
122
+ expect(isBrowserTabUnavailableError(unavailable)).toBe(true);
123
+ expect(isBrowserTabUnavailableError(new Error("Browser tab is no longer available"))).toBe(
124
+ true,
125
+ );
126
+ expect(isBrowserTabUnavailableError(expired)).toBe(false);
127
+ expect(isBrowserTabUnavailableError(new Error("Browser is closed"))).toBe(false);
128
+ expect(
129
+ isBrowserTabUnavailableError(
130
+ new Error("Could not parse: Browser tab is no longer available"),
131
+ ),
132
+ ).toBe(false);
133
+ expect(isBrowserTabUnavailableError(null)).toBe(false);
134
+ });
135
+ });
@@ -5,6 +5,11 @@
5
5
  */
6
6
  const VIEWER_EXPIRED_MESSAGE = "Viewer token is invalid or expired";
7
7
  const BROWSER_CLOSED_MESSAGE = "Browser is closed";
8
+ const BROWSER_TAB_UNAVAILABLE_MESSAGE = "Browser tab is no longer available";
9
+ // The supervisor wraps runtime rejections as "Workspace runtime <operation> failed: <message>".
10
+ const UNAVAILABLE_TAB_PATTERN = new RegExp(
11
+ `^(?:Workspace runtime \\w+ failed: )?${BROWSER_TAB_UNAVAILABLE_MESSAGE}(?: requestType=|$)`,
12
+ );
8
13
 
9
14
  /** Recognize the server's explicit close fence without confusing it with a transport failure. */
10
15
  export function isBrowserClosedError(error: unknown): boolean {
@@ -76,3 +81,18 @@ export function createBrowserViewerRecovery() {
76
81
  return true;
77
82
  };
78
83
  }
84
+
85
+ /**
86
+ * A remembered tab can outlive its page; the server then rejects attaching to it. The runtime
87
+ * wraps that rejection ("Workspace runtime identity failed: ..."), so match the phrase.
88
+ */
89
+ export function isBrowserTabUnavailableError(error: unknown): boolean {
90
+ if (!error || typeof error !== "object") return false;
91
+ const value = error as { message?: unknown; code?: unknown };
92
+ if (typeof value.message !== "string") return false;
93
+ if (value.code !== undefined && value.code !== "handler_error") return false;
94
+ const message = value.message.startsWith("Request failed: ")
95
+ ? value.message.slice("Request failed: ".length)
96
+ : value.message;
97
+ return UNAVAILABLE_TAB_PATTERN.test(message);
98
+ }
@@ -4,8 +4,8 @@
4
4
  * presentation models live in focused counterpart hooks; this module never
5
5
  * replays published mutations or substitutes visible pixels for input authority.
6
6
  */
7
- import { type PluginWorkspacePanelProps, useRpc } from "@getpaseo/plugin/client";
8
- import { Icon, Modal } from "@getpaseo/plugin/client/react-native";
7
+ import { type PluginWorkspacePanelProps, useRpc, useTabState } from "@getpaseo/plugin/client";
8
+ import { Icon, Modal } from "@getpaseo/plugin/client/ui";
9
9
  import { useQuery, useQueryClient } from "@tanstack/react-query";
10
10
  import {
11
11
  type ReactNode,
@@ -53,7 +53,7 @@ import {
53
53
  type CaptureDensityMode,
54
54
  getAutomaticCaptureDensity,
55
55
  } from "./browser-auto-capture-density";
56
- import { type BrowserCanvasDisplayMode, getBrowserCanvasLayout } from "./browser-canvas-layout";
56
+ import { getBrowserCanvasLayout } from "./browser-canvas-layout";
57
57
  import { BrowserCanvasViewport } from "./browser-canvas-viewport";
58
58
  import {
59
59
  CanvasPlaceholder,
@@ -75,6 +75,7 @@ import { type EmulationSelection, matchingResolutionPresetId } from "./browser-e
75
75
  import { getFillViewportResolution } from "./browser-fill-viewport";
76
76
  import type { FrameCandidate } from "./browser-frame-buffer";
77
77
  import { BrowserFrameImage } from "./browser-frame-image";
78
+ import { browserPanelStateSchema, DEFAULT_BROWSER_PANEL_STATE } from "./browser-panel-state";
78
79
  import { createStyles, DIMENSION, SPACE } from "./browser-panel-styles";
79
80
  import { BrowserResolutionPicker } from "./browser-resolution-picker";
80
81
  import { BrowserTabs } from "./browser-tabs";
@@ -86,7 +87,11 @@ import {
86
87
  } from "./browser-toolbar-menu";
87
88
  import { isFrameCurrent, isVideoSourceCurrent } from "./browser-video-currency";
88
89
  import { BrowserVideoSurface } from "./browser-video-surface";
89
- import { isBrowserClosedError, isExpiredBrowserViewerError } from "./browser-viewer-recovery";
90
+ import {
91
+ isBrowserClosedError,
92
+ isBrowserTabUnavailableError,
93
+ isExpiredBrowserViewerError,
94
+ } from "./browser-viewer-recovery";
90
95
  import { createFrameLifecycle } from "./frame-lifecycle";
91
96
  import { useBrowserAutoCaptureDensity } from "./use-browser-auto-capture-density";
92
97
  import { useBrowserCanvasInput } from "./use-browser-canvas-input";
@@ -188,6 +193,7 @@ export function SharedBrowserPanel({
188
193
  host,
189
194
  layout,
190
195
  workspaceId,
196
+ tab,
191
197
  }: PluginWorkspacePanelProps) {
192
198
  const queryClient = useQueryClient();
193
199
  const styles = useMemo(() => createStyles(theme, layout.compact), [theme, layout.compact]);
@@ -245,7 +251,38 @@ export function SharedBrowserPanel({
245
251
  const [operationError, setOperationError] = useState<string | null>(null);
246
252
  const [reconnecting, setReconnecting] = useState(false);
247
253
  const [browserClosed, setBrowserClosed] = useState(false);
248
- const [selectedTabId, setSelectedTabId] = useState<string | null>(null);
254
+ const [panelState, writePanelState] = useTabState(
255
+ tab,
256
+ browserPanelStateSchema,
257
+ DEFAULT_BROWSER_PANEL_STATE,
258
+ );
259
+ // Writes replace the whole persisted value; merge into the latest one so two
260
+ // updates in the same tick (e.g. Fill viewport) cannot drop each other.
261
+ const panelStateRef = useRef(panelState);
262
+ panelStateRef.current = panelState;
263
+ const updatePanelState = useCallback(
264
+ (patch: Partial<typeof panelState>) => {
265
+ const next = { ...panelStateRef.current, ...patch };
266
+ if (
267
+ next.scaleMode === panelStateRef.current.scaleMode &&
268
+ next.selectedTabId === panelStateRef.current.selectedTabId
269
+ ) {
270
+ return;
271
+ }
272
+ panelStateRef.current = next;
273
+ writePanelState(next);
274
+ },
275
+ [writePanelState],
276
+ );
277
+ const { selectedTabId, scaleMode } = panelState;
278
+ const setSelectedTabId = useCallback(
279
+ (tabId: string | null) => updatePanelState({ selectedTabId: tabId }),
280
+ [updatePanelState],
281
+ );
282
+ const setScaleMode = useCallback(
283
+ (mode: typeof scaleMode) => updatePanelState({ scaleMode: mode }),
284
+ [updatePanelState],
285
+ );
249
286
  const selectedTabIdRef = useRef(selectedTabId);
250
287
  selectedTabIdRef.current = selectedTabId;
251
288
  const [tabMutationPending, setTabMutationPending] = useState(false);
@@ -268,7 +305,6 @@ export function SharedBrowserPanel({
268
305
  const [keysSubmenuOpen, setKeysSubmenuOpen] = useState(false);
269
306
  const keysAnchorRef = useRef<View | null>(null);
270
307
  const [toolbarMenu, setToolbarMenu] = useState<"display" | "actions" | null>(null);
271
- const [scaleMode, setScaleMode] = useState<BrowserCanvasDisplayMode>("fit");
272
308
  const paneRef = useRef<View | null>(null);
273
309
  const displayAnchorRef = useRef<View | null>(null);
274
310
  const actionsAnchorRef = useRef<View | null>(null);
@@ -426,6 +462,12 @@ export function SharedBrowserPanel({
426
462
  if (isBrowserClosedError(attachQuery.error)) setBrowserClosed(true);
427
463
  }, [attachQuery.error]);
428
464
 
465
+ // A restored selection can name a page that no longer exists; without the
466
+ // attach there is no viewer to list tabs with, so fall back to the default page.
467
+ useEffect(() => {
468
+ if (selectedTabId && isBrowserTabUnavailableError(attachQuery.error)) setSelectedTabId(null);
469
+ }, [attachQuery.error, selectedTabId, setSelectedTabId]);
470
+
429
471
  useEffect(() => {
430
472
  if (!closedView) return;
431
473
  setControlRequest(null);
@@ -457,13 +499,21 @@ export function SharedBrowserPanel({
457
499
  setOperationError(null);
458
500
  setRuntimeNotice(null);
459
501
  setBrowserClosed(false);
460
- setSelectedTabId(null);
461
502
  setTabCloseConfirmationOpen(false);
462
503
  setControlRequest(null);
463
504
  setPendingControlAction(null);
464
505
  setCloseConfirmationOpen(false);
465
506
  }, [reset, workspaceId, host.id, inputLifecycle]);
466
507
 
508
+ // The remembered tab belongs to this host and workspace. Keep it on mount,
509
+ // where it was just restored, and clear it only when the scope really changes.
510
+ const selectionScopeRef = useRef(panelScope);
511
+ useEffect(() => {
512
+ if (selectionScopeRef.current === panelScope) return;
513
+ selectionScopeRef.current = panelScope;
514
+ setSelectedTabId(null);
515
+ }, [panelScope, setSelectedTabId]);
516
+
467
517
  useEffect(() => {
468
518
  // A tab selection changes the viewer's page and frame authority, even
469
519
  // though the underlying Chromium process and saved profile stay shared.
package/index.client.tsx CHANGED
@@ -7,7 +7,6 @@ export default function contribute(client: PluginClientContext) {
7
7
  id: "shared-browser",
8
8
  title: "Shared Browser",
9
9
  icon: "PanelsTopLeft",
10
- context: "workspace",
11
10
  locations: ["workspace", "explorer"],
12
11
  Component: SharedBrowserPanel,
13
12
  });
@@ -8,7 +8,7 @@
8
8
  "name": "@omercnet/paseo-shared-browser",
9
9
  "version": "3.0.0",
10
10
  "dependencies": {
11
- "@getpaseo/plugin": "0.13.0-beta.2",
11
+ "@getpaseo/plugin": "0.13.0",
12
12
  "@modelcontextprotocol/sdk": "1.30.0",
13
13
  "agent-browser": "0.39.1",
14
14
  "esbuild": "0.28.2",
@@ -441,29 +441,29 @@
441
441
  }
442
442
  },
443
443
  "node_modules/@getpaseo/client": {
444
- "version": "0.13.0-beta.2",
445
- "resolved": "https://registry.npmjs.org/@getpaseo/client/-/client-0.13.0-beta.2.tgz",
446
- "integrity": "sha512-f8tSt3fMpDvKjWespbKjGZa9Y5bszWpCnde2hJEAATcLRCobGPpCueWA6Vj7qsQ7oa7WpzbAnqysswS4IWkJHg==",
444
+ "version": "0.13.0",
445
+ "resolved": "https://registry.npmjs.org/@getpaseo/client/-/client-0.13.0.tgz",
446
+ "integrity": "sha512-379gl/OHIowATqKBZugupxX6HaOPWQhkOeVnlMQNWnZ5oKbyfCTEPGSkzA8Kotvv1Uue2u+j8z2WnLXyZ9ChYQ==",
447
447
  "peer": true,
448
448
  "dependencies": {
449
- "@getpaseo/protocol": "0.13.0-beta.2",
450
- "@getpaseo/relay": "0.13.0-beta.2",
449
+ "@getpaseo/protocol": "0.13.0",
450
+ "@getpaseo/relay": "0.13.0",
451
451
  "ws": "^8.20.0",
452
452
  "zod": "^4.4.3"
453
453
  }
454
454
  },
455
455
  "node_modules/@getpaseo/plugin": {
456
- "version": "0.13.0-beta.2",
457
- "resolved": "https://registry.npmjs.org/@getpaseo/plugin/-/plugin-0.13.0-beta.2.tgz",
458
- "integrity": "sha512-c0E0gCyhIDWJk8Lhjf9n5dch6QAKKQm/gS9xGRRIxzhnUMHeBgSJTl017NzBjs7cxsnEPaDv2N8Fmo2apD9tyw==",
456
+ "version": "0.13.0",
457
+ "resolved": "https://registry.npmjs.org/@getpaseo/plugin/-/plugin-0.13.0.tgz",
458
+ "integrity": "sha512-8aGH0lf5kF9yjaM0dVsm8Awu5eB2jHhmbX9ljgf6+jttNWX3XRbRNeylg3whPoKORGrJNlpuLx3XvJMC5KSCaQ==",
459
459
  "dependencies": {
460
460
  "@agentclientprotocol/sdk": "^1.4.0",
461
461
  "i18next": "^26.3.0",
462
462
  "use-sync-external-store": "^1.6.0"
463
463
  },
464
464
  "peerDependencies": {
465
- "@getpaseo/client": "0.13.0-beta.2",
466
- "@getpaseo/protocol": "0.13.0-beta.2",
465
+ "@getpaseo/client": "0.13.0",
466
+ "@getpaseo/protocol": "0.13.0",
467
467
  "react": ">=19.1.0 <20",
468
468
  "react-native": ">=0.81.5",
469
469
  "zod": "^4.4.3"
@@ -475,9 +475,9 @@
475
475
  }
476
476
  },
477
477
  "node_modules/@getpaseo/protocol": {
478
- "version": "0.13.0-beta.2",
479
- "resolved": "https://registry.npmjs.org/@getpaseo/protocol/-/protocol-0.13.0-beta.2.tgz",
480
- "integrity": "sha512-1C3nPR4Cj1E12LpTpnQr9g7xFce2ZT/KVGPKzfziNrS+7VPPUFT/oJufiwPtDtndWlV5fN1zTeSWnMCwYnvXpw==",
478
+ "version": "0.13.0",
479
+ "resolved": "https://registry.npmjs.org/@getpaseo/protocol/-/protocol-0.13.0.tgz",
480
+ "integrity": "sha512-g5Hp6QP5hV7KTCrSRBVye2+qhptwnhhLXRn6PHaS1L8PgmeRh5LvrE8PZq3HRq8B1WxflVDuV2WRB2Z3FNcvJA==",
481
481
  "peer": true,
482
482
  "dependencies": {
483
483
  "semver": "^7.8.5",
@@ -485,9 +485,9 @@
485
485
  }
486
486
  },
487
487
  "node_modules/@getpaseo/relay": {
488
- "version": "0.13.0-beta.2",
489
- "resolved": "https://registry.npmjs.org/@getpaseo/relay/-/relay-0.13.0-beta.2.tgz",
490
- "integrity": "sha512-W7IILPB+TvX1qFa9NQFq/5+8AFlbf1XhFIC2DXD13pBIdjj1vJ+bSlOEbNOakAlF5QyE4pwxcHt/CfyCDmSrVg==",
488
+ "version": "0.13.0",
489
+ "resolved": "https://registry.npmjs.org/@getpaseo/relay/-/relay-0.13.0.tgz",
490
+ "integrity": "sha512-Yd/RR+2hStTEtmkutwE21drjVmgz+RnQWyjmoh1++dPysp2oNsrimurnbfN0yoBJ5xXWlyuvn8Sr7CziTpkv7Q==",
491
491
  "peer": true,
492
492
  "dependencies": {
493
493
  "@noble/ciphers": "^2.4.0",
@@ -1229,9 +1229,9 @@
1229
1229
  }
1230
1230
  },
1231
1231
  "node_modules/i18next": {
1232
- "version": "26.5.6",
1233
- "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.5.6.tgz",
1234
- "integrity": "sha512-GFWrDwGmYhOQS3bKvjA2Ir2ob9ScEdyY1UbeQJ10jyVACGQ6XxmDYqC/lxEj/IPEJlPQYNRnYffDET6TM8mnFg==",
1232
+ "version": "26.5.8",
1233
+ "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.5.8.tgz",
1234
+ "integrity": "sha512-EQx2RyhwINMekbykhZpAJQ2ILeJqnYOpRjWEkSVv45pR8yeVbb0thr2zxQ+dOo365OIb7h06BKPtt16WE8Wliw==",
1235
1235
  "funding": [
1236
1236
  {
1237
1237
  "type": "individual",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-shared-browser",
3
- "version": "3.0.0-next.284.1",
3
+ "version": "3.0.0-next.292.1",
4
4
  "description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
5
5
  "keywords": [
6
6
  "browser",
@@ -54,7 +54,7 @@
54
54
  "test:video": "vitest run tests/video.smoke.test.ts --testTimeout=30000"
55
55
  },
56
56
  "dependencies": {
57
- "@getpaseo/plugin": "0.13.0-beta.2",
57
+ "@getpaseo/plugin": "0.13.0",
58
58
  "@modelcontextprotocol/sdk": "1.30.0",
59
59
  "agent-browser": "0.39.1",
60
60
  "esbuild": "0.28.2",
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "^2.5.10",
65
- "@getpaseo/server": "0.13.0-beta.2",
65
+ "@getpaseo/server": "0.13.0",
66
66
  "@tanstack/react-query": "^5.102.3",
67
67
  "@types/node": "^24.10.1",
68
68
  "@types/react": "^19.2.18",
package/paseo-plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "shared-browser",
3
3
  "description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
4
- "requirements": { "paseo": "^0.12.0 || ^0.13.0" },
4
+ "requirements": { "paseo": "^0.13.0" },
5
5
  "build": [["node", "scripts/prepare-dependencies.mjs"], ["npm", "run", "prepare:runtime"]]
6
6
  }