Files
1jehuang--jcode/docs/BROWSER_PROVIDER_PROTOCOL.md
wehub-resource-sync a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

818 lines
17 KiB
Markdown

# Browser Provider Protocol
Status: draft
Owner: jcode
Audience: jcode core, browser bridge authors, adapter authors
## Why this exists
jcode should expose a single first-class `browser` tool while remaining compatible with multiple browser automation backends:
- Firefox Agent Bridge
- Chrome Agent Bridge
- Chrome remote debugging / CDP adapters
- WebDriver / BiDi adapters
- Safari automation adapters
- other third-party browser control systems
The protocol in this document defines the **normalized contract** between jcode and a browser provider.
This is intentionally **not** a demand that every bridge speak exactly the same native command language. Instead:
- jcode defines a **core semantic layer** it can rely on
- providers declare the capabilities and commands they support
- providers may expose **provider-specific commands** beyond the core
- adapters can translate a provider's native model into this protocol
That gives us both consistency and room for bridge-specific power features.
---
## Design goals
1. **One first-class tool in jcode**
- The model should use a single `browser` tool.
2. **Multiple provider implementations**
- Firefox, Chrome, Safari, Edge, WebDriver, and other systems should fit.
3. **Capability negotiation**
- jcode should know what each provider can and cannot do.
4. **Extensibility without fragmentation**
- We need a standard core, but providers must have room for browser-specific features.
5. **Stable session and element references**
- The model should be able to snapshot a page, then act on returned references.
6. **Transport-neutral semantics**
- The semantic protocol should be the same whether the provider is in-process, over stdio, over a socket, or wrapped through another adapter.
---
## Non-goals
1. Standardizing every low-level browser primitive.
2. Forcing all providers to support deep DOM, network, or JS introspection.
3. Requiring all providers to attach to the user's existing browser profile.
4. Making provider-specific commands part of the required core.
---
## Terminology
- **browser tool**: the user/model-facing jcode tool.
- **provider**: a backend implementation that satisfies this protocol.
- **bridge**: an external browser integration such as Firefox Agent Bridge.
- **adapter**: glue code that translates a bridge's native API into this protocol.
- **browser session**: the provider's isolated session or attachment scope for a jcode session.
- **page**: a tab, target, or browsing surface under a session.
- **element ref**: an opaque provider-issued handle for an actionable element.
---
## Conformance model
Providers do not need to implement everything.
### Core required for certification
A provider should support these normalized operations to be considered `certified`:
- `provider.describe`
- `provider.status`
- `session.ensure`
- `session.close`
- `page.open`
- `page.snapshot`
- `page.click`
- `page.type`
- `page.wait`
- `page.screenshot`
### Optional but recommended
- `page.go_back`
- `page.go_forward`
- `page.reload`
- `tab.list`
- `tab.activate`
- `tab.close`
- `page.eval`
- `page.press`
- `page.scroll`
- `page.select`
- `download.list`
### Provider-specific extensions
Providers may expose additional commands such as:
- `firefox.install_extension`
- `chrome.attach_debug_target`
- `cdp.send`
- `webdriver.perform_actions`
These are allowed, but they are not part of the required core.
---
## Transport model
This protocol defines **message semantics**, not one required wire format.
Supported implementation styles may include:
- direct Rust trait calls inside jcode
- stdio JSON request/response
- local socket RPC
- wrapped remote API
For external-process integrations, the recommended envelope is a JSON-RPC-like shape.
---
## Message envelope
For external providers, requests and responses should use a stable envelope.
### Request
```json
{
"protocol_version": "0.1",
"id": "req_123",
"method": "page.open",
"params": {
"session_id": "sess_abc",
"url": "https://example.com"
}
}
```
### Success response
```json
{
"protocol_version": "0.1",
"id": "req_123",
"ok": true,
"result": {
"page_id": "page_1",
"url": "https://example.com",
"title": "Example Domain"
},
"warnings": []
}
```
### Error response
```json
{
"protocol_version": "0.1",
"id": "req_123",
"ok": false,
"error": {
"code": "unsupported_method",
"message": "This provider does not implement page.eval",
"retryable": false,
"details": {}
}
}
```
### Event envelope
If a provider emits async events, use:
```json
{
"protocol_version": "0.1",
"event": "page.navigated",
"payload": {
"session_id": "sess_abc",
"page_id": "page_1",
"url": "https://example.com/next"
}
}
```
Events are optional in v1.
---
## Discovery and handshake
### `provider.describe`
Returns static and semi-static metadata about the provider.
Example:
```json
{
"provider_id": "firefox_agent_bridge",
"provider_label": "Firefox Agent Bridge",
"provider_version": "1.2.3",
"protocol_version": "0.1",
"browser_families": ["firefox"],
"transport": "stdio-json",
"certification_tier": "candidate",
"capabilities": {
"core_methods": [
"session.ensure",
"session.close",
"page.open",
"page.snapshot",
"page.click",
"page.type",
"page.wait",
"page.screenshot"
],
"optional_methods": [
"tab.list",
"tab.activate",
"page.eval"
],
"features": [
"element_refs",
"a11y_snapshot",
"attach_existing_browser",
"persistent_profile"
],
"custom_methods": [
{
"name": "firefox.install_extension",
"stability": "experimental",
"description": "Install or verify the Firefox extension"
}
]
}
}
```
### `provider.status`
Returns current availability and setup state.
Example fields:
```json
{
"availability": "ready",
"browser_detected": true,
"browser_running": true,
"setup_state": "complete",
"requires_manual_setup": false,
"recommended_browser": "firefox",
"manual_steps": [],
"diagnostics": [
{
"level": "info",
"code": "native_host_detected",
"message": "Native host manifest found"
}
]
}
```
Suggested enums:
- `availability`: `ready | degraded | unavailable`
- `setup_state`: `complete | partial | required | broken`
---
## Session model
jcode should not care whether a provider uses tabs, contexts, profiles, or remote targets internally.
It only needs a stable handle it can reuse.
### `session.ensure`
Creates or reuses a browser session for a jcode session.
Request:
```json
{
"client_session_id": "jcode_session_123",
"browser_preference": "auto",
"isolation": "per_jcode_session",
"attach": "prefer",
"persist": true,
"metadata": {
"owner": "agent",
"purpose": "browser_tool"
}
}
```
Response:
```json
{
"session_id": "browser_sess_1",
"browser_family": "firefox",
"browser_label": "Firefox",
"attached_to_existing_browser": true,
"isolation": "per_jcode_session",
"default_page_id": "page_1"
}
```
### `session.close`
Closes or detaches the provider session.
Providers may choose whether this closes tabs, detaches from a target, or merely releases provider-side state. The behavior should be documented in `provider.describe` or `provider.status` diagnostics.
---
## Resource identifiers
All resource identifiers are opaque strings.
Examples:
- `session_id`
- `page_id`
- `tab_id`
- `element_ref`
- `download_id`
jcode must not assume identifier shape or encode browser semantics into them.
---
## Normalized core methods
These are the semantics jcode can rely on.
### `page.open`
Open a URL in the current page or a new page.
Request fields:
- `session_id` required
- `url` required
- `page_id` optional
- `new_page` optional
- `foreground` optional
- `wait_until` optional: `load | domcontentloaded | networkidle | provider_default`
Response fields:
- `page_id`
- `url`
- `title` optional
- `navigation_state` optional
### `page.snapshot`
Return a normalized view of the current page for agent reasoning.
This is the most important method for model use.
Request fields:
- `session_id` required
- `page_id` optional
- `include_screenshot` optional
- `include_html` optional
- `include_dom` optional
- `include_a11y` optional
- `include_text` optional
- `max_nodes` optional
Response fields:
- `page_id`
- `url`
- `title`
- `snapshot`
- `elements`
- `text`
- `screenshot_ref` optional
- `provider_data` optional
#### Snapshot shape
Providers may use different internal representations, but `page.snapshot` should normalize into a common minimum format:
```json
{
"snapshot": {
"format": "jcode.page_snapshot.v1",
"root": {
"node_id": "n1",
"role": "document",
"name": "Example Domain",
"children": ["n2", "n3"]
},
"nodes": [
{
"node_id": "n2",
"role": "heading",
"name": "Example Domain",
"text": "Example Domain",
"element_ref": "el_1",
"actionable": false
},
{
"node_id": "n3",
"role": "link",
"name": "More information...",
"text": "More information...",
"element_ref": "el_2",
"actionable": true
}
]
}
}
```
#### Element list
For agent convenience, providers should also return a flattened actionable list when possible:
```json
{
"elements": [
{
"element_ref": "el_2",
"role": "link",
"name": "More information...",
"text": "More information...",
"actionable": true,
"enabled": true,
"selector_hint": "a"
}
]
}
```
A provider that cannot produce rich DOM/a11y data may still return a weaker snapshot, but it should say so in capabilities.
### `page.click`
Click an element.
Request should support multiple targeting modes:
- `element_ref`
- `selector`
- `text_query`
- `position`
At least one must be provided.
Response fields:
- `page_id`
- `clicked` boolean
- `navigation_occurred` optional
- `url` optional
Providers should prefer `element_ref` when available.
### `page.type`
Type or set text into an input-like target.
Request fields:
- `element_ref` optional
- `selector` optional
- `text` required
- `replace` optional
- `submit` optional
Response fields:
- `page_id`
- `typed` boolean
### `page.wait`
Wait for a condition.
Request fields may include:
- `text_present`
- `text_absent`
- `selector_present`
- `selector_absent`
- `element_ref_present`
- `url_matches`
- `navigation_complete`
- `timeout_ms`
Response fields:
- `satisfied` boolean
- `matched_condition` optional
- `url` optional
### `page.screenshot`
Capture a screenshot.
Request fields:
- `session_id`
- `page_id` optional
- `full_page` optional
- `clip` optional
- `element_ref` optional
Response fields:
- `page_id`
- `image` or `image_ref`
- `media_type`
- `width`
- `height`
Providers may return inline base64 data or a provider-managed image reference depending on transport constraints.
---
## Optional normalized methods
These methods are standardized when present, but not required for certification in the first pass.
### Navigation
- `page.go_back`
- `page.go_forward`
- `page.reload`
### Keyboard and form interaction
- `page.press`
- `page.select`
- `page.hover`
- `page.scroll`
### Tabs and pages
- `tab.list`
- `tab.activate`
- `tab.close`
- `tab.new`
### Introspection and debugging
- `page.eval`
- `network.list`
- `console.list`
- `storage.get`
- `cookie.list`
### Files and downloads
- `download.list`
- `download.wait`
- `upload.set_files`
---
## Extensibility model
This is the key part that allows leeway for provider-specific commands.
### Rule 1: providers may expose custom methods
Custom methods should use a namespaced method name, for example:
- `firefox.install_extension`
- `chrome.attach_debug_target`
- `cdp.send`
- `webdriver.actions`
### Rule 2: providers must advertise custom methods
Every custom method should appear in `provider.describe.capabilities.custom_methods` with:
- `name`
- `description`
- `stability`: `stable | experimental | deprecated`
- optional `input_schema`
- optional `output_schema`
### Rule 3: jcode core should only rely on normalized methods by default
The main `browser` tool should prefer the standard core and optional normalized methods.
Provider-specific methods should only be used when:
- the user explicitly asks for them
- a jcode-side adapter knows how to use them safely
- or a future advanced/debug mode is enabled
### Rule 4: provider-native passthrough is allowed, but should be explicit
If we want an escape hatch, the browser tool can support something like:
```json
{
"action": "provider_command",
"provider_method": "cdp.send",
"params": {
"method": "Network.enable"
}
}
```
This should be considered advanced/debug behavior, not the primary UX.
---
## Capability schema
Providers should report both methods and higher-level features.
### Methods
Concrete callable operations:
- `page.open`
- `page.snapshot`
- `tab.list`
### Features
Semantics or qualities that influence jcode behavior:
- `element_refs`
- `a11y_snapshot`
- `dom_snapshot`
- `html_snapshot`
- `full_page_screenshot`
- `attach_existing_browser`
- `persistent_profile`
- `isolated_contexts`
- `js_eval`
- `network_observe`
- `console_observe`
- `file_upload`
- `download_observe`
- `manual_setup_required`
- `extension_required`
- `remote_debugging_required`
### Stability
Each feature or method may optionally include a stability tag:
- `stable`
- `experimental`
- `deprecated`
---
## Setup and diagnostics
A browser provider often requires manual setup. The protocol should make that machine-readable.
### Diagnostic item
```json
{
"level": "warning",
"code": "extension_missing",
"message": "Firefox extension is not installed",
"manual_steps": [
"Open Firefox",
"Install the extension from /path/to/bridge.xpi",
"Restart Firefox if prompted"
]
}
```
### Recommended setup-oriented methods
- `provider.status`
- `provider.setup_guide` optional
- `provider.verify` optional
`provider.setup_guide` may return browser-specific instructions, URLs, file paths, permissions, or troubleshooting steps.
---
## Error model
Standard error codes should include:
- `unsupported_method`
- `unsupported_target`
- `invalid_request`
- `invalid_selector`
- `element_not_found`
- `element_not_actionable`
- `navigation_timeout`
- `not_ready`
- `setup_required`
- `permission_denied`
- `browser_not_running`
- `session_not_found`
- `page_not_found`
- `internal_error`
Providers may add provider-specific detail codes in `error.details`.
---
## Versioning
The protocol should be versioned independently from provider versions.
### Rules
- `protocol_version` identifies the semantic protocol version.
- Providers should declare the protocol version they implement.
- Minor additive changes should not break existing certified providers.
- Breaking changes require a new protocol version.
For now use:
- `protocol_version = "0.1"`
---
## Certification guidance
A provider can be classified as:
### Certified
- passes conformance tests for required core methods
- returns stable identifiers and normalized results
- reports setup/diagnostics correctly
- behaves predictably across repeated runs
### Compatible
- supports some or most normalized methods
- may have missing features or partial behavior
- useful, but not yet fully certified
### Experimental
- adapter exists, but semantics are incomplete or unstable
---
## Minimal conformance scenarios
A future conformance suite should verify at least:
1. `provider.describe` succeeds
2. `provider.status` reports a coherent state
3. `session.ensure` creates or reuses a session
4. `page.open` navigates to a test page
5. `page.snapshot` returns usable text and at least one actionable reference when applicable
6. `page.click` can activate a known element
7. `page.type` can fill a known input
8. `page.wait` observes a deterministic page change
9. `page.screenshot` returns an image
10. `session.close` cleans up or detaches cleanly
---
## Recommended jcode integration policy
The jcode `browser` tool should:
1. prefer normalized core methods
2. choose a provider based on user preference, availability, and capability quality
3. expose provider-specific methods only behind an explicit advanced path
4. return setup guidance when no ready provider is available
5. avoid baking Firefox-specific or Chrome-specific assumptions into the core tool API
---
## Open questions
These are intentionally left open for the next iteration.
1. Should screenshots always be inline, or can providers return file/image handles?
2. Should event streaming be required for advanced integrations?
3. How much of raw HTML/DOM should be normalized versus returned as provider data?
4. Should `page.snapshot` support multiple named formats beyond `jcode.page_snapshot.v1`?
5. Should provider-specific methods be invokable through the same `browser` tool or only via debug mode?
6. Should setup/install flows themselves be standardized beyond status and diagnostics?
---
## Proposed next steps
1. Review this document and tighten the core method set.
2. Decide the exact normalized `page.snapshot` format.
3. Define a Rust trait matching this protocol.
4. Implement the first provider adapter for Firefox Agent Bridge.
5. Build a conformance test harness.
6. Add README browser setup and compatibility documentation after the protocol stabilizes.