Skip to content

feat(widget): add opt-in built-in DOM page actions - #23

Merged
mrgoonie merged 1 commit into
mainfrom
feat/dom-actions
Nov 1, 2026
Merged

mrgoonie merged 1 commit into
mainfrom
feat/dom-actions

Conversation

@mrgoonie

@mrgoonie mrgoonie commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Why

The agent could navigate a host page but could not scroll, click or type. The widget ships no DOM actions: it only runs the actions a host registers in JS, and the examples register just navigate. Calls to scroll/click/fill came back as Unknown action "click". Available: navigate, and CDN-only hosts (data-* attributes) could not register any action at all.

What

Opt-in built-in page actions, off by default:

  • Enable with domActions (JS, React, Vue) or data-dom-actions="true" on the CDN script; domActionsRoot / data-dom-actions-root limits where the agent may act.
  • page.snapshot (read): visible links, buttons and fields as e3 textbox "Email" value="…" lines, with a generation; supports query, offset, limit.
  • page.scroll (read): one screen up/down, top/bottom, or a ref into view.
  • page.click, page.fill (mutate): always need the visitor's approval; the dialog (and a host confirm hook, as args.target) names the element, e.g. button "Increment".
  • Refs are valid only with the latest snapshot's generation; stale, removed, hidden or now-sensitive targets are refused.
  • Never listed or touched: password, card and one-time-code fields (type, autocomplete, name hints), hidden elements, [data-dewee-ignore] subtrees and the widget itself.
  • page.fill sets the value through the prototype setter and fires input + change, so React/Vue controlled inputs update; handles textarea, select (value or label) and contenteditable.
  • Host actions keep working; a host action with a built-in name wins. The combined manifest stays within 32 actions.
  • No backend, PHP or gateway change: the built-ins are ordinary manifest entries.

Docs: README "Built-in DOM actions" section and options table, docs/http-contract.md table of names, args and results.

Verification

  • pnpm -r test: 18 files passed, 253 tests (PHP parity suites skipped as before, no PHP locally). New test/dom-actions.test.ts (11 tests) and 3 widget cases in test/widget-page-actions.test.ts.
  • pnpm typecheck, pnpm -r build (all examples), pnpm test:bundlers: pass.
  • Live in Chromium: a static page loading only the built IIFE with data-dom-actions="true", a React 18 controlled input and a stubbed backend. Snapshot skipped the password field; scroll reached the bottom; Allow on the real dialog ran the click (counter 0 → 1) and the fill (React state became "Dewee", onChange fired); results came back status: ok.

Not verified: a real agent on the gateway choosing these actions from the manifest, and the react-vite example against a live backend.

🤖 Generated with Claude Code

Pages can let the agent operate their DOM without writing handlers. With
domActions (or data-dom-actions="true" on the CDN script) the widget offers
page.snapshot, page.scroll, page.click and page.fill. Snapshot refs are tied
to a generation and stale refs are refused; password, card and one-time-code
fields, hidden elements, [data-dewee-ignore] and the widget are never listed
or touched. click and fill always need the visitor's approval, and the dialog
names the target element. fill uses the native value setter so React and Vue
controlled inputs see the change. Host actions with the same name win, and
domActionsRoot limits where the agent may act.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mrgoonie
mrgoonie merged commit a0097fa into main Nov 1, 2026
2 checks passed
@mrgoonie mrgoonie mentioned this pull request Nov 1, 2026
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