AWP - Agent Web Protocol (Draft Specification v0.1)

Project: Plasmate

Abstract

The Agent Web Protocol (AWP) is a purpose-built, intent-forward protocol for controlling a headless browser engine in AI agent workloads.

AWP is designed as a replacement for the Chrome DevTools Protocol (CDP) in agentic environments. CDP is a debugging interface optimized for human developers and pixel-oriented automation. AWP is optimized for:

AWP is transport-agnostic but specified primarily over WebSocket. Payload encoding is binary (MessagePack) by default.

Status of This Document

This is a draft for discussion and implementation planning. It is intentionally exhaustive so an agentic engineering team can implement:

  1. A minimal AWP server
  2. A minimal AWP client SDK
  3. A Rust proof of concept focused on SOM generation and basic action execution

Table of Contents

  1. Goals and Non-Goals
  2. Terminology
  3. Architecture Overview
  4. Versioning and Capability Negotiation
  5. Transport
  6. Encoding
  7. Message Model
  8. Error Model
  9. Authentication and Authorization
  10. Resources and Identifiers
  11. Sessions
  12. Page Lifecycle
  13. Observation: SOM Snapshots and Mutations
  14. Actions: Intent-Based Interaction
  15. Data Extraction
  16. Network Controls: Proxy, Profiles, Stealth
  17. State: Cookies, Storage, Downloads
  18. Skills: WebAssembly Extensions
  19. Telemetry and Auditing
  20. Determinism and Reproducibility
  21. Examples
  22. Compatibility and Migration from CDP
  23. Implementation Notes for a Rust PoC

Goals and Non-Goals

Goals

  1. Intent-first: agents issue semantic actions, not pixels.
  2. Token efficiency: primary observation output is the Semantic Object Model (SOM), not screenshots.
  3. Deterministic references: stable element addressing across SPA mutations.
  4. Massive concurrency: protocol supports thousands of sessions with strict resource limits.
  5. Robustness: sessions survive reconnects, and operations are idempotent when practical.
  6. Extensibility: site-specific logic is implemented as Wasm skills, not baked into the engine.
  7. Observability: first-class telemetry for latency, errors, retries, costs, and provenance.
  8. Security: clear trust boundaries and permission model for high-risk operations.

Non-Goals

  1. AWP does not define a UI, rendering pipeline, or pixel output requirements.
  2. AWP does not define scraping legality or usage policy.
  3. AWP does not mandate a specific SOM schema beyond normative minimums.
  4. AWP does not prescribe a particular LLM, agent framework, or planning strategy.

Terminology


Architecture Overview

AWP is designed around a simple loop:

  1. Agent requests observation (page.observe) and receives a SOM snapshot or mutations.
  2. Agent decides action and sends intent (page.act) targeting SOM elements.
  3. Engine executes using primitives or skills.
  4. Engine returns structured results and emits events.

AWP explicitly separates:


Versioning and Capability Negotiation

Protocol Version

AWP version is a semantic version string: MAJOR.MINOR.

Handshake

Upon WebSocket connection, the client MUST send awp.hello.

Request

{
  "id": "1",
  "type": "request",
  "method": "awp.hello",
  "params": {
    "client": {
      "name": "plasmate-python",
      "version": "0.1.0"
    },
    "awp": {
      "versions": ["0.1"]
    },
    "wants": {
      "encoding": ["msgpack", "json"],
      "compression": ["none", "zstd"],
      "features": [
        "som.snapshots",
        "som.mutations",
        "intents.level2",
        "skills.invoke",
        "sessions.persist"
      ]
    }
  }
}

Response

