How Friday works
Actions
When Friday clicks a button or fills a form for a visitor, that's an action. Actions go through a fixed lifecycle so you (the site owner) can see, audit, and gate every one of them.
Lifecycle
- Scan. The visitor sends a message. The widget's scanner snapshots the page and emits an action manifest — a list of things the page lets you do.
- Server picks tools. The chat server sends the message + manifest + vocabulary + your action policy to the LLM. The model can either reply with text, or call a tool —
navigate,click,scroll_to,fill_form,open_link. - SSE tool_call. The server streams a
tool_callevent back to the widget over SSE. The payload contains the kind, the target selector, the values to fill, and whether confirmation is required. - Frame confirm bubble (if required). If the action needs confirmation (per your action policy), the widget renders a small bubble next to the target showing what's about to happen. The visitor clicks "Do it" or "Cancel."
- Launcher executes. The actions runtime resolves the selector, dispatches the right DOM events, waits for the page to settle, then reports back. Success or failure is streamed back to the server (and logged to your audit log).
The five tool kinds
navigate— go to a path on the current site. Uses the History API where possible so SPAs don't do a hard reload.click— dispatch a realclickevent on the target. Works for buttons, links, tabs, accordion toggles.scroll_to— smooth scroll a section into view.fill_form— set values on a set of fields. Each field is dispatched with bothinputandchangeevents so React/Vue controlled inputs update their state.open_link— open an external URL. New tab unless the visitor explicitly asked otherwise.
Compound actions
Sometimes one visitor request needs more than one step ("fill out the contact form and send it"). The server can stream multiple tool_call events in sequence; the widget executes them one at a time, waiting for each to settle before the next.
Confirmation is per-step. If the form-fill is auto but submit is confirm, the visitor sees one confirm bubble — on the submit, not on every field.
fill_form: the submit flag
fill_form takes an explicit submit: boolean flag. false means just type the values and stop; true means also click the form's submit button afterwards. The policy can require confirm on submit even when fill is auto — that's the recommended default.
{
"tool": "fill_form",
"fields": [
{ "selector": "[name='email']", "value": "ada@example.com" },
{ "selector": "[name='message']", "value": "Hello!" }
],
"submit": true
}Why this design
- Server decides, client executes. The model never directly drives the browser; it emits structured tool calls. That keeps the surface area small and auditable.
- One scanner snapshot per turn. The model only "sees" what the scanner sent. No background DOM reads.
- Selectors are resolved client-side. If a selector goes stale between scan and execute (a re-render), the runtime fails fast and reports back, rather than clicking the wrong thing.