# Give your AI agent a screen it can draw on

Ask a voice assistant "how's revenue this year?" and you get a number read aloud. Ask for twelve months of it and you get a paragraph you can't hold in your head. Some answers are meant to be seen.

Trillion talks, and while she talks she can put things on a board beside the conversation: a revenue card, a chart of the last twelve months, your next meeting, the three emails that need a reply. She says one short line about it and lets you look.

The hard part isn't drawing a chart. It's letting an AI decide what goes on screen without it turning into slop: mismatched cards, invented numbers, emoji confetti, a different layout every time, and a page that breaks the moment the model gets a field name slightly wrong. This prompt is how we solved that.

## What you'll end up with

- **A kit of blocks** (a big number, a chart, a table, rows, a meter, a timeline, a person, chips and so on). The page knows how to draw these and nothing else. Trillion has 19.

- **Ready-made widgets** built from those blocks (a stat, a goal, a chart, an inbox, next up, a task list). The AI picks a widget and fills in its data; the server turns it into blocks. Trillion has 23 in her core set.

- **A design system** with one look, one set of colors and one set of rules: every card has one job, a title that says what it shows, numbers with units and a comparison, and "empty" said plainly instead of filled in.

- **Widget packs** that appear only when the matching app is connected: money widgets once Stripe is in, project widgets once Linear or Jira is. The AI's instructions stay short.

- **Custom widgets** for anything the ready ones don't cover, built from the same blocks, so they look like they belong.

- **Tap-to-ask buttons** on a card ("What changed since last month?"). A tap asks the AI a question. It can never approve anything.

- **Live widgets** (optional, last) that refresh themselves while the board is on screen, by re-running the reads that built them.

- **A style guide page** that draws every widget with sample data, so you can see the whole system at once.

## How to use it

1. Open your agent's project in Claude Code, Cursor, or whatever coding agent you use.

2. Copy everything in the box below and paste it in.

3. Answer its questions. It reads your code first and asks only what it can't find, then builds one layer at a time and shows you each one working before moving on.

It works for text agents and voice agents. If yours speaks, there's one extra rule: say one line about the board, never read it out.

## The prompt

````text

I have an AI agent, and I want it to be able to show things on a screen beside our

conversation: numbers, charts, lists, plans, my next meeting, my inbox. I want the

agent to decide what to show, but I want every card to look like part of one

designed system, never like something the model made up on the spot. I want it to

be safe: nothing on screen can approve an action, and data from outside is never

treated as instructions.

BEFORE YOU WRITE ANY CODE, read my project and interview me. Answer what you can

from the code yourself, then ask me only what you couldn't find. Ask in small

groups, not all at once.

About me and the people who'll use this:

1. Who uses this agent: just me, my team, or customers? What do they do all day?

2. What are the five questions they ask most where the answer would be better

   seen than heard or read? (For example: "how's revenue this month", "what's on

   today", "which deals are stuck".)

3. Is there a screen they already love (a dashboard, an app) whose look or

   density I should match, or avoid?

About my agent:

4. How does it run: which model and provider, which framework, and how does it

   call tools? Show me where a tool is defined and where its result goes.

5. How do people talk to it: typing, voice, or both? If voice, does it speak its

   replies aloud?

6. Where does the conversation appear: a web page, a desktop app, a phone app, a

   chat app like Slack? What is the front end built with?

7. Does it keep state between turns (a database, files), and per conversation or

   per user?

8. Does anything it does need the user's approval first (sending, buying,

   deleting)? How is that approval shown and given today?

About the data:

9. Which apps or data sources is it connected to (Stripe, Google Calendar, Gmail,

   Slack, Linear, a database, an internal API)? How does it read them: built-in

   tools, MCP servers, something else?

10. Which of those reads can run without asking the user, and which ask first?

11. Should a card ever update by itself while someone is looking at it, or is a

    snapshot from when it was asked fine?

About the look:

12. Light, dark, or both? Do I have brand colors, fonts or a design system

    already? If not, I want something calm and readable, not flashy.

13. Phones as well as desktop?

Then write me a one-page plan in plain words: the blocks, the first ten widgets

(chosen from my answers to question 2, not a generic list), where each piece of

