Skip to content

feat: Add Ripple AI browsing agent (Browse Mode + Work Mode)#228

Draft
iamEvanYT wants to merge 13 commits intomainfrom
opencode/add-ripple-ai-agent
Draft

feat: Add Ripple AI browsing agent (Browse Mode + Work Mode)#228
iamEvanYT wants to merge 13 commits intomainfrom
opencode/add-ripple-ai-agent

Conversation

@iamEvanYT
Copy link
Member

Summary

Adds Ripple, an opt-in AI browsing agent powered by the OpenCode SDK, with two modes:

  • Browse Mode — A second sidebar (always opposite the main sidebar) with a per-tab chat interface. The agent interacts with the current website via MCP browser tools (page content, navigation, clicking, typing, JS evaluation, screenshots, etc.). No filesystem access by default.
  • Work Mode — A dedicated page at flow://ripple with a full-page chat interface for working on the user's desktop and filesystem.

Key Design Decisions

  • Fully opt-in: A rippleEnabled boolean setting (default: false) gates everything — sidebar, menu items, keyboard shortcuts, and the Work Mode page. Nothing about Ripple is visible when disabled.
  • No bundled binary: Users must have opencode installed on their system. Flow detects and starts it.
  • MCP browser tools: 12 tools running as an MCP server in the Electron main process with direct webContents access.
  • Dual-sidebar layout: Extended the single-sidebar system to support two independent sidebars with proper bounds computation, resize handles, and animation sync.

Changes

Dependencies

  • Added @opencode-ai/sdk and @modelcontextprotocol/sdk

Shared Types

  • FlowRippleAPI IPC interface with full session/message/event API
  • Extended PageLayoutParams with ripple sidebar fields

Main Process

  • MCP Browser Server — 12 browser tools (get_page_content, navigate, click_element, type_text, scroll_page, evaluate_js, screenshot, etc.)
  • RippleService — Singleton managing OpenCode SDK lifecycle, sessions, prompt streaming, events
  • IPC handlers — Full request/response and event push support
  • Dual-sidebar bounds — Updated recomputePageBounds() with independent rippleSidebarInterpolation
  • Keyboard shortcutCmd/Ctrl+Shift+. to toggle Ripple sidebar
  • Menu item — Conditionally visible based on rippleEnabled setting

Renderer

  • RippleSidebarProvider — Independent React context with isEnabled gating
  • RippleSidebar — Attached panel component with resize support
  • Chat UI — Message bubbles, tool call display, chat input, settings panel, session list
  • 5-slot layout — Ripple left, main sidebar left, content, main sidebar right, Ripple right
  • Work Mode page — Full-page chat at flow://ripple with collapsible session sidebar

Settings

  • rippleEnabled boolean setting (default: false)
  • Ripple settings section with enable toggle and getting-started info
  • Menu rebuilds on setting change via settingsEmitter

Preload

  • flow.ripple.* API with "app" permission level

Validation

  • bun typecheck — passes clean
  • bun lint — passes clean
  • bun format — all files unchanged

Add the Ripple design document (design/RIPPLE.md) which serves as the
source of truth for the AI browsing agent feature. Install @opencode-ai/sdk
and @modelcontextprotocol/sdk as dependencies.
…xtension

- Create FlowRippleAPI interface with methods for initialize, sessions,
  messages, prompts, abort, status, and event subscriptions
- Define RippleEvent, RippleMessageInfo, RippleMessagePart, RippleSessionInfo,
  RippleStatus, and RippleMode types
- Extend PageLayoutParams with rippleSidebarWidth, rippleSidebarVisible,
  and rippleSidebarAnimating fields for dual-sidebar layout
- Add ripple: FlowRippleAPI to the global flow type declaration
- Create MCP browser server with 12 browser tools (get_page_content,
  navigate, click_element, type_text, scroll_page, evaluate_js,
  screenshot, get_page_links, get_page_inputs, etc.) that interact
  with tab webContents via Electron APIs
- Create RippleService singleton that manages the OpenCode SDK lifecycle,
  session creation/management, prompt streaming, and event dispatching
- Support both Browse Mode (per-tab sessions) and Work Mode sessions
- Register IPC handlers for all Ripple operations (initialize, sessions,
  messages, prompts, abort, status, events, toggle sidebar)
- Add flow.ripple.* preload API with wrapAPI(rippleAPI, "app") permission
- Import ripple IPC module in ipc/index.ts
… and menu item

- Update recomputePageBounds() to compute leftWidth/rightWidth from both
  sidebars based on which side the main sidebar is on (Ripple always opposite)
