Orkestia
Blog
Chat

Card catalog

Every structured chat card DGI can return, what it shows and what the person can do with it. Forms, confirms with a diff, tables, charts, KPI tiles, logs, runs, links, compositions, documents, and the DAG, schema, datagrid, query console, diff, detail and timeline views, plus view controls and the query action

A card is one JSON object, the same everywhere: DGI returns it as ui from dgi.chat.turn, and a Buzz actor posts it inside its buzz-ui block (Option C). This page lists every renderer, what it draws, and the answers it sends. Structured chat is part of DGI and is Alpha.

Rules for every card

  • Built in code, never by a model. DGI builds each card from its typed output and from the full result of the read. Chart and KPI numbers come from data.
  • Views are chosen by the shape of the data. Jev picks the view from what the read returned: a record becomes a detail, a long list a datagrid, nodes and edges a dag. An explicit request ("as a chart"), a form, a confirm or a build always wins.
  • Offered actions only. A card sends an action only when its actions list offers it. Otherwise the control shows disabled, or not at all.
  • Opaque refs, never ids. Rows, nodes, tables, related lists, saved queries and timeline items carry a ref such as r1, n2, t3, rel1 or sq1. The client sends it back and never draws it. What it stands for stays on the server.
  • Prompts and views. form, confirm and select are prompts: answered once, within 30 minutes. Every other card is a view: it never expires in the client, and nothing sent from it consumes it.
  • Unknown renderer: show the text. The text that comes with a card always stands on its own.

Where each card is drawn

ClientCards
Hosted chat page (Option A)All of them. The newer views need the current page: run buzz.space.publish-chat (with also_site_slug: true when people use the slug address) after an upgrade
<BuzzChat> (Option B)All of them from @ltinteg/app-component-chat 0.9.0. Earlier versions draw the newer views as text
Your own client (Option C, Option D)What you implement. document is returned by dgi.chat.* only

Prompts

form

Asks for the typed inputs of a workflow. Field types: text, textarea, number, email, url, date, time, select, multiselect, boolean, file. Required fields are marked, and submit stays disabled until they are filled. Long forms become a wizard (pending_input.steps, "Step n of m"), and a form can open in a dialog (surface: "modal"). A file field uploads through the chat's own media host and submits the resulting https URL.

The person canSends
Fill and submitsubmit with the values. Empty fields are left out; booleans are always sent
Cancelcancel

confirm

Shows a proposed change before it runs: a one-line summary, the workflow, and its inputs as plain labels. A destructive action is styled as a warning.

With a diff. Every update-style proposal carries pending_action.diff: the current values (read with the matching get workflow, as the caller, when one exists) next to the proposed ones. The card shows the diff in place of the plain input list. Without a matching get, the diff says the current values were not read. Ids and fields named like a secret never show. The diff is display only: the confirm still runs the stored input and its action_input_hash.

The person canSends
Confirmconfirm. The server runs its own stored copy; values are ignored
Cancelcancel
Edit, when offerededit with the changed inputs. DGI proposes again on a new card, with a new hash

select

Pick one option, drawn as buttons or a dropdown. Sends select with {value, label, prompt?}.

actionlist

A list of items, each with a title, a subtitle and its own buttons. Sends select with the item's index as item.

Views

table

Rows from a read, with a toolbar (Refresh, and Live in the Buzz chat) and up to 4 row actions per row, such as Details, Comment or Assign. On a narrow card each row gets a menu instead of inline buttons.

The person canSends
Refreshrefresh. The card is rewritten in place
Live, Stop (Buzz chat only)live, stop
Press a row actionrow_action with {row, action}. A read shows its view; a write asks with a form or proposes a confirm

chart

A bar, line or pie chart, drawn inline, with a legend and a hidden data table for screen readers. At most 6 series and 60 points. A read with a time column, one to six number columns and 3 to 60 rows becomes a line chart by itself. Bar and pie charts keep the top points (12 by default) and group the rest as "Other". Card actions: refresh, and in the Buzz chat live and stop.

kpi

Up to 8 tiles, each with a value, a unit, a delta and a trend arrow (up, down or flat). A total that hits the read's own limit is labeled as capped, for example "Total (capped at 500)". Card actions as for chart.

logs

Monospace text, scrollable, with a copy button. At most 12,000 characters. Card actions as for chart.

run

A live card for a workflow run DGI started in this conversation, usually right after a confirm: status, current state, steps and times. It updates in place until the run ends. A failure shows a short code, never the error text or the run's data. It accepts no answer.

One button that opens an https address in a new tab, for example to set up a connection in the console. Only addresses on the organization console are posted. It accepts no answer.

composition

The step list of a composition you are editing, with actions per step and for the whole plan. It appears only for replies that run as an organization member with compose on. When the steps carry edges, the plan draws as a diagram, with a switch back to the list. Step and plan actions send select (with item for a step's action).

document

The turn's markdown text, presented as a card, for answers that are a document rather than data. Returned by dgi.chat.*. It accepts no answer.

dag

A workflow diagram: nodes with a status (pending, running, succeeded, failed, skipped), edges with optional labels, laid out left to right or top to bottom. The client supports pan, zoom and fit. At most 200 nodes and 600 edges; the rest is counted. DGI draws it from a run's DAG (with each step's status), a composition plan, or any read that returns nodes and edges.