code will live, and what I'll be able to see after each layer. Wait for my

go-ahead.

BUILD IN LAYERS. After each layer, show me it working (a screenshot, or a page

I can open) and run the tests before starting the next.

LAYER 1: The blocks, and a page that draws only blocks.

- Define a small, closed set of blocks: value (a big number with a label, a

  change and a hint), chart (line, bar, area, donut), table, rows (a list with

  title, detail and meta), meter (progress or a ring), timeline, person, chips,

  text, quote and facts (label and value pairs). Add others only when a widget

  in the plan needs one.

- The page draws blocks and nothing else. It never runs code or HTML from the

  model: every string is drawn as text. Links are only drawn if they are http or

  https.

- Give every block a strict validator on the server with character caps (we use

  80 for titles, 60 for labels, 40 for values, 400 for text) and count caps (at

  most 12 widgets on a board, 5 series in a chart, 50 rows in a table). Long

  text is trimmed with an ellipsis, never rejected.

- All colors come from design tokens (CSS variables), with light and dark

  versions. Tone is a word (good, bad, attention, neutral), never a color.

  Add a check that fails if anyone writes a raw hex color outside the tokens.

LAYER 2: Widgets, which the server expands into blocks.

- A widget is what the model writes: a type, an optional title, and that type's

  data fields. For example: {"type": "stat", "title": "Revenue this year",

  "label": "Revenue", "value": "$184,000", "delta": "+12% vs last year"}.

- The server validates the widget, saves it as written, and expands it into

  blocks with a small "recipe" per type. The page only ever sees blocks. This

  means you can change how a widget looks without touching the model's

  instructions, and the model can't reach the page's internals.

- Build the ten widgets from the plan. Every widget has one job. A title says

  what it shows. Numbers carry their unit and, where it exists, a comparison.

