Screen context, actions and components
Tell the agent what the user can see, let it act in your app through handlers you write, check which actions work, mask your own formats, and show the components it draws — lists, plans, slots, choices — on every platform.
Three things make the agent part of your app rather than beside it: it knows the screen and its fields (screen context), it can do what you allow (actions: named handlers your app writes), and it can draw components — a list, a plan, time slots, a choice — whose tap is the user's next turn.
What it needs in the dashboard
| Where | Setting | Value |
|---|---|---|
| Embed & Widgets → Actions | Each action the agent may call: its name, what it does, its arguments | Declared and saved (an import that is not saved does nothing). The name must match your handler's |
| Agent → Web components | Each component: template, content, Tool name | Built once |
| Embed & Widgets → On screen | The components this agent may draw | Ticked, and named in the prompt |
| Embed & Widgets → Friction | When the agent speaks up for a user who looks stuck | Optional |
What to do, in order
- Report each screen when it appears and whenever a field changes: an id, a title, the fields with their real values (personal numbers are masked on the device before they leave it).
- Register a handler per declared action, once, at launch. → The agent calls it mid-conversation; you do the work and say what actually happened, within six seconds.
- Check, once at startup, which actions work: registered and declared (usable), registered only (undeclared — the agent cannot call it), declared only (unhandled — it will be refused).
- Show the components: the SDK's bar and chat draw them (on iOS, place
GraineWidgetViewwhere they should appear). → A tap answers the agent. - Mask your own formats — a policy number, an internal id — once.
- Clean up: unregister a handler a screen registered, and clear the screen when it goes away.
The code, platform by platform
1. Report the screen:
A labelled control is noticed when tapped: Button("Track") { … }.graineTap("Track order").
2. Register an action — the main thread; return what actually happened:
Work that finishes later: registerAction("upload_document", deferred: { args, done in … done(.ok("Uploaded.")) }).
3. Check:
4. Components: GraineWidgetView() above the bar
(install) draws them and answers the agent.
5. Mask a format of your own:
6. Clean up: stop(); GraineInApp.shared.clearScreen(nil).
Troubleshooting
| What you see | Why | Fix |
|---|---|---|
| The agent says it cannot do something | The action is not declared, not saved, or not registered — or the names differ | checkActions(); declare and save it; register a handler with the same name |
| "It's done", and it is not | The handler returned ok without doing it | Return refused or error with the reason |
| The agent does not know the screen | setScreen not called on that screen, or not on every change | Call it on appear and on each change |
| No component | Not ticked under On screen, not named in the prompt, or (iOS) no GraineWidgetView | Components → Is it actually on the agent? |
In depth: Screen context · Actions · Components · Friction.

