Skip to content

Action Protocol ​

The action protocol is the contract between Zo and the extension: Zo returns a JSON array of actions, and the extension executes them in the DOM. The canonical shapes below are validated against the Zod schema in tests/schemas/actions.ts — the single source of truth.

Action types ​

Seven action types, discriminated by type:

Go to a URL.

json
{ "type": "navigate", "url": "https://example.com" }
FieldTypeRequiredNotes
type"navigate"✅
urlstring (valid URL)✅

click ​

Click an element identified by a CSS selector.

json
{ "type": "click", "selector": "#pricing" }
FieldTypeRequired
type"click"✅
selectorstring (min 1 char)✅

fill ​

Fill an editable field with a value.

json
{ "type": "fill", "selector": "input[name=email]", "value": "user@example.com" }
FieldTypeRequired
type"fill"✅
selectorstring (min 1 char)✅
valuestring✅

The content script sets the value and dispatches both input and change events so the page's listeners fire.

extract ​

Extract data from an element.

json
{ "type": "extract", "selector": "body", "attribute": "textContent" }
FieldTypeRequiredNotes
type"extract"✅
selectorstring✅
attributestring❌If omitted, returns the element's textContent

scroll ​

Scroll the page.

json
{ "type": "scroll", "direction": "down", "amount": 400 }
FieldTypeRequiredNotes
type"scroll"✅
direction"up" | "down"❌Defaults to down
selectorstring❌
amountnumber❌Defaults to ~70% of viewport height

wait ​

Pause for a number of milliseconds (e.g. after a click that loads content).

json
{ "type": "wait", "ms": 1500 }
FieldTypeRequiredNotes
type"wait"✅
msinteger ≥ 0❌Defaults to 1000

done ​

Terminal action — signals the task is finished, carrying the final answer.

json
{ "type": "done", "response": "The form was submitted successfully." }
FieldTypeRequired
type"done"✅
responsestring✅

The done.response becomes the assistant's final prose.

The compact action schema ​

When a mode requests JSON (expectJson: true — only Co-browse), the prompt ships this compact schema so Zo knows exactly what to emit:

Respond with JSON {"actions":[...]}. Actions: click{selector} | fill{selector,value} |
extract{selector,attribute} | navigate{url} | scroll{direction,amount?} |
wait{ms} | done{response}.

Normalization ​

Zo sometimes emits actions in a non-canonical shape. normalizeActions() (in extension/lib/modes.js) converts all three to the canonical type-first form:

Input shapeExampleResult
Type-first (canonical){ type: 'extract', selector: 'body', attribute: 'textContent' }Passes through unchanged
Key-first (Zo variant){ extract: { selector: 'body', attribute: 'textContent' } }{ type: 'extract', ... }
action variant{ action: 'click', ... }{ type: 'click', ... }

Without normalization these variants would silently drop out of every consumer (a.type === 'done', executeActions, the timeline) and the raw {actions:[...]} blob would leak into the chat. Non-conforming entries are dropped rather than risk raw JSON rendering.

Execution ​

Actions execute either through:

  1. Content script (extension/content.js → executeDomAction), via chrome.tabs.sendMessage(..., EXECUTE_ACTION) — the preferred path. click and fill use waitForElement() (a MutationObserver with a 5s timeout) so they work on elements that appear after the page settles.
  2. chrome.scripting.executeScript — fallback when the content script isn't loaded yet (fresh tabs, document_idle injection not complete).

Actions auto-run as a batch when Zo returns them (pendingActions in the side panel), with a per-action status timeline — pending → running → done.

Released under the MIT License.