{
  "id": "1",
  "type": "response",
  "result": {
    "awp": {"version": "0.1"},
    "encoding": {"selected": "msgpack"},
    "compression": {"selected": "none"},
    "features": {
      "som": {
        "snapshot": true,
        "mutation": true,
        "formats": ["som+json", "som+msgpack"],
        "max_bytes": 1048576
      },
      "skills": {
        "invoke": true,
        "registry": true
      },
      "network": {
        "proxy": true,
        "profiles": ["chrome128", "firefox128", "safari17"]
      },
      "limits": {
        "max_sessions": 500,
        "max_pages_per_session": 16
      }
    },
    "server": {
      "name": "plasmate-engine",
      "version": "0.1.0",
      "build": "dev"
    }
  }
}

Capability Rules


Transport

WebSocket

Message Ordering

Heartbeats


Encoding

Default Encoding

Compression

Canonical JSON

When JSON is used, keys MUST be lower_snake_case.


Message Model

Envelope

Every message uses this envelope:

{
  "id": "string-optional",
  "type": "request|response|event",
  "method": "string-optional",
  "params": {"...": "..."},
  "result": {"...": "..."},
  "error": {"code": "...", "message": "...", "details": {}}
}

Rules:

Idempotency

Requests MAY include meta.idempotency_key.

{
  "meta": {
    "idempotency_key": "uuid",
    "trace_id": "uuid"
  }
}

If provided, the server SHOULD guarantee that repeating the request yields the same effect (when feasible).

Tracing

All responses and events SHOULD include meta.trace_id and meta.span_id.


Error Model

Standard Error Codes

Code Meaning
INVALID_REQUEST Malformed message or missing fields
UNSUPPORTED Feature or method not supported
NOT_FOUND Session/page/element not found
TIMEOUT Operation timed out
CONFLICT Concurrent state conflict
RATE_LIMITED Server rate limit
PERMISSION_DENIED Action not permitted
NAVIGATION_FAILED Failed to load page
SCRIPT_ERROR JS evaluation error
SKILL_ERROR Skill execution failed
INTERNAL Unexpected error

Error Object

{
  "code": "TIMEOUT",
  "message": "wait_for condition not met",
  "details": {
    "timeout_ms": 10000,
    "method": "page.wait_for"
  }
}

Authentication and Authorization

AWP supports several deployment modes:

  1. Local engine: no auth required.
  2. LAN engine: shared secret.
  3. Cloud engine: API key + per-tenant permissions.

Auth Methods

Permissions

Actions are permissioned because some operations are dangerous:

Permissions are expressed as scopes:

The server MAY enforce per-domain allowlists.


Resources and Identifiers

AWP uses stable identifiers:

Element References

AWP prefers stable element IDs from the SOM.

A target is specified as one of:

  1. element_ref: direct reference by SOM ID.
  2. query: semantic query.
  3. fallback: list of fallbacks.

Example

{
  "target": {
    "element_ref": "e:9f2c...",
    "fallback": [
      {"query": {"text": "Add to Cart", "role": "button"}},
      {"query": {"css": "button[data-testid='add-to-cart']"}}
    ]
  }
}

Sessions

session.create

Creates a browsing session.

{
  "id": "10",
  "type": "request",
  "method": "session.create",
  "params": {
    "persist": false,
    "ttl_ms": 3600000,
    "profile": {
      "impersonation": "chrome128",
      "locale": "en-US",
      "timezone": "America/New_York",
      "viewport": {"width": 1280, "height": 720}
    },
    "network": {
      "proxy": {"mode": "none"}
    },
    "limits": {
      "max_pages": 8,
      "max_js_heap_mb": 64
    }
  }
}

Response includes session_id.

session.close

Closes and optionally persists state.

session.export / session.import

Optional methods for portable session snapshots.


Page Lifecycle

page.create

Creates a page (tab) inside a session.

{
  "id": "20",
  "type": "request",
  "method": "page.navigate",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "url": "https://example.com",
    "wait": {"until": "network_idle", "timeout_ms": 15000}
  }
}

Wait strategies:

page.close


Observation: SOM Snapshots and Mutations

page.observe

Returns either:

Snapshot request

{
  "id": "30",
  "type": "request",
  "method": "page.observe",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "mode": "snapshot",
    "format": "som+json",
    "max_bytes": 262144
  }
}