The person canSends
Pick a node, when offeredselect with {node: "<ref>"}. DGI opens what the node stands for

schema

An entity diagram: each table as a field list with primary and foreign key markers, and each foreign key as an edge. At most 60 tables and 40 fields per table. DGI draws it from reads that return tables with fields, such as data.appdata.structure.query.

The person canSends
Pick a tableselect with {node: "<table ref>"}. A table usually opens its records

datagrid

A grid paged by the server, for results larger than a table shows (more than 50 rows) or reads that page themselves. Columns are typed (text, number, date, bool, json, badge) and each says whether it is sortable and filterable. 50 rows a page, at most 500 rows and 40 columns per page. The client offers copy cell and a CSV export of the current page.

The person canSends
Sort by a sortable columnquery with {sort: {key, dir}}
Filter a filterable columnquery with {filters: {column: text}} ({} clears)
Go to the next or previous pagequery with {cursor}, a cursor the card issued
Open a rowThe row action whose key is open: row_action with {row, action, ref}
Press another row actionrow_action with {row, action, ref}

Sorting, filtering and paging never happen in the client: the server re-runs the stored read and rewrites the card in place. A total that hit a cap reads "N of 1,000+".

query

A read-only SQL console over AppData, for organization members only. The statement editor sits above the result, drawn as a datagrid with its own paging. It always shows a "Read-only" badge; a console that is not read-only is never drawn.

The person canSends
Run the statementsubmit with {statement}, their own text, up to 16,384 characters
Pick a saved queryselect with {saved: "<ref>"}
Sort, filter or page the resultquery, as on a datagrid

DGI never runs SQL a model wrote. When a model or Jev tries to start the query workflow, the console opens with the statement filled in, not run. A statement runs only when the person presses Run or picks a saved query. A refused or failed statement comes back on the card as error.

diff

Before and after, per field: the removed value struck through, the added one highlighted, numbers with their delta, and unchanged fields collapsed. At most 100 fields. It accepts no answer. The same shape appears inside a confirm card as pending_action.diff.

detail

One record: a title, a subtitle, up to 8 badges, up to 20 sections of labeled fields, and up to 20 related lists with a count. DGI draws it for a single record, such as the result of a *.get read.

The person canSends
Open a related listselect with {related: "<ref>"}

timeline

A waterfall: bars on a relative time axis, nested by depth (0 to 8), each with a status. An item still running draws to the end of the window. At most 300 items. DGI draws it from reads that return spans or steps with a start and an end, such as a trace.

The person canSends
Pick an itemselect with {item: "<ref>"}

View controls and the query action

A read card (table, chart, kpi, datagrid, timeline) can carry controls, drawn as a compact bar under the title. DGI adds a control only for an input the read itself declares:

ControlkindAppears forSends
Date rangedate_rangeA since and until pair (or from and to){params: {<key>: {from, to}}}, in order, at most 366 days
Relative rangesegmentedA lone since: 24h, 7d, 30d or 90d, recomputed at every rerun{params: {<key>: "<value>"}}
Segmented or chipssegmented, chipsAn enum input such as status (segmented includes All)A string, or the full list for chips
Group bysegmentedA chart or KPIThe field to group by

At most 6 controls of 12 options each.

Every change sends the query card action. Its rules:

  • It never consumes the card. The server answers by rewriting the same card in place (same ui_id), like a refresh.
  • Everything is checked against the stored card: a sort key must be a sortable column and dir asc or desc; a filter key a filterable column (up to 200 characters); a cursor one this card issued for its current page; a param key one of the card's controls and its value one of that control's options. Anything else is refused (ui_field_invalid, ui_field_unknown or ui_action_not_offered).
  • The read is re-run with only those values. A read that pages itself gets the offset or cursor, and its own sort and filters when it declares them. Any other read is fetched whole and DGI sorts, filters and pages it, 50 rows a page.
  • The card's columns, controls and row actions never move on a rerun.
  • At most one query every 2 seconds per card (ui_rate_limited).

Living Surfaces take the same query action through dgi.surface.tick signals. See Living Surfaces.

Ask your AI assistant

prompts
Using https://docs.orkestia.dev/raw/chat/card-catalog.md, write a React renderer for the datagrid card that sends the query action for sort, filter and paging and never sorts locally.

Which card will DGI return for the output of ticket.get, and what can the person do with it?

List every card that can be answered more than once, and explain why a confirm cannot.

For AI agents

RuleDetail
DrawTreat every value in a card as untrusted text. Never render markdown or HTML inside a card, and open only https links
RefsSend refs back unchanged. Never display them, and never put an id in their place
QueryUse the query action for sort, filter, paging and controls. Never sort or filter a server-paged grid in the client
SQLNever run a statement for the user. Fill the console and let the person press Run
ConfirmsA confirm card runs only when the person presses Confirm. A diff is display only