When an answer needs to be explored, changed, or filled in, turn it into a Widget. A Widget can be a dashboard, form, table, simulation, or any other page that works better than a long chat message.

Install the CLI#
npm install -g @openduo/duoduo-widgets
The hosted service at aidgets.dev is ready to use; you do not need to deploy a backend first.
The lifecycle#
open → update or patch → finalize → wait or inspect
opencreates a draft and returns viewer and control information.updatesends HTML or a targeted DOM patch.finalizefreezes an immutable revision.waitorgetreads structured user input when interaction is enabled.
Send the viewer URL to the user. Keep the control URL and token with the agent.
Patch instead of repainting#
Long-lived dashboards can update one selector at a time:
[
{ "op": "text", "selector": "#status", "text": "Ready" },
{ "op": "append", "selector": "#rows", "html": "<tr><td>New row</td></tr>" }
]
Patch mode avoids sending the whole page for every change and keeps scripts and stable DOM state in place.
Collect structured input#
A widget can submit an action and JSON payload through the window.duoduo bridge. The agent reads the structured result with wait or get, with no need to parse it back out of a chat reply. Widget submission is handled by the Widget service; it is not disguised as a DuoDuo channel message.
Self-host when control matters#
You can run the service on Cloudflare Workers, using Durable Objects for drafts and R2 for finished revisions. The same CLI commands and viewer/control split still apply.