Mutation request

{
  "id": "31",
  "type": "request",
  "method": "page.observe",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "mode": "mutations",
    "cursor": "c:12345",
    "max_events": 100
  }
}

SOM Minimum Requirements

A SOM snapshot MUST include:

page.som_mutation Event

{
  "type": "event",
  "method": "page.som_mutation",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "cursor": "c:12346",
    "patch": [
      {"op": "replace", "path": "/regions/0/items/2/badge", "value": "4"}
    ]
  }
}

Actions: Intent-Based Interaction

page.act

This is the central operation.

{
  "id": "40",
  "type": "request",
  "method": "page.act",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "intent": {
      "action": "click",
      "target": {"element_ref": "e:9f2c"}
    },
    "wait": {"until": "som_stable", "timeout_ms": 10000}
  }
}

Intent Object

intent has:

Examples:

Intent Levels

AWP reserves namespaces:

Examples:

Servers MAY accept un-namespaced action strings for convenience, but MUST normalize internally.

Result Object

{
  "status": "ok",
  "effects": {
    "navigation": true,
    "som_changed": true
  },
  "target_resolved": {
    "element_id": "e:9f2c",
    "confidence": 0.98
  },
  "artifacts": {
    "som_cursor": "c:12350"
  }
}

Data Extraction

page.extract

Extracts structured data based on selectors or semantic queries.

{
  "id": "50",
  "type": "request",
  "method": "page.extract",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "schema": {
      "type": "object",
      "properties": {
        "price": {"type": "number"},
        "title": {"type": "string"}
      },
      "required": ["price", "title"]
    },
    "queries": {
      "title": {"query": {"role": "heading", "level": 1}},
      "price": {"query": {"text_regex": "\\$[0-9]+\\.[0-9]{2}"}}
    }
  }
}

Response returns JSON data and provenance (element IDs used).


Network Controls: Proxy, Profiles, Stealth

session.set_network

Allows selecting proxy mode and impersonation profile.

{
  "id": "60",
  "type": "request",
  "method": "session.set_network",
  "params": {
    "session_id": "s:1",
    "proxy": {"mode": "sticky_per_domain", "pool": "residential-us"},
    "impersonation": "chrome128"
  }
}

Security: server SHOULD require network:configure scope.


State: Cookies, Storage, Downloads

session.cookies

Methods:

page.downloads

Methods:


Skills: WebAssembly Extensions

skills.list

Returns installed skills and provided actions.

skills.invoke

{
  "id": "70",
  "type": "request",
  "method": "skills.invoke",
  "params": {
    "session_id": "s:1",
    "page_id": "p:1",
    "skill": "stripe-checkout",
    "action": "stripe_pay",
    "args": {
      "card_number": "4242424242424242",
      "expiry": "12/30",
      "cvc": "123"
    }
  }
}

Security: server MUST treat skill invocation as privileged and permissioned.


Telemetry and Auditing

page.telemetry Event

Servers SHOULD emit structured telemetry:

Telemetry MUST NOT include secrets (passwords, card numbers).


Determinism and Reproducibility

AWP encourages reproducibility:

A minimal reproducible trace should allow replaying:


Examples

Example 1: Search Workflow

  1. awp.hello
  2. session.create
  3. page.create
  4. page.navigate to search engine
  5. page.observe snapshot
  6. page.act type into search input
  7. page.act submit
  8. page.observe snapshot of results
  9. page.extract titles and links

Example 2: Checkout Workflow (Skill + Fallback)

  1. Observe page
  2. If skills.list shows stripe-checkout, call skills.invoke
  3. Else fallback to page.act fill_form + submit

Compatibility and Migration from CDP

Migration strategy:


Implementation Notes for a Rust PoC

A minimal Rust PoC can implement:

And defer:

This PoC proves the thesis: SOM is token-efficient and deterministic.


End of AWP Draft v0.1