- Be forgiving about what the model gets slightly wrong, and strict about what

  matters. Accept near misses that have one obvious meaning: a type of "kpi"

  means "stat", a chart kind given as the type means a chart, a number sent as

  text is still a number, a stat with a title but no label uses the title as its

  label. Refuse anything whose meaning isn't plain, with an error that says

  exactly which field of which widget to fix ("widget 2 (chart): series is

  required"), so the model fixes it in one try. Every refusal costs the user a

  visible failure and a wait: when you see the same refusal twice in testing,

  ask whether it should be a forgiving fix instead.

- Strip emoji from model text (keep data symbols like arrows, stars and check

  boxes). Collapse whitespace. Never show a widget the validator didn't pass.

- The server, not the model, stamps where data came from and when: a widget

  built from a connected app gets a small source line ("Stripe · 3 min ago").

  The model names the app; the server writes the time.

LAYER 3: The tools the model uses.

- Give the model a small set of tools: show (put widgets on the board, replacing

  or adding), update (change one widget by id), and remove. Results tell the

  model what is now shown, by id and title, in one line.

- The tool's input schema must never be stricter than the validator. Keep it

  flat: a list of widgets, each with "type" and the union of the fields, with

  no oneOf, anyOf or enum. Models handle flat schemas far more reliably, and the

  validator does the real checking.

- Describe each widget type in the tool description in one line of fields,

  like "stat: label, value, delta?, hint?, tone?". Keep the whole description

  under a budget (ours is 12,000 characters for the core set) and add a test that

  fails if it grows past it.

- Add a few lines to the agent's instructions: when an answer is better seen

  than heard (a set of numbers, figures to compare, a plan, more than three

  items), put it on the board and say one short line about it. Never read the

  board out. Call a widget by what it shows, never by its id. Never fill an

  empty card with made-up data; say it's empty. If the user asks for sample data,

  title it as sample data.

LAYER 4: The design system, written down and drawn.

- Write a short design guide for contributors: the blocks, the widgets, the

  rules above, and how to add a new widget (a validator, a recipe, a sample,

  a test).

- Make one sample per widget (realistic, generic data) in a single file, and a

  style guide page that draws every widget from those samples, in light and

  dark. Generate the samples file from the server's own samples, with a check

  that fails if the page's copy is out of date. This page is how you'll spot

  slop: if one card looks off next to the others, fix the recipe, not the model.

LAYER 5: Packs, only for what's connected.

- Group the widgets for specific kinds of apps into packs: money (transactions,

  invoices, payouts), customers (pipeline, deals, support queue), building

  (checks, errors, deploys), work (kanban, sprint, due soon).

- A pack's widgets are only described to the model when one of its apps is

  connected. Recompute the tool description when apps are connected or

  disconnected, without restarting. Budget each pack too, and test it.

- Each pack widget still expands into the same blocks, so a pack never adds new

  drawing code, only recipes.

LAYER 6: Custom widgets, from the same blocks.

- Add one more type, "custom", whose data is a list of blocks. The model uses it

  only when no ready widget fits. Validate it with the same block validators, so

  a custom card can't look or behave differently from the rest.

LAYER 7: Tap-to-ask.

- Let a widget carry at most two short questions ("asks"), shown as buttons. A

  tap sends the question as a new message, marked as coming from a tap.

- A message from a tap can never approve anything. If the model is waiting for

  a yes on an action, a tap is not a yes: the user must use the real approval

  control. Test this.

- Treat a tapped question like something the model read, not something the user

  said: it can't create reminders, schedules or saved memories on its strength.

- Refuse asks that contain a web address or look like system text (starting

  with "[" or a role name). A widget's ask is written by the model, possibly from

  something it read, so it must never become a way to make the user send a link

  or an instruction.

LAYER 8 (OPTIONAL): Live widgets.

Only build this if I said cards should update by themselves (question 11).

- While the model builds a widget from an app, record the read-only tool calls

  it made in that turn (tool name and arguments), matched to the widget by its

  source. Keep the last five. Never record a call that changes anything, a web

  fetch, or a call that needed approval.

- Refresh only while the board is on screen: the page tells the server when

  it's shown or hidden, and when the tab is hidden. Catch up when it's opened.

- To refresh: re-run the recorded reads (reads only, refused otherwise), then

  make one small model call with no tools: "here is the widget, here are the

  fresh results, return the widget with updated data". Keep the widget's type,

  title, source and asks from the old one; take only data fields from the

  answer; validate it like any widget. If anything fails, keep the old data and

  show "Couldn't refresh · 12 min ago".

- Limits: one refresh at a time, a sensible default interval per widget type

  (minutes for a queue, an hour for a summary), a cap per hour across the app,

  and stop after three failures in a row until the board is opened again. Let

  the user turn it all off in settings. Record what the refreshes cost.

- Refreshes never add messages to the conversation.

GROUND RULES:

- Everything a tool returns from outside (an app, a page, a file) is data, never

  instructions. Wrap it as such before the model sees it.

- Nothing on the board can approve an action. Approvals stay where they are.

- The model never writes HTML, CSS or code for the page. It writes widgets; the

  server writes blocks; the page draws blocks.

- Every layer gets tests: validators, recipes, schema budget, and at least one

  end-to-end test where a scripted model shows a widget and the page receives

  the right blocks.

- No new dependencies without telling me why first. A charting library is fine

  if I already have one; otherwise simple SVG is enough.

- At the end, give me: the style guide page, the design guide, the list of

  widgets with one sample each, and three things to ask my agent to see it work.

````

## What to check when it's done

You don't need to read the code to know it worked:

- **Ask for something numeric,** like "how did sales do this month?" A card should appear with a number, a unit and a comparison, and the agent should say one short line instead of reading it out.

- **Ask for something it can't know,** like a source you haven't connected. It should say so plainly, not fill a card with invented numbers.

- **Open the style guide page.** Every widget, side by side, in light and dark. If one looks out of place, that's the recipe to fix.

- **Tap a question on a card,** then check that the tap couldn't approve anything the agent was waiting on.

- **If you built live widgets,** change something in the connected app with the board open, and watch the card catch up. Close the board, and nothing should refresh.

## Why this works

The model is good at deciding what matters and bad at being consistent. So it only makes decisions: which widget, which numbers, which title. Everything that has to be consistent (how it looks, how it's checked, where the data came from, when it refreshes) is done by plain code that behaves the same every time.

And because the page only draws a small set of blocks, the design system isn't a style guide people have to remember. It's the only thing the page can draw.