- Add independent rippleSidebarInterpolation instance for smooth animation
- Register ripple.toggleSidebar shortcut (CommandOrControl+Shift+.)
- Add 'Toggle Ripple Sidebar' menu item gated on rippleEnabled setting
- Subscribe AppMenuController to settingsEmitter to rebuild menu on changes
- Create RippleSidebarProvider with independent React context, isEnabled
  gating, animation management, and resize persistence
- Create RippleSidebar attached panel component with PixelBasedResizablePanel
- Build chat UI: RippleSidebarInner orchestrator, MessageBubble, ToolCall,
  ChatMessages, ChatInput, SettingsPanel, and SessionList components
- Sidebar is always on the opposite side of the main sidebar
- One session per tab model for Browse Mode
- Add 5-slot ResizablePanelGroup layout (Ripple left, main sidebar left,
  content, main sidebar right, Ripple right) in main.tsx
- Create RippleSidebarGate wrapper to read rippleEnabled setting and pass
  isEnabled prop to RippleSidebarProvider
- Gate PresenceRippleSidebar rendering on isEnabled
- Send ripple sidebar params (width, visible, animating) in
  BrowserContent's setLayoutParams call
- Create route config with dark theme provider
- Build full-page Work Mode chat UI with collapsible session sidebar,
  auto-session creation, event streaming, and message rendering
- Add RippleGate wrapper that checks rippleEnabled setting and shows
  a disabled message when Ripple is not enabled
- Register flow://ripple static domain in config.ts
- Add rippleEnabled boolean setting (default: false) to BasicSettings
- Create Ripple settings section with enable toggle and getting-started
  info (prerequisites, Browse Mode shortcut, Work Mode URL)
- Add Ripple section to settings-layout with SparklesIcon and nav entry
- Ripple is fully opt-in: nothing visible when disabled
@github-actions
Copy link
Contributor

github-actions bot commented Mar 3, 2026

Build artifacts for all platforms are ready! 🚀

Download the artifacts for:

One-line installer (Unstable):
bunx flow-debug-build --open 22649620553

(execution 22649620553 / attempt 1)

…n main process

The SDK only exports ESM ("import" condition, no "require"), but
electron-vite externalizes dependencies by default, causing Node's CJS
resolver to fail with ERR_PACKAGE_PATH_NOT_EXPORTED at runtime. Adding
the SDK to the externalizeDeps exclude list tells Vite to bundle it
into the main process output instead of requiring it at runtime.
…lify IPC

Move session management and messaging from main process IPC to direct
SDK client usage in the renderer. The main process now only handles
server lifecycle (start with port:0 for random port, status, URL).

- Add ripple-client.ts with singleton SDK client, model listing, and
  message conversion utilities
- Add ModelPicker component for selecting from connected providers
- Rewrite browse mode sidebar (inner.tsx) to use SDK client directly
  with per-tab session tracking via React refs
- Rewrite work mode page (page.tsx) with SDK client, session list,
  and model picker in header
- Remove settings-panel.tsx (fs-access toggle was non-functional)
- Simplify FlowRippleAPI to 4 methods: initialize, getStatus,
  getServerUrl, toggleSidebar
… parts

- Fix convertSdkPart() to return null for non-displayable part types
  (reasoning, step-finish, snapshot, patch, agent, retry, compaction,
  file, subtask) instead of dumping raw JSON into chat bubbles
- Switch from blocking session.prompt() to promptAsync() + event.subscribe()
  SSE pattern for real-time streaming in both Browse and Work modes
- Add Ripple system prompts so the agent identifies as 'Ripple' instead
  of 'opencode', with mode-appropriate behavior guidelines
…nt tool restriction

- Rewrite MCP browser server HTTP handler to use stateless mode: each
  POST creates a fresh McpServer + StreamableHTTPServerTransport, fixing
  the 404 issue caused by calling mcpServer.connect() on every request
- Handle GET/DELETE with proper 405 responses (not applicable in stateless mode)
- Pass parsed body as third arg to transport.handleRequest() instead of
  Object.assign hack
- Configure 'browse' and 'work' custom agents in OpenCode server config:
  browse agent denies edit/bash/webfetch/external_directory permissions,
  work agent allows all
- Pass agent: 'browse' in Browse mode promptAsync calls
- Pass agent: 'work' in Work mode promptAsync calls
@iamEvanYT iamEvanYT force-pushed the main branch 2 times, most recently from ce3f35d to b2cbe93 Compare March 22, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant