Architecture
WCI separates what the agent sees, what the site allows, and how actions execute into three cooperating layers.
flowchart TB
subgraph html [HTML Layer]
A[data-wci-* attributes]
S[wci.txt / wci.json / wci.md]
end
subgraph distill [Distiller Layer]
P[pruneDOM]
J[JSON serializer]
M[Markdown serializer]
end
subgraph bridge [Bridge Layer]
D[dispatchAction]
B[WciBridge]
R[ActionResult]
end
subgraph ctx [Context Layer]
L[WciContextLoader]
E[PolicyEngine]
end
A --> P
P --> J
P --> M
J --> LLM[LLM / Agent]
M --> LLM
S --> L
L --> E
LLM --> B
B --> D
D --> DOM[Live DOM]
DOM --> R
R --> LLM
E -.->|scope checks| BLayer 1 — Semantic HTML (@webcontextinterface/spec)
Standard DOM nodes carry machine-readable metadata:
- Identity —
data-wci-id - Role —
action,form,display,nav,status,landmark - Intent —
data-wci-desc,data-wci-action - State —
data-wci-state(JSON snapshot) - Guards —
data-wci-precondition,data-wci-required
readWciNodeSpec(element) maps attributes to the WciNodeSpec TypeScript interface.
Layer 2 — Distiller (@webcontextinterface/distiller)
The distiller walks the DOM, collects annotated nodes, sorts by priority, and emits a compact WciView (JSON) or Markdown string suitable for LLM context windows.
Design goals:
- Drop decorative/layout nodes (
data-wci-hidden) - Scope to a landmark (
scopeoption) - Cap node count (
maxNodes) for token budgets - Optionally attach site summary metadata
Layer 3 — Bridge (@webcontextinterface/bridge)
The bridge translates agent decisions into real DOM interactions:
| Action | DOM effect |
|---|---|
click | .click(), updates state |
fill | Native value setter + input/change events |
select | <select>.value + change |
check | checkbox checked + change |
focus | .focus() |
clear | Clears input value |
submit | form.requestSubmit() |
navigate | window.location.href |
Every dispatch returns a typed ActionResult: success flag, before/after state, optional side effects on sibling nodes, and structured errors (NODE_NOT_FOUND, PRECONDITION_UNMET, etc.).
State changes also emit wci:state-change on document for observers.
Site context (@webcontextinterface/context)
Before touching a page, agents can load site policy:
- HTTP headers (
X-WCI-*) - Well-known URIs (
/.well-known/wci/*) - Root fallbacks (
/wci.txt,/wci.json,/wci.md)
PolicyEngine enforces allow/deny scopes, auth requirements, and human-confirmation scopes. Attach it to WciBridge via setPolicy so every dispatch is validated before DOM mutation.
Typical agent loop
WciContextLoader.load()→ system prompt + policyWciDistiller.distilJSON()→ page tool context- LLM chooses
{ nodeId, action, value } WciBridge.dispatch()→ActionResult- Re-distil or read
sideEffects→ next turn
Size and deployment
Packages are tree-shakeable ESM/CJS builds. Use only the layers you need:
- Read-only crawlers:
@webcontextinterface/distiller+@webcontextinterface/spec - In-page agents: add
@webcontextinterface/bridge - Multi-page flows: add
@webcontextinterface/